# Path clipping in 3D only works in two out of three planes?

In the following code, I'm trying to draw three clipped circles in three orthogonal planes. I'm using this approach to clip the appropriate sections of the circles. This works nicely in the xy and yz planes, but not in the xz plane, as shown in the output.

Based on the output, it seems I have made a simple mistake, but I just can't find it. I've tried checking the coordinates in the scope for the xz plane (red circle), and they appear to be correct. I've attached three figures which show the clipping paths for each scope. (The drawn clipping paths show some minor inaccuracies, but these are irrelevant to the problem.) Any help finding the error would be much appreciated.

Some mathematical properties which might make the code clearer:

• The center of the box is the point (1,1,1)
• The drawn axes meet at (1,1,1), not the origin, so the figure is essentially shifted [1,1,1]
• All the circles are concentric with center point (1,1,1)
• The radius of the circles is 2

\documentclass[11pt]{standalone}
\usepackage[utf8]{inputenc}
\usepackage{tikz,tikz-3dplot}
\usetikzlibrary{arrows}

\begin{document}

\definecolor{darkgreen}{rgb}{0.1,0.7,0.1}
\tdplotsetmaincoords{60}{125}

\begin{tikzpicture}[tdplot_main_coords,>=latex]

\pgfsetlinewidth{0.8}

\tdplotsetcoord{P}{3.4641}{54.74}{45}

% box
\draw[fill=black!10] (Px) -- (Pxy) -- (Py) -- (Pyz) -- (Pz) -- (Pxz) -- cycle;
\draw (Pxy)  --  (P);
\draw (Pxz)  --  (P);
\draw (Pyz)  --  (P);

\pgfsetlinewidth{1.4}

\begin{scope} % blue circle, xy plane
\color{blue}
\tikzstyle{reverseclip}=[insert path={(3.1,3.1,1) -- (-1.1,3.1,1) -- (-1,-1.1,1) -- (3.1,-1.1,1) -- (3.1,3.1,1)}]
\begin{pgfinterruptboundingbox}
\path[clip] (1,1,1) -- (1.3,-1.1,1) -- (-1.1,-1.1,1) -- (-1.1,2,1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{2}{0}{0}}{\pgfpointxyz{0}{2}{0}}
\pgfusepath{draw}
\end{scope}

\begin{scope} % green circle, yz plane
\color{darkgreen}
\tikzstyle{reverseclip}=[insert path={(1,3.1,3.1) -- (1,3.1,-1.1) -- (1,-1.1,-1.1) -- (1,-1.1,3.1) -- (1,3.1,3.1)}]
\begin{pgfinterruptboundingbox}
\path[clip] (1,1,1) -- (1,-1.1,1.8) -- (1,-1.1,-1.1) -- (1,1.7,-1.1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{0}{2}{0}}{\pgfpointxyz{0}{0}{2}}
\pgfusepath{draw}
\end{scope}

\begin{scope} % red circle, xz plane
\color{red}
\tikzstyle{reverseclip}=[insert path={(3.1,1,3.1) -- (-1.1,1,3.1) -- (-1.1,1,-1.1) -- (3.1,1,-1.1) -- (3.1,1,3.1)}]
\begin{pgfinterruptboundingbox}
\path[clip] (1,1,1) -- (-1.1,1,2) -- (-1.1,1,-1.1) -- (1.5,1,-1.1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{2}{0}{0}}{\pgfpointxyz{0}{0}{2}}
\pgfusepath{draw}
\end{scope}

% axes
\draw[line width=0.8pt,->]  (2,1,1)  --  (4,1,1)  node[anchor=north  east]{\textbf{x}};
\draw[line width=0.8pt,->]  (1,2,1)  --  (1,4,1)  node[anchor=north  west]{\textbf{y}};
\draw[line width=0.8pt,->]  (1,1,2)  --  (1,1,4)  node[anchor=south]{\textbf{z}};

\end{tikzpicture}

\end{document}


-
It's impressive how much work went into asking this question! –  Charles Staats May 16 at 16:57
@CharlesStaats, should I not try and solve my own problem before I ask here? I still can't seem to figure out what's wrong. Perhaps I'm blind. –  eiterorm May 16 at 22:34
@eiterorm: Of course you should. Its just that most people don't make that much of an effort to debug the problem - am sure I am guilty of that on occasion. So, it is great that you did. And in this case it is good to have such a question here, even if you had solved it. Self answers are perfectly acceptable. See: Is it proper to post a first attempt as a self-answer? and Should I self answer my question? –  Peter Grill May 16 at 23:57
When I compile your code, the bottom of the image is cut off. E.g. I don't see the bottom bit of the red circle show in your picture. –  cfr May 17 at 2:29
@cfr, very true! I should have said that the pgf command draw (on a previously defined path) doesn't seem to expand the canvas (because the expansion is caused by the path command). –  eiterorm May 17 at 21:11

Use

\path[clip] (1.5,1,-1.1) -- (-1.1,1,-1.1) -- (-1.1,1,2) -- (1,1,1) -- cycle [reverseclip];


in the last case, reversing the direction of the clipping path.

\documentclass[11pt]{standalone}
\usepackage[utf8]{inputenc}
\usepackage{tikz,tikz-3dplot}
\usetikzlibrary{arrows}

\begin{document}

\definecolor{darkgreen}{rgb}{0.1,0.7,0.1}
\tdplotsetmaincoords{60}{125}

\begin{tikzpicture}[tdplot_main_coords,>=latex]

\pgfsetlinewidth{0.8}

\tdplotsetcoord{P}{3.4641}{54.74}{45}

% box
\draw[fill=black!10] (Px) -- (Pxy) -- (Py) -- (Pyz) -- (Pz) -- (Pxz) -- cycle;
\draw (Pxy)  --  (P);
\draw (Pxz)  --  (P);
\draw (Pyz)  --  (P);

\pgfsetlinewidth{1.4}

\begin{scope} % blue circle, xy plane
\color{blue}
\tikzstyle{reverseclip}=[insert path={(3.1,3.1,1) -- (-1.1,3.1,1) -- (-1,-1.1,1) -- (3.1,-1.1,1) -- (3.1,3.1,1)}]
\begin{pgfinterruptboundingbox}
\path[clip] (1,1,1) -- (1.3,-1.1,1) -- (-1.1,-1.1,1) -- (-1.1,2,1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{2}{0}{0}}{\pgfpointxyz{0}{2}{0}}
\pgfusepath{draw}
\end{scope}

\begin{scope} % green circle, yz plane
\color{darkgreen}
\tikzstyle{reverseclip}=[insert path={(1,3.1,3.1) -- (1,3.1,-1.1) -- (1,-1.1,-1.1) -- (1,-1.1,3.1) -- (1,3.1,3.1)}]
\begin{pgfinterruptboundingbox}
\path[clip] (1,1,1) -- (1,-1.1,1.8) -- (1,-1.1,-1.1) -- (1,1.7,-1.1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{0}{2}{0}}{\pgfpointxyz{0}{0}{2}}
\pgfusepath{draw}
\end{scope}

\begin{scope} % red circle, xz plane
\color{red}
\tikzstyle{reverseclip}=[insert path={(3.1,1,3.1) -- (-1.1,1,3.1) -- (-1.1,1,-1.1) -- (3.1,1,-1.1) -- (3.1,1,3.1)}]
\begin{pgfinterruptboundingbox}
\path[clip] (1.5,1,-1.1) -- (-1.1,1,-1.1) -- (-1.1,1,2) -- (1,1,1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{2}{0}{0}}{\pgfpointxyz{0}{0}{2}}
\pgfusepath{draw}
\end{scope}

% axes
\draw[line width=0.8pt,->]  (2,1,1)  --  (4,1,1)  node[anchor=north  east]{\textbf{x}};
\draw[line width=0.8pt,->]  (1,2,1)  --  (1,4,1)  node[anchor=north  west]{\textbf{y}};
\draw[line width=0.8pt,->]  (1,1,2)  --  (1,1,4)  node[anchor=south]{\textbf{z}};

\end{tikzpicture}

\end{document}


This gave me the idea:

\documentclass[11pt,crop=false,preview=false]{standalone}
\usepackage[utf8]{inputenc}
\usepackage{tikz,tikz-3dplot}
\usetikzlibrary{arrows}

\begin{document}

\definecolor{darkgreen}{rgb}{0.1,0.7,0.1}
\tdplotsetmaincoords{60}{125}

\begin{tikzpicture}[tdplot_main_coords,>=latex]

\pgfsetlinewidth{0.8}

\tdplotsetcoord{P}{3.4641}{54.74}{45}

% box
\draw[fill=black!10] (Px) -- (Pxy) -- (Py) -- (Pyz) -- (Pz) -- (Pxz) -- cycle;
\draw (Pxy)  --  (P);
\draw (Pxz)  --  (P);
\draw (Pyz)  --  (P);

\pgfsetlinewidth{1.4}

\begin{scope} % blue circle, xy plane
\color{blue}
\tikzstyle{reverseclip}=[insert path={(3.1,3.1,1) -- (-1.1,3.1,1) -- (-1,-1.1,1) -- (3.1,-1.1,1) -- (3.1,3.1,1)}]
\begin{pgfinterruptboundingbox}
\path[clip] (1,1,1) -- (1.3,-1.1,1) -- (-1.1,-1.1,1) -- (-1.1,2,1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{2}{0}{0}}{\pgfpointxyz{0}{2}{0}}
\pgfusepath{draw}
\end{scope}

\begin{scope} % green circle, yz plane
\color{darkgreen}
\tikzstyle{reverseclip}=[insert path={(1,3.1,3.1) -- (1,3.1,-1.1) -- (1,-1.1,-1.1) -- (1,-1.1,3.1) -- (1,3.1,3.1)}]
\begin{pgfinterruptboundingbox}
\draw[purple,->] (1,1,1) -- (1,-1.1,1.8) -- (1,-1.1,-1.1) -- (1,1.7,-1.1);
\path[clip] (1,1,1) -- (1,-1.1,1.8) -- (1,-1.1,-1.1) -- (1,1.7,-1.1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{0}{2}{0}}{\pgfpointxyz{0}{0}{2}}
\pgfusepath{draw}
\end{scope}

\begin{scope} % red circle, xz plane
\color{red}
\tikzstyle{reverseclip}=[insert path={(3.1,1,3.1) -- (-1.1,1,3.1) -- (-1.1,1,-1.1) -- (3.1,1,-1.1) -- (3.1,1,3.1)}]
\begin{pgfinterruptboundingbox}
\draw[orange,->] (1,1,1) -- (-1.1,1,2) -- (-1.1,1,-1.1) -- (1.5,1,-1.1);
\path[clip] (1,1,1) -- (-1.1,1,2) -- (-1.1,1,-1.1) -- (1.5,1,-1.1) -- cycle [reverseclip];
\end{pgfinterruptboundingbox}
\pgfpathellipse{\pgfpointxyz{1}{1}{1}}{\pgfpointxyz{2}{0}{0}}{\pgfpointxyz{0}{0}{2}}
\pgfusepath{draw}
\end{scope}

% axes
\draw[line width=0.8pt,->]  (2,1,1)  --  (4,1,1)  node[anchor=north  east]{\textbf{x}};
\draw[line width=0.8pt,->]  (1,2,1)  --  (1,4,1)  node[anchor=north  west]{\textbf{y}};
\draw[line width=0.8pt,->]  (1,1,2)  --  (1,1,4)  node[anchor=south]{\textbf{z}};

\end{tikzpicture}

\end{document}

-
Thanks! I didn't realize the direction of the path was essential to the result. –  eiterorm May 17 at 20:45
@eiterorm I don't realise anything if it is tikz but I'm trying to learn and sometimes playing helps. I just noticed the paths went in opposite directions and thought maybe, possibly... (Very unscientific explanation, too, since the paths are in different planes so it is not at all obvious what 'opposite' means.) –  cfr May 17 at 20:48
if I were to guess, I'd say it is related to the direction of the path of the outer path (named 'reverseclip' in this example). I haven't investigated this, though. –  eiterorm May 17 at 20:53

Perhaps in this case you can get away using arcs and the rotate around keys which rotate the coordinate system around the specified axis vector as shown below (note, a shift is to the center of the box is needed for this to wrk). It does, however, require the latest version of PGF. I have taken some liberties with the original code.

\documentclass[tikz,border=5]{standalone}
\usepackage{tikz-3dplot}
\begin{document}

\definecolor{dark green}{rgb}{0.1,0.7,0.1}
\tdplotsetmaincoords{60}{125}

\begin{tikzpicture}[tdplot_main_coords,>=latex, thick, line cap=round, line join=round]

\tdplotsetcoord{P}{3.4641}{54.74}{45}
\coordinate (O) at (1,1,1);
% box
\draw[fill=black!10] (Px) -- (Pxy) -- (Py) -- (Pyz) -- (Pz) -- (Pxz) -- cycle;
\draw (Pxy) --  (P) (Pxz) --  (P) (Pyz) --  (P);

\tikzset{shift={(O)}, ultra thick}

\draw  [blue, rotate around z=-90]
(15:2) arc (15:245:2);
\draw  [dark green, rotate around z=-90, rotate around x=90]
(15:2) arc (15:245:2);
\draw  [red, rotate around z=-90, rotate around y=90]
(15:2) arc (15:245:2);

\tikzset{thick}
% axes
\draw [->] (1,0,0) -- (3,0,0) node[below left]  {\textbf{x}};
\draw [->] (0,1,0) -- (0,3,0) node[below right] {\textbf{y}};
\draw [->] (0,0,1) -- (0,0,3) node[above]       {\textbf{z}};

\end{tikzpicture}

\end{document}


-
Thanks for the answer. While it doesn't solve the actual problem with the reversed clipping, it shows a great, different way to draw the same figure. –  eiterorm May 17 at 20:49