67

I wish to draw an arc for a reflex angle on the outside of a triangle with coordinates (A), (B) and (C).

If I wanted to draw the arc inside the triangle I could use something like

\begin{scope}
\path[clip] (A) -- (B) -- (C) -- cycle;
\draw (B) circle (2mm);    % this is the little angle marker
\end{scope}

Is there a straightforward way to exclude the part of the circle that's in the triangle, as in the pseudo-code below?

\begin{scope}
\path[magicalinverseclipcommand] (A) -- (B) -- (C) -- cycle;
\draw (B) circle (2mm);    % this is the little angle marker
\end{scope}

EDIT: Although I included the code above as a motivating example of what an inverse clip would do, I would prefer a general solution that works systematically with any closed path, and not just in the particular example of a triangle with a circle clipped out of it. Looking at my question, I realise this isn't clear, and I will be happy with the best solution to my particular problem, but wonder if there is a less ad hoc solution than those listed so far.

8
  • 2
    Good question! I was trying to do something like this to construct an answer to tex.stackexchange.com/q/3528/86 (a question about cutting out a circle from an ellipse) but haven't found anything yet. Feb 25, 2011 at 8:55
  • 1
    Thanks! I'm having to do messy things like creating new closed paths to clip against, but I'd prefer to have a systematic solution so I can use it in \newcommands easily.
    – bryn
    Feb 25, 2011 at 9:03
  • Maybe you could edit your question to explain in a little more detail what you are looking for that is not provided by the answers already given? The bounty you set out is likely to attract people to the question, but when they see three posts that seem to answer your question, they are less likely to provide answers of their own.
    – Jake
    Mar 5, 2011 at 12:57
  • 1
    Bryn, I still don't see what isn't answered by Jake's answer. His method provides a way to invert an arbitrary clip, so anything you previously wanted to clip against can be inverted. Do you have an example where it fails? Or a more complicated example where you can't see how to implement it? Mar 9, 2011 at 9:41
  • 1
    @Jake: Thanks -- I think this looks like a good solution now, and hopefully will be stored for posterity (until Till Tantau implements it natively maybe!).
    – bryn
    Mar 12, 2011 at 3:21

8 Answers 8

59
+50

What you can do is add a rectangle to your clipping path that's larger than the current bounding box, and clip with that. Andrew Stacey suggested using the current page as the clipping rectangle, because that will catch all elements that follow. By using the pgfinterruptboundingbox environment when defining the clipping rectangle, the actual size of the tikzpicture will not be influenced.

Note that, in order to use the current page, the remember picture,overlay options need to be passed to the tikzpicture, and you need two compile runs to get the positioning of all the elements right. Furthermore, this doesn't work with the minimal documentclass.

\documentclass{article} % Has to be a proper class, not minimal
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}[remember picture,overlay]

% A path that follows the edges of the current page
\tikzstyle{reverseclip}=[insert path={(current page.north east) --
  (current page.south east) --
  (current page.south west) --
  (current page.north west) --
  (current page.north east)}
]

\coordinate (A) at (0,0);
\coordinate (B) at (1,0);
\coordinate (C) at (1,1);

\begin{pgfinterruptboundingbox} % To make sure our clipping path does not mess up the placement of the picture
\path [clip] (A) -- (B) -- (C) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}

\draw[thick] (A) circle (2mm);
\draw[thick] (B) circle (2mm);    
\draw[thick] (C) circle (2mm);   

\end{tikzpicture}
\end{document}


And just to show that it works for the general case:

\documentclass{article}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}[remember picture,overlay]

% A path that follows the edges of the current page
\tikzstyle{reverseclip}=[insert path={(current page.north east) --
  (current page.south east) --
  (current page.south west) --
  (current page.north west) --
  (current page.north east)}
]

\draw [step=0.1,red] (0,0) grid  (2,2);

\begin{pgfinterruptboundingbox} % To make sure our clipping path does not mess up the placement of the picture
\path [clip,rounded corners] (0,0) -- (.75,0) -- (1.2,.8) -- (2,1) -- (1.4,1) -- (1.2,2) -- (.3,.75) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}

\draw [step=0.1,thick] (0,0) grid  (2,2);

\end{tikzpicture}
\end{document}

general path clipped

21
  • If you're doing it in one action (as you are here, unlike my answer) why not use the page itself as the "infinitely large" box? Although 1m seems pretty big, I have some pictures that are on canvas bigger than that! Or maybe it's a big picture scaled down a lot. Would that work? (PS I like the answer - much better than mine) Feb 25, 2011 at 13:13
  • @Andrew: I tried using the current page originally, but somehow couldn't get it to work and so settled for the pretty big rectangle. But you're right, there's always going to be something that's a bit bigger than that. So following your comment, I revisited my answer and found out that the current page node does not work with the minimal class, but otherwise it's fine. I've added the approach to my answer.
    – Jake
    Feb 25, 2011 at 13:36
  • Great! I'd vote again but I'm not allowed. Now I'm wondering whether the choice of "even odd rule" or the other one leads to strange behaviour ... but I can experiment with that myself! Feb 25, 2011 at 14:01
  • Oh, and you have 10 minutes to adapt this to an answer to tex.stackexchange.com/q/3528/86 before I steal your idea and do so myself. Feb 25, 2011 at 14:01
  • @Andrew: I think the OP of the question you linked to wants the resulting shape to have an outline, and I don't see how that can be achieved. I've bookmarked the question, however, and am looking forward to seeing your solution!
    – Jake
    Feb 25, 2011 at 14:17
34
+500

To avoid remember picture and overlay, I mix Jack's solution and Altermundus's solution using the bigger rectangle that TikZ/PGF (TeX?) can used (Edit: as suggested by Qrrbrbirlbel, I add [reset cm] to get a solution independent from any scale transformations).

First tikzpicture shows two (inv)clipping triangles.

Second tikzpicture shows the effect of nonzero rule (even odd rule can't be used directly in a clipping path, see note below).

\documentclass{standalone}
\usepackage{tikz}
\begin{document}

\tikzset{invclip/.style={clip,insert path={{[reset cm]
      (-16383.99999pt,-16383.99999pt) rectangle (16383.99999pt,16383.99999pt)
    }}}}

\begin{tikzpicture}[outer sep=0mm]
  \coordinate (A) at (0,0);
  \coordinate (B) at (1,0);
  \coordinate (C) at (.5,1);
  \coordinate (Ap) at (0,1);
  \coordinate (Bp) at (1,1);
  \coordinate (Cp) at (.5,0);
  \begin{scope}
  \begin{pgfinterruptboundingbox} % useful to avoid the rectangle in the bounding box
    \path[invclip]
    (A) -- (B) -- (C) -- (A)
    (Ap) -- (Cp) -- (Bp) -- (Ap);
  \end{pgfinterruptboundingbox} 

  \fill[orange!50] (-1,-1) rectangle (2,2);

  \draw (A) circle (2mm);    % this is the little angle marker
  \draw (B) circle (2mm);    % this is the little angle marker
  \draw (C) circle (2mm);    % this is the little angle marker

  \draw (Ap) circle (2mm);    % this is the little angle marker
  \draw (Bp) circle (2mm);    % this is the little angle marker
  \draw (Cp) circle (2mm);    % this is the little angle marker

  \end{scope}

  \draw[red] (current bounding box.south west)
  rectangle (current bounding box.north east);
\end{tikzpicture}

\begin{tikzpicture}[outer sep=0mm]
  \coordinate (A) at (0,0);
  \coordinate (B) at (1,0);
  \coordinate (C) at (.5,1);
  \coordinate (Ap) at (0,1);
  \coordinate (Bp) at (1,1);
  \coordinate (Cp) at (.5,0);
  \begin{scope}
  \begin{pgfinterruptboundingbox} % useful to avoid the rectangle in the bounding box
    \path[invclip]
    (A) -- (B) -- (C) -- (A)
    (Ap) -- (Bp) -- (Cp) -- (Ap);
  \end{pgfinterruptboundingbox} 

  \fill[orange!50] (-1,-1) rectangle (2,2);

  \draw (A) circle (2mm);    % this is the little angle marker
  \draw (B) circle (2mm);    % this is the little angle marker
  \draw (C) circle (2mm);    % this is the little angle marker

  \draw (Ap) circle (2mm);    % this is the little angle marker
  \draw (Bp) circle (2mm);    % this is the little angle marker
  \draw (Cp) circle (2mm);    % this is the little angle marker

  \end{scope}

  \draw[red] (current bounding box.south west)
  rectangle (current bounding box.north east);
\end{tikzpicture}
\end{document}

enter image description here

Note about rules and clip:

It's impossible to combine clip and even odd rule in a path (it seems to me that it's almost a bug). But, if you add the even odd rule option to the enclosing scope, the clip operation uses it. Applied on the previous example, the clipping paths can use any direction of rotation:

\documentclass{standalone}
\usepackage{tikz}
\tikzset{invclip/.style={clip,insert path={{[reset cm]
        (-16383.99999pt,-16383.99999pt) rectangle (16383.99999pt,16383.99999pt)}}}}
\begin{document}
\begin{tikzpicture}[outer sep=0mm]


  \coordinate (A) at (0,0);
  \coordinate (B) at (1,0);
  \coordinate (C) at (.5,1);
  \coordinate (Ap) at (0,1);
  \coordinate (Bp) at (1,1);
  \coordinate (Cp) at (.5,0);
  \begin{scope}[even odd rule]

    \begin{pgfinterruptboundingbox} % useful to avoid the rectangle in the bounding box
      \path[invclip]
      (A) -- (B) -- (C) -- (A)
      (Ap) -- (Bp) -- (Cp) -- (Ap);
    \end{pgfinterruptboundingbox} 

    \fill[orange!50] (-1,-1) rectangle (2,2);

    \draw (A) circle (2mm);    % this is the little angle marker
    \draw (B) circle (2mm);    % this is the little angle marker
    \draw (C) circle (2mm);    % this is the little angle marker

    \draw (Ap) circle (2mm);    % this is the little angle marker
    \draw (Bp) circle (2mm);    % this is the little angle marker
    \draw (Cp) circle (2mm);    % this is the little angle marker

  \end{scope}

  \draw[red] (current bounding box.south west)
  rectangle (current bounding box.north east);
\end{tikzpicture}
9
  • 2
    Using insert path={{[reset cm] (-16383.99999pt,-16383.99999pt) rectangle (16383.99999pt,16383.99999pt)}} makes this solution independent from any [x|y] scale options. Jun 18, 2013 at 21:55
  • @Qrrbrbirlbel This is a very good suggestion! Jun 19, 2013 at 22:28
  • I really like this answer, but I don't fully understand how it works. In the PGF manual it says the following about pgfinterruptboundingbox: "This environment temporarily interrupts the computation of the bounding box and sets up a new bounding box. At the beginning of the environment the old bounding box is saved and an empty bounding box is installed. After the environment the original bounding box is reinstalled as if nothing has happened." What does this last part mean? It cannot be that it completely undoes whatever it is that you did within the pgfinterruptboundingbox, right?
    – gablin
    Oct 10, 2014 at 11:53
  • 1
    @gablin It means that it does not affect the bounding box of the overall resulting picture i.e. doesn't make it bigger (or smaller).
    – cfr
    Jan 10, 2016 at 22:42
  • 1
    @gablin You may read the description of the nonzero rule in pgfmanual (p.172, v3.0.1a). The direction of rotation of the paths is fundamental. Jan 12, 2018 at 14:21
14

@Jack send me here from a question regarding invclip: Reverse clipping in a 3D figure - Projection issue.

That question shows a problem of @PaulGaborit's answer that double braces in

\tikzset{invclip/.style={clip,insert path={{[reset cm]
      (-16383.99999pt,-16383.99999pt) rectangle (16383.99999pt,16383.99999pt)
    }}}}

does not limit the scope of reset cm. In fact, it took four in the case in the link above. Therefore, in the link above, I suggested the following code

\tikzset{
    clip even odd rule/.code={\pgfseteorule}, % Credit to Andrew Stacey 
    invclip/.style={
        clip,insert path=
            [clip even odd rule]{
                [reset cm](-\maxdimen,-\maxdimen)rectangle(\maxdimen,\maxdimen)
            }
    }
}

A complete example goes like this

\documentclass[border=9,tikz]{standalone}
\usetikzlibrary{graphs,graphs.standard}
\begin{document}

\tikzset{
    clip even odd rule/.code={\pgfseteorule}, % Credit to Andrew Stacey 
    invclip/.style={
        clip,insert path=
            [clip even odd rule]{
                [reset cm](-\maxdimen,-\maxdimen)rectangle(\maxdimen,\maxdimen)
            }
    }
}

\tikz{
    \begin{pgfinterruptboundingbox}
        \clip[invclip](1,0)--(0,1)--(0,-1)--(-1,0)--cycle;
    \end{pgfinterruptboundingbox}
    \graph{subgraph K_n[n=20,clockwise,radius=50]};
}

\end{document}
1
  • 1
    @percusse Learning something everyday
    – Symbol 1
    Feb 1, 2016 at 10:23
8

The solution to draw an arc outside the triangle is to use my new package tkz-euclide and with this way, you don' need to invert a clip selection

\documentclass{minimal}
\usepackage{tkz-euclide} 
\usetkzobj{all}
\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}

\coordinate (A) at (0,0);
\coordinate (B) at (2,0);
\coordinate (C) at (2,2);

\draw (A) -- (B) -- (C) -- cycle;
\draw (B) circle (2mm);    % this is the little angle marker
\tkzDrawArc[R with nodes,color=red](B,1cm)(A,C)  
\tkzDrawArc[R with nodes,color=blue](B,1cm)(C,A) 
\end{tikzpicture}
\end{document}

arcs

Now if you want, I can extract the code from my package. You need to use an internal macro of pgf named \pgfmathanglebetweenpoints; in my package I use \tkzmathanglebetweenpoints because I want a good precision so I work with fp and not with pgfmath from some parts of the code. Then the problem it's to use angles with + or - because I want to draw from (B,A) towards (B,C) or from (B,C) towards (B,A). If you want all the details, I give you in some hours because now, I need to work with my students

I complete my answer without tkz-euclide

\documentclass{minimal}
\usepackage{tikz} 
\usetikzlibrary{calc}

\makeatletter
\def\drawarc{\pgfutil@ifnextchar[{\draw@arc}{\draw@arc[]}}  
\def\draw@arc[#1](#2,#3)(#4,#5){% 
 \begingroup
 \pgfmathanglebetweenpoints{\pgfpointanchor{#2}{center}}{%
                            \pgfpointanchor{#4}{center}} 
\global\let\FirstAngle\pgfmathresult 
 \pgfmathanglebetweenpoints{\pgfpointanchor{#2}{center}}{%
                            \pgfpointanchor{#5}{center}} 
\global\let\SecondAngle\pgfmathresult        
  \pgfmathgreaterthan{\FirstAngle}{0}   
  \ifdim\pgfmathresult pt=1 pt\relax%  
    \pgfmathgreaterthan{\FirstAngle}{\SecondAngle}
    \ifdim\pgfmathresult pt=1 pt\relax%
     \pgfmathsubtract{\FirstAngle}{360}
     \edef\FirstAngle{\pgfmathresult}%
 \fi 
 \else
     \pgfmathgreaterthan{\FirstAngle}{\SecondAngle}
    \ifdim\pgfmathresult pt=1 pt\relax%
        \pgfmathadd{\SecondAngle}{360}
        \edef\SecondAngle{\pgfmathresult}%
     \fi 
 \fi
     \draw[#1,shift = {(#2)}](\FirstAngle:#3) arc (\FirstAngle:\SecondAngle:#3);
\endgroup  
}  
\makeatother

\begin{document}        
\begin{tikzpicture}

\coordinate (A) at (2,2);
\coordinate (B) at (1,0);
\coordinate (C) at (3,1);    
\draw (A) -- (B) -- (C) -- cycle;
\drawarc[red](B,1cm)(A,C)
\drawarc[blue](B,1cm)(C,A)     
\end{tikzpicture}  
\end{document}

enter image description here

1
  • It's not the answer at "How to invert a clip selection" but at the subquestion " how to draw an arc outside a triangle" and you don't need to use an invert clip. The Jake'answer is very interesting. Mar 4, 2011 at 8:29
6

To get the ball rolling on this one, here's a method that uses fadings. What I don't like about it is that to make this work, one has to specify a large rectangle in the fading that one hopes (!) is large enough - I don't know how to automate this. The issue is that when specifying a fading, everything outside the fading is assumed to be transparent, whereas for this one wants everything outside to be visible.

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{fadings}
\begin{document}

\begin{tikzfadingfrompicture}[name=fadeit]
\fill[white] (-10,-10) rectangle (10,10);
\path (0,0) coordinate (A) +(0:2) coordinate (B) +(50:2) coordinate (C);
\fill[black] (B) -- (A) -- (C) -- cycle;
\end{tikzfadingfrompicture}

\begin{tikzpicture}
\draw[path fading=fadeit,fit fading=false] (0,0) circle (1);
\end{tikzpicture}
\end{document}

It's also not so great because one has to split the definition of the cutout from the picture (I wonder if it's possible to fix this using remember picture ...).

reverse clipped picture

6

this all can be done very easily using the even odd rule, see http://www.texample.net/tikz/examples/venn-diagram/

1
  • 6
    Welcome to TeX.sx! Having thought about this quite a bit when it was first posted, I'd really want to see an example before I was convinced that this could be done easily using the even-odd rule. The assumption with a clip is that the outside will be thrown away and that's true for any clipping rule as far as I'm aware, and certainly is true of the venn diagram example. Nov 12, 2011 at 21:35
4

I search how to avoid the use of remember picture with the Jake's solution. I find this :

\documentclass{article}
\usepackage{tikz}

\begin{document}
\fbox{\begin{tikzpicture}[invclip/.style={insert path={(-1,-1) rectangle (2,2)}}]
 % a rectangle is necessary

\path coordinate (A) at (0,0)
      coordinate (B) at (1,0)
      coordinate (C) at (1,1);

\begin{pgfinterruptboundingbox} % useful to avoid the rectangle in the bounding box
\path[clip] (A) -- (B) -- (C) -- cycle [invclip];
\end{pgfinterruptboundingbox}  

\draw[thick] (A) circle (2mm)
             (B) circle (2mm)
             (C) circle (2mm);
\end{tikzpicture}}
\end{document}

( I added fbox to verify the bounding box) enter image description here

6
  • I had this in the first version of my solution (it's still in the edit history), but I think the general consensus was that the drawback of having to decide on a rectangle size (how do you know if it's big enough?) is serious enough to warrant the use of remember picture
    – Jake
    Mar 29, 2011 at 16:48
  • @Jake No problem you can write : invclip/.style={insert path={(-10,-10) rectangle (20,20)}}or bigger if necessary. Another possibility, is to use the `fit' library. Mar 29, 2011 at 17:12
  • See Andrew Stacey's first comment on my answer regarding the size of the box (even 1 metre might not be big enough for some things). I'd be interested to see a solution with the fit library, however!
    – Jake
    Mar 29, 2011 at 17:16
  • @Jake The fit idea does not work. It was to get the rectangle size but I can't place the rectangle correctly. I think the rectangle size is not a good argument. When you draw something, generally you have an idea of the final size. Why do you need overlay in your solution? Without it the bounding box is correct within no. Another suggestion is insert path={ (current page.south west) rectangle (current page.north east)}. Mar 29, 2011 at 21:38
  • Sorry, my previous (now deleted) comment was not correct: You need the remember picture, overlay to make sure the current page node is positioned correctly. In reply to your previous comment: I agree, in the vast majority of cases specifying a large clipping rectangle will work just fine, but I wanted a solution that will work for the general case. Using the current page node ensures that the clipping rectangle is always large enough and you don't need to make adjustments to the clipping path for different pictures.
    – Jake
    Mar 29, 2011 at 21:55
0

As I needed a solution were I could reuse an existing path (which does not work in the otherwise best answer from @Symbol1), I’ve explored other related questions and found another solution from marmotton, also building on top of @Symbol1 and @paul-gaborit answers.

So basically it boils down to:

\documentclass[tikz]{standalone}

\makeatletter
\tikzset{
    reuse path/.code={\pgfsyssoftpath@setcurrentpath{#1}},
    even odd clip/.code={\pgfseteorule},
    reverseclip/.code={\clip[overlay,even odd clip,reuse path=#1] [reset cm] (-\maxdimen,-\maxdimen) rectangle (\maxdimen,\maxdimen);}
}
\makeatother

\begin{document}
\begin{tikzpicture}
    \coordinate (A) at (0,0);
    \coordinate (B) at (1,0);
    \coordinate (C) at (1,1);

    \draw [save path=\rectclip] (A) -- (B) -- (C) -- cycle;
    \begin{pgfinterruptboundingbox}
    \tikzset{reverseclip=\rectclip}
    \end{pgfinterruptboundingbox}
    \draw (A) circle (2mm)
          (B) circle (2mm)
          (C) circle (2mm);
\end{tikzpicture}
\end{document}

ResultResult without pgfinterruptboundingbox

The use of overlay makes pgfinterruptboundingbox generally not necessary (in my actual use case I did not need it), but here if you don’t add that the figure is cut to the triangle (figure on the right).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .