TeX - LaTeX Asked on November 1, 2021
This is a continuation of STeX Exchange 552946.
I’ve been trying to get some 3D effects on tikz-cd, and got an amazing answer by @ZhiyuanLck. The only remaining problem is getting text/arrows to be drawn along curved surfaces (or look that way). Is it possible to achieve such an effect?
For instance, how would we do that for the Rightarrow
s and their labels theta_f
and theta_g
in the diagram below?
(This diagram is the ice cream cone condition for lax slice bicategories, as in Section 7.1 of Johnson–Yau’s new book on bicategories.)
Compilable code for this diagram:
documentclass[english,11pt]{standalone}
RequirePackage{luatex85}
usepackage{tikz}
usepackage{tikz-cd}
usetikzlibrary{3d}
makeatletter
tikzset{
plane/.code args={#1and#2}{
tikz@scan@one@pointpgf@process#1
edeftemp@a{(thepgf@x, thepgf@y)};
tikz@scan@one@pointpgf@process#2
edeftemp@b{(thepgf@x, thepgf@y)};
pgfkeysalso{
plane x={temp@a},
plane y={temp@b},
canvas is plane,
}
},
}
makeatother
usepackage{libertine}
usepackage{mathtools}
usepackage[libertine]{newtxmath}
tikzcdset{
arrow style=tikz,
%diagrams={>={Straight Barb[scale=1.5]}}
diagrams={>={Stealth[round,length=4pt,width=4.95pt,inset=2.75pt]}}
}
begin{document}
newsavebox{BoxNodeOne}
savebox{BoxNodeOne}{
begin{tikzcd}[row sep={4.5em,between origins}, column sep={4.5em,between origins}, ampersand replacement=&]
{}
arrow[r, "F(A)"{plane={(1,0) and (0,0.7)}},phantom]
&
{}
end{tikzcd}
}
newsavebox{BoxNodeTwo}
savebox{BoxNodeTwo}{
begin{tikzcd}[row sep={4.5em,between origins}, column sep={4.5em,between origins}, ampersand replacement=&]
{}
arrow[r, "F(B)"{plane={(1,0) and (0,0.7)}},phantom]
&
{}
end{tikzcd}
}
newsavebox{BoxOne}
savebox{BoxOne}{
begin{tikzcd}[row sep={4.5em,between origins}, column sep={4.5em,between origins}, ampersand replacement=&]
{}
\
arrow[u, Rightarrow]
{}
end{tikzcd}
}
newsavebox{BoxTwo}
savebox{BoxTwo}{
begin{tikzcd}[row sep={3.6em,between origins}, column sep={3.6em,between origins}, ampersand replacement=&]
{}
&
{}
\
{}
arrow[ru, Rightarrow, bend right=35]
&
{}
end{tikzcd}
}
begin{tikzcd}[row sep={14.4em,between origins}, column sep={6.3em,between origins}, ampersand replacement=&]
{hspace{+1.25em}usebox{BoxNodeOne}}
arrow[rr, "F(g)"{name=3,description,plane={(1,0) and (0,0.7)}},bend left=30]
arrow[rr, "F(f)"{name=2,description,plane={(1,0) and (0,0.7)}},bend right=30]
arrow[rd, "phi_{A}"'{name=1},start anchor={[xshift=+0.5em]}]
&
&
{hspace{-1.25em}usebox{BoxNodeTwo}}
arrow[ld, "phi_{B}",start anchor={[xshift=-0.5em]}]
\
{}
&
X
&
{}
% 2-Arrows
arrow[from=1,to=1-3,"theta_{f}"{description,yshift=-0.2em},shorten=2.5em,Rightarrow,xshift=-1.0em,yshift=-1.0em,bend right=15]
arrow[from=2,to=3,"usebox{BoxOne}"{plane={(1,0) and (0,0.6)}},shorten=0.5em,phantom]
arrow[from=2,to=3,"scalebox{0.75}{$F(alpha)$}"{description,plane={(1,0) and (0,0.6)}},shorten=0.5em,phantom]
end{tikzcd}
=
begin{tikzcd}[row sep={14.4em,between origins}, column sep={6.3em,between origins}, ampersand replacement=&]
{hspace{+1.25em}usebox{BoxNodeOne}}
arrow[rr, "F(g)"{name=3,description,plane={(1,0) and (0,0.7)}},bend left=30]
arrow[rd, "phi_{A}"'{name=1},start anchor={[xshift=+0.5em]}]
&
&
{hspace{-1.25em}usebox{BoxNodeTwo}}
arrow[ld, "phi_{B}",start anchor={[xshift=-0.5em]}]
\
&
X
&
% 2-Arrows
arrow[from=1,to=1-3,"theta_{g}"{description,yshift=+0.15em},shorten=1.5em,Rightarrow,bend left=15,xshift=+0.25em,yshift=0.5em]
end{tikzcd}
end{document}
The main idea is to combine text along path
of library decorations.text
with canvas is plane
of library 3d
. In my last answer, I have shown how to place node on specified plane by set x unit vector and y unit vector. Decoration text along path
put every character in a qboxsynced
, which means before inserting the text, the current coordinate transformation matrix is applied to the current canvas transformation matrix.
I define a new decoration 3d text along path
. Its main code is from decoration text along path
, and I add some code that is from tikz@canvas@is@plane
in tikzlibrary3d.code.tex
deftikz@canvas@is@plane{
pgf@process{tikz@plane@x}%
pgf@xa=pgf@x%
pgf@ya=pgf@y%
pgf@process{tikz@plane@y}%
pgf@xb=pgf@x%
pgf@yb=pgf@y%
pgf@process{tikz@plane@origin}%
edefpgf@marshal{noexpandtikz@addtransform{%
noexpandpgftransformtriangle
{noexpandpgfqpoint{thepgf@x}{thepgf@y}}
{noexpandpgfqpoint{thepgf@xa}{thepgf@ya}}
{noexpandpgfqpoint{thepgf@xb}{thepgf@yb}}
noexpandpgftransformscale{0.035146}%
noexpandpgfsetxvec{noexpandpgfpoint{1cm}{0cm}}%
noexpandpgfsetyvec{noexpandpgfpoint{0cm}{1cm}}%
noexpandpgfsetzvec{noexpandpgfpoint{0cm}{0cm}}%
}}%
pgf@marshal%
}%
Next thing is to applied correct canvas transformation before pgfqboxsyncedpgf@hbox
. The code looks like
pgftransformtriangle%
{pgfpointxy{0}{0}}%
{pgfpointxy{1}{0}}%
{pgfpointpolarxy{<some angle>}{1}}%
pgftransformscale{0.035146}%
pgfsetxvec{pgfpointxy{1}{0}}%
pgfsetyvec{pgfpointxy{0}{1}}%
pgfsetzvec{pgfpointxy{0}{0}}%
It seems that almost nothing has been changed. Note that before this piece of code, all the transformation has been applied, which means pgfpoingxy{1}{0}
points to the direction of the path.
The key point is the y unit vector, as is said above, if <some angle>
is 90
, then y unit vector is perpendicular to x unit vector, which is the default behavior.
By setting y unit vector, there are two ways to make text appear to be placed on a curved plane:
First way is easy to implement, just set <some angle>
to <angle of constant y unit vector> - pgfdecoratedangle
(minus pgfdecoratedangle
to invert the segement rotation).
Second way need calculate angle of different y unit vector before decoration segment is decorated. This work can be done in persistent precomputation
of state typeset
persistent precomputation={
pgfmathanglebetweenpoints%
{pgfpointlineatdistance{pgfdecoratedinputsegmentcompleteddistance}{pgf@decorate@inputsegment@first}{pgf@decorate@inputsegment@last}}%
{pgf@decorate@rel@point}
xdefpgf@decorate@yvec@angle{pgfmathresult}
}
At last, define the user interface
pgfkeys{%
/pgf/decoration/.cd,
3d raise/.store in=tikz@lib@dec@te@threedimraisevar,
3d raise=0pt,
yvec/.code={tikz@handle@vec{tikz@lib@dec@te@yvec@point}{tikz@lib@dec@te@yvec@angle}#1relax}
yvec/.initial=90,
}
tikzset{
mytext/.style={
postaction=decorate,
decoration={
3d text along path,
3d raise=.8ex,
text align={align=center},#1
}
}
}
Then you can use mytext={...}
to specify a piece of text on curved plane, all original keys of /pgf/decoration
is supported and two new keys are added, you can set y unit vector to a constant vector by yvec=<angle>
or to a vector point to some point by yvec={<point>}
. Also, use 3d raise=<dimen>
to get a correct shift in y unit vector.
Here is some examples and complete code:
main.tex
documentclass[tikz, border=1cm]{standalone}
usetikzlibrary{decorations,decorations.text}
input{text.tex}
tikzset{
mytext/.style={
postaction=decorate,
decoration={
3d text along path,
3d raise=.8ex,
text align={align=center},#1
}
}
}
begin{document}
begin{tikzpicture}
draw [mytext={text along path, text={Do you know $x + y = z$? You don't know, what a pity!}}] (0, 0) arc (90:90+360:4cm and 1cm);
draw [yshift=2.5cm, mytext={text={Do you know $x + y = z$? You don't know, what a pity!}, yvec={(0,12)}}] (0, 0) arc (90:90+360:4cm and 1cm);
draw [yshift=5cm, mytext={text={Do you know $x + y = z$? You don't know, what a pity!}, yvec=90}] (0, 0) arc (90:90+360:4cm and 1cm);
draw [yshift=6cm, mytext={text={Do you know $x + y = z$? You don't know, what a pity!}, yvec=120}] (0, 0) to[relative, out=80, in=-80, distance=7cm] (0, 4);
node at (0, -1) {ellipse plane};
node at (0, 1.5) {point to curve plane};
node at (0, 4) {shifted curve plane};
end{tikzpicture}
end{document}
text.tex
makeatletter
newififtikz@lib@dec@te@yvecispoint
lettikz@lib@dec@te@yvecendpoint=pgfutil@empty
deftikz@lib@dec@te@yvecangle{90}
lettikz@lib@dec@te@threedimraisevar=pgfutil@empty
% keys for 3d text
pgfkeys{%
/pgf/decoration/.cd,
3d raise/.store in=tikz@lib@dec@te@threedimraisevar,
3d raise=0pt,
yvec/.code={tikz@handle@vec{tikz@lib@dec@te@yvec@point}{tikz@lib@dec@te@yvec@angle}#1relax}
}
% Parse yvec
% If #1 is an angle, save the angle as the direction of yvec
% If #1 is a point, parse the point and save it in
% `tikz@lib@dec@te@yvecendpoint'
deftikz@lib@dec@te@yvec@angle#1{%
tikz@lib@dec@te@yvecispointfalse%
deftikz@lib@dec@te@yvecangle{#1}%
}%
deftikz@lib@dec@te@yvec@point#1{%
tikz@lib@dec@te@yvecispointtrue%
deftikz@lib@dec@te@yvecendpoint{#1}%
}%
% 3d text along path
pgfdeclaredecoration{3d text along path}{initial}{%
state{initial}[
width=+0pt, next state=left indent,
persistent precomputation={%
edefpgf@lib@dec@text@indent@left{pgfkeysvalueof{/pgf/decoration/text align/left indent}}%
edefpgf@lib@dec@text@indent@right{pgfkeysvalueof{/pgf/decoration/text align/right indent}}%
edefpgf@lib@dec@text@align{pgfkeysvalueof{/pgf/decoration/text align/align}}%
pgfdecoratedremainingdistance=pgfdecoratedpathlength%
advancepgfdecoratedremainingdistance by-pgf@lib@dec@text@indent@rightrelax%
edefpgfdecoratedpathlength{thepgfdecoratedremainingdistance}%
pgf@lib@dec@text@getwidth%
pgf@x=pgf@lib@dec@text@widthrelax%
pgf@y=pgfdecoratedremainingdistance%
ifpgf@lib@dec@text@fit%
advancepgf@y by-pgf@lib@dec@text@indent@leftrelax%
advancepgf@y by-pgf@x%
ifpgf@lib@dec@text@stretch@spaces%
defpgf@lib@dec@text@character@shift{0pt}%
dividepgf@y bypgf@lib@dec@space@countrelax%
edefpgf@lib@dec@text@space@shift{thepgf@y}%
else%
c@pgf@counta=pgf@lib@dec@character@countrelax%
advancec@pgf@counta by-1relax%
dividepgf@y byc@pgf@countarelax%
edefpgf@lib@dec@text@character@shift{thepgf@y}%
defpgf@lib@dec@text@space@shift{0pt}%
fi%
ifdimpgf@y<0ptrelax%
pgf@lib@dec@text@fitfalse%
pgf@lib@dec@text@stretch@spacesfalse%
defpgf@lib@dec@text@character@shift{0pt}%
defpgf@lib@dec@text@space@shift{0pt}%
fi%
else%
defpgf@lib@dec@text@character@shift{0pt}%
defpgf@lib@dec@text@space@shift{0pt}%
ifxpgf@lib@dec@text@alignpgf@lib@dec@text@left@text%
else%
ifxpgf@lib@dec@text@alignpgf@lib@dec@text@right@text%
advancepgf@y by-pgf@x%
edefpgf@lib@dec@text@indent@left{thepgf@y}%
else%
advancepgf@y by-pgf@x%
advancepgf@y by-pgf@lib@dec@text@indent@leftrelax%
pgf@y=0.5pgf@y%
advancepgf@y bypgf@lib@dec@text@indent@leftrelax%
edefpgf@lib@dec@text@indent@left{thepgf@y}%
fi%
fi%
fi%
letpgfdecorationrestoftext=pgfdecorationtext%
}]{}%
state{left indent}[width=+pgf@lib@dec@text@indent@left, next state=scan]{}%
%
state{scan}[
width=+0pt,
next state=before typeset,
persistent precomputation={
pgf@lib@dec@text@scanchar%
ifvoidpgf@lib@dec@text@box%
setboxpgf@lib@dec@text@boxhbox{}%
wdpgf@lib@dec@text@box16383ptrelax%
fi%
}]{}%
%
state{before typeset}[width=+.5wdpgf@lib@dec@text@box, next state=typeset]{}%
%
state{typeset}[width=+0pt, next state=after typeset,
persistent precomputation={
iftikz@lib@dec@te@yvecispoint
pgfmathanglebetweenpoints%
{pgfpointlineatdistance{pgfdecoratedinputsegmentcompleteddistance}{pgf@decorate@inputsegment@first}{pgf@decorate@inputsegment@last}}%
{tikz@lib@dec@te@yvecendpoint}%
edeftikz@lib@dec@te@yvecangle{pgfmathresult}
fi
}
]
{%
pgftransformxshift{+-.5wdpgf@lib@dec@text@box}%
setboxpgf@hboxhbox{copypgf@lib@dec@text@box}%
pgftransformtriangle%
{pgfpointxy{0}{0}}%
{pgfpointxy{1}{0}}%
{pgfpointpolarxy{tikz@lib@dec@te@yvecangle-pgfdecoratedangle}{1}}%
% {pgfpointpolarxy{tikz@lib@dec@te@yvecangle-pgfdecoratedangle}{1}}%
pgftransformscale{0.035146}%
pgfsetxvec{pgfpointxy{1}{0}}%
pgfsetyvec{pgfpointxy{0}{1}}%
pgfsetzvec{pgfpointxy{0}{0}}%
pgftransformshift{pgfpoint{0pt}{tikz@lib@dec@te@threedimraisevar}}
pgfqboxsyncedpgf@hbox%
}%
state{after typeset}[width=+.5wdpgf@lib@dec@text@box, next state=shift,
persistent precomputation={%
ifpgf@lib@dec@text@fit%
ifpgf@lib@dec@text@stretch@spaces%
ifpgf@lib@dec@text@scan@space%
letpgf@lib@dec@text@shift=pgf@lib@dec@text@space@shift%
else%
defpgf@lib@dec@text@shift{0pt}%
fi%
else%
letpgf@lib@dec@text@shift=pgf@lib@dec@text@character@shift%
fi%
else%
defpgf@lib@dec@text@shift{0pt}%
fi%
}]{}%
state{shift}[width=+pgf@lib@dec@text@shift, next state=scan]{}%
state{final}{}%
}%
makeatother
Answered by ZhiyuanLck on November 1, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP