21

I am struggling with drawing a plane and a intersecting line. I would like to have the line C,P',P in the image, disappear if it is "behind" the plane and have it reappear if it is not behind the plane anymore.

The result I would like to get

Right now I solve this issue by plotting the line in two segments.

Here is a MWE:

\documentclass{minimal}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\draw[->] (0,0) node[anchor=east]{C} -- (5,0)node[anchor=west] {Z};
\draw[->] (0,0) -- (0,3)node[anchor=south] {Y};
\draw[->] (-1.0,-1.0) -- (1.5,1.5)node[anchor=west] {X};
\draw (0,0) -- (3,1.2);
\filldraw[fill=lightgray] (2.5,-2.5) -- (2.5,1) --  (4,3) -- (4,-0.5) -- (2.5,-2.5);
\draw[->] (3.25,0) -- (3.25,0.75)node[anchor=south] {y};
\draw[->] (3.25,0) -- (3.5,0.5)node[anchor=west] {x};
\draw[->] (3.25,0) -- (5,0);
\draw (3,1.2) -- (5,2);
\fill (5,2) circle[radius=2pt] node[anchor=south] {P};
\fill (3,1.2) circle[radius=2pt] node[anchor=south] {P'};
\draw ((2.5,-2.5)node[anchor=west, font=\footnotesize] {image plane};
\end{tikzpicture}
\end{document}

However, I believe there must be an easier solution to this problem, as this now requires quite some annoying hand calculations. So my question is:

What is the easiest way to draw a line that is intersecting a plane?

8
  • Hello Zwähnia. Could you please post compilable code, starting with \documentclass{...} and ending by \end{document}?
    – jub0bs
    Mar 27, 2013 at 12:00
  • 3
    Hi Jubobs, a MWE was added.
    – Ingo
    Apr 4, 2013 at 14:51
  • only tikz? can it be pgfplots?
    – Pouya
    Apr 24, 2013 at 12:48
  • @Pouya I was indeed hoping for getting a solution using tikz, yes.
    – Zwähnia
    Apr 24, 2013 at 14:17
  • @Zwähnia, note that pgfplots is basically a makro-package for tikz, It should be fine to use as well. But I'm interested in a plain tikz solution as well.
    – Ingo
    Apr 24, 2013 at 14:18

1 Answer 1

17
+50

Doing this automatically in TikZ is complicated because TikZ is not a 3D-aware package. It draws things in the order in which you draw them with almost no capability for automatically removing stuff that is "behind" something already there. There are "layers", but these are just a way of re-ordering the things that are drawn and don't get round this fundamental problem.

So when you have things like the z-axis and the line which have parts drawn before and after the plane, you have to draw them in two pieces. I know of no way around that (even using clips still requires them to be drawn twice). And so you need some way of telling TikZ where to make the break. As it is fundamentally a 2D-package, it doesn't know how to compute the intersection of the line and plane and so doesn't know when to stop drawing the line. So you have to tell it.

Fortunately, after all that doom and gloom, you can use 3D coordinates for specifying points in TikZ. These are instantly converted to 2D coordinates for plotting, but it is much easier to specify the intersection points in 3D coordinates than in 2D coordinates, especially in this example.

So here's how I'd draw your diagram using 3D coordinates. Since your frame is not the usual TikZ frame (basically, you've rotated around the y-axis) I needed to adjust the frame at the start. Don't worry overmuch about this bit.

Then the main thing is sorting out which bits are drawn behind the plane and which in front. Now, I have used the layer facility of PGF here which allows me to jiggle around with the order in which things are drawn relative to the order in which they are specified. This means that I can keep the two pieces of, say, the Z-axis together in the code making it easier to maintain. For this, I've borrowed code from "Z-level" in TikZ (the author said I could) to make the code for the picture simpler. All the stuff in \makeatletter ... \makeatother could be gotten away with at the expense of more crowded picture code.

\documentclass{article}
%\url{https://tex.stackexchange.com/q/104529/86}
\usepackage{tikz}

\makeatletter
\tikzset{
  xyz frame/.code n args={3}{%
    \begingroup
    \tikz@scan@one@point\pgfutil@firstofone#1\relax
    \pgf@xa=\pgf@x
    \pgf@ya=\pgf@y
    \tikz@scan@one@point\pgfutil@firstofone#2\relax
    \pgf@xb=\pgf@x
    \pgf@yb=\pgf@y
    \tikz@scan@one@point\pgfutil@firstofone#3\relax
    \edef\tikz@marshall{\noexpand\endgroup\noexpand\pgfsetxvec{\noexpand\pgfpoint{\the\pgf@xa}{\the\pgf@ya}}%
      \noexpand\pgfsetyvec{\noexpand\pgfpoint{\the\pgf@xb}{\the\pgf@yb}}%
      \noexpand\pgfsetzvec{\noexpand\pgfpoint{\the\pgf@x}{\the\pgf@y}}}%
    \tikz@marshall
  },
  on layer/.code={
    \pgfonlayer{#1}\begingroup
    \aftergroup\endpgfonlayer
    \aftergroup\endgroup
  },
}
\makeatother

\pgfdeclarelayer{behind}
\pgfdeclarelayer{in front}
\pgfsetlayers{behind,main,in front}

\begin{document}
\begin{tikzpicture}[xyz frame={(0,0,-1)}{(0,1,0)}{(1,0,0)}]
\draw[on layer=behind] (0,0,0) node[anchor=east]{C} -- (0,0,3);
\draw[->,on layer=in front] (0,0,3) -- (0,0,5)node[right] {Z};
\draw[->] (0,0,0) -- (0,5,0)node[above] {Y};
\draw[->] (-3,0,0) -- (5,0,0)node[above right] {X};
\filldraw[fill=lightgray] (2.5,-2.5,3) -- (2.5,2.5,3) --  (-2.5,2.5,3) -- (-2.5,-2.5,3) -- cycle;
\draw[->] (0,0,3) -- +(0,1,0) node[above] {y};
\draw[->] (0,0,3) -- +(1,0,0) node[right] {x};
\draw[on layer=behind] (0,0,0) -- (-1,2,3);
\draw[on layer=in front]  (-1,2,3) -- (-2,4,6);
\fill (-2,4,6) circle[radius=2pt] node[anchor=south] {P};
\fill (-1,2,3) circle[radius=2pt] node[anchor=south] {P'};
\draw (-2.5,-2.5,3)node[right, font=\footnotesize] {image plane};
\end{tikzpicture}
\end{document}

plane with line through it

You must log in to answer this question.

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