2

I have an issue with a reverse clipping on a 2D surface represented in a 3D space.

Although the reversed clipping is working, the cut part is not projected on a plane.

enter image description here

Here is the MWE:

\documentclass{standalone}

\usepackage{amsmath, amssymb}
\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{arrows,decorations.pathmorphing}
\usepackage{tikz-3dplot}
\usetikzlibrary{arrows}
\usetikzlibrary{calc}
\usetikzlibrary{shadings}

\tikzset{>=latex}
\tdplotsetmaincoords{70}{120}

%Cavity
\def \xcav {5}
\def \zcav {5}
\def \ycav {2}

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

\begin{document}
    \begin{tikzpicture}
         [
         scale=1,
         tdplot_main_coords
         ]

    % Cavity
    \draw (-\xcav/2,-\ycav,-\zcav/2) --++ (\xcav,0,0) --++ (0,0,\zcav) --++ (-\xcav,0,0) -- cycle;
    \draw (-\xcav/2,-\ycav,-\zcav/2) --++ (0,\ycav,0) --++ (0,0,\zcav) --++ (0,-\ycav,0) -- cycle;
    \draw (-\xcav/2,-\ycav,-\zcav/2) --++ (0,\ycav,0) --++ (\xcav,0,0) --++ (0,-\ycav,0) -- cycle;

    \begin{scope}[canvas is xz plane at y=0]
        \begin{pgfinterruptboundingbox} % useful to avoid the rectangle in the bounding box
            \path[invclip](0,0) circle (0.25);
        \end{pgfinterruptboundingbox} 
       \shadedraw[clip] (-\xcav/2,-\zcav/2) --++ (\xcav,0) --++ (0,\zcav) --++ (-\xcav,0) -- cycle;
    \end{scope} 

    \draw (\xcav/2,-\ycav,-\zcav/2) --++ (0,\ycav,0) --++ (0,0,\zcav) --++ (0,-\ycav,0) -- cycle;
    \draw (-\xcav/2,-\ycav,+\zcav/2) --++ (0,\ycav,0) --++ (\xcav,0,0) --++ (0,-\ycav,0) -- cycle;

    \end{tikzpicture}
\end{document}

Any thoughts ?

  • You reset cm... what did you expect? – Symbol 1 Jan 29 '16 at 11:42
  • 1
    Try invclip/.style={clip,insert path={{(-10,-10)rectangle(10,10)}}. – Symbol 1 Jan 29 '16 at 11:44
  • Ok it works, my bad, do you want to post a response so I can validate it ? – Thomas Jan 29 '16 at 12:04
  • Not yet. I am trying to come upwith a general solutoin. – Symbol 1 Jan 29 '16 at 13:39
4

Some mumble-jumble:

The notable solution to invert clipping is to insert a path that is as large as possible. Currently the limit is associated to TeX's dimensions: about 214pt. However, users could have done some transformations so that a barely valid path, such as (-16383pt,16383pt)rectangle(16383pt,16383pt), will definitely exceed the limit.

That is why reset cm comes in: it resets all transformations and guarantees that a (the) valid path is still valid. Nevertheless, the drawback is also obvious. After resetting, the user need either to reconstruct the transformation or work harder with no transformation.

So a good solution to clipping-inverting should have limit the scope of reset cm, this is why you see double braces at insert path={{. But it does not work, probably because that value is passed between macros too many times and eventually TeX will "flay" all braces.

A solution to this is to add even more braces. Experiment shows that four braces is enough. By enough, I mean the result does change: The circle is not replace by the projected one, but just disappears.

It turns out that the scoping affects the calculation of winding number. Since nonzero-rule is default to clip, everything lies inside the large large path is reserved, making clipping meaningless.

A solution to this is to add a predefined option

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

into insert path. Experiment shows that the following works

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

Magically, now braces become unnecessary.

Complete code

\documentclass{standalone}

\usepackage{amsmath, amssymb}
\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{arrows,decorations.pathmorphing}
\usepackage{tikz-3dplot}
\usetikzlibrary{arrows}
\usetikzlibrary{calc}
\usetikzlibrary{shadings}

\tikzset{>=latex}
\tdplotsetmaincoords{70}{120}

%Cavity
\def \xcav {5}
\def \zcav {5}
\def \ycav {2}

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

\begin{document}
    \begin{tikzpicture}
         [
         scale=1,
         tdplot_main_coords
         ]

    % Cavity
    \draw (-\xcav/2,-\ycav,-\zcav/2) --++ (\xcav,0,0) --++ (0,0,\zcav) --++ (-\xcav,0,0) -- cycle;
    \draw (-\xcav/2,-\ycav,-\zcav/2) --++ (0,\ycav,0) --++ (0,0,\zcav) --++ (0,-\ycav,0) -- cycle;
    \draw (-\xcav/2,-\ycav,-\zcav/2) --++ (0,\ycav,0) --++ (\xcav,0,0) --++ (0,-\ycav,0) -- cycle;

    \begin{scope}[canvas is xz plane at y=0]
        \begin{pgfinterruptboundingbox} % useful to avoid the rectangle in the bounding box
            \path[invclip](0,0) circle (0.25);
        \end{pgfinterruptboundingbox} 
       \shadedraw[clip] (-\xcav/2,-\zcav/2) --++ (\xcav,0) --++ (0,\zcav) --++ (-\xcav,0) -- cycle;
    \end{scope} 

    \draw (\xcav/2,-\ycav,-\zcav/2) --++ (0,\ycav,0) --++ (0,0,\zcav) --++ (0,-\ycav,0) -- cycle;
    \draw (-\xcav/2,-\ycav,+\zcav/2) --++ (0,\ycav,0) --++ (\xcav,0,0) --++ (0,-\ycav,0) -- cycle;

    \end{tikzpicture}
\end{document}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.