TikZ – How is the Interior of a Path Determined When Reverse Clipping?

path-clippingtikz-pgf

I'm been playing around with [reverseclip] from How can I invert a 'clip' selection within TikZ?. It works quite well. When you apply [reverseclip] it seems to add another part to your path—a clockwise traversal of the current page. However, I am having trouble understanding how the interior of a multi-part path is determined. An example:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[remember picture,overlay]
% A path that follows the edges of the current page, CW
\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}
   \path [clip] (A) -- (B) -- (C) [reverseclip]; % good, CCW
%  \path [clip] (C) -- (B) -- (A) [reverseclip]; % bad, CW
\end{pgfinterruptboundingbox}
\fill[red!30] (A) circle (3cm);
%\fill[red!30,even odd rule] (A) circle (3cm); % does not make bad good
\end{tikzpicture}
\end{document}

So what happens is that I only get the desired reverse clipping if the orientation of my path is counter-clockwise. When I make the orientation clockwise, everything is undesirably filled.

In the documentation, Section 15.4.2 Graphic Parameters: Interior Rules, I learned about the nonzero and even odd rules. I rationalized this behavior by imagining the circle drawn in a CW fashion. Then, the first clip path is CCW and everything should work. With the nonzero rule, it made sense why a CW clip path would not clip anything. But then, I tried specifying the even odd rule, expecting that this would make the result independent of the orientation of the clip path. My expectations were wrong.

It seems that the interior rules are not applying in the way I understood they would.

  1. When (reverse) clipping, how is the interior region determined?
  2. How can I reverse a path saved via \pgfsyssoftpath@getcurrentpath{\mypath}?
  3. Besides defining [reverseclipCW] and [reverseclipCCW], is there a way to make the reverseclip concept more robust to the orientation of paths?
  4. What is the orientation of basic shapes (circle, rectangle, etc)? Clockwise?

Best Answer

1- How to use nonzero rule and even odd rule with paths

The current rule (nonzero rule or even odd rule) is applied operation by operation and path by path.

In the following picture:

  • on the left column, all circles (inner and outer) are CCW (CCW=counter clockwise),
  • on the right column, outer circles are CCW and inner circles are CW (CW=clockwise),
  • on the first line, two paths are used to draw each pair of circles,
  • on the second line and on the third line, a single path is used to draw each pair of circles,
  • on the first line and on the second lines, filling uses the nonzero rule (implicitly).
  • on the third line, filling uses the even odd rule.

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[every node/.style={align=flush left,font=\bfseries}]
  \node at (0,3){Outer circle=CCW\\Inner circle=CCW};
  \node at (4.5,3){Outer circle=CCW\\Inner circle=CW};
  \begin{scope}[yshift=0mm,
    every path/.style={draw=black,fill=cyan}]

    \path (0,0) ++(0:2cm) arc (0:360:2cm);
    \path (0,0) ++(0:1.8cm) arc (0:360:1.4cm);

    \path (4.5,0) ++(0:2cm) arc (0:360:2cm);
    \path (4.5,0) ++(0:1.8cm) arc (360:0:1.4cm);

    \node[right] at (7,0) {Each circle=one path};
  \end{scope}
  \begin{scope}[yshift=-4.5cm,
    every path/.style={draw=black,fill=orange}]

    \path (0,0) ++(0:2cm) arc (0:360:2cm)
          (0,0) ++(0:1.8cm) arc (0:360:1.4cm);

    \path (4.5,0) ++(0:2cm) arc (0:360:2cm)
          (4.5,0) ++(0:1.8cm) arc (360:0:1.4cm);

    \node[right] at (7,0)
    {Outer and inner circles=single path\\nonzero rule};
  \end{scope}
  \begin{scope}[yshift=-9cm,
    even odd rule,every path/.style={draw=black,fill=lime}]

    \path (0,0) ++(0:2cm) arc (0:360:2cm)
          (0,0) ++(0:1.8cm) arc (0:360:1.4cm);


    \path (4.5,0) ++(0:2cm) arc (0:360:2cm)
          (4.5,0) ++(0:1.8cm) arc (360:0:1.4cm);

    \node[right] at (7,0)
    {Outer and inner circles=single path\\even odd rule};
  \end{scope}

\end{tikzpicture}
\end{document}

2- How to use nonzero rule and even odd rule with clipping paths

The even odd rule can't be applied directly to a clipping path (it's a bug).

In the following example, each line shows a filled rectangle. Each rectangle is clipped by four circles in a single path. The first line uses nonzero rule (implicitly), the second line uses a scope to apply even odd rule to the clipping path, and the third line uses the new option clip even odd rule suggested by Andrew Stacey:

\tikzset{clip even odd rule/.code={\pgfseteorule}} % Credit to Andrew Stacey

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\tikzset{clip even odd rule/.code={\pgfseteorule}} % Credit to Andrew Stacey 
\begin{document}
\begin{tikzpicture}[every node/.style={align=flush left,font=\bfseries}]
  \node at (2.25,3) {All circles in a single clipping path};
  \node at (0,2){Outer circle=CCW\\Inner circle=CCW};
  \node at (4.5,2){Outer circle=CCW\\Inner circle=CW};
  \begin{scope}[yshift=0mm]
    \node[right] at (7,0) {clipping path with nonzero rule};

    \clip
    (0,0) circle (2cm)
    (0,0) ++(0:1.8cm) arc (0:360:1.4cm)
    (4.5,0) circle (2cm)
    (4.5,0) ++(0:1.8cm) arc (360:0:1.4cm);

    \draw[fill=red] (-2,-1) rectangle (7,1);
  \end{scope}

  \begin{scope}[yshift=-4.5cm,even odd rule]
    \node[right] at (7,0) {clipping path with even odd rule\\(via scope)};

    \clip
    (0,0) circle(2cm)
    (0,0) ++(0:1.8cm) arc (0:360:1.4cm)
    (4.5,0) circle(2cm)
    (4.5,0) ++(0:1.8cm) arc (360:0:1.4cm);

    \draw[fill=orange] (-2,-1) rectangle (7,1);
  \end{scope}

  \begin{scope}[yshift=-9cm]
    \node[right] at (7,0)
    {clipping path with even odd rule\\(via new \texttt{clip even odd rule} option)};

    \clip[clip even odd rule]
    (0,0) circle(2cm)
    (0,0) ++(0:1.8cm) arc (0:360:1.4cm)
    (4.5,0) circle(2cm)
    (4.5,0) ++(0:1.8cm) arc (360:0:1.4cm);

    \draw[fill=lime] (-2,-1) rectangle (7,1);
  \end{scope}
\end{tikzpicture}
\end{document}

Note on CCW and CW:

In a path:

  • A circle (or ellipse) operation is always CCW.
  • A rectangle operation can be CCW or CW depending on corners provided:

    (a) rectangle (b) is equivalent to (a) -| (b) |- (a).

Related Question