# More elegant way to achieve this same camera perspective projection model?

I just finished a camera perspective projection model using TikZ. Although, while I'm pretty satisfied with the result, it took me a lot of time to do it. It's easy to see from the code below that I'm not very used to Latex and even less to TikZ. So, I'm sure that there are much better ways of drawing this same picture.

I did find a package that supported 3d drawing with TikZ (tikz-3dplot), but at first glance I didn't think it would help me too much. So I didn't bother learning another package. Maybe there are other alternatives which I'm not aware of.

The basic idea of my code is defining three vectores for the picture's coordinate system (x_c, y_c and z_c). Then everything is drawn based on these vectors. So, for example, if I want to move 3 units in the x direction, I simply do something like \draw (0,0) -- (3*\xOne, 3*\xTwo), where \xOne and \xTwo are the first and second values of the defined x vector, respectively (hence the decomposition of this vector in the absolute TikZ x and y direction).

I though that maybe I could learn by finding different solutions from people with more experience. While I would prefer PGF/TikZ solutions since this is the only one that I know something, other alternatives like PSTricks and Metapost would be welcomed too (maybe this is the opportunity for me to learn them).

This is the picture I have:

And this is my code:

\documentclass{article}

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}

\usetikzlibrary{calc}

% Picture's vectors definition
\def\xOne{1}
\def\xTwo{0.5}
\def\yOne{0}
\def\yTwo{-1.3}
\def\zOne{-1}
\def\zTwo{0.5}

% CAMERA COORDINATE SYSTEM
%\draw[thick,->] (0,0) -- (\xOne,\xTwo) node[anchor=north]{$x$};
%\draw[thick,->] (0,0) -- (\yOne,\yTwo) node[anchor=west]{$y$};
%\draw[thick,->] (0,0) -- (\zOne,\zTwo) node[anchor=north,yshift=-2pt,xshift=3pt]{$z$};
\draw[very thick,->] (-\zOne/2,-\zTwo/2) -- (-\zOne/2+\xOne,-\zTwo/2+\xTwo) node[anchor=north west, xshift=-3pt,font=\footnotesize]{$x_c$};
\draw[very thick,->] (-\zOne/2,-\zTwo/2) -- (-\zOne/2+\yOne,-\zTwo/2+\yTwo) node[anchor=west,font=\footnotesize]{$y_c$};
\draw[very thick,->] (-\zOne/2,-\zTwo/2) -- (\zOne/2,\zTwo/2) node[anchor=north,yshift=-2pt,xshift=3pt,font=\footnotesize]{$z_c$};
\draw (-\zOne/2,-\zTwo/2) node[anchor=north west,font=\footnotesize]{$\mathcal{F}_c$};

% CAMERA AXIS ELONGATION
\draw[very thin,solid] (-\zOne/2-2*\xOne,-\zTwo/2-2*\xTwo) -- (-\zOne/2+2*\xOne,-\zTwo/2+2*\xTwo); % x elongation
\draw[very thin,solid] (3*\zOne,3*\zTwo) -- (6*\zOne,6*\zTwo); % optical axis behind projection plane

% REFERENCE LINES
%\draw[thin,dashed] (1.4*\xOne-\zOne/2,1.4*\xTwo-\zTwo/2) -- (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo); % object x position
\draw[very thin,solid] (6*\zOne-2*\xOne,6*\zTwo-2*\xTwo) -- (6*\zOne+2*\xOne,6*\zTwo+2*\xTwo) node[anchor=west]{}; %object z position

% WORLD OBJECT
\draw[-latex,line width=3pt,blue,line cap=round] (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo) -- (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo+1.1) node[anchor=south,font=\footnotesize]{$P = (X,Y,Z)$};
\node[circle,inner sep=0pt,minimum size=0.2cm,fill=blue] (object) at (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo+1.1) {};

% PROJECTION LINE BEHIND PROJECTION PLANE
\draw[thick,solid,red] (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69) -- (6*\zOne+1.4*\xOne,6*\zTwo+1.4*\xTwo+1.1);

%% PROJECTION PLANE
\filldraw[fill=gray!20,draw=gray!70,opacity=0.8] (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo) -- (3*\zOne+1.5*\xOne-1.5*\yOne,3*\zTwo+1.5*\xTwo-1.5*\yTwo) -- (3*\zOne+1.5*\xOne+1.5*\yOne,3*\zTwo+1.5*\xTwo+1.5*\yTwo) -- (3*\zOne-1.5*\xOne+1.5*\yOne,3*\zTwo-1.5*\xTwo+1.5*\yTwo) -- (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo);

% PLOJECTION PLANE COORDINATE SYSTEM u,v
\draw[->,thick,green!70!black,dashed] (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo) -- (3*\zOne+2*\xOne-1.5*\yOne,3*\zTwo+2*\xTwo-1.5*\yTwo)
node[anchor=north west, xshift=-3pt,font=\footnotesize]{$u$};
\draw[->,thick,green!70!black,dashed] (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo-1.5*\yTwo) -- (3*\zOne-1.5*\xOne-1.5*\yOne,3*\zTwo-1.5*\xTwo+2*\yTwo)
node[anchor=west,font=\footnotesize]{$v$};

% PROJECTION PLANE COORDINATE SYSTEM x,y
\draw[->,thick,cyan,dashed] (3*\zOne-2*\xOne,3*\zTwo-2*\xTwo) -- (3*\zOne+2*\xOne,3*\zTwo+2*\xTwo)
node[anchor=north west, xshift=-3pt,font=\footnotesize]{$x$};
\draw[->,thick,cyan,dashed] (3*\zOne-2*\yOne,3*\zTwo-2*\yTwo) -- (3*\zOne+2*\yOne,3*\zTwo+2*\yTwo)
node[anchor=west,font=\footnotesize]{$y$};

% PROJECTION  OBJECT
\draw[-latex,line width=1.5pt,blue,line cap=round] (3*\zOne+0.69*\xOne,3*\zTwo+0.69*\xTwo) -- (3*\zOne+0.69*\xOne,3*\zTwo+0.69*\xTwo+0.69);
\node[circle,inner sep=0pt,minimum size=0.1cm,fill=blue] (object) at (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69) {};

% PIXEL OBJECT
\filldraw[red,opacity=0.6] (3*\zOne+6*0.105*\xOne,3*\zTwo+0.75+6*0.105*\xTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo);

% PROJECTION LINE IN FRONT OF PROJECTION PLANE
\draw[thick,solid,red] (-\zOne/2,-\zTwo/2) -- (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69);

% OPTICAL AXIS IN FRONT OF PROJECTION PLANE
\draw[thin,solid] (0,0) -- (3*\zOne,3*\zTwo);

% ANNOTATIONS
% z = f
\draw (3*\zOne-1*\xOne+1.3*\yOne,3*\zTwo-1*\xTwo+1.3*\yTwo) node[gray!70,rotate=28] {$z = f$};
% bar(u)
\draw[to-to, green!70!black] (3*\zOne+0.13*\xOne+0.08*\yOne,3*\zTwo+0.13*\xTwo+0.08*\yTwo) -- (3*\zOne+0.7*\xOne+0.08*\yOne,3*\zTwo+0.7*\xTwo+0.08*\yTwo) node[midway,anchor=north west,xshift=-2pt,yshift=2pt,font=\scriptsize] {$\bar{u}$};
% bar(v)
\draw[to-to, green!70!black] (3*\zOne-0.1*\xOne-0.04*\yOne,3*\zTwo-0.1*\xTwo-0.04*\yTwo) -- (3*\zOne-0.1*\xOne,3*\zTwo-0.1*\xTwo+0.75) node[midway,anchor=east,xshift=2pt,font=\scriptsize] {$\bar{v}$};
% (u,v)
\node[green!70!black,anchor=west,font=\scriptsize] at (3*\zOne+0.69*\xOne,3*\zTwo+0.7*\xTwo+0.69) {$(u,v)$};
% principal point
\draw[very thin] (3*\zOne-0.02*\xOne+0.02*\yOne,3*\zTwo-0.02*\xTwo+0.02*\yTwo) .. controls (3*\zOne-0.1*\xOne+0.3*\yOne,3*\zTwo-0.1*\xTwo+0.3*\yTwo) and (3*\zOne-0.3*\xOne+0.1*\yOne,3*\zTwo-0.3*\xTwo+0.1*\yTwo) ..  (3*\zOne-0.6*\xOne+0.4*\yOne,3*\zTwo-0.6*\xTwo+0.4*\yTwo) node[anchor=north,align=center,font=\sffamily\scriptsize] {ponto \\ principal};
% optical axis
\draw[very thin] (5.5*\zOne-0.02*\xOne+0.02*\yOne,5.5*\zTwo-0.02*\xOne+0.02*\yOne) .. controls (5.5*\zOne-0.1*\xOne+0.3*\yOne,5.5*\zTwo-0.1*\xTwo+0.3*\yTwo) and (5.5*\zOne-0.3*\xOne+0.1*\yOne,5.5*\zTwo-0.3*\xTwo+0.1*\yTwo) ..  (5.5*\zOne-0.6*\xOne+0.4*\yOne,5.5*\zTwo-0.6*\xTwo+0.4*\yTwo) node[anchor=north,align=center,font=\sffamily\scriptsize] {eixo \\ \'otico};

% PIXEL POSITION
\draw[thin,gray!70] (3*\zOne,3*\zTwo+0.75) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.105*\yOne,-0.105*\yTwo) -- ++(0.21*\xOne,0.21*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.21*\yOne,-0.21*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo) -- ++(-0.105*\xOne,-0.105*\xTwo) -- ++(-0.19*\yOne,-0.19*\yTwo) -- ++(0.105*\xOne,0.105*\xTwo) -- ++(0.105*\yOne,0.105*\yTwo);

\end{tikzpicture}
\end{document}

-

WOW! This is really quite elegant.
You could have made your life a lot easier though, if you had used the TikZ 3D coordinate system, defined as \draw (0,1,2)--(2,3,4); and, if needed, adjusting the x,y and z coorinates by \begin{tikzpicture}[x={(1cm,0cm)},y={(0cm,1cm)},z={(0.73cm,0.73cm)}] or something alike. This changes the lengths with which the x,y and z are projected onto x and y.

As an alternative, you could have used thetikz-3dplot package to rotate your camera perspective about azimuth and elevation. Really just a two liner:

\tdplotsetmaincoords{70}{110}
\begin{tikzpicture}[tdplot_main_coords]


for 70 degree azimuth and 110 degree of elevation.

When rotating, you have to remember though, that TikZ has no real 3D support, as in:
z-buffering is not (yet?) supported. You might therefore have to adjust some colors after rotation. This is because, with rotation, the drawing order can change. Then things are in front of others, because of the time they are calculated, not where they are in 3D. This is explained in the tikz3dplot example manual, chapter 3.3.

Also, the use of relative coordinates helps to focus on the contents, rather than the creation of the figure. \draw (2,3,4)--++(0,0,1); takes you towards z by one, from where you used to be.

On a rather unrelated note, you should load the TikZ-packages in the preamble, otherwise you would have to reload them in every scope. This would slow down compilation speed, for larger documents.

-
I wasn't aware of this 3D coordinate system support in TikZ. Is it in the manual? A search for 3D doesn't show this. Looks interesting. What do you mean by having to adjust colors? Can't see the relation of it with this topic. I'll try this tikz-3dplot idea. Looks nice too. I used relative coordinates in the pixel squares, but it didn't seem to help much with the other drawings. Maybe with the 3D system this is solved. –  perr0 Jan 30 '13 at 23:00
You can find it, in the manual, in chapter 13.2 - Coordinate systems, alongside some other predefined coordinate systems and an explanation on how to define your own coordinate systems. They can all exist in the same picture and you can mix them. Try to avoid mixing 2D and 3D references in one draw command though. Relative coordinate systems really only help to simplify writing a tiny bit, but it can help. On colors: assume you rotate until the blue P=(x,y,z) arrow is partially behind the plane. The color then gets distorted and you might have to rearrange the drawing order or colors. –  user24276 Jan 31 '13 at 9:20