Axis View

For some time now, I have been looking for a good solution to draw simple 3d schematics involving axes and angles between axes/vectors. After several attempts using gnuplot and even Google Sketchup, I finally got my head into doing it directly in TikZ.

A guide for the idea behind this is given here

3dplotb.png
3d coordinate system in TikZ, where a secondary, rotated coordinate system can be specified for full flexibility.
3dplot.png
3d plot of vector drawn in Tikz, where the perspective and vector coordinates are all specified.


The code

Here is updated code


\documentclass[10pt]{article}
\usepackage{tikz}
\usepackage[active, tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\setlength\PreviewBorder{2mm}
\usetikzlibrary{calc}

\begin{document}

\newcommand{\pgfmathsinandcos}[3]{%
  \pgfmathsetmacro{#1}{sin(#3)}%
  \pgfmathsetmacro{#2}{cos(#3)}%
}

\newcommand{\pgfmathmult}[3]{%
	\pgfmathsetmacro{#1}{#2*#3}%
}

\newcommand{\pgfmathdiv}[3]{%
	\pgfmathsetmacro{#1}{#2/#3}%
}

%Definitions
%-----------

%define the display transformation: viewing angle.  \thetadisplay defines a rotation about the x axis, and \phidisplay defines a rotation about the (rotated) z^c axis.   Note that (0,0) is standard display, where z points out of the page.  
\def\thetadisplay{60}
\def\phidisplay{110}

%define polar angles for vector to be displayed in this coordinate system
\def\thetavec{40}
\def\phivec{60}

%define Euler angles for a rotated coordinate system to be displayed within this display.
%in this example, z^l points out defined by the vector polar angles, and is antiparallel to a vector \hat{k}_\nu which points in toward the origin of the original coordinate system.
\def\alpharot{\phivec}
\def\betarot{\thetavec}
\def\gammarot{0}


%Calculations
%------------

%perform some trig for the display transformation
\pgfmathsinandcos{\sintheta}{\costheta}{\thetadisplay} 
\pgfmathsinandcos{\sinphi}{\cosphi}{\phidisplay}

\pgfmathmult{\stsp}{\sintheta}{\sinphi}
\pgfmathmult{\stcp}{\sintheta}{\cosphi}
\pgfmathmult{\ctsp}{\costheta}{\sinphi}
\pgfmathmult{\ctcp}{\costheta}{\cosphi}

%determine rotation matrix elements for display transformation
\pgfmathsetmacro{\raarot}{\cosphi}
\pgfmathsetmacro{\rabrot}{\sinphi}
%NOTE: \rac is zero for this rotation, where z^c always points vertical on the page
\pgfmathsetmacro{\racrot}{0}
\pgfmathsetmacro{\rbarot}{-\ctsp}
\pgfmathsetmacro{\rbbrot}{\ctcp}
\pgfmathsetmacro{\rbcrot}{\sintheta}
%NOTE: third row of rotation matrix not needed for display since screen z is always flat on the page.  It is, however, needed when performing further transformations using the Euler transformation.
\pgfmathsetmacro{\rcarot}{\stsp}
\pgfmathsetmacro{\rcbrot}{-\stcp}
\pgfmathsetmacro{\rccrot}{\costheta}


%perform some trig for the Euler transformation
\pgfmathsinandcos{\sinalpha}{\cosalpha}{\alpharot} 
\pgfmathsinandcos{\sinbeta}{\cosbeta}{\betarot}
\pgfmathsinandcos{\singamma}{\cosgamma}{\gammarot}

\pgfmathmult{\sasb}{\sinalpha}{\sinbeta}
\pgfmathmult{\sbsg}{\sinbeta}{\singamma}
\pgfmathmult{\sasg}{\sinalpha}{\singamma}
\pgfmathmult{\sasbsg}{\sasb}{\singamma}

\pgfmathmult{\sacb}{\sinalpha}{\cosbeta}
\pgfmathmult{\sacg}{\sinalpha}{\cosgamma}
\pgfmathmult{\sbcg}{\sinbeta}{\cosgamma}
\pgfmathmult{\sacbsg}{\sacb}{\singamma}
\pgfmathmult{\sacbcg}{\sacb}{\cosgamma}

\pgfmathmult{\casb}{\cosalpha}{\sinbeta}
\pgfmathmult{\cacb}{\cosalpha}{\cosbeta}
\pgfmathmult{\cacg}{\cosalpha}{\cosgamma}
\pgfmathmult{\casg}{\cosalpha}{\singamma}

\pgfmathmult{\cacbsg}{\cacb}{\singamma}
\pgfmathmult{\cacbcg}{\cacb}{\cosgamma}


%determine rotation matrix elements for Euler transformation
\pgfmathsetmacro{\raaeul}{\cacbcg - \sasg}
\pgfmathsetmacro{\rabeul}{-\cacbsg - \sacg}
\pgfmathsetmacro{\raceul}{\casb}
\pgfmathsetmacro{\rbaeul}{\sacbcg + \casg}
\pgfmathsetmacro{\rbbeul}{-\sacbsg + \cacg}
\pgfmathsetmacro{\rbceul}{\sasb}
\pgfmathsetmacro{\rcaeul}{-\sbcg}
\pgfmathsetmacro{\rcbeul}{\sbsg}
\pgfmathsetmacro{\rcceul}{\cosbeta}


%DEBUG: display euler matrix elements
%\raaeul\ \rabeul\ \raceul

%\rbaeul\ \rbbeul\ \rbceul

%\rcaeul\ \rcbeul\ \rcceul


%now, determine master rotation matrix to define euler-rotated coordinates within the display coordinate frame
\pgfmathmult{\raaeaa}{\raarot}{\raaeul}
\pgfmathmult{\rabeba}{\rabrot}{\rbaeul}
\pgfmathmult{\raceca}{\racrot}{\rcaeul}

\pgfmathmult{\raaeab}{\raarot}{\rabeul}
\pgfmathmult{\rabebb}{\rabrot}{\rbbeul}
\pgfmathmult{\racecb}{\racrot}{\rcbeul}

\pgfmathmult{\raaeac}{\raarot}{\raceul}
\pgfmathmult{\rabebc}{\rabrot}{\rbceul}
\pgfmathmult{\racecc}{\racrot}{\rcceul}

\pgfmathmult{\rbaeaa}{\rbarot}{\raaeul}
\pgfmathmult{\rbbeba}{\rbbrot}{\rbaeul}
\pgfmathmult{\rbceca}{\rbcrot}{\rcaeul}

\pgfmathmult{\rbaeab}{\rbarot}{\rabeul}
\pgfmathmult{\rbbebb}{\rbbrot}{\rbbeul}
\pgfmathmult{\rbcecb}{\rbcrot}{\rcbeul}

\pgfmathmult{\rbaeac}{\rbarot}{\raceul}
\pgfmathmult{\rbbebc}{\rbbrot}{\rbceul}
\pgfmathmult{\rbcecc}{\rbcrot}{\rcceul}

%the third row is not needed for screen display
%\pgfmathmult{\rcaeaa}{\rcarot}{\raaeul}
%\pgfmathmult{\rcbeba}{\rcbrot}{\rbaeul}
%\pgfmathmult{\rcceca}{\rccrot}{\rcaeul}

%\pgfmathmult{\rcaeab}{\rcarot}{\rabeul}
%\pgfmathmult{\rcbebb}{\rcbrot}{\rbbeul}
%\pgfmathmult{\rccecb}{\rccrot}{\rcbeul}

%\pgfmathmult{\rcaeac}{\rcarot}{\raceul}
%\pgfmathmult{\rcbebc}{\rcbrot}{\rbceul}
%\pgfmathmult{\rccecc}{\rccrot}{\rcceul}

%set up the master rotation matrix elements
\pgfmathsetmacro{\raa}{\raaeaa + \rabeba + \raceca}
\pgfmathsetmacro{\rab}{\raaeab + \rabebb + \racecb}
\pgfmathsetmacro{\rac}{\raaeac + \rabebc + \racecc}
\pgfmathsetmacro{\rba}{\rbaeaa + \rbbeba + \rbceca}
\pgfmathsetmacro{\rbb}{\rbaeab + \rbbebb + \rbcecb}
\pgfmathsetmacro{\rbc}{\rbaeac + \rbbebc + \rbcecc}
%the third row is not needed for screen display
%\pgfmathsetmacro{\rca}{\rcaeaa + \rcbeba + \rcceca}
%\pgfmathsetmacro{\rcb}{\rcaeab + \rcbebb + \rccecb}
%\pgfmathsetmacro{\rcc}{\rcaeac + \rcbebc + \rccecc}


%DEBUG: display master matrix elements
%\raa\ \rab\ \rac

%\rba\ \rbb\ \rbc

%\rca\ \rcb\ \rcc


%theta plane calculations:  special coordinate frame rotation such that z' points perpendicular to plane containing \theta.  This is useful for drawing arcs and lines within the theta plane
%new view rotation about z^c axis
\pgfmathsetmacro{\phitplane}{\phidisplay - \phivec}

%perform some trig
\pgfmathsinandcos{\sintheta}{\costheta}{\thetadisplay} 
\pgfmathsinandcos{\sinphi}{\cosphi}{\phitplane}

\pgfmathmult{\stsp}{\sintheta}{\sinphi}
\pgfmathmult{\stcp}{\sintheta}{\cosphi}
\pgfmathmult{\ctsp}{\costheta}{\sinphi}
\pgfmathmult{\ctcp}{\costheta}{\cosphi}

%determine rotation matrix elements for theta plane
\pgfmathsetmacro{\raatp}{\cosphi}
\pgfmathsetmacro{\rabtp}{\sinphi}
%NOTE: \rac is zero for this rotation, where one axis always points up
\pgfmathsetmacro{\rbatp}{-\ctsp}
\pgfmathsetmacro{\rbbtp}{\ctcp}
\pgfmathsetmacro{\rbctp}{\sintheta}
%NOTE: third row of rotation matrix not needed since screen z is always flat


%do some trig to determine vector position
\pgfmathsinandcos{\sinthetavec}{\costhetavec}{\thetavec} 
\pgfmathsinandcos{\sinphivec}{\cosphivec}{\phivec}
\pgfmathmult{\stcpv}{\sinthetavec}{\cosphivec}
\pgfmathmult{\stspv}{\sinthetavec}{\sinphivec}

%define half the arc length for the xy-projection and theta plane.  useful for labelling the phi and theta arcs
\pgfmathdiv{\projlenhalf}{\sinthetavec}{2}
\pgfmathdiv{\phivechalf}{\phivec}{2}
\pgfmathdiv{\thetavechalf}{\thetavec}{2}



\begin{tikzpicture}[scale=5,%
x={(\raarot cm,\rbarot cm)},y={(\rabrot cm, \rbbrot cm)},z={(\racrot, \rbcrot cm)}]

%Define some coordinates and frames
%----------------------------------

%the origin of the main coordinate system
\coordinate (Origin) at (0,0,0);

%endpoint of the vector
\coordinate (P) at ($0.8*(\stcpv,\stspv,\costhetavec) $);

%coordinate of x-y projection of vector
\coordinate (Pxy) at ($0.8*(\stcpv,\stspv,0) $);

%define the theta plane axes useful for drawing the arc and label for the elevation angle, \thetavec.  Note that the various axes are permuted and inverted to orient the rotated axes in a convenient fashion for polar coordinates
\tikzset{theta_plane_axes/.style={y={(-\raatp cm,-\rbatp cm)},z={(-\rabtp cm, -\rbbtp cm)},x={(0, \rbctp cm)}}}


%define the rotated, translated coordinate frame.  This can be translated anywhere, by adjusting the shift=(P) parameter
\tikzset{rotated_translated_axes/.style={shift=(P),x={(\raa cm,\rba cm)},y={(\rab cm, \rbb cm)},z={(\rac cm, \rbc cm)}}}



%Draw stuff
%----------

%draw axes
\draw[thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x^c$};
\draw[thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y^c$};
\draw[thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z^c$};

%draw rotated, translated axes
\draw[thick,->,rotated_translated_axes] (0,0,0) -- (.3,0,0) node[anchor=west]{$x^l$};
\draw[thick,->,rotated_translated_axes] (0,0,0) -- (0,.3,0) node[anchor=west]{$y^l$};
\draw[thick,->,rotated_translated_axes] (0,0,0) -- (0,0,.3) node[anchor=south]{$z^l$};


%DEBUG: draw axes in theta plane
%\draw[thick,->,theta_plane_axes] (0,0,0) -- (1,0,0) node[anchor=north east]{$x'$};
%\draw[thick,->,theta_plane_axes] (0,0,0) -- (0,1,0) node[anchor=north west]{$y'$};
%\draw[thick,->,theta_plane_axes] (0,0,0) -- (0,0,1) node[anchor=south]{$z'$};

%draw vector
\draw[dashed] (Origin) -- (P);
\draw[->, color=red, thick] (P) -- ($0.2*(P)$) node[xshift=3pt, anchor=west]{$\hat{k}_\nu$};;


%draw x-y projection of vector
%\draw[dashed] (Origin) -- (Pxy);

%draw connection between vector and x-y projection
\draw[dashed] (Pxy) -- (P);

%draw phi arc
\draw (\projlenhalf,0,0) arc (0:\phivec:\projlenhalf);

%draw phi label
\node[anchor=north] at (\phivechalf:\projlenhalf){$\phi_\nu$};

%draw theta arc
\draw[theta_plane_axes] (0.5,0,0) arc (0:-\thetavec:0.5);

\draw[theta_plane_axes, dashed] (.8,0,0) arc (0:-90:.8);
\draw[theta_plane_axes, dashed] (0,0-.8) -- (Origin);

%draw theta label
\node[theta_plane_axes,anchor=south west] at (-\thetavechalf:.5){$\theta_\nu$};



\end{tikzpicture}
\end{document}

The earlier code


Here is the earlier code, showing simple axes and a vector.

\documentclass[10pt]{article}
\usepackage{tikz}
\usepackage[active,tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\setlength\PreviewBorder{2mm}


\begin{document}

\newcommand{\pgfmathsinandcos}[3]{%
  \pgfmathsetmacro{#1}{sin(#3)}%
  \pgfmathsetmacro{#2}{cos(#3)}%
}

\newcommand{\pgfmathmult}[3]{%
	\pgfmathsetmacro{#1}{#2*#3}%
}

\newcommand{\pgfmathdiv}[3]{%
	\pgfmathsetmacro{#1}{#2/#3}%
}

%define the viewing angle
\def\viewtheta{60}
\def\viewphi{110}

%perform some trig
\pgfmathsinandcos{\sintheta}{\costheta}{\viewtheta} 
\pgfmathsinandcos{\sinphi}{\cosphi}{\viewphi}

\pgfmathmult{\sinthetasinphi}{\sintheta}{\sinphi}
\pgfmathmult{\sinthetacosphi}{\sintheta}{\cosphi}
\pgfmathmult{\costhetasinphi}{\costheta}{\sinphi}
\pgfmathmult{\costhetacosphi}{\costheta}{\cosphi}

%determine rotation matrix elements
\pgfmathsetmacro{\raa}{\cosphi}
\pgfmathsetmacro{\rab}{\sinphi}
%NOTE: \rac is zero for this rotation, where z always points up
\pgfmathsetmacro{\rba}{-\costhetasinphi}
\pgfmathsetmacro{\rbb}{\costhetacosphi}
\pgfmathsetmacro{\rbc}{\sintheta}
%NOTE: third row of rotation matrix not needed since screen z is always flat

\begin{tikzpicture}[scale=5,%
x={(\raa cm,\rba cm)},y={(\rab cm, \rbb cm)},z={(0, \rbc cm)}]

%define polar angles
\def\thetael{40} 						% elevation of vector
\def\phiaz{50} 							% azimuth of vector

%do some trig
\pgfmathsinandcos{\sintheta}{\costheta}{\thetael} 
\pgfmathsinandcos{\sinphi}{\cosphi}{\phiaz}

\pgfmathmult{\sinthetacosphi}{\sintheta}{\cosphi}
\pgfmathmult{\sinthetasinphi}{\sintheta}{\sinphi}

%define the length of the xy-projection, divided by two.  useful for drawing the phi arc
\pgfmathdiv{\projlenhalf}{\sintheta}{2}
\pgfmathdiv{\phiazhalf}{\phiaz}{2}
\pgfmathdiv{\thetaelhalf}{\thetael}{2}


%endpoint of the vector
\coordinate (P) at (\sinthetacosphi,\sinthetasinphi,\costheta);

%coordinate of x-y projection of vector
\coordinate (Pxy) at (\sinthetacosphi,\sinthetasinphi,0);


%theta plane calculations:  idea: rotate coordinate frame such that z' points perpendicular to plane containing \theta

%new view rotation about z axis
\pgfmathsetmacro{\tplanephi}{\viewphi - \phiaz}

%perform some trig
\pgfmathsinandcos{\sintheta}{\costheta}{\viewtheta} 
\pgfmathsinandcos{\sinphi}{\cosphi}{\tplanephi}

\pgfmathmult{\sinthetasinphi}{\sintheta}{\sinphi}
\pgfmathmult{\sinthetacosphi}{\sintheta}{\cosphi}
\pgfmathmult{\costhetasinphi}{\costheta}{\sinphi}
\pgfmathmult{\costhetacosphi}{\costheta}{\cosphi}

%determine rotation matrix elements
\pgfmathsetmacro{\raa}{\cosphi}
\pgfmathsetmacro{\rab}{\sinphi}
%NOTE: \rac is zero for this rotation, where z always points up
\pgfmathsetmacro{\rba}{-\costhetasinphi}
\pgfmathsetmacro{\rbb}{\costhetacosphi}
\pgfmathsetmacro{\rbc}{\sintheta}
%NOTE: third row of rotation matrix not needed since screen z is always flat

%the rotated axes define coordinates useful for drawing the arc and label for the elevation angle, \thetael.  Note that the various axes are permuted and inverted to orient the rotated axes in a convenient fashion for polar coordinates
\tikzset{rotated_axes/.style={y={(-\raa cm,-\rba cm)},z={(-\rab cm, -\rbb cm)},x={(0, \rbc cm)}}}





%draw axes
\draw[thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};
\draw[thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};
\draw[thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};

%DEBUG: draw rotated axes for reference and debug purposes
%\draw[thick,->,rotated_axes] (0,0,0) -- (1,0,0) node[anchor=north east]{$x'$};
%\draw[thick,->,rotated_axes] (0,0,0) -- (0,1,0) node[anchor=north west]{$y'$};
%\draw[thick,->,rotated_axes] (0,0,0) -- (0,0,1) node[anchor=south]{$z'$};


%draw vector
\draw[->] (0,0,0) -- (P);

%draw x-y projection of vector
\draw[dashed] (0,0,0) -- (Pxy);

%draw connection between vector and x-y projection
\draw[dashed] (Pxy) -- (P);

%draw phi arc
\draw (\projlenhalf,0,0) arc (0:\phiaz:\projlenhalf);

%draw phi label
\node[anchor=north] at (\phiazhalf:\projlenhalf){$\phi$};

%draw theta arc
\draw[rotated_axes] (0.5,0,0) arc (0:-\thetael:0.5);

%draw theta label
\node[rotated_axes,anchor=south west] at (-\thetaelhalf:.5){$\theta$};



\end{tikzpicture}

\end{document}