Asymptote does give 3D arrows when drawing three dimensional figures (they even have shade!). Could we fake it with TikZ? Here's an example, manually written, with just one arrow worked:
\documentclass{scrartcl}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\begin{document}
\begin{tikzpicture}[->,scale=2]
\draw[>={Triangle[width=3pt,length=5pt]}] (0,0,0) -- (1,0,0);
\draw[>={Triangle[width=3pt,length=5pt]}] (0,0,0) -- (0,1,0);
\draw[>={Circle[sep=-.9pt,width=3.08pt,length=2pt]Triangle[width=3pt,length=5pt]}] (0,0,0) -- (0,0,1);
\end{tikzpicture}
\end{document}
But that's (wrongly) manually calculated by trial and error (apart from the fact that it's not perfect, seeing how the other two axis —x and y— are oriented, it should have certain slant). It would be nice to have an interface like the other arrows
>={3D[length=⟨real length⟩,width=⟨real width of the base⟩]}
Which would then calculate from those values (and, of course, also taking into account x=⟨x vect⟩,y=⟨y vect⟩,z=⟨z vect⟩
)
Circle[sep=⟨value⟩,width=⟨value⟩,length=⟨value⟩,slant=⟨value⟩]
Triangle[width=⟨value⟩,length=⟨value⟩] % And I'm not sure if `slant=⟨value⟩`
% is necessary in this Triangle case
Example
Using the code from the answer (thanks to Symbol1), for instance, this code (taking the idea from another question, link in the comments)
\begin{tikzpicture}[x={(0cm,1cm)},
y={({cos(30)*1cm},{sin(30)*-1cm})},
z={({cos(20)*1cm},{sin(20)*1cm})},
thick,
scale=3]
\pgfmathsetmacro{\ax}{.4}
\pgfmathsetmacro{\ay}{.6}
\pgfmathsetmacro{\az}{-.6}
\pgfmathsetmacro{\bx}{.4}
\pgfmathsetmacro{\by}{-.7}
\pgfmathsetmacro{\bz}{-.8}
\draw[densely dotted,thin] (\ax,\ay,\az) -- (\ax,0,\az);
\draw[densely dotted,thin] (\ax,\ay,\az) -- (0,\ay,\az);
\draw[densely dotted,thin] (\ax,0,0) -- (\ax,0,\az) -- (0,0,\az);
\draw[densely dotted,thin] (0,0,\az) -- (0,\ay,\az) -- (0,\ay,0);
\draw[densely dotted,thin] (\bx,\by,\bz) -- (\bx,0,\bz);
\draw[densely dotted,thin] (\bx,\by,\bz) -- (0,\by,\bz);
\draw[densely dotted,thin] (\bx,0,0) -- (\bx,0,\bz) -- (0,0,\bz);
\draw[densely dotted,thin] (0,0,\bz) -- (0,\by,\bz) -- (0,\by,0);
\draw[-{Cone3}] (0,0,0) -- (1,0,0) node[right] {$x$};
\draw[-{Cone3}] (0,0,0) -- (0,1,0) node[above right] {$y$};
\draw[-{Cone3}] (0,0,0) -- (0,0,1) node[below right] {$z$};
\draw[-{Cone3}] (0,0,0) -- (0,0,-1) node[above left] {$-z$};
\draw[thin] (0,0,0) -- (0,-.9,0);
\draw[-{Cone3}] (0,0,0) -- (\ax,\ay,\az) node[below left] {source};
\draw[-{Cone3}] (0,0,0) -- (\bx,\by,\bz) node[above right] {sim};
\end{tikzpicture}
gives this figure
Best Answer
This approach allows you to assign a pitch from
0
to90
, where0
means lying on screen and90
means perpendicular to screen. (Certainly you can assign91.1
or5566
or any good number. Currently theabs
in the code wipe out all funny situations.)Once you fix the pitch, the
drawing code
part will do some necessary calculations such as positions of the tangent points. Notice that while predefined arrow tips have some additional options such asopen
andharpoon
, my tip does not implement them. I even usefill
to get rid of line-width issue.Update
I a wrote three tips
Cone1
,Cone2
, andCone3
.Cone1
It uses pitch like the
Cone
above except that it now checks ifsin(pitch)
is positive. If so, it presumes that the arrow is pointing to your eyes and hence add a white dot. (At the same time,cos(pitch)
is mandatorily positive.)Speaking of dots, it is hard to decide the correct size and the color of it. Currently
Cone1
reads the setting inline width
and fill a white circle of which the diameter is the width. This is a good idea since the line width is not used anywhere else but also a bad idea if you do care about code legibility.Cone2
This tip accepts a tangent option by
tangent={(1,2,3)}
so that it can calculate the pitch (actually the sine and cosine of it).The problem is, throughout the world of TikZ no one has ever cared about projections of 3D-vectors perpendicular to the screen. If we are lucky enough that the current projection to the screen is of orthogonal type, which is probably assigned by
tikz-3dplot
, then the projection perpendicular to the screen is well defined in mathematics manner up to a sign. (We cannot tell the difference between into screen and out of screen given the projection to the screen.)Unfortunately in most cases if you assign
x=
,y=
, andz=
by hand it would not be a orthogonal projection.Here I simply use a cross-product to calculate the missing projection subjected to the condition that the result will be correct if one uses
tikz-3dplot
to assign the projection.More precisely,
\tikz@scan@one@point
is a command used in TikZ to parse the coordinates such as(1,2)
,(3:4)
,(A)
, or(5,6,7)
. When I writeand
#1
is, say,(5,6,7)
, TikZ will end up withAnd then, according to the definition of
\pgfarrowtangenttosincos
, PGF will executesNow
(\pgftemp@x,\pgftemp@y,\pgftemp@z)
is(5,6,7)
.(\pgf@x,\pgf@y)
is the projection of(5,6,7)
on the screen.(\tdplotresx,\tdplotresy,\tdplotresz)
is the result of cross product.So
sqrt(\pgf@x*\pgf@x+\pgf@y*\pgf@y)
is the length on the screen(\tdplotresx,\tdplotresy,\tdplotresz)
and(\tdplotresx,\tdplotresy,\tdplotresz)
is the length off the screenSo
a/c
b/c
After that everything works like
Cone1
.Cone3
I hack
\pgfpointxyz
so that it buffers two recent 3D-coordinates. While the arrow tip is being drawn, I presume that the old, buffered coordinate is the end and the older, buffered coordinate is the start. So the tangent should be the difference of these two coordinate.Code