Suppose I have a plane E: a*x_1 + b*x_2 + c*x_3 = d (a,..,d should be concrete numbers) in space and a point P. Is there a way to draw a 3-d coordinate system, E, P and perpendicular line to E through P?
Asked
Active
Viewed 5,972 times
10
-
How do you want the plane to be drawn? – Caramdir Jun 05 '11 at 17:42
-
Perhaps one need to distinguish different cases: If the plane intersects all three coordinate axis, I want to connect those intersection points and draw the resulting triangle. If this is not the case I want to have something like this – student Jun 05 '11 at 18:06
1 Answers
20
TikZ has a built-in 3d coordinate system. You can set the directions of the unit vectors with the x, y and z options. Then you could just do the math by hand and draw the points and lines. Or, you can define some macros to do the job:
\documentclass{article}
\usepackage{tikz}
\makeatletter
% Set some defaults
\tikzset{
plane max x/.initial=2,
plane max y/.initial=2,
plane max z/.initial=2
}
\tikzset{plane/.style={fill opacity=0.5}}
% Define a plane.
% #1 = name of the plane
% #2*x + #3*y + #4*z = #5 is the equation of the plane
\newcommand*\definePlaneByEquation[5]{
\expandafter\gdef\csname tsx@plane@#1\endcsname{
\def\tsx@plane@xcoeff{#2}
\def\tsx@plane@ycoeff{#3}
\def\tsx@plane@zcoeff{#4}
\def\tsx@plane@scalar{#5}
}
}
% Draw a plane.
% The optional first argument is passed as options to TikZ.
% The mandatory second argument is the name of the plane.
\newcommand\drawPlane[2][]{
\tikzset{plane max x/.get=\tsx@plane@maxx}
\tikzset{plane max y/.get=\tsx@plane@maxy}
\tikzset{plane max z/.get=\tsx@plane@maxz}
\csname tsx@plane@#2\endcsname
\ifdim\tsx@plane@xcoeff pt=0pt
\ifdim\tsx@plane@ycoeff pt=0pt
\ifdim\tsx@plane@zcoeff pt=0pt
%invalid plane
\else % x=0, y=0
\filldraw[plane,#1,shift={(0,0,\tsx@plane@scalar/\tsx@plane@zcoeff)}]
(0,0,0) --
(\tsx@plane@maxx,0,0) --
(\tsx@plane@maxx,\tsx@plane@maxy,0) --
(0,\tsx@plane@maxy,0) --
cycle;
\fi
\else % x=0, y != 0
\ifdim\tsx@plane@zcoeff pt=0pt % x=0, z=0
\filldraw[plane,#1,shift={(0,\tsx@plane@scalar/\tsx@plane@ycoeff,0)}]
(0,0,0) --
(\tsx@plane@maxx,0,0) --
(\tsx@plane@maxx,0,\tsx@plane@maxz) --
(0,0,\tsx@plane@maxz) --
cycle;
\else % x=0
\filldraw[plane,#1]
(0,\tsx@plane@scalar/\tsx@plane@ycoeff,0) --
(0,0,\tsx@plane@scalar/\tsx@plane@zcoeff) --
(\tsx@plane@maxx,0,\tsx@plane@scalar/\tsx@plane@zcoeff) --
(\tsx@plane@maxx,\tsx@plane@scalar/\tsx@plane@ycoeff,0) --
cycle;
\fi
\fi
\else % x!=0
\ifdim\tsx@plane@ycoeff pt=0pt % x!=0,y=0
\ifdim\tsx@plane@zcoeff pt=0pt % x!=0,y=0,z=0
\filldraw[plane,#1,shift={(\tsx@plane@scalar/\tsx@plane@xcoeff,0,0)}]
(0,0,0) --
(0,0,\tsx@plane@maxz) --
(0,\tsx@plane@maxy,\tsx@plane@maxz) --
(0,\tsx@plane@maxy,0) --
cycle;
\else % x!=0,y=0,z!=0
\filldraw[plane,#1]
(\tsx@plane@scalar/\tsx@plane@xcoeff,0) --
(0,0,\tsx@plane@scalar/\tsx@plane@zcoeff) --
(0,\tsx@plane@maxy,\tsx@plane@scalar/\tsx@plane@zcoeff) --
(\tsx@plane@scalar/\tsx@plane@xcoeff,\tsx@plane@maxy,0) --
cycle;
\fi
\else % x!=0,y!=0
\ifdim\tsx@plane@zcoeff pt=0pt % x!=0,y!=0,z=0
\filldraw[plane,#1]
(\tsx@plane@scalar/\tsx@plane@xcoeff,0) --
(0,\tsx@plane@scalar/\tsx@plane@ycoeff,0) --
(0,\tsx@plane@scalar/\tsx@plane@ycoeff,\tsx@plane@maxz) --
(\tsx@plane@scalar/\tsx@plane@xcoeff,0,\tsx@plane@maxz) --
cycle;
\else % x!=0,y!=0,z!=0
\filldraw[plane,#1]
(\tsx@plane@scalar/\tsx@plane@xcoeff,0,0) --
(0,\tsx@plane@scalar/\tsx@plane@ycoeff,0) --
(0,0,\tsx@plane@scalar/\tsx@plane@zcoeff) --
cycle;
\fi
\fi
\fi
}
% Define a point.
% #1 = name of the point
% (#2,#3,#4) is the location.
% Also creates a coordinate node of name #1 at the location.
\newcommand\definePointByXYZ[4]{
\coordinate (#1) at (#2,#3,#4);
\expandafter\gdef\csname tsx@point@#1\endcsname{
\def\tsx@point@x{#2}
\def\tsx@point@y{#3}
\def\tsx@point@z{#4}
}
}
% Project a point to a plane.
% #1 = name of the new point
% #2 = name of old point
% #3 = name of plane
\newcommand\projectPointToPlane[3]{{
\csname tsx@point@#2\endcsname
\csname tsx@plane@#3\endcsname
% square of norm of the normal vector
\pgfmathparse{\tsx@plane@xcoeff*\tsx@plane@xcoeff + \tsx@plane@ycoeff*\tsx@plane@ycoeff + \tsx@plane@zcoeff*\tsx@plane@zcoeff}
\let\nnormsq\pgfmathresult
% Calculate distance in terms of the (non-normalized) normal vector
\pgfmathparse{(\tsx@point@x*\tsx@plane@xcoeff + \tsx@point@y*\tsx@plane@ycoeff + \tsx@point@z*\tsx@plane@zcoeff - \tsx@plane@scalar) / \nnormsq}
\let\distance\pgfmathresult
% Calculate point
\pgfmathparse{\tsx@point@x - \distance*\tsx@plane@xcoeff}
\let\x\pgfmathresult
\pgfmathparse{\tsx@point@y - \distance*\tsx@plane@ycoeff}
\let\y\pgfmathresult
\pgfmathparse{\tsx@point@z - \distance*\tsx@plane@zcoeff}
\let\z\pgfmathresult
\definePointByXYZ{#1}{\x}{\y}{\z}
}}
\makeatother
\begin{document}
\begin{tikzpicture}[x={(240:0.8cm)}, y={(-10:1cm)}, z={(0,1cm)},
plane max z=3]
\draw[->] (0,0,0) -- (3,0,0);
\draw[->] (0,0,0) -- (0,3,0);
\draw[->] (0,0,0) -- (0,0,3);
\definePlaneByEquation{myplane}{1}{1.5}{0}{2}
\drawPlane[thick,fill=blue]{myplane}
\definePointByXYZ{mypoint}{1}{1.5}{2};
\draw (mypoint) circle [radius=1pt];
\projectPointToPlane{proj}{mypoint}{myplane}
\fill (proj) circle [radius=1pt];
\draw[->, shorten <=1pt,shorten >=1pt] (mypoint) -- (proj);
\end{tikzpicture}
\end{document}

Some comments:
- I hope I didn't mess up some case.
- With the
plane max x/y/zoptions, you can specify how far a plane with zero parameters should extend. If you want to draw a line through the point, normal to the plane, you can use
\definePlaneByEquation{myplane}{1}{1.5}{0}{2} \definePointByXYZ{mypoint}{1}{1.5}{2}; \projectPointToPlane{proj}{mypoint}{myplane} \draw ($(proj)!-2cm!(mypoint)$) -- (proj); \drawPlane[thick,fill=blue]{myplane} \draw (proj) -- ($(mypoint)!-2cm!(proj)$);The order is important to get correct overpainting in case the plane is opaque or the line is non-black.
- The tests for 0 should really be tests for being smaller than epsilon.
- Maybe it would be useful to apply
\pgfmathparseto the arguments of the definition macros.
Caramdir
- 89,023
- 26
- 255
- 291
-
Fine! It's a good starting point. I started a package about 3D geometry and this answer is interesting. – Alain Matthes Jun 06 '11 at 08:40
-
@Astramundus, do you know the
tikz-3dplotpackage? I use it all the time, might also be a good source for your 3d package? Check it out at: http://archive.cs.uu.nl/mirror/CTAN/graphics/pgf/contrib/tikz-3dplot/tikz-3dplot_documentation.pdf – romeovs Jun 06 '11 at 08:48 -
@Altermundus: I was wondering whether I should invest more time in this and add more things for 3D geometry (e.g. intersect various things) or if you are already working on a 3D version of TkZ. – Caramdir Jun 06 '11 at 15:56
-
@romeovs:
tikz-3dplotis nice, but as far as I am aware it doesn't have features to define planes and do intersections and such. – Caramdir Jun 06 '11 at 15:58 -
@romeovs Yes :) but the author of the
tikz-3dplotpackage uses some of my ideas. The author found interesting my "annotated-3d-box" example on texample.net: http://www.texample.net/tikz/examples/annotated-3d-box/ – Alain Matthes Jun 06 '11 at 16:14 -
@Caramdir I have some codes inside a tkz-3d.sty but actually I worked a lot on Euclidean geometry and graphs theory but now I can invest more time on the 3D geometry but something about planes, pyramids, cubes and parallelepiped because It's hard to calculate with TikZ. Perhaps with lua and tikz ? – Alain Matthes Jun 06 '11 at 16:22
-
Well done. But perhaps you could edit your code for supporting rotating the planes. I'm thinking in this question. Can we pass a "rotation" parameter? – somenxavier Nov 25 '15 at 08:51
-
@Caramdir Really appreciate your answer, it's exactly what I've been looking for. Any chance you could answer this question?:) – joni Jan 16 '17 at 17:46
-
First of all thank you for this great piece of code. There's just a tiny problem: if I call \projectPointToPlane with a point defined by a previous call to the same command (e.g. I'm trying to find the projection on plane B of the projection on plane A of a point), LaTeX says "Undefined control sequence \tsx@point@x ->\x" for all 3 coords. Using just { ... } for defining the command works but then the definition of the first projection is no longer correct (becomes equals to the second one). Can the code be easily fixed? – Marco Boschi Sep 23 '18 at 16:41
-