13

I've searched, but I can't find a previous question sufficiently close to this one to find an answer. At the same time, this problem seems like it must have been solved by someone before now, so apologies if it's a duplicate.

I want to clip a path (particularly a circle, but a general solution would be useful too) using a node with a shape. Here's an example:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{through}
\begin{document}

\begin{tikzpicture}
\node [draw,circle,fill=white,minimum size=1mm] (u) at (0,0) {};
\node [draw,circle through=(u)] at (1,0) {};
\end{tikzpicture}

\end{document}

Two circles MWE

I'd like to have the red circle clipped by the black, but still have at least the black circle remain a node. I also want to specify the black node first if at all possible.

I'm guessing that something like a postaction might be what I need, but I haven't been able to make enough sense of those yet to work it out.

Edit: clarification and something a bit strange

What I want is something looking like this:

enter image description here

Which can obviously be got with code like:

\begin{tikzpicture}
\node [circle,minimum size=5mm] (u) at (0,0) {};
\node [draw, circle through=(u),red] at (1,0) {};
\node [draw,circle,fill=white,minimum size=5mm] at (0,0) {};
\end{tikzpicture}

But that necessitates the first node being specified twice.

The use case for this is a load of graphs I've already done, using a vertex style for all the vertex nodes and ideally I want to just edit this style to do the reverse clipping.

Finally, there's something very strange about the technique used at this answer

This code works as expected

\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)}
]

\node [circle,minimum size=8mm] (u) at (0,0) {};

\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,draw] (A) -- (B) -- (C) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}

\draw [fill=red] (1,0) circle  (1cm);

\end{tikzpicture}

but this doesn't

\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)}
]

\node [circle,minimum size=8mm] (u) at (0,0) {};

\coordinate (A) at (u.east);
\coordinate (B) at (u.south west);
\coordinate (C) at (u.north west);

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

\draw [fill=red] (1,0) circle  (1cm);

\end{tikzpicture}

which makes absolutely no sense to me. The first reverse-clips as expected, the second just draws the filled circle on top as if no clipping is happening at all.

4
  • Does How can I invert a 'clip' selection within TikZ? provide any assistance?
    – Werner
    Mar 2, 2012 at 18:40
  • I haven't tried it yet, but I suspect the technique I used in this answer would work. It's not something you can do in the middle of a tikzpicture, though, so it may not be as flexible as you'd want.
    – Ben Lerner
    Mar 2, 2012 at 18:47
  • @Werner possibly, but I'm having trouble seeing how to use that in my case. I'm not claiming it doesn't work, just that I'm not tikz-savvy enough to understand how! Mar 2, 2012 at 18:49
  • @JosephCooper: You can use \clip (u.west) -- (u.north) -- (u.east) -- (u.south)--cycle; Mar 2, 2012 at 19:02

5 Answers 5

8

Try drawing the small circle after the large one, so its fill will cover the arc inside of it. Basically, you should place a \coordinate at the location you want its center to be, and then draw both circles relative to that point.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{through}
\begin{document}

\begin{tikzpicture}
\coordinate (u) at (0,0);
\node [draw,circle through=(u)] at (1,0) {};
\node [draw,circle,fill=white,minimum size=1mm] at (u) {};
\end{tikzpicture}

\end{document}

enter image description here

3
  • I knew I could do this, but I was hoping to avoid the 'long' way if I could work out some way of automating it in some sense. Still, thankyou for a solution to the problem. Mar 2, 2012 at 19:55
  • @JosephCooper: I don't think it's the "long" way, actually. It's just a different way of thinking about the structure of the picture. I've found it's often useful in TikZ to start with some setup commands that just define coordinates, and then place various objects at them. Also: in comparison with the code you just added to the question, this does not draw the circle twice. You don't need all of the small circle to draw the large circle through its center; you just need the center. There is no repetition in my answer.
    – Ryan Reich
    Mar 2, 2012 at 19:58
  • 1
    No, I understand, and that wasn't supposed to be a criticism. It's just that I've got a lot of these things to change, and I deliberately defined a style for all the vertices with the hope of planning ahead. I definitely think that this is a valid way of doing things, and defining coordinates at the start is probably the way I'll do things in the future. It's just the "long way" for changing things already done! Mar 2, 2012 at 20:06
8

With reverseclip ! (werner's suggestion and and the great answer from Jake)

enter image description here

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,through}   

\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)}
]
\node [draw,circle,fill=white,minimum size=6mm,inner sep=0pt] (u)  at (0,0) {};

\begin{pgfinterruptboundingbox} % To make sure our clipping path does not mess up the placement of the picture
\path [clip]  let  \p1 = ($ (u.center) - (u.east) $) in   circle ({veclen(\x1,\y1)})-- cycle [reverseclip];
\end{pgfinterruptboundingbox}

\node [draw,circle through=(u)] at (1,0) {};  
\end{tikzpicture}
\end{document}   

update With reverseclip from Jake and append after command from percusse

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,through}   

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

\tikzstyle{reverseclip}=[insert path={(current page.north east) --
  (current page.south east) --
  (current page.south west) --
  (current page.north west) --
  (current page.north east)}
]
\path[clip,draw]  node [draw,circle,fill=white,minimum size=6mm,inner sep=0pt,
   append after command={%
   let  \p1 = ($ (u.center) - (u.east) $) in circle ({veclen(\x1,\y1)})-- cycle   
               [reverseclip]}] (u)  at (0,0) {};
\node [red,draw,circle through=(u)] at (1,0) {};  
\end{tikzpicture}
\end{document}

enter image description here

2
  • No, the 'inverse' of that, where part of the larger circle isn't drawn where it is inside the smaller one. Mar 2, 2012 at 19:31
  • Ok You need only to add inverseclip Mar 2, 2012 at 20:44
7

This isn't really a proper answer, more a comment, but I have no reputation and therefore can't leave comments yet. Also, I realise this is an old question, but this might help someone avoid the confusion that I've just experienced!

In response to the "there's something very strange about the technique used at this answer" remark: I came across a similar phenomenon, and became increasingly confused over a couple of hours until I eventually twigged. When you specify two concentric loops for \path[clip], it either restricts (clips) to the area between them, or ignores the inner one and restricts to the whole area enclosed by the outer one. It does the former if the orientations of the two loops are different, and the latter if they are the same.

I think this is reversed by the even odd rule: The example here uses the even odd rule to clip between two circles, and I assume that it's necessary in this case because the two loops are both created using circle, and therefore have the same orientation (or are not assigned one; I don't know exactly what goes on in the background).

2
  • Welcome to TeX.SX! Certainly, too long for a comment, and actually a partial answer, so +1.
    – mafp
    Jul 3, 2013 at 14:56
  • +1 for your second link that shows an usage of even odd rule and clip! Jan 24, 2018 at 17:32
5

In the manual there is this nice option append after command. It says that it's for experts so don't tell anyone that I used it :) Here's some more black magic:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\draw[thick,red] node [
        draw=black,
        circle,
        fill=white,
        minimum size=1cm,
        append after command={
            let \p1 = (u), \p2 = (2,0), \p3 = ($ (\p2) - (\p1) $),\n1 = {veclen(\x3,\y3)} 
            in (\p2) circle (\n1)
            }
] (u) at (0,0) {};
\end{tikzpicture}
\end{document}

enter image description here

I have made the shapes slightly bigger to make it more visible. I didn't actually go for making the red circle a node itself just because you know where its center would be so you can add it easily.

2
  • If I understand correctly, you're doing the circle through manually in the append after. Is that correct? Mar 2, 2012 at 20:03
  • @JosephCooper Yes, kind of. append after lets you to use the node name before it is drawn but after its coordinates are known. So I am calculating the radius using that information. The rest is pretty straightforward.
    – percusse
    Mar 2, 2012 at 20:07
5

Here is a very simple solution via backgrounds TikZ library:

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{through,backgrounds}

\begin{document}
\begin{tikzpicture}
  \node [draw,circle,fill=white,minimum size=1mm] (u) at (0,0) {};
  \begin{scope}[on background layer]
    \node [draw=red,circle through=(u)] at (1,0) {};
  \end{scope}
\end{tikzpicture}
\end{document}

You must log in to answer this question.

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