Drawing ramified coverings with tikz
I want to draw a diagram similar to this one:
For that I started with the following code:
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,0) -- (7,0);
draw[thick] (1,2) -- (7,2);
draw[thick] (1,2.5) -- (7,2.5);
draw[thick] (1,1.5) -- (7,1.5);
end{tikzpicture}
The only thing that I don't know how to do is the curvy parts. I would appreciate some indication.
tikz-pgf
add a comment |
I want to draw a diagram similar to this one:
For that I started with the following code:
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,0) -- (7,0);
draw[thick] (1,2) -- (7,2);
draw[thick] (1,2.5) -- (7,2.5);
draw[thick] (1,1.5) -- (7,1.5);
end{tikzpicture}
The only thing that I don't know how to do is the curvy parts. I would appreciate some indication.
tikz-pgf
Although it doesn't havecurvy
links,tikz-timing
could be useful to draw this kind of diagrams.
– Ignasi
17 hours ago
add a comment |
I want to draw a diagram similar to this one:
For that I started with the following code:
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,0) -- (7,0);
draw[thick] (1,2) -- (7,2);
draw[thick] (1,2.5) -- (7,2.5);
draw[thick] (1,1.5) -- (7,1.5);
end{tikzpicture}
The only thing that I don't know how to do is the curvy parts. I would appreciate some indication.
tikz-pgf
I want to draw a diagram similar to this one:
For that I started with the following code:
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,0) -- (7,0);
draw[thick] (1,2) -- (7,2);
draw[thick] (1,2.5) -- (7,2.5);
draw[thick] (1,1.5) -- (7,1.5);
end{tikzpicture}
The only thing that I don't know how to do is the curvy parts. I would appreciate some indication.
tikz-pgf
tikz-pgf
edited 2 days ago
Cragfelt
2,96531028
2,96531028
asked 2 days ago
Gabriel RibeiroGabriel Ribeiro
35519
35519
Although it doesn't havecurvy
links,tikz-timing
could be useful to draw this kind of diagrams.
– Ignasi
17 hours ago
add a comment |
Although it doesn't havecurvy
links,tikz-timing
could be useful to draw this kind of diagrams.
– Ignasi
17 hours ago
Although it doesn't have
curvy
links, tikz-timing
could be useful to draw this kind of diagrams.– Ignasi
17 hours ago
Although it doesn't have
curvy
links, tikz-timing
could be useful to draw this kind of diagrams.– Ignasi
17 hours ago
add a comment |
7 Answers
7
active
oldest
votes
This uses the same in and out
trick as Skillmon and puts it into a style dip
, which takes as arguments the horizontal position and the depth, where the sign decides whether the dip is a dip (minus) or a bump (plus). (Let me also mention that you do not need to do something like draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
. If you name the nodes, you can just do draw[<-] (Y) -- (X) node[left, midway] {$f$};
and TikZ will make sure to shorten the arrow without you having to compute the coordinates.)
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style args={#1/#2}{/utils/exec=stepcounter{dip},
insert path={%
coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3)
(aux1) -- (aux2|-aux1) to[out=0,in=180]
++({abs(#2)},#2) coordinate(dip-thevalue{dip}) to[out=0,in=180] (aux3|-aux1)
}}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) [dip=5.5cm/-2.5mm]-- (7,2.5);
fill (dip-1) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,2) [dip/.list={2.5cm/-2.5mm,5.5cm/2.5mm}] -- (7,2);
fill (dip-2) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,1.5) [dip/.list={2.5cm/2.5mm,5.5cm/-5mm}] -- (7,1.5);
fill (dip-5) circle[radius=2pt] node[above right=0pt and 5pt]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) [dip=5.5cm/5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
fill (dip-X|-Y) circle[radius=2pt];
draw[dashed] (dip-X|-Y) -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Just for fun: a variation for Skillmon.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style={/utils/exec=stepcounter{dip},
insert path={%
to[out=0,in=180]
++(0.25,#1) node[bullet](dip-thevalue{dip}){}
to[out=0,in=180] ++(0.25,-1*#1)
}},bullet/.style={circle,fill,inner sep=1.5pt}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) -- (5.25,2.5) [dip=-2.5mm]-- (7,2.5);
node[right=3pt of dip-1]{$b=1$};
draw (1,2) -- (2.25,2) [dip=-2.5mm] -- (5.25,2) [dip=2.5mm] --(7,2);
node[right=3pt of dip-2]{$b=1$};
draw (1,1.5) -- (2.25,1.5) [dip=2.5mm] --(5.25,1.5) [dip=-5mm] -- (7,1.5);
node[above right=-1.5pt and 5pt of dip-5]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) --(5.25,0.5) [dip=5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
draw[dashed] (dip-X|-Y) node[bullet]{} -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Very nice automation. Just out of curiosity (coding TikZ is still something I'm not good at), is it possible to also draw the filled circles inside thedip
style?
– Skillmon
yesterday
1
@Skillmon Yes, of course. E.g.dip/.style args={#1/#2}{/utils/exec=stepcounter{dip}, insert path={% coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3) (aux1) -- (aux2|-aux1) to[out=0,in=180] ++({abs(#2)},#2) node[circle,fill,inner sep=1.5pt](dip-thevalue{dip}){} to[out=0,in=180] (aux3|-aux1) }}
.
– marmot
yesterday
add a comment |
The following is a pretty manual way to do this. I only did it for the first two lines, I hope you can apply it to the other occurrences. It uses the in
and out
keys of the to
path construction:
documentclass[tikz]{standalone}
begin{document}
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,2.5) -- (7,2.5) coordinate(a);
draw[thick] (1,2) -- (7,2) coordinate(b);
draw[thick] (1,1.5) -- (7,1.5) coordinate(c);
draw[thick] (1,0) -- (7,0) coordinate(d);
draw[thick]
(a) ++(.25,-.25) coordinate(ab) to[out=180,in=0] (a)
(ab) to[out=180,in=0] (b)
(ab) to[out=0,in=180] ++(.25,.25)
(ab) to[out=0,in=180] ++(.25,-.25)
;
filldraw
(ab) circle(.05)
;
end{tikzpicture}
end{document}
add a comment |
This is a proof of concept how to use parser
library to define the following "language" :
=
stay at the same level
u
goes half up
U
goes one up
d
goes half down
D
goes one down
x
plot a (red) dot
.
end
Here is the code.
documentclass[tikz,border=7pt]{standalone}
usepgfmodule{parser}
% -----------------------------
% macro that add #1 to the current path when used inside pgfextra
definsertpath#1{tikzset{insert path={#1}}}
% define the parser "sheet path"
pgfparserdef{sheet path}{initial}{the character =}{insertpath{ -- ++(1, 0)}}
pgfparserdef{sheet path}{initial}{the letter u}{insertpath{ to[out=0,in=180] ++(1, .5)}}
pgfparserdef{sheet path}{initial}{the letter U}{insertpath{ to[out=0,in=180] ++(1, 1)}}
pgfparserdef{sheet path}{initial}{the letter d}{insertpath{ to[out=0,in=180] ++(1,-.5)}}
pgfparserdef{sheet path}{initial}{the letter D}{insertpath{ to[out=0,in=180] ++(1, -1)}}
pgfparserdef{sheet path}{initial}{the letter x}{insertpath{ node[red,scale=4]{.}}}
pgfparserdef{sheet path}{initial}{the character .}{pgfparserswitch{final}}
% the sheet interface macro
defsheet#1.{pgfextra{pgfparserparse{sheet path}#1.}}
% -----------------------------
begin{document}
tikzdraw[thick]
(0,-1) sheet======du=.
(0,-2) sheet==du==uxd=.
(0,-3) sheet==uxd==DU=.
(0,-4) sheet=========.
(0,-5) sheet======UxD=.;
end{document}
3
+1, I didn't know parser, wow!
– CarLaTeX
yesterday
add a comment |
I think Bézier curves can do the task. It can make sure that the starting points and the ending points of all curves are placed with a consistent ratio.
documentclass[tikz]{standalone}
definnersep{.5em}
usetikzlibrary{patterns}
begin{document}
begin{tikzpicture}[y=baselineskip+2*innersep]
draw (0,0)--(6.5,0);
draw (0,1.5)--(6.5,1.5);
draw (0,1)--(4.75,1) .. controls (4.9,1) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,1) .. (5.25,1)--(6.5,1);
draw (0,2)--(1.25,2) .. controls (1.4,2) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2) .. (1.75,2)--(4.75,2) .. controls (4.9,2) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,2) .. (5.25,2)--(6.5,2);
draw (0,2.5)--(1.25,2.5) .. controls (1.4,2.5) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2.5) .. (1.75,2.5)--(4.75,2.5) .. controls (4.9,2.5) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,2.5) .. (5.25,2.5)--(6.5,2.5);
draw (0,3)--(4.75,3) .. controls (4.9,3) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,3) .. (5.25,3)--(6.5,3);
fill (1.5,2.25) circle (1.5pt) (5,1.5) circle (1.5pt) (5,2.75) circle (1.5pt) (1.5,0) circle (1.5pt) (5,0) circle (1.5pt);
draw[dashed] (1.5,0)--(1.5,3.25) (5,0)--(5,3.25);
draw (1.5,2.25) node[right=1ex] {$b=1$};
draw (5,1.75) node[right=1ex] {$b=2$};
draw (5,2.75) node[right=1ex] {$b=1$};
draw (-0.5,0) node (n) {$N$};
draw (-0.5,2.25) node (m) {$M$};
draw[-latex] (m)--(n) node[midway,left] {$f$};
end{tikzpicture}
end{document}
add a comment |
This is a proof of concept how to use plot[smooth] coordinates
to do this.
documentclass[tikz,border=7pt]{standalone}
begin{document}
begin{tikzpicture}[yscale=.5,
sheet/.style={red,thick,smooth},
point/.style={insert path={node[scale=4]{.}}}]
% ---- sheets ----
foreach[count=i] pts in {
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7,-1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2,-1) (3, 0) (6, 0) (7, 1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 1) (3, 0) (6, 0) (7,-2) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 0) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 2) (8, 0) (9, 0)},%
{(0,0)},%
{(0, 0) (9, 0)}%
}{
draw[yshift=-2*i cm,sheet] plot coordinates {pts};
}
% ---- singular points and labels ----
path
(2,-5) [point] (3,-5) node{$b=1$}
(7,-3) [point] (8,-3) node{$b=1$}
(7,-8) [point] (8,-7.5) node{$b=2$}
;
% ---- vertical lines ----
draw[dashed]
(2,0) -- +(0,-14) [point]
(7,0) -- +(0,-14) [point]
;
end{tikzpicture}
end{document}
add a comment |
This is a proof of concept how to use turtle
library to define the following "moves" :
up
make an up bump
up*
make an up bump with (red) dot
dn
make a down bump
dn*
make a down bump with (red) dot
*
put a (red) dot
documentclass[tikz,border=7pt]{standalone}
usetikzlibrary{turtle}
tikzset{
turtle/.cd,
how/.style={out=0,in=180},
home/.append style=right,
*/.style={/tikz/insert path={node[red,scale=4]{.}}},
forward/.default=1.4142135,
up/.style={left=45,forward,right,forward,left=45},
up*/.style={left=45,forward,*,right,forward,left=45},
dn/.style={right=45,forward,left,forward,right=45},
dn*/.style={right=45,forward,*,left,forward,right=45},
next/.style={/tikz/yshift=-1cm}
}
begin{document}
tikz
draw[thick]
[turtle={home,fd,fd,fd,fd,fd,fd,dn,fd},next,next]
[turtle={home,fd,fd,dn,fd,fd,up*,fd},next,next]
[turtle={home,fd,fd,up*,fd,fd,dn,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,fd,fd,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,up*,fd}]
;
end{document}
add a comment |
Edit notes:
spaces can now be handled, but are ignored by default
alternative syntax for
myparserdef
added
Inspired by Kpym's answer but thinking that the possibilities of pgfparser
are insufficient, I wrote my own parser, that should be similar to pgf's, but allows the parsed elements to grab arguments. The following arguments are supported:
m
a mandatory argument
r{<delim>}
a mandatory argument delimited by<delim>
o
an optional argument in brackets (you can test for it withmyIfNoValueTF
)
O{<default>}
an optional argument defaulting to<default>
d<delim1><delim2>
an optional argument delimited by<delim1>
and<delim2>
(you can test for it withmyIfNoValueTF
)
D<delim1><delim2>{<default>}
an optional argument delimited by<delim1>
and<delim2>
defaulting to<default>
t<token>
an optional token (you can test for it withmyIfBooleanTF
)
Despite the fact that the argument names are inspired by xparse
, none of them care for balanced delimiters (e.g., [[ab]]
would not be grabbed as [ab]
, but as [ab
with an orphaned ]
, so you'd have to use [{[ab]}]
, this is like the LaTeX2e behaviour). Also all arguments are long by nature unlike xparse
where you'd need to specify that using +
.
You can define a parser using myparserdef{<name>}{<state>}{<meaning>}[<args>]{<code>}
, with <name>
the name of the parser, <state>
the state, <meaning>
the meaning of a token, <args>
an argument string (built from the arguments listed above, up to 9 arguments are supported) and <code>
the code which should be executed for that combination of <name>
, <state>
and <meaning>
. You can switch states using myparserstate{<state>}
, if you switch to final
parsing is ended, the initial state is initial
. All in all this is pretty similar to pgfparser
s way of doing things. The biggest difference is, that my parser ignores blanks by default, however you can define an action for blanks with myparserdef{<name>}{<state>}{blank space}[<args>]{<code>}
. Many consecutive blanks are considered as one.
You can also use a different syntax for myparserdef
namely: myparserdef{<name>}{<state>}<token>[<args>]{<code>}
with <token>
being a single token not surrounded by {}
(spaces are ignored here, it is not checked whether <token>
is really a single token, so be careful). In that case the same will be done as if you'd typed the meaning
of the <token>
, so myparserdef{foo}{bar}a{baz}
does the same as myparserdef{foo}{bar}{the letter a}{baz}
.
A parser is executed using myparserrun{<name>}
. A parser needs at least one rule, else an error is thrown.
This parser system allows me to place coordinates inside of sheet
, which was the main reason why I found pgfparser
insufficient.
I did all of this without using any packages, only LaTeX-Kernel macros, just for procrastinating reasons. Using for example xparse
would shorten the code considerably.
documentclass[tikz,border=7pt]{standalone}
% my parser >>>
makeatletter
% logic helpers >>>
longdefmyfi@An#1fi#2{fi}
longdefmyfi@Ay#1fi#2{fi#2}
longdefmyfi@Byfi#1{fi#1}
longdefmyfi@BTbfi#1#2#3{fi#2}
% <<<
% mynewdef >>>
@ifdefinablemyifundefTF%>>>
{%
defmyifundefTF#1%
{%
ifdefined#1%
myfi@Ay
else
myfi@BTb
fi
{%
ifx#1relax
myfi@BTb
fi
@secondoftwo
}%
}%
}%<<<
@ifdefinablemynewdef%>>>
{%
protecteddefmynewdef{@ifnextchar[{mynewdef@a}{mynewdef@a}}%
}%<<<
@ifdefinablemynewdef@a%>>>
{%
longdefmynewdef@a[#1]#2#3#{mynewdef@b{#1}#2{#3}}%
}%<<<
@ifdefinablemynewdef@b%>>>
{%
protectedlongdefmynewdef@b#1#2#3#4%
{%
myifundefTF#2%
{%
#1def#2#3{#4}%
}
{%
PackageError{my}{Command string#2space already defined}{}%
}
}%
}%<<<
mynewdefmyifcsundefTF#1%>>>
{%
ifcsname #1endcsname
myfi@Ay
else
myfi@BTb
fi
{%
expandafterifxcsname #1endcsnamerelax
myfi@BTb
fi
@secondoftwo
}%
}%<<<
% <<<
% opt arg parsing >>>
mynewdef[long]my@ifmark#1%>>>
{%
ifxmy@mark#1%
myfi@BTb
fi
@secondoftwo
}%<<<
mynewdef[protectedlong]myoarg@oarg#1%>>>
{%
@ifnextchar[{myoarg@oarg@{#1}}{#1{my@mark}}%
}%<<<
mynewdef[long]myoarg@oarg@#1[#2]%>>>
{%
#1{#2}%
}%<<<
mynewdef[protectedlong]myoarg@Oarg#1#2%>>>
{%
@ifnextchar[{myoarg@oarg@{#2}}{#2{#1}}%
}%<<<
mynewdef[protectedlong]myoarg@darg#1#2#3%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#3}}{#3{my@mark}}%
}%<<<
mynewdef[protectedlong]myoarg@Darg#1#2#3#4%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#4}}{#4{#3}}%
}%<<<
% <<<
% macros for string comparison >>>
begingroupdef:{endgroupletmy@sptoken= }:
mynewdefmy@mark{my@mark@if@you@see@this@report@it}
mynewdefmy@stop{my@stop@if@you@see@this@report@it}
mynewdefmyparser@final{final}
mynewdefmyparser@noarg{noarg}
mynewdefmyparser@blankspace{blank space}
% <<<
mynewdef[protected]myparserdef#1#2%>>>
{%
defmyparserdef@twoargs{{#1}{#2}}%
myparserdef@a
}%<<<
mynewdef[protected]myparserdef@a%>>>
{%
futureletmyparserdef@argmyparserdef@b
}%<<<
mynewdefmyparserdef@b%>>>
{%
ifxmyparserdef@argmy@sptoken
myfi@BTb
fi
@secondoftwo
{myparserdef@gobble@space}
{myparserdef@c}%
}%<<<
mynewdef[protected]myparserdef@gobble@space%>>>
{%
afterassignmentmyparserdef@a
letmyparserdef@arg= % space after = to get spaces, too
}%<<<
mynewdef[protected]myparserdef@c#1%>>>
{%
ifxmyparserdef@argbgroup
myfi@BTb
fi
@secondoftwo
{%
defmyparserdef@arg{#1}%
ifxmyparser@blankspacemyparserdef@arg
myfi@BTb
fi
@secondoftwo
{expandaftermyparserdef@dmyparserdef@twoargs{blank space space}}
{expandaftermyparserdef@dmyparserdef@twoargs{#1}}%
}
{expandaftermyparserdef@dmyparserdef@twoargs{meaningmyparserdef@arg}}%
}%<<<
mynewdef[long]myparserdef@d#1#2#3%>>>
{%
myoarg@oarg{myparserdef@e{#1}{#2}{#3}}%
}%<<<
mynewdef[protectedlong]myparserdef@e#1#2#3#4#5%>>>
{%
% if that is the first rule for the parser, define space to be a no-op
myifcsundefTF{myparser@name{#1}}%
{%
expandafterdef
csname myparser@name{#1} all blank space spacespace typeendcsname
{noarg}%
expandafterdef
csname myparser@name{#1} all blank space spacespace codeendcsname
{myparser@getnexttoken}%
% use a macro as a flag that there is at least one parser rule defined
expandafterdefcsnamemyparser@name{#1}endcsname{}%
}
{}%
% check whether the optional argument was used
my@ifmark{#4}
{%
% use a macro as flag that there is a rule for the specified state
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{noarg}%
defmyparserdef@arg@count{0}%
}
{%
edefmyparserdef@arg@count
{thenumexprmyparserdef@argcount#4my@mark}%
ifnummyparserdef@arg@count>9%
PackageError{my}{Too many arguments for parser rule}
{A maximum of 9 parameters is supported.}%
else
ifnummyparserdef@arg@count=0%
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname
{noarg}%
else
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{#4}%
fi
fi
}%
% define what the code does
expandafterdefcsname myparser@name{#1} #2 #3 codeendcsname{}%
expandafterrenewcommandcsname myparser@name{#1} #2 #3 codeendcsname
[myparserdef@arg@count]{#5myparser@getnexttoken}%
}%<<<
mynewdefmyparserdef@argcount#1%>>>
{%
ifxmy@mark#1%
myfi@An
else
myfi@By
fi
{csname myparserdef@argcount@#1endcsname}%
}%<<<
mynewdefmyparserdef@argcount@m%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@o%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@O#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@d#1#2%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@D#1#2#3%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@r#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@t#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparser@name#1%>>>
{%
myparser #1%
}%<<<
mynewdefmyparser@rule#1%>>>
{%
myparser@currentspace #1space meaningmyparser@token
}%<<<
mynewdefmyparser@type#1%>>>
{%
myparser@rule{#1} type%
}%<<<
mynewdefmyparser@code#1%>>>
{%
myparser@rule{#1} code%
}%<<<
mynewdef[protected]myparserrun#1%>>>
{%
myifcsundefTF{myparser@name{#1}}
{PackageError{my}{No parser named '#1' defined}{}}
{%
edefmyparser@current{myparser@name{#1}}%
defmyparser@usersname{#1}%
defmyparser@state{initial}%
myparser@getnexttoken
}%
}%<<<
mynewdef[protected]myparser@getnexttoken%>>>
{%
ifxmyparser@statemyparser@final
myfi@An
else
myfi@By
fi
{%
afterassignmentmyparser@getnexttoken@a
letmyparser@token= % space after = to get spaces, too
}%
}%<<<
mynewdef[protected]myparser@getnexttoken@a%>>>
{%
myifcsundefTF{myparser@type{myparser@state}}
{%
myifcsundefTF{myparser@type{all}}
{%
PackageError{my}
{%
No rule for parser 'myparser@usersname' in state
'myparser@state' for 'meaningmyparser@token'.
Ignoring token%
}
{}%
myparser@getnexttoken
}
{myparser@handle{all}}%
}
{myparser@handle{myparser@state}}%
}%<<<
mynewdef[protected]myparser@handle#1%>>>
{%
expandafterifxcsnamemyparser@type{#1}endcsnamemyparser@noarg
myfi@BTb
fi
@secondoftwo
{csnamemyparser@code{#1}endcsname}
{myparser@handle@args{#1}}%
}%<<<
mynewdef[protected]myparserstate#1%>>>
{%
defmyparser@state{#1}%
}%<<<
mynewdef[protected]myparser@handle@args#1%>>>
{%
defmyparser@stateorall{#1}%
expandafterexpandafterexpandaftermyparser@handle@args@a
csnamemyparser@type{#1}endcsnamemy@markmy@stop
}%<<<
mynewdefmyparser@handle@args@a%>>>
{%
myparser@handle@args@b{}%
}%<<<
mynewdef[long]myparser@handle@args@b#1#2#3my@stop%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{%
myifcsundefTF{myparser@handle@args@#2}
{PackageError{my}{Unknown argument type '#2'}{}}
{csname myparser@handle@args@#2endcsname{#1}{#3}}%
}%
}%<<<
mynewdef[long]myparser@handle@args@c#1#2%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{myparser@handle@args@b{#1}#2my@stop}%
}%<<<
mynewdef[long]myparser@handle@args@m#1#2#3%>>>
{%
myparser@handle@args@c{#1{#3}}{#2}%
}%<<<
mynewdef[protectedlong]myparser@handle@args@o#1#2%>>>
{%
myoarg@oarg{myparser@handle@args@m{#1}{#2}}%
}%<<<
mynewdef[long]myparser@handle@args@O#1#2%>>>
{%
myparser@handle@args@O@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@O@#1#2#3my@stop%>>>
{%
myoarg@Oarg{#2}{myparser@handle@args@m{#1}{#3}}%
}%<<<
mynewdef[long]myparser@handle@args@d#1#2%>>>
{%
myparser@handle@args@d@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@d@#1#2#3#4my@stop%>>>
{%
myoarg@darg{#2}{#3}{myparser@handle@args@m{#1}{#4}}%
}%<<<
mynewdef[long]myparser@handle@args@D#1#2%>>>
{%
myparser@handle@args@D@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@D@#1#2#3#4#5my@stop%>>>
{%
myoarg@Darg{#2}{#3}{#4}{myparser@handle@args@m{#1}{#5}}%
}%<<<
mynewdef[long]myparser@handle@args@r#1#2%>>>
{%
myparser@handle@args@r@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@r@a#1#2#3my@stop%>>>
{%
defmyparser@handle@args@r@b##1#2{myparser@handle@args@c{#1{##1}}{#3}}%
myparser@handle@args@r@b
}%<<<
mynewdef[long]myparser@handle@args@t#1#2%>>>
{%
myparser@handle@args@t@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@t@a#1#2#3my@stop%>>>
{%
@ifnextchar#2%
{@firstoftwo{myparser@handle@args@c{#1{my@mark}}{#3}}}
{myparser@handle@args@c{#1{my@stop}}{#3}}%
}%<<<
letmyIfNoValueTFmy@ifmark
letmyIfBooleanTFmy@ifmark
makeatother
% <<<
% the sheet path parser >>>
makeatletter
definsertpath#1{edefsheetpath@{unexpandedexpandafter{sheetpath@#1}}}
defsheetpathcurve#1#2#3#4%
{%
to[out=0,in=180] ++({.5*sheetlength},{#1*sheetheight})
myIfBooleanTF{#3}{}{node[scale=sheetdotsize,inner sep=0pt]{.}}
myIfNoValueTF{#4}{}{coordinate({#4})}
to[out=0,in=180] ++({.5*sheetlength},{#2*sheetheight})
}
defoutputpath{drawsheetpath@;}
defsheet{defsheetpath@{}myparserrun{sheet path}}
makeatother
myparserdef{sheet path}{initial}{the character (}[r,r)]
{insertpath{({#1},{#2*sheetheight})}myparserstate{started}}
myparserdef{sheet path}{initial}{the character =}
{insertpath{(0,0)--++(sheetlength,0)}myparserstate{started}}
myparserdef{sheet path}{started}{the character (}[r)]
{insertpath{coordinate({#1})}}
myparserdef{sheet path}{started}{the character =}
{insertpath{--++(sheetlength,0)}}
myparserdef{sheet path}{started}{the letter u}[txd()]
{insertpath{sheetpathcurve{.5}{-.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter U}[txd()]
{insertpath{sheetpathcurve{1}{-1}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter d}[txd()]
{insertpath{sheetpathcurve{-.5}{.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter D}[txd()]
{insertpath{sheetpathcurve{-1}{1}{#1}{#2}}}
% making '.' a no-op, that way we can use it to end the search for an optional
% argument for instance
myparserdef{sheet path}{started} .{}
myparserdef{sheet path}{started}{the letter x}[d()]
{%
myIfNoValueTF{#1}
{%
insertpath
{--node[scale=sheetdotsize,inner sep=0pt]{.}++(sheetlength,0)}%
}
{%
insertpath
{%
--node[scale=sheetdotsize,inner sep=0pt]{.}
coordinate({#1})
++(sheetlength,0)
}%
}%
}
myparserdef{sheet path}{all}{the character ;}{myparserstate{final}outputpath}
% <<<
% sheet height and sheet length >>>
pgfset
{
,sheet height/.store in=sheetheight
,sheet height=1
,sheet length/.store in=sheetlength
,sheet length=1
,sheet dot size/.store in=sheetdotsize
,sheet dot size=4
}
%<<<
begin{document}
begin{tikzpicture}
begin{scope}[thick, sheet height=0.75]
sheet === ===d(sp1)=;
sheet (0,-1) ==d(sp2)===ux =;
sheet (0,-2) ==ux ===Dx =;
sheet (0,-3) === ==== =;
sheet (0,-4) === ===U(sp3)=;
sheet (0,-5.5) ==x(b1) ===x(b2) =;
end{scope}
draw[dashed]
(b2) -- (sp1)
(b1) -- (sp2)
;
end{tikzpicture}
end{document}
Wow ! This is a huge work. I'm impressed (like always I'm by the texperts ;)).
– Kpym
4 hours ago
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "85"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f481125%2fdrawing-ramified-coverings-with-tikz%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
7 Answers
7
active
oldest
votes
7 Answers
7
active
oldest
votes
active
oldest
votes
active
oldest
votes
This uses the same in and out
trick as Skillmon and puts it into a style dip
, which takes as arguments the horizontal position and the depth, where the sign decides whether the dip is a dip (minus) or a bump (plus). (Let me also mention that you do not need to do something like draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
. If you name the nodes, you can just do draw[<-] (Y) -- (X) node[left, midway] {$f$};
and TikZ will make sure to shorten the arrow without you having to compute the coordinates.)
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style args={#1/#2}{/utils/exec=stepcounter{dip},
insert path={%
coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3)
(aux1) -- (aux2|-aux1) to[out=0,in=180]
++({abs(#2)},#2) coordinate(dip-thevalue{dip}) to[out=0,in=180] (aux3|-aux1)
}}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) [dip=5.5cm/-2.5mm]-- (7,2.5);
fill (dip-1) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,2) [dip/.list={2.5cm/-2.5mm,5.5cm/2.5mm}] -- (7,2);
fill (dip-2) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,1.5) [dip/.list={2.5cm/2.5mm,5.5cm/-5mm}] -- (7,1.5);
fill (dip-5) circle[radius=2pt] node[above right=0pt and 5pt]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) [dip=5.5cm/5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
fill (dip-X|-Y) circle[radius=2pt];
draw[dashed] (dip-X|-Y) -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Just for fun: a variation for Skillmon.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style={/utils/exec=stepcounter{dip},
insert path={%
to[out=0,in=180]
++(0.25,#1) node[bullet](dip-thevalue{dip}){}
to[out=0,in=180] ++(0.25,-1*#1)
}},bullet/.style={circle,fill,inner sep=1.5pt}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) -- (5.25,2.5) [dip=-2.5mm]-- (7,2.5);
node[right=3pt of dip-1]{$b=1$};
draw (1,2) -- (2.25,2) [dip=-2.5mm] -- (5.25,2) [dip=2.5mm] --(7,2);
node[right=3pt of dip-2]{$b=1$};
draw (1,1.5) -- (2.25,1.5) [dip=2.5mm] --(5.25,1.5) [dip=-5mm] -- (7,1.5);
node[above right=-1.5pt and 5pt of dip-5]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) --(5.25,0.5) [dip=5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
draw[dashed] (dip-X|-Y) node[bullet]{} -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Very nice automation. Just out of curiosity (coding TikZ is still something I'm not good at), is it possible to also draw the filled circles inside thedip
style?
– Skillmon
yesterday
1
@Skillmon Yes, of course. E.g.dip/.style args={#1/#2}{/utils/exec=stepcounter{dip}, insert path={% coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3) (aux1) -- (aux2|-aux1) to[out=0,in=180] ++({abs(#2)},#2) node[circle,fill,inner sep=1.5pt](dip-thevalue{dip}){} to[out=0,in=180] (aux3|-aux1) }}
.
– marmot
yesterday
add a comment |
This uses the same in and out
trick as Skillmon and puts it into a style dip
, which takes as arguments the horizontal position and the depth, where the sign decides whether the dip is a dip (minus) or a bump (plus). (Let me also mention that you do not need to do something like draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
. If you name the nodes, you can just do draw[<-] (Y) -- (X) node[left, midway] {$f$};
and TikZ will make sure to shorten the arrow without you having to compute the coordinates.)
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style args={#1/#2}{/utils/exec=stepcounter{dip},
insert path={%
coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3)
(aux1) -- (aux2|-aux1) to[out=0,in=180]
++({abs(#2)},#2) coordinate(dip-thevalue{dip}) to[out=0,in=180] (aux3|-aux1)
}}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) [dip=5.5cm/-2.5mm]-- (7,2.5);
fill (dip-1) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,2) [dip/.list={2.5cm/-2.5mm,5.5cm/2.5mm}] -- (7,2);
fill (dip-2) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,1.5) [dip/.list={2.5cm/2.5mm,5.5cm/-5mm}] -- (7,1.5);
fill (dip-5) circle[radius=2pt] node[above right=0pt and 5pt]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) [dip=5.5cm/5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
fill (dip-X|-Y) circle[radius=2pt];
draw[dashed] (dip-X|-Y) -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Just for fun: a variation for Skillmon.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style={/utils/exec=stepcounter{dip},
insert path={%
to[out=0,in=180]
++(0.25,#1) node[bullet](dip-thevalue{dip}){}
to[out=0,in=180] ++(0.25,-1*#1)
}},bullet/.style={circle,fill,inner sep=1.5pt}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) -- (5.25,2.5) [dip=-2.5mm]-- (7,2.5);
node[right=3pt of dip-1]{$b=1$};
draw (1,2) -- (2.25,2) [dip=-2.5mm] -- (5.25,2) [dip=2.5mm] --(7,2);
node[right=3pt of dip-2]{$b=1$};
draw (1,1.5) -- (2.25,1.5) [dip=2.5mm] --(5.25,1.5) [dip=-5mm] -- (7,1.5);
node[above right=-1.5pt and 5pt of dip-5]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) --(5.25,0.5) [dip=5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
draw[dashed] (dip-X|-Y) node[bullet]{} -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Very nice automation. Just out of curiosity (coding TikZ is still something I'm not good at), is it possible to also draw the filled circles inside thedip
style?
– Skillmon
yesterday
1
@Skillmon Yes, of course. E.g.dip/.style args={#1/#2}{/utils/exec=stepcounter{dip}, insert path={% coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3) (aux1) -- (aux2|-aux1) to[out=0,in=180] ++({abs(#2)},#2) node[circle,fill,inner sep=1.5pt](dip-thevalue{dip}){} to[out=0,in=180] (aux3|-aux1) }}
.
– marmot
yesterday
add a comment |
This uses the same in and out
trick as Skillmon and puts it into a style dip
, which takes as arguments the horizontal position and the depth, where the sign decides whether the dip is a dip (minus) or a bump (plus). (Let me also mention that you do not need to do something like draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
. If you name the nodes, you can just do draw[<-] (Y) -- (X) node[left, midway] {$f$};
and TikZ will make sure to shorten the arrow without you having to compute the coordinates.)
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style args={#1/#2}{/utils/exec=stepcounter{dip},
insert path={%
coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3)
(aux1) -- (aux2|-aux1) to[out=0,in=180]
++({abs(#2)},#2) coordinate(dip-thevalue{dip}) to[out=0,in=180] (aux3|-aux1)
}}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) [dip=5.5cm/-2.5mm]-- (7,2.5);
fill (dip-1) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,2) [dip/.list={2.5cm/-2.5mm,5.5cm/2.5mm}] -- (7,2);
fill (dip-2) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,1.5) [dip/.list={2.5cm/2.5mm,5.5cm/-5mm}] -- (7,1.5);
fill (dip-5) circle[radius=2pt] node[above right=0pt and 5pt]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) [dip=5.5cm/5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
fill (dip-X|-Y) circle[radius=2pt];
draw[dashed] (dip-X|-Y) -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Just for fun: a variation for Skillmon.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style={/utils/exec=stepcounter{dip},
insert path={%
to[out=0,in=180]
++(0.25,#1) node[bullet](dip-thevalue{dip}){}
to[out=0,in=180] ++(0.25,-1*#1)
}},bullet/.style={circle,fill,inner sep=1.5pt}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) -- (5.25,2.5) [dip=-2.5mm]-- (7,2.5);
node[right=3pt of dip-1]{$b=1$};
draw (1,2) -- (2.25,2) [dip=-2.5mm] -- (5.25,2) [dip=2.5mm] --(7,2);
node[right=3pt of dip-2]{$b=1$};
draw (1,1.5) -- (2.25,1.5) [dip=2.5mm] --(5.25,1.5) [dip=-5mm] -- (7,1.5);
node[above right=-1.5pt and 5pt of dip-5]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) --(5.25,0.5) [dip=5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
draw[dashed] (dip-X|-Y) node[bullet]{} -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
This uses the same in and out
trick as Skillmon and puts it into a style dip
, which takes as arguments the horizontal position and the depth, where the sign decides whether the dip is a dip (minus) or a bump (plus). (Let me also mention that you do not need to do something like draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
. If you name the nodes, you can just do draw[<-] (Y) -- (X) node[left, midway] {$f$};
and TikZ will make sure to shorten the arrow without you having to compute the coordinates.)
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style args={#1/#2}{/utils/exec=stepcounter{dip},
insert path={%
coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3)
(aux1) -- (aux2|-aux1) to[out=0,in=180]
++({abs(#2)},#2) coordinate(dip-thevalue{dip}) to[out=0,in=180] (aux3|-aux1)
}}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) [dip=5.5cm/-2.5mm]-- (7,2.5);
fill (dip-1) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,2) [dip/.list={2.5cm/-2.5mm,5.5cm/2.5mm}] -- (7,2);
fill (dip-2) circle[radius=2pt] node[right=3pt]{$b=1$};
draw (1,1.5) [dip/.list={2.5cm/2.5mm,5.5cm/-5mm}] -- (7,1.5);
fill (dip-5) circle[radius=2pt] node[above right=0pt and 5pt]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) [dip=5.5cm/5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
fill (dip-X|-Y) circle[radius=2pt];
draw[dashed] (dip-X|-Y) -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
Just for fun: a variation for Skillmon.
documentclass[tikz,border=3.14mm]{standalone}
usetikzlibrary{positioning}
newcounter{dip}
begin{document}
begin{tikzpicture}[dip/.style={/utils/exec=stepcounter{dip},
insert path={%
to[out=0,in=180]
++(0.25,#1) node[bullet](dip-thevalue{dip}){}
to[out=0,in=180] ++(0.25,-1*#1)
}},bullet/.style={circle,fill,inner sep=1.5pt}]
begin{scope}[thick,local bounding box=dips]
draw (1,2.5) -- (5.25,2.5) [dip=-2.5mm]-- (7,2.5);
node[right=3pt of dip-1]{$b=1$};
draw (1,2) -- (2.25,2) [dip=-2.5mm] -- (5.25,2) [dip=2.5mm] --(7,2);
node[right=3pt of dip-2]{$b=1$};
draw (1,1.5) -- (2.25,1.5) [dip=2.5mm] --(5.25,1.5) [dip=-5mm] -- (7,1.5);
node[above right=-1.5pt and 5pt of dip-5]{$b=2$};
draw (1,1) -- (7,1);
draw (1,0.5) --(5.25,0.5) [dip=5mm] -- (7,0.5);
end{scope}
node[left=2pt of dips.west] (X) {$X$};
draw (7,-0.5) -- (1,-0.5) node[left=2pt] (Y) {$Y$};
draw[<-] (Y) -- (X) node[left, midway] {$f$};
foreach X in {1,2}
{
draw[dashed] (dip-X|-Y) node[bullet]{} -- (dip-X|-0,2.75);
}
end{tikzpicture}
end{document}
edited yesterday
answered 2 days ago
marmotmarmot
111k5140264
111k5140264
Very nice automation. Just out of curiosity (coding TikZ is still something I'm not good at), is it possible to also draw the filled circles inside thedip
style?
– Skillmon
yesterday
1
@Skillmon Yes, of course. E.g.dip/.style args={#1/#2}{/utils/exec=stepcounter{dip}, insert path={% coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3) (aux1) -- (aux2|-aux1) to[out=0,in=180] ++({abs(#2)},#2) node[circle,fill,inner sep=1.5pt](dip-thevalue{dip}){} to[out=0,in=180] (aux3|-aux1) }}
.
– marmot
yesterday
add a comment |
Very nice automation. Just out of curiosity (coding TikZ is still something I'm not good at), is it possible to also draw the filled circles inside thedip
style?
– Skillmon
yesterday
1
@Skillmon Yes, of course. E.g.dip/.style args={#1/#2}{/utils/exec=stepcounter{dip}, insert path={% coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3) (aux1) -- (aux2|-aux1) to[out=0,in=180] ++({abs(#2)},#2) node[circle,fill,inner sep=1.5pt](dip-thevalue{dip}){} to[out=0,in=180] (aux3|-aux1) }}
.
– marmot
yesterday
Very nice automation. Just out of curiosity (coding TikZ is still something I'm not good at), is it possible to also draw the filled circles inside the
dip
style?– Skillmon
yesterday
Very nice automation. Just out of curiosity (coding TikZ is still something I'm not good at), is it possible to also draw the filled circles inside the
dip
style?– Skillmon
yesterday
1
1
@Skillmon Yes, of course. E.g.
dip/.style args={#1/#2}{/utils/exec=stepcounter{dip}, insert path={% coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3) (aux1) -- (aux2|-aux1) to[out=0,in=180] ++({abs(#2)},#2) node[circle,fill,inner sep=1.5pt](dip-thevalue{dip}){} to[out=0,in=180] (aux3|-aux1) }}
.– marmot
yesterday
@Skillmon Yes, of course. E.g.
dip/.style args={#1/#2}{/utils/exec=stepcounter{dip}, insert path={% coordinate (aux1) ({#1-abs(#2)},0) coordinate (aux2) ({#1+abs(#2)},0) coordinate (aux3) (aux1) -- (aux2|-aux1) to[out=0,in=180] ++({abs(#2)},#2) node[circle,fill,inner sep=1.5pt](dip-thevalue{dip}){} to[out=0,in=180] (aux3|-aux1) }}
.– marmot
yesterday
add a comment |
The following is a pretty manual way to do this. I only did it for the first two lines, I hope you can apply it to the other occurrences. It uses the in
and out
keys of the to
path construction:
documentclass[tikz]{standalone}
begin{document}
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,2.5) -- (7,2.5) coordinate(a);
draw[thick] (1,2) -- (7,2) coordinate(b);
draw[thick] (1,1.5) -- (7,1.5) coordinate(c);
draw[thick] (1,0) -- (7,0) coordinate(d);
draw[thick]
(a) ++(.25,-.25) coordinate(ab) to[out=180,in=0] (a)
(ab) to[out=180,in=0] (b)
(ab) to[out=0,in=180] ++(.25,.25)
(ab) to[out=0,in=180] ++(.25,-.25)
;
filldraw
(ab) circle(.05)
;
end{tikzpicture}
end{document}
add a comment |
The following is a pretty manual way to do this. I only did it for the first two lines, I hope you can apply it to the other occurrences. It uses the in
and out
keys of the to
path construction:
documentclass[tikz]{standalone}
begin{document}
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,2.5) -- (7,2.5) coordinate(a);
draw[thick] (1,2) -- (7,2) coordinate(b);
draw[thick] (1,1.5) -- (7,1.5) coordinate(c);
draw[thick] (1,0) -- (7,0) coordinate(d);
draw[thick]
(a) ++(.25,-.25) coordinate(ab) to[out=180,in=0] (a)
(ab) to[out=180,in=0] (b)
(ab) to[out=0,in=180] ++(.25,.25)
(ab) to[out=0,in=180] ++(.25,-.25)
;
filldraw
(ab) circle(.05)
;
end{tikzpicture}
end{document}
add a comment |
The following is a pretty manual way to do this. I only did it for the first two lines, I hope you can apply it to the other occurrences. It uses the in
and out
keys of the to
path construction:
documentclass[tikz]{standalone}
begin{document}
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,2.5) -- (7,2.5) coordinate(a);
draw[thick] (1,2) -- (7,2) coordinate(b);
draw[thick] (1,1.5) -- (7,1.5) coordinate(c);
draw[thick] (1,0) -- (7,0) coordinate(d);
draw[thick]
(a) ++(.25,-.25) coordinate(ab) to[out=180,in=0] (a)
(ab) to[out=180,in=0] (b)
(ab) to[out=0,in=180] ++(.25,.25)
(ab) to[out=0,in=180] ++(.25,-.25)
;
filldraw
(ab) circle(.05)
;
end{tikzpicture}
end{document}
The following is a pretty manual way to do this. I only did it for the first two lines, I hope you can apply it to the other occurrences. It uses the in
and out
keys of the to
path construction:
documentclass[tikz]{standalone}
begin{document}
begin{tikzpicture}
draw (0,0) node {$Y$};
draw (0,2) node {$X$};
draw[<-] (0,0.35) -- (0,1.65) node[left, midway] {$f$};
draw[thick] (1,2.5) -- (7,2.5) coordinate(a);
draw[thick] (1,2) -- (7,2) coordinate(b);
draw[thick] (1,1.5) -- (7,1.5) coordinate(c);
draw[thick] (1,0) -- (7,0) coordinate(d);
draw[thick]
(a) ++(.25,-.25) coordinate(ab) to[out=180,in=0] (a)
(ab) to[out=180,in=0] (b)
(ab) to[out=0,in=180] ++(.25,.25)
(ab) to[out=0,in=180] ++(.25,-.25)
;
filldraw
(ab) circle(.05)
;
end{tikzpicture}
end{document}
edited 2 days ago
answered 2 days ago
SkillmonSkillmon
23.8k12248
23.8k12248
add a comment |
add a comment |
This is a proof of concept how to use parser
library to define the following "language" :
=
stay at the same level
u
goes half up
U
goes one up
d
goes half down
D
goes one down
x
plot a (red) dot
.
end
Here is the code.
documentclass[tikz,border=7pt]{standalone}
usepgfmodule{parser}
% -----------------------------
% macro that add #1 to the current path when used inside pgfextra
definsertpath#1{tikzset{insert path={#1}}}
% define the parser "sheet path"
pgfparserdef{sheet path}{initial}{the character =}{insertpath{ -- ++(1, 0)}}
pgfparserdef{sheet path}{initial}{the letter u}{insertpath{ to[out=0,in=180] ++(1, .5)}}
pgfparserdef{sheet path}{initial}{the letter U}{insertpath{ to[out=0,in=180] ++(1, 1)}}
pgfparserdef{sheet path}{initial}{the letter d}{insertpath{ to[out=0,in=180] ++(1,-.5)}}
pgfparserdef{sheet path}{initial}{the letter D}{insertpath{ to[out=0,in=180] ++(1, -1)}}
pgfparserdef{sheet path}{initial}{the letter x}{insertpath{ node[red,scale=4]{.}}}
pgfparserdef{sheet path}{initial}{the character .}{pgfparserswitch{final}}
% the sheet interface macro
defsheet#1.{pgfextra{pgfparserparse{sheet path}#1.}}
% -----------------------------
begin{document}
tikzdraw[thick]
(0,-1) sheet======du=.
(0,-2) sheet==du==uxd=.
(0,-3) sheet==uxd==DU=.
(0,-4) sheet=========.
(0,-5) sheet======UxD=.;
end{document}
3
+1, I didn't know parser, wow!
– CarLaTeX
yesterday
add a comment |
This is a proof of concept how to use parser
library to define the following "language" :
=
stay at the same level
u
goes half up
U
goes one up
d
goes half down
D
goes one down
x
plot a (red) dot
.
end
Here is the code.
documentclass[tikz,border=7pt]{standalone}
usepgfmodule{parser}
% -----------------------------
% macro that add #1 to the current path when used inside pgfextra
definsertpath#1{tikzset{insert path={#1}}}
% define the parser "sheet path"
pgfparserdef{sheet path}{initial}{the character =}{insertpath{ -- ++(1, 0)}}
pgfparserdef{sheet path}{initial}{the letter u}{insertpath{ to[out=0,in=180] ++(1, .5)}}
pgfparserdef{sheet path}{initial}{the letter U}{insertpath{ to[out=0,in=180] ++(1, 1)}}
pgfparserdef{sheet path}{initial}{the letter d}{insertpath{ to[out=0,in=180] ++(1,-.5)}}
pgfparserdef{sheet path}{initial}{the letter D}{insertpath{ to[out=0,in=180] ++(1, -1)}}
pgfparserdef{sheet path}{initial}{the letter x}{insertpath{ node[red,scale=4]{.}}}
pgfparserdef{sheet path}{initial}{the character .}{pgfparserswitch{final}}
% the sheet interface macro
defsheet#1.{pgfextra{pgfparserparse{sheet path}#1.}}
% -----------------------------
begin{document}
tikzdraw[thick]
(0,-1) sheet======du=.
(0,-2) sheet==du==uxd=.
(0,-3) sheet==uxd==DU=.
(0,-4) sheet=========.
(0,-5) sheet======UxD=.;
end{document}
3
+1, I didn't know parser, wow!
– CarLaTeX
yesterday
add a comment |
This is a proof of concept how to use parser
library to define the following "language" :
=
stay at the same level
u
goes half up
U
goes one up
d
goes half down
D
goes one down
x
plot a (red) dot
.
end
Here is the code.
documentclass[tikz,border=7pt]{standalone}
usepgfmodule{parser}
% -----------------------------
% macro that add #1 to the current path when used inside pgfextra
definsertpath#1{tikzset{insert path={#1}}}
% define the parser "sheet path"
pgfparserdef{sheet path}{initial}{the character =}{insertpath{ -- ++(1, 0)}}
pgfparserdef{sheet path}{initial}{the letter u}{insertpath{ to[out=0,in=180] ++(1, .5)}}
pgfparserdef{sheet path}{initial}{the letter U}{insertpath{ to[out=0,in=180] ++(1, 1)}}
pgfparserdef{sheet path}{initial}{the letter d}{insertpath{ to[out=0,in=180] ++(1,-.5)}}
pgfparserdef{sheet path}{initial}{the letter D}{insertpath{ to[out=0,in=180] ++(1, -1)}}
pgfparserdef{sheet path}{initial}{the letter x}{insertpath{ node[red,scale=4]{.}}}
pgfparserdef{sheet path}{initial}{the character .}{pgfparserswitch{final}}
% the sheet interface macro
defsheet#1.{pgfextra{pgfparserparse{sheet path}#1.}}
% -----------------------------
begin{document}
tikzdraw[thick]
(0,-1) sheet======du=.
(0,-2) sheet==du==uxd=.
(0,-3) sheet==uxd==DU=.
(0,-4) sheet=========.
(0,-5) sheet======UxD=.;
end{document}
This is a proof of concept how to use parser
library to define the following "language" :
=
stay at the same level
u
goes half up
U
goes one up
d
goes half down
D
goes one down
x
plot a (red) dot
.
end
Here is the code.
documentclass[tikz,border=7pt]{standalone}
usepgfmodule{parser}
% -----------------------------
% macro that add #1 to the current path when used inside pgfextra
definsertpath#1{tikzset{insert path={#1}}}
% define the parser "sheet path"
pgfparserdef{sheet path}{initial}{the character =}{insertpath{ -- ++(1, 0)}}
pgfparserdef{sheet path}{initial}{the letter u}{insertpath{ to[out=0,in=180] ++(1, .5)}}
pgfparserdef{sheet path}{initial}{the letter U}{insertpath{ to[out=0,in=180] ++(1, 1)}}
pgfparserdef{sheet path}{initial}{the letter d}{insertpath{ to[out=0,in=180] ++(1,-.5)}}
pgfparserdef{sheet path}{initial}{the letter D}{insertpath{ to[out=0,in=180] ++(1, -1)}}
pgfparserdef{sheet path}{initial}{the letter x}{insertpath{ node[red,scale=4]{.}}}
pgfparserdef{sheet path}{initial}{the character .}{pgfparserswitch{final}}
% the sheet interface macro
defsheet#1.{pgfextra{pgfparserparse{sheet path}#1.}}
% -----------------------------
begin{document}
tikzdraw[thick]
(0,-1) sheet======du=.
(0,-2) sheet==du==uxd=.
(0,-3) sheet==uxd==DU=.
(0,-4) sheet=========.
(0,-5) sheet======UxD=.;
end{document}
answered yesterday
KpymKpym
17k24090
17k24090
3
+1, I didn't know parser, wow!
– CarLaTeX
yesterday
add a comment |
3
+1, I didn't know parser, wow!
– CarLaTeX
yesterday
3
3
+1, I didn't know parser, wow!
– CarLaTeX
yesterday
+1, I didn't know parser, wow!
– CarLaTeX
yesterday
add a comment |
I think Bézier curves can do the task. It can make sure that the starting points and the ending points of all curves are placed with a consistent ratio.
documentclass[tikz]{standalone}
definnersep{.5em}
usetikzlibrary{patterns}
begin{document}
begin{tikzpicture}[y=baselineskip+2*innersep]
draw (0,0)--(6.5,0);
draw (0,1.5)--(6.5,1.5);
draw (0,1)--(4.75,1) .. controls (4.9,1) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,1) .. (5.25,1)--(6.5,1);
draw (0,2)--(1.25,2) .. controls (1.4,2) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2) .. (1.75,2)--(4.75,2) .. controls (4.9,2) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,2) .. (5.25,2)--(6.5,2);
draw (0,2.5)--(1.25,2.5) .. controls (1.4,2.5) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2.5) .. (1.75,2.5)--(4.75,2.5) .. controls (4.9,2.5) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,2.5) .. (5.25,2.5)--(6.5,2.5);
draw (0,3)--(4.75,3) .. controls (4.9,3) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,3) .. (5.25,3)--(6.5,3);
fill (1.5,2.25) circle (1.5pt) (5,1.5) circle (1.5pt) (5,2.75) circle (1.5pt) (1.5,0) circle (1.5pt) (5,0) circle (1.5pt);
draw[dashed] (1.5,0)--(1.5,3.25) (5,0)--(5,3.25);
draw (1.5,2.25) node[right=1ex] {$b=1$};
draw (5,1.75) node[right=1ex] {$b=2$};
draw (5,2.75) node[right=1ex] {$b=1$};
draw (-0.5,0) node (n) {$N$};
draw (-0.5,2.25) node (m) {$M$};
draw[-latex] (m)--(n) node[midway,left] {$f$};
end{tikzpicture}
end{document}
add a comment |
I think Bézier curves can do the task. It can make sure that the starting points and the ending points of all curves are placed with a consistent ratio.
documentclass[tikz]{standalone}
definnersep{.5em}
usetikzlibrary{patterns}
begin{document}
begin{tikzpicture}[y=baselineskip+2*innersep]
draw (0,0)--(6.5,0);
draw (0,1.5)--(6.5,1.5);
draw (0,1)--(4.75,1) .. controls (4.9,1) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,1) .. (5.25,1)--(6.5,1);
draw (0,2)--(1.25,2) .. controls (1.4,2) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2) .. (1.75,2)--(4.75,2) .. controls (4.9,2) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,2) .. (5.25,2)--(6.5,2);
draw (0,2.5)--(1.25,2.5) .. controls (1.4,2.5) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2.5) .. (1.75,2.5)--(4.75,2.5) .. controls (4.9,2.5) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,2.5) .. (5.25,2.5)--(6.5,2.5);
draw (0,3)--(4.75,3) .. controls (4.9,3) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,3) .. (5.25,3)--(6.5,3);
fill (1.5,2.25) circle (1.5pt) (5,1.5) circle (1.5pt) (5,2.75) circle (1.5pt) (1.5,0) circle (1.5pt) (5,0) circle (1.5pt);
draw[dashed] (1.5,0)--(1.5,3.25) (5,0)--(5,3.25);
draw (1.5,2.25) node[right=1ex] {$b=1$};
draw (5,1.75) node[right=1ex] {$b=2$};
draw (5,2.75) node[right=1ex] {$b=1$};
draw (-0.5,0) node (n) {$N$};
draw (-0.5,2.25) node (m) {$M$};
draw[-latex] (m)--(n) node[midway,left] {$f$};
end{tikzpicture}
end{document}
add a comment |
I think Bézier curves can do the task. It can make sure that the starting points and the ending points of all curves are placed with a consistent ratio.
documentclass[tikz]{standalone}
definnersep{.5em}
usetikzlibrary{patterns}
begin{document}
begin{tikzpicture}[y=baselineskip+2*innersep]
draw (0,0)--(6.5,0);
draw (0,1.5)--(6.5,1.5);
draw (0,1)--(4.75,1) .. controls (4.9,1) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,1) .. (5.25,1)--(6.5,1);
draw (0,2)--(1.25,2) .. controls (1.4,2) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2) .. (1.75,2)--(4.75,2) .. controls (4.9,2) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,2) .. (5.25,2)--(6.5,2);
draw (0,2.5)--(1.25,2.5) .. controls (1.4,2.5) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2.5) .. (1.75,2.5)--(4.75,2.5) .. controls (4.9,2.5) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,2.5) .. (5.25,2.5)--(6.5,2.5);
draw (0,3)--(4.75,3) .. controls (4.9,3) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,3) .. (5.25,3)--(6.5,3);
fill (1.5,2.25) circle (1.5pt) (5,1.5) circle (1.5pt) (5,2.75) circle (1.5pt) (1.5,0) circle (1.5pt) (5,0) circle (1.5pt);
draw[dashed] (1.5,0)--(1.5,3.25) (5,0)--(5,3.25);
draw (1.5,2.25) node[right=1ex] {$b=1$};
draw (5,1.75) node[right=1ex] {$b=2$};
draw (5,2.75) node[right=1ex] {$b=1$};
draw (-0.5,0) node (n) {$N$};
draw (-0.5,2.25) node (m) {$M$};
draw[-latex] (m)--(n) node[midway,left] {$f$};
end{tikzpicture}
end{document}
I think Bézier curves can do the task. It can make sure that the starting points and the ending points of all curves are placed with a consistent ratio.
documentclass[tikz]{standalone}
definnersep{.5em}
usetikzlibrary{patterns}
begin{document}
begin{tikzpicture}[y=baselineskip+2*innersep]
draw (0,0)--(6.5,0);
draw (0,1.5)--(6.5,1.5);
draw (0,1)--(4.75,1) .. controls (4.9,1) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,1) .. (5.25,1)--(6.5,1);
draw (0,2)--(1.25,2) .. controls (1.4,2) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2) .. (1.75,2)--(4.75,2) .. controls (4.9,2) and (4.85,1.5) .. (5,1.5) .. controls (5.15,1.5) and (5.1,2) .. (5.25,2)--(6.5,2);
draw (0,2.5)--(1.25,2.5) .. controls (1.4,2.5) and (1.35,2.25) .. (1.5,2.25) .. controls (1.65,2.25) and (1.6,2.5) .. (1.75,2.5)--(4.75,2.5) .. controls (4.9,2.5) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,2.5) .. (5.25,2.5)--(6.5,2.5);
draw (0,3)--(4.75,3) .. controls (4.9,3) and (4.85,2.75) .. (5,2.75) .. controls (5.15,2.75) and (5.1,3) .. (5.25,3)--(6.5,3);
fill (1.5,2.25) circle (1.5pt) (5,1.5) circle (1.5pt) (5,2.75) circle (1.5pt) (1.5,0) circle (1.5pt) (5,0) circle (1.5pt);
draw[dashed] (1.5,0)--(1.5,3.25) (5,0)--(5,3.25);
draw (1.5,2.25) node[right=1ex] {$b=1$};
draw (5,1.75) node[right=1ex] {$b=2$};
draw (5,2.75) node[right=1ex] {$b=1$};
draw (-0.5,0) node (n) {$N$};
draw (-0.5,2.25) node (m) {$M$};
draw[-latex] (m)--(n) node[midway,left] {$f$};
end{tikzpicture}
end{document}
answered yesterday
JouleVJouleV
7,89222053
7,89222053
add a comment |
add a comment |
This is a proof of concept how to use plot[smooth] coordinates
to do this.
documentclass[tikz,border=7pt]{standalone}
begin{document}
begin{tikzpicture}[yscale=.5,
sheet/.style={red,thick,smooth},
point/.style={insert path={node[scale=4]{.}}}]
% ---- sheets ----
foreach[count=i] pts in {
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7,-1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2,-1) (3, 0) (6, 0) (7, 1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 1) (3, 0) (6, 0) (7,-2) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 0) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 2) (8, 0) (9, 0)},%
{(0,0)},%
{(0, 0) (9, 0)}%
}{
draw[yshift=-2*i cm,sheet] plot coordinates {pts};
}
% ---- singular points and labels ----
path
(2,-5) [point] (3,-5) node{$b=1$}
(7,-3) [point] (8,-3) node{$b=1$}
(7,-8) [point] (8,-7.5) node{$b=2$}
;
% ---- vertical lines ----
draw[dashed]
(2,0) -- +(0,-14) [point]
(7,0) -- +(0,-14) [point]
;
end{tikzpicture}
end{document}
add a comment |
This is a proof of concept how to use plot[smooth] coordinates
to do this.
documentclass[tikz,border=7pt]{standalone}
begin{document}
begin{tikzpicture}[yscale=.5,
sheet/.style={red,thick,smooth},
point/.style={insert path={node[scale=4]{.}}}]
% ---- sheets ----
foreach[count=i] pts in {
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7,-1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2,-1) (3, 0) (6, 0) (7, 1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 1) (3, 0) (6, 0) (7,-2) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 0) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 2) (8, 0) (9, 0)},%
{(0,0)},%
{(0, 0) (9, 0)}%
}{
draw[yshift=-2*i cm,sheet] plot coordinates {pts};
}
% ---- singular points and labels ----
path
(2,-5) [point] (3,-5) node{$b=1$}
(7,-3) [point] (8,-3) node{$b=1$}
(7,-8) [point] (8,-7.5) node{$b=2$}
;
% ---- vertical lines ----
draw[dashed]
(2,0) -- +(0,-14) [point]
(7,0) -- +(0,-14) [point]
;
end{tikzpicture}
end{document}
add a comment |
This is a proof of concept how to use plot[smooth] coordinates
to do this.
documentclass[tikz,border=7pt]{standalone}
begin{document}
begin{tikzpicture}[yscale=.5,
sheet/.style={red,thick,smooth},
point/.style={insert path={node[scale=4]{.}}}]
% ---- sheets ----
foreach[count=i] pts in {
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7,-1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2,-1) (3, 0) (6, 0) (7, 1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 1) (3, 0) (6, 0) (7,-2) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 0) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 2) (8, 0) (9, 0)},%
{(0,0)},%
{(0, 0) (9, 0)}%
}{
draw[yshift=-2*i cm,sheet] plot coordinates {pts};
}
% ---- singular points and labels ----
path
(2,-5) [point] (3,-5) node{$b=1$}
(7,-3) [point] (8,-3) node{$b=1$}
(7,-8) [point] (8,-7.5) node{$b=2$}
;
% ---- vertical lines ----
draw[dashed]
(2,0) -- +(0,-14) [point]
(7,0) -- +(0,-14) [point]
;
end{tikzpicture}
end{document}
This is a proof of concept how to use plot[smooth] coordinates
to do this.
documentclass[tikz,border=7pt]{standalone}
begin{document}
begin{tikzpicture}[yscale=.5,
sheet/.style={red,thick,smooth},
point/.style={insert path={node[scale=4]{.}}}]
% ---- sheets ----
foreach[count=i] pts in {
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7,-1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2,-1) (3, 0) (6, 0) (7, 1) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 1) (3, 0) (6, 0) (7,-2) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 0) (8, 0) (9, 0)},%
{(0, 0) (1, 0) (2, 0) (3, 0) (6, 0) (7, 2) (8, 0) (9, 0)},%
{(0,0)},%
{(0, 0) (9, 0)}%
}{
draw[yshift=-2*i cm,sheet] plot coordinates {pts};
}
% ---- singular points and labels ----
path
(2,-5) [point] (3,-5) node{$b=1$}
(7,-3) [point] (8,-3) node{$b=1$}
(7,-8) [point] (8,-7.5) node{$b=2$}
;
% ---- vertical lines ----
draw[dashed]
(2,0) -- +(0,-14) [point]
(7,0) -- +(0,-14) [point]
;
end{tikzpicture}
end{document}
answered yesterday
KpymKpym
17k24090
17k24090
add a comment |
add a comment |
This is a proof of concept how to use turtle
library to define the following "moves" :
up
make an up bump
up*
make an up bump with (red) dot
dn
make a down bump
dn*
make a down bump with (red) dot
*
put a (red) dot
documentclass[tikz,border=7pt]{standalone}
usetikzlibrary{turtle}
tikzset{
turtle/.cd,
how/.style={out=0,in=180},
home/.append style=right,
*/.style={/tikz/insert path={node[red,scale=4]{.}}},
forward/.default=1.4142135,
up/.style={left=45,forward,right,forward,left=45},
up*/.style={left=45,forward,*,right,forward,left=45},
dn/.style={right=45,forward,left,forward,right=45},
dn*/.style={right=45,forward,*,left,forward,right=45},
next/.style={/tikz/yshift=-1cm}
}
begin{document}
tikz
draw[thick]
[turtle={home,fd,fd,fd,fd,fd,fd,dn,fd},next,next]
[turtle={home,fd,fd,dn,fd,fd,up*,fd},next,next]
[turtle={home,fd,fd,up*,fd,fd,dn,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,fd,fd,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,up*,fd}]
;
end{document}
add a comment |
This is a proof of concept how to use turtle
library to define the following "moves" :
up
make an up bump
up*
make an up bump with (red) dot
dn
make a down bump
dn*
make a down bump with (red) dot
*
put a (red) dot
documentclass[tikz,border=7pt]{standalone}
usetikzlibrary{turtle}
tikzset{
turtle/.cd,
how/.style={out=0,in=180},
home/.append style=right,
*/.style={/tikz/insert path={node[red,scale=4]{.}}},
forward/.default=1.4142135,
up/.style={left=45,forward,right,forward,left=45},
up*/.style={left=45,forward,*,right,forward,left=45},
dn/.style={right=45,forward,left,forward,right=45},
dn*/.style={right=45,forward,*,left,forward,right=45},
next/.style={/tikz/yshift=-1cm}
}
begin{document}
tikz
draw[thick]
[turtle={home,fd,fd,fd,fd,fd,fd,dn,fd},next,next]
[turtle={home,fd,fd,dn,fd,fd,up*,fd},next,next]
[turtle={home,fd,fd,up*,fd,fd,dn,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,fd,fd,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,up*,fd}]
;
end{document}
add a comment |
This is a proof of concept how to use turtle
library to define the following "moves" :
up
make an up bump
up*
make an up bump with (red) dot
dn
make a down bump
dn*
make a down bump with (red) dot
*
put a (red) dot
documentclass[tikz,border=7pt]{standalone}
usetikzlibrary{turtle}
tikzset{
turtle/.cd,
how/.style={out=0,in=180},
home/.append style=right,
*/.style={/tikz/insert path={node[red,scale=4]{.}}},
forward/.default=1.4142135,
up/.style={left=45,forward,right,forward,left=45},
up*/.style={left=45,forward,*,right,forward,left=45},
dn/.style={right=45,forward,left,forward,right=45},
dn*/.style={right=45,forward,*,left,forward,right=45},
next/.style={/tikz/yshift=-1cm}
}
begin{document}
tikz
draw[thick]
[turtle={home,fd,fd,fd,fd,fd,fd,dn,fd},next,next]
[turtle={home,fd,fd,dn,fd,fd,up*,fd},next,next]
[turtle={home,fd,fd,up*,fd,fd,dn,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,fd,fd,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,up*,fd}]
;
end{document}
This is a proof of concept how to use turtle
library to define the following "moves" :
up
make an up bump
up*
make an up bump with (red) dot
dn
make a down bump
dn*
make a down bump with (red) dot
*
put a (red) dot
documentclass[tikz,border=7pt]{standalone}
usetikzlibrary{turtle}
tikzset{
turtle/.cd,
how/.style={out=0,in=180},
home/.append style=right,
*/.style={/tikz/insert path={node[red,scale=4]{.}}},
forward/.default=1.4142135,
up/.style={left=45,forward,right,forward,left=45},
up*/.style={left=45,forward,*,right,forward,left=45},
dn/.style={right=45,forward,left,forward,right=45},
dn*/.style={right=45,forward,*,left,forward,right=45},
next/.style={/tikz/yshift=-1cm}
}
begin{document}
tikz
draw[thick]
[turtle={home,fd,fd,fd,fd,fd,fd,dn,fd},next,next]
[turtle={home,fd,fd,dn,fd,fd,up*,fd},next,next]
[turtle={home,fd,fd,up*,fd,fd,dn,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,fd,fd,fd},next]
[turtle={home,fd,fd,fd,fd,fd,fd,up*,fd}]
;
end{document}
answered yesterday
KpymKpym
17k24090
17k24090
add a comment |
add a comment |
Edit notes:
spaces can now be handled, but are ignored by default
alternative syntax for
myparserdef
added
Inspired by Kpym's answer but thinking that the possibilities of pgfparser
are insufficient, I wrote my own parser, that should be similar to pgf's, but allows the parsed elements to grab arguments. The following arguments are supported:
m
a mandatory argument
r{<delim>}
a mandatory argument delimited by<delim>
o
an optional argument in brackets (you can test for it withmyIfNoValueTF
)
O{<default>}
an optional argument defaulting to<default>
d<delim1><delim2>
an optional argument delimited by<delim1>
and<delim2>
(you can test for it withmyIfNoValueTF
)
D<delim1><delim2>{<default>}
an optional argument delimited by<delim1>
and<delim2>
defaulting to<default>
t<token>
an optional token (you can test for it withmyIfBooleanTF
)
Despite the fact that the argument names are inspired by xparse
, none of them care for balanced delimiters (e.g., [[ab]]
would not be grabbed as [ab]
, but as [ab
with an orphaned ]
, so you'd have to use [{[ab]}]
, this is like the LaTeX2e behaviour). Also all arguments are long by nature unlike xparse
where you'd need to specify that using +
.
You can define a parser using myparserdef{<name>}{<state>}{<meaning>}[<args>]{<code>}
, with <name>
the name of the parser, <state>
the state, <meaning>
the meaning of a token, <args>
an argument string (built from the arguments listed above, up to 9 arguments are supported) and <code>
the code which should be executed for that combination of <name>
, <state>
and <meaning>
. You can switch states using myparserstate{<state>}
, if you switch to final
parsing is ended, the initial state is initial
. All in all this is pretty similar to pgfparser
s way of doing things. The biggest difference is, that my parser ignores blanks by default, however you can define an action for blanks with myparserdef{<name>}{<state>}{blank space}[<args>]{<code>}
. Many consecutive blanks are considered as one.
You can also use a different syntax for myparserdef
namely: myparserdef{<name>}{<state>}<token>[<args>]{<code>}
with <token>
being a single token not surrounded by {}
(spaces are ignored here, it is not checked whether <token>
is really a single token, so be careful). In that case the same will be done as if you'd typed the meaning
of the <token>
, so myparserdef{foo}{bar}a{baz}
does the same as myparserdef{foo}{bar}{the letter a}{baz}
.
A parser is executed using myparserrun{<name>}
. A parser needs at least one rule, else an error is thrown.
This parser system allows me to place coordinates inside of sheet
, which was the main reason why I found pgfparser
insufficient.
I did all of this without using any packages, only LaTeX-Kernel macros, just for procrastinating reasons. Using for example xparse
would shorten the code considerably.
documentclass[tikz,border=7pt]{standalone}
% my parser >>>
makeatletter
% logic helpers >>>
longdefmyfi@An#1fi#2{fi}
longdefmyfi@Ay#1fi#2{fi#2}
longdefmyfi@Byfi#1{fi#1}
longdefmyfi@BTbfi#1#2#3{fi#2}
% <<<
% mynewdef >>>
@ifdefinablemyifundefTF%>>>
{%
defmyifundefTF#1%
{%
ifdefined#1%
myfi@Ay
else
myfi@BTb
fi
{%
ifx#1relax
myfi@BTb
fi
@secondoftwo
}%
}%
}%<<<
@ifdefinablemynewdef%>>>
{%
protecteddefmynewdef{@ifnextchar[{mynewdef@a}{mynewdef@a}}%
}%<<<
@ifdefinablemynewdef@a%>>>
{%
longdefmynewdef@a[#1]#2#3#{mynewdef@b{#1}#2{#3}}%
}%<<<
@ifdefinablemynewdef@b%>>>
{%
protectedlongdefmynewdef@b#1#2#3#4%
{%
myifundefTF#2%
{%
#1def#2#3{#4}%
}
{%
PackageError{my}{Command string#2space already defined}{}%
}
}%
}%<<<
mynewdefmyifcsundefTF#1%>>>
{%
ifcsname #1endcsname
myfi@Ay
else
myfi@BTb
fi
{%
expandafterifxcsname #1endcsnamerelax
myfi@BTb
fi
@secondoftwo
}%
}%<<<
% <<<
% opt arg parsing >>>
mynewdef[long]my@ifmark#1%>>>
{%
ifxmy@mark#1%
myfi@BTb
fi
@secondoftwo
}%<<<
mynewdef[protectedlong]myoarg@oarg#1%>>>
{%
@ifnextchar[{myoarg@oarg@{#1}}{#1{my@mark}}%
}%<<<
mynewdef[long]myoarg@oarg@#1[#2]%>>>
{%
#1{#2}%
}%<<<
mynewdef[protectedlong]myoarg@Oarg#1#2%>>>
{%
@ifnextchar[{myoarg@oarg@{#2}}{#2{#1}}%
}%<<<
mynewdef[protectedlong]myoarg@darg#1#2#3%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#3}}{#3{my@mark}}%
}%<<<
mynewdef[protectedlong]myoarg@Darg#1#2#3#4%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#4}}{#4{#3}}%
}%<<<
% <<<
% macros for string comparison >>>
begingroupdef:{endgroupletmy@sptoken= }:
mynewdefmy@mark{my@mark@if@you@see@this@report@it}
mynewdefmy@stop{my@stop@if@you@see@this@report@it}
mynewdefmyparser@final{final}
mynewdefmyparser@noarg{noarg}
mynewdefmyparser@blankspace{blank space}
% <<<
mynewdef[protected]myparserdef#1#2%>>>
{%
defmyparserdef@twoargs{{#1}{#2}}%
myparserdef@a
}%<<<
mynewdef[protected]myparserdef@a%>>>
{%
futureletmyparserdef@argmyparserdef@b
}%<<<
mynewdefmyparserdef@b%>>>
{%
ifxmyparserdef@argmy@sptoken
myfi@BTb
fi
@secondoftwo
{myparserdef@gobble@space}
{myparserdef@c}%
}%<<<
mynewdef[protected]myparserdef@gobble@space%>>>
{%
afterassignmentmyparserdef@a
letmyparserdef@arg= % space after = to get spaces, too
}%<<<
mynewdef[protected]myparserdef@c#1%>>>
{%
ifxmyparserdef@argbgroup
myfi@BTb
fi
@secondoftwo
{%
defmyparserdef@arg{#1}%
ifxmyparser@blankspacemyparserdef@arg
myfi@BTb
fi
@secondoftwo
{expandaftermyparserdef@dmyparserdef@twoargs{blank space space}}
{expandaftermyparserdef@dmyparserdef@twoargs{#1}}%
}
{expandaftermyparserdef@dmyparserdef@twoargs{meaningmyparserdef@arg}}%
}%<<<
mynewdef[long]myparserdef@d#1#2#3%>>>
{%
myoarg@oarg{myparserdef@e{#1}{#2}{#3}}%
}%<<<
mynewdef[protectedlong]myparserdef@e#1#2#3#4#5%>>>
{%
% if that is the first rule for the parser, define space to be a no-op
myifcsundefTF{myparser@name{#1}}%
{%
expandafterdef
csname myparser@name{#1} all blank space spacespace typeendcsname
{noarg}%
expandafterdef
csname myparser@name{#1} all blank space spacespace codeendcsname
{myparser@getnexttoken}%
% use a macro as a flag that there is at least one parser rule defined
expandafterdefcsnamemyparser@name{#1}endcsname{}%
}
{}%
% check whether the optional argument was used
my@ifmark{#4}
{%
% use a macro as flag that there is a rule for the specified state
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{noarg}%
defmyparserdef@arg@count{0}%
}
{%
edefmyparserdef@arg@count
{thenumexprmyparserdef@argcount#4my@mark}%
ifnummyparserdef@arg@count>9%
PackageError{my}{Too many arguments for parser rule}
{A maximum of 9 parameters is supported.}%
else
ifnummyparserdef@arg@count=0%
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname
{noarg}%
else
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{#4}%
fi
fi
}%
% define what the code does
expandafterdefcsname myparser@name{#1} #2 #3 codeendcsname{}%
expandafterrenewcommandcsname myparser@name{#1} #2 #3 codeendcsname
[myparserdef@arg@count]{#5myparser@getnexttoken}%
}%<<<
mynewdefmyparserdef@argcount#1%>>>
{%
ifxmy@mark#1%
myfi@An
else
myfi@By
fi
{csname myparserdef@argcount@#1endcsname}%
}%<<<
mynewdefmyparserdef@argcount@m%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@o%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@O#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@d#1#2%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@D#1#2#3%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@r#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@t#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparser@name#1%>>>
{%
myparser #1%
}%<<<
mynewdefmyparser@rule#1%>>>
{%
myparser@currentspace #1space meaningmyparser@token
}%<<<
mynewdefmyparser@type#1%>>>
{%
myparser@rule{#1} type%
}%<<<
mynewdefmyparser@code#1%>>>
{%
myparser@rule{#1} code%
}%<<<
mynewdef[protected]myparserrun#1%>>>
{%
myifcsundefTF{myparser@name{#1}}
{PackageError{my}{No parser named '#1' defined}{}}
{%
edefmyparser@current{myparser@name{#1}}%
defmyparser@usersname{#1}%
defmyparser@state{initial}%
myparser@getnexttoken
}%
}%<<<
mynewdef[protected]myparser@getnexttoken%>>>
{%
ifxmyparser@statemyparser@final
myfi@An
else
myfi@By
fi
{%
afterassignmentmyparser@getnexttoken@a
letmyparser@token= % space after = to get spaces, too
}%
}%<<<
mynewdef[protected]myparser@getnexttoken@a%>>>
{%
myifcsundefTF{myparser@type{myparser@state}}
{%
myifcsundefTF{myparser@type{all}}
{%
PackageError{my}
{%
No rule for parser 'myparser@usersname' in state
'myparser@state' for 'meaningmyparser@token'.
Ignoring token%
}
{}%
myparser@getnexttoken
}
{myparser@handle{all}}%
}
{myparser@handle{myparser@state}}%
}%<<<
mynewdef[protected]myparser@handle#1%>>>
{%
expandafterifxcsnamemyparser@type{#1}endcsnamemyparser@noarg
myfi@BTb
fi
@secondoftwo
{csnamemyparser@code{#1}endcsname}
{myparser@handle@args{#1}}%
}%<<<
mynewdef[protected]myparserstate#1%>>>
{%
defmyparser@state{#1}%
}%<<<
mynewdef[protected]myparser@handle@args#1%>>>
{%
defmyparser@stateorall{#1}%
expandafterexpandafterexpandaftermyparser@handle@args@a
csnamemyparser@type{#1}endcsnamemy@markmy@stop
}%<<<
mynewdefmyparser@handle@args@a%>>>
{%
myparser@handle@args@b{}%
}%<<<
mynewdef[long]myparser@handle@args@b#1#2#3my@stop%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{%
myifcsundefTF{myparser@handle@args@#2}
{PackageError{my}{Unknown argument type '#2'}{}}
{csname myparser@handle@args@#2endcsname{#1}{#3}}%
}%
}%<<<
mynewdef[long]myparser@handle@args@c#1#2%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{myparser@handle@args@b{#1}#2my@stop}%
}%<<<
mynewdef[long]myparser@handle@args@m#1#2#3%>>>
{%
myparser@handle@args@c{#1{#3}}{#2}%
}%<<<
mynewdef[protectedlong]myparser@handle@args@o#1#2%>>>
{%
myoarg@oarg{myparser@handle@args@m{#1}{#2}}%
}%<<<
mynewdef[long]myparser@handle@args@O#1#2%>>>
{%
myparser@handle@args@O@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@O@#1#2#3my@stop%>>>
{%
myoarg@Oarg{#2}{myparser@handle@args@m{#1}{#3}}%
}%<<<
mynewdef[long]myparser@handle@args@d#1#2%>>>
{%
myparser@handle@args@d@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@d@#1#2#3#4my@stop%>>>
{%
myoarg@darg{#2}{#3}{myparser@handle@args@m{#1}{#4}}%
}%<<<
mynewdef[long]myparser@handle@args@D#1#2%>>>
{%
myparser@handle@args@D@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@D@#1#2#3#4#5my@stop%>>>
{%
myoarg@Darg{#2}{#3}{#4}{myparser@handle@args@m{#1}{#5}}%
}%<<<
mynewdef[long]myparser@handle@args@r#1#2%>>>
{%
myparser@handle@args@r@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@r@a#1#2#3my@stop%>>>
{%
defmyparser@handle@args@r@b##1#2{myparser@handle@args@c{#1{##1}}{#3}}%
myparser@handle@args@r@b
}%<<<
mynewdef[long]myparser@handle@args@t#1#2%>>>
{%
myparser@handle@args@t@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@t@a#1#2#3my@stop%>>>
{%
@ifnextchar#2%
{@firstoftwo{myparser@handle@args@c{#1{my@mark}}{#3}}}
{myparser@handle@args@c{#1{my@stop}}{#3}}%
}%<<<
letmyIfNoValueTFmy@ifmark
letmyIfBooleanTFmy@ifmark
makeatother
% <<<
% the sheet path parser >>>
makeatletter
definsertpath#1{edefsheetpath@{unexpandedexpandafter{sheetpath@#1}}}
defsheetpathcurve#1#2#3#4%
{%
to[out=0,in=180] ++({.5*sheetlength},{#1*sheetheight})
myIfBooleanTF{#3}{}{node[scale=sheetdotsize,inner sep=0pt]{.}}
myIfNoValueTF{#4}{}{coordinate({#4})}
to[out=0,in=180] ++({.5*sheetlength},{#2*sheetheight})
}
defoutputpath{drawsheetpath@;}
defsheet{defsheetpath@{}myparserrun{sheet path}}
makeatother
myparserdef{sheet path}{initial}{the character (}[r,r)]
{insertpath{({#1},{#2*sheetheight})}myparserstate{started}}
myparserdef{sheet path}{initial}{the character =}
{insertpath{(0,0)--++(sheetlength,0)}myparserstate{started}}
myparserdef{sheet path}{started}{the character (}[r)]
{insertpath{coordinate({#1})}}
myparserdef{sheet path}{started}{the character =}
{insertpath{--++(sheetlength,0)}}
myparserdef{sheet path}{started}{the letter u}[txd()]
{insertpath{sheetpathcurve{.5}{-.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter U}[txd()]
{insertpath{sheetpathcurve{1}{-1}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter d}[txd()]
{insertpath{sheetpathcurve{-.5}{.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter D}[txd()]
{insertpath{sheetpathcurve{-1}{1}{#1}{#2}}}
% making '.' a no-op, that way we can use it to end the search for an optional
% argument for instance
myparserdef{sheet path}{started} .{}
myparserdef{sheet path}{started}{the letter x}[d()]
{%
myIfNoValueTF{#1}
{%
insertpath
{--node[scale=sheetdotsize,inner sep=0pt]{.}++(sheetlength,0)}%
}
{%
insertpath
{%
--node[scale=sheetdotsize,inner sep=0pt]{.}
coordinate({#1})
++(sheetlength,0)
}%
}%
}
myparserdef{sheet path}{all}{the character ;}{myparserstate{final}outputpath}
% <<<
% sheet height and sheet length >>>
pgfset
{
,sheet height/.store in=sheetheight
,sheet height=1
,sheet length/.store in=sheetlength
,sheet length=1
,sheet dot size/.store in=sheetdotsize
,sheet dot size=4
}
%<<<
begin{document}
begin{tikzpicture}
begin{scope}[thick, sheet height=0.75]
sheet === ===d(sp1)=;
sheet (0,-1) ==d(sp2)===ux =;
sheet (0,-2) ==ux ===Dx =;
sheet (0,-3) === ==== =;
sheet (0,-4) === ===U(sp3)=;
sheet (0,-5.5) ==x(b1) ===x(b2) =;
end{scope}
draw[dashed]
(b2) -- (sp1)
(b1) -- (sp2)
;
end{tikzpicture}
end{document}
Wow ! This is a huge work. I'm impressed (like always I'm by the texperts ;)).
– Kpym
4 hours ago
add a comment |
Edit notes:
spaces can now be handled, but are ignored by default
alternative syntax for
myparserdef
added
Inspired by Kpym's answer but thinking that the possibilities of pgfparser
are insufficient, I wrote my own parser, that should be similar to pgf's, but allows the parsed elements to grab arguments. The following arguments are supported:
m
a mandatory argument
r{<delim>}
a mandatory argument delimited by<delim>
o
an optional argument in brackets (you can test for it withmyIfNoValueTF
)
O{<default>}
an optional argument defaulting to<default>
d<delim1><delim2>
an optional argument delimited by<delim1>
and<delim2>
(you can test for it withmyIfNoValueTF
)
D<delim1><delim2>{<default>}
an optional argument delimited by<delim1>
and<delim2>
defaulting to<default>
t<token>
an optional token (you can test for it withmyIfBooleanTF
)
Despite the fact that the argument names are inspired by xparse
, none of them care for balanced delimiters (e.g., [[ab]]
would not be grabbed as [ab]
, but as [ab
with an orphaned ]
, so you'd have to use [{[ab]}]
, this is like the LaTeX2e behaviour). Also all arguments are long by nature unlike xparse
where you'd need to specify that using +
.
You can define a parser using myparserdef{<name>}{<state>}{<meaning>}[<args>]{<code>}
, with <name>
the name of the parser, <state>
the state, <meaning>
the meaning of a token, <args>
an argument string (built from the arguments listed above, up to 9 arguments are supported) and <code>
the code which should be executed for that combination of <name>
, <state>
and <meaning>
. You can switch states using myparserstate{<state>}
, if you switch to final
parsing is ended, the initial state is initial
. All in all this is pretty similar to pgfparser
s way of doing things. The biggest difference is, that my parser ignores blanks by default, however you can define an action for blanks with myparserdef{<name>}{<state>}{blank space}[<args>]{<code>}
. Many consecutive blanks are considered as one.
You can also use a different syntax for myparserdef
namely: myparserdef{<name>}{<state>}<token>[<args>]{<code>}
with <token>
being a single token not surrounded by {}
(spaces are ignored here, it is not checked whether <token>
is really a single token, so be careful). In that case the same will be done as if you'd typed the meaning
of the <token>
, so myparserdef{foo}{bar}a{baz}
does the same as myparserdef{foo}{bar}{the letter a}{baz}
.
A parser is executed using myparserrun{<name>}
. A parser needs at least one rule, else an error is thrown.
This parser system allows me to place coordinates inside of sheet
, which was the main reason why I found pgfparser
insufficient.
I did all of this without using any packages, only LaTeX-Kernel macros, just for procrastinating reasons. Using for example xparse
would shorten the code considerably.
documentclass[tikz,border=7pt]{standalone}
% my parser >>>
makeatletter
% logic helpers >>>
longdefmyfi@An#1fi#2{fi}
longdefmyfi@Ay#1fi#2{fi#2}
longdefmyfi@Byfi#1{fi#1}
longdefmyfi@BTbfi#1#2#3{fi#2}
% <<<
% mynewdef >>>
@ifdefinablemyifundefTF%>>>
{%
defmyifundefTF#1%
{%
ifdefined#1%
myfi@Ay
else
myfi@BTb
fi
{%
ifx#1relax
myfi@BTb
fi
@secondoftwo
}%
}%
}%<<<
@ifdefinablemynewdef%>>>
{%
protecteddefmynewdef{@ifnextchar[{mynewdef@a}{mynewdef@a}}%
}%<<<
@ifdefinablemynewdef@a%>>>
{%
longdefmynewdef@a[#1]#2#3#{mynewdef@b{#1}#2{#3}}%
}%<<<
@ifdefinablemynewdef@b%>>>
{%
protectedlongdefmynewdef@b#1#2#3#4%
{%
myifundefTF#2%
{%
#1def#2#3{#4}%
}
{%
PackageError{my}{Command string#2space already defined}{}%
}
}%
}%<<<
mynewdefmyifcsundefTF#1%>>>
{%
ifcsname #1endcsname
myfi@Ay
else
myfi@BTb
fi
{%
expandafterifxcsname #1endcsnamerelax
myfi@BTb
fi
@secondoftwo
}%
}%<<<
% <<<
% opt arg parsing >>>
mynewdef[long]my@ifmark#1%>>>
{%
ifxmy@mark#1%
myfi@BTb
fi
@secondoftwo
}%<<<
mynewdef[protectedlong]myoarg@oarg#1%>>>
{%
@ifnextchar[{myoarg@oarg@{#1}}{#1{my@mark}}%
}%<<<
mynewdef[long]myoarg@oarg@#1[#2]%>>>
{%
#1{#2}%
}%<<<
mynewdef[protectedlong]myoarg@Oarg#1#2%>>>
{%
@ifnextchar[{myoarg@oarg@{#2}}{#2{#1}}%
}%<<<
mynewdef[protectedlong]myoarg@darg#1#2#3%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#3}}{#3{my@mark}}%
}%<<<
mynewdef[protectedlong]myoarg@Darg#1#2#3#4%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#4}}{#4{#3}}%
}%<<<
% <<<
% macros for string comparison >>>
begingroupdef:{endgroupletmy@sptoken= }:
mynewdefmy@mark{my@mark@if@you@see@this@report@it}
mynewdefmy@stop{my@stop@if@you@see@this@report@it}
mynewdefmyparser@final{final}
mynewdefmyparser@noarg{noarg}
mynewdefmyparser@blankspace{blank space}
% <<<
mynewdef[protected]myparserdef#1#2%>>>
{%
defmyparserdef@twoargs{{#1}{#2}}%
myparserdef@a
}%<<<
mynewdef[protected]myparserdef@a%>>>
{%
futureletmyparserdef@argmyparserdef@b
}%<<<
mynewdefmyparserdef@b%>>>
{%
ifxmyparserdef@argmy@sptoken
myfi@BTb
fi
@secondoftwo
{myparserdef@gobble@space}
{myparserdef@c}%
}%<<<
mynewdef[protected]myparserdef@gobble@space%>>>
{%
afterassignmentmyparserdef@a
letmyparserdef@arg= % space after = to get spaces, too
}%<<<
mynewdef[protected]myparserdef@c#1%>>>
{%
ifxmyparserdef@argbgroup
myfi@BTb
fi
@secondoftwo
{%
defmyparserdef@arg{#1}%
ifxmyparser@blankspacemyparserdef@arg
myfi@BTb
fi
@secondoftwo
{expandaftermyparserdef@dmyparserdef@twoargs{blank space space}}
{expandaftermyparserdef@dmyparserdef@twoargs{#1}}%
}
{expandaftermyparserdef@dmyparserdef@twoargs{meaningmyparserdef@arg}}%
}%<<<
mynewdef[long]myparserdef@d#1#2#3%>>>
{%
myoarg@oarg{myparserdef@e{#1}{#2}{#3}}%
}%<<<
mynewdef[protectedlong]myparserdef@e#1#2#3#4#5%>>>
{%
% if that is the first rule for the parser, define space to be a no-op
myifcsundefTF{myparser@name{#1}}%
{%
expandafterdef
csname myparser@name{#1} all blank space spacespace typeendcsname
{noarg}%
expandafterdef
csname myparser@name{#1} all blank space spacespace codeendcsname
{myparser@getnexttoken}%
% use a macro as a flag that there is at least one parser rule defined
expandafterdefcsnamemyparser@name{#1}endcsname{}%
}
{}%
% check whether the optional argument was used
my@ifmark{#4}
{%
% use a macro as flag that there is a rule for the specified state
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{noarg}%
defmyparserdef@arg@count{0}%
}
{%
edefmyparserdef@arg@count
{thenumexprmyparserdef@argcount#4my@mark}%
ifnummyparserdef@arg@count>9%
PackageError{my}{Too many arguments for parser rule}
{A maximum of 9 parameters is supported.}%
else
ifnummyparserdef@arg@count=0%
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname
{noarg}%
else
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{#4}%
fi
fi
}%
% define what the code does
expandafterdefcsname myparser@name{#1} #2 #3 codeendcsname{}%
expandafterrenewcommandcsname myparser@name{#1} #2 #3 codeendcsname
[myparserdef@arg@count]{#5myparser@getnexttoken}%
}%<<<
mynewdefmyparserdef@argcount#1%>>>
{%
ifxmy@mark#1%
myfi@An
else
myfi@By
fi
{csname myparserdef@argcount@#1endcsname}%
}%<<<
mynewdefmyparserdef@argcount@m%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@o%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@O#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@d#1#2%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@D#1#2#3%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@r#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@t#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparser@name#1%>>>
{%
myparser #1%
}%<<<
mynewdefmyparser@rule#1%>>>
{%
myparser@currentspace #1space meaningmyparser@token
}%<<<
mynewdefmyparser@type#1%>>>
{%
myparser@rule{#1} type%
}%<<<
mynewdefmyparser@code#1%>>>
{%
myparser@rule{#1} code%
}%<<<
mynewdef[protected]myparserrun#1%>>>
{%
myifcsundefTF{myparser@name{#1}}
{PackageError{my}{No parser named '#1' defined}{}}
{%
edefmyparser@current{myparser@name{#1}}%
defmyparser@usersname{#1}%
defmyparser@state{initial}%
myparser@getnexttoken
}%
}%<<<
mynewdef[protected]myparser@getnexttoken%>>>
{%
ifxmyparser@statemyparser@final
myfi@An
else
myfi@By
fi
{%
afterassignmentmyparser@getnexttoken@a
letmyparser@token= % space after = to get spaces, too
}%
}%<<<
mynewdef[protected]myparser@getnexttoken@a%>>>
{%
myifcsundefTF{myparser@type{myparser@state}}
{%
myifcsundefTF{myparser@type{all}}
{%
PackageError{my}
{%
No rule for parser 'myparser@usersname' in state
'myparser@state' for 'meaningmyparser@token'.
Ignoring token%
}
{}%
myparser@getnexttoken
}
{myparser@handle{all}}%
}
{myparser@handle{myparser@state}}%
}%<<<
mynewdef[protected]myparser@handle#1%>>>
{%
expandafterifxcsnamemyparser@type{#1}endcsnamemyparser@noarg
myfi@BTb
fi
@secondoftwo
{csnamemyparser@code{#1}endcsname}
{myparser@handle@args{#1}}%
}%<<<
mynewdef[protected]myparserstate#1%>>>
{%
defmyparser@state{#1}%
}%<<<
mynewdef[protected]myparser@handle@args#1%>>>
{%
defmyparser@stateorall{#1}%
expandafterexpandafterexpandaftermyparser@handle@args@a
csnamemyparser@type{#1}endcsnamemy@markmy@stop
}%<<<
mynewdefmyparser@handle@args@a%>>>
{%
myparser@handle@args@b{}%
}%<<<
mynewdef[long]myparser@handle@args@b#1#2#3my@stop%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{%
myifcsundefTF{myparser@handle@args@#2}
{PackageError{my}{Unknown argument type '#2'}{}}
{csname myparser@handle@args@#2endcsname{#1}{#3}}%
}%
}%<<<
mynewdef[long]myparser@handle@args@c#1#2%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{myparser@handle@args@b{#1}#2my@stop}%
}%<<<
mynewdef[long]myparser@handle@args@m#1#2#3%>>>
{%
myparser@handle@args@c{#1{#3}}{#2}%
}%<<<
mynewdef[protectedlong]myparser@handle@args@o#1#2%>>>
{%
myoarg@oarg{myparser@handle@args@m{#1}{#2}}%
}%<<<
mynewdef[long]myparser@handle@args@O#1#2%>>>
{%
myparser@handle@args@O@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@O@#1#2#3my@stop%>>>
{%
myoarg@Oarg{#2}{myparser@handle@args@m{#1}{#3}}%
}%<<<
mynewdef[long]myparser@handle@args@d#1#2%>>>
{%
myparser@handle@args@d@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@d@#1#2#3#4my@stop%>>>
{%
myoarg@darg{#2}{#3}{myparser@handle@args@m{#1}{#4}}%
}%<<<
mynewdef[long]myparser@handle@args@D#1#2%>>>
{%
myparser@handle@args@D@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@D@#1#2#3#4#5my@stop%>>>
{%
myoarg@Darg{#2}{#3}{#4}{myparser@handle@args@m{#1}{#5}}%
}%<<<
mynewdef[long]myparser@handle@args@r#1#2%>>>
{%
myparser@handle@args@r@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@r@a#1#2#3my@stop%>>>
{%
defmyparser@handle@args@r@b##1#2{myparser@handle@args@c{#1{##1}}{#3}}%
myparser@handle@args@r@b
}%<<<
mynewdef[long]myparser@handle@args@t#1#2%>>>
{%
myparser@handle@args@t@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@t@a#1#2#3my@stop%>>>
{%
@ifnextchar#2%
{@firstoftwo{myparser@handle@args@c{#1{my@mark}}{#3}}}
{myparser@handle@args@c{#1{my@stop}}{#3}}%
}%<<<
letmyIfNoValueTFmy@ifmark
letmyIfBooleanTFmy@ifmark
makeatother
% <<<
% the sheet path parser >>>
makeatletter
definsertpath#1{edefsheetpath@{unexpandedexpandafter{sheetpath@#1}}}
defsheetpathcurve#1#2#3#4%
{%
to[out=0,in=180] ++({.5*sheetlength},{#1*sheetheight})
myIfBooleanTF{#3}{}{node[scale=sheetdotsize,inner sep=0pt]{.}}
myIfNoValueTF{#4}{}{coordinate({#4})}
to[out=0,in=180] ++({.5*sheetlength},{#2*sheetheight})
}
defoutputpath{drawsheetpath@;}
defsheet{defsheetpath@{}myparserrun{sheet path}}
makeatother
myparserdef{sheet path}{initial}{the character (}[r,r)]
{insertpath{({#1},{#2*sheetheight})}myparserstate{started}}
myparserdef{sheet path}{initial}{the character =}
{insertpath{(0,0)--++(sheetlength,0)}myparserstate{started}}
myparserdef{sheet path}{started}{the character (}[r)]
{insertpath{coordinate({#1})}}
myparserdef{sheet path}{started}{the character =}
{insertpath{--++(sheetlength,0)}}
myparserdef{sheet path}{started}{the letter u}[txd()]
{insertpath{sheetpathcurve{.5}{-.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter U}[txd()]
{insertpath{sheetpathcurve{1}{-1}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter d}[txd()]
{insertpath{sheetpathcurve{-.5}{.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter D}[txd()]
{insertpath{sheetpathcurve{-1}{1}{#1}{#2}}}
% making '.' a no-op, that way we can use it to end the search for an optional
% argument for instance
myparserdef{sheet path}{started} .{}
myparserdef{sheet path}{started}{the letter x}[d()]
{%
myIfNoValueTF{#1}
{%
insertpath
{--node[scale=sheetdotsize,inner sep=0pt]{.}++(sheetlength,0)}%
}
{%
insertpath
{%
--node[scale=sheetdotsize,inner sep=0pt]{.}
coordinate({#1})
++(sheetlength,0)
}%
}%
}
myparserdef{sheet path}{all}{the character ;}{myparserstate{final}outputpath}
% <<<
% sheet height and sheet length >>>
pgfset
{
,sheet height/.store in=sheetheight
,sheet height=1
,sheet length/.store in=sheetlength
,sheet length=1
,sheet dot size/.store in=sheetdotsize
,sheet dot size=4
}
%<<<
begin{document}
begin{tikzpicture}
begin{scope}[thick, sheet height=0.75]
sheet === ===d(sp1)=;
sheet (0,-1) ==d(sp2)===ux =;
sheet (0,-2) ==ux ===Dx =;
sheet (0,-3) === ==== =;
sheet (0,-4) === ===U(sp3)=;
sheet (0,-5.5) ==x(b1) ===x(b2) =;
end{scope}
draw[dashed]
(b2) -- (sp1)
(b1) -- (sp2)
;
end{tikzpicture}
end{document}
Wow ! This is a huge work. I'm impressed (like always I'm by the texperts ;)).
– Kpym
4 hours ago
add a comment |
Edit notes:
spaces can now be handled, but are ignored by default
alternative syntax for
myparserdef
added
Inspired by Kpym's answer but thinking that the possibilities of pgfparser
are insufficient, I wrote my own parser, that should be similar to pgf's, but allows the parsed elements to grab arguments. The following arguments are supported:
m
a mandatory argument
r{<delim>}
a mandatory argument delimited by<delim>
o
an optional argument in brackets (you can test for it withmyIfNoValueTF
)
O{<default>}
an optional argument defaulting to<default>
d<delim1><delim2>
an optional argument delimited by<delim1>
and<delim2>
(you can test for it withmyIfNoValueTF
)
D<delim1><delim2>{<default>}
an optional argument delimited by<delim1>
and<delim2>
defaulting to<default>
t<token>
an optional token (you can test for it withmyIfBooleanTF
)
Despite the fact that the argument names are inspired by xparse
, none of them care for balanced delimiters (e.g., [[ab]]
would not be grabbed as [ab]
, but as [ab
with an orphaned ]
, so you'd have to use [{[ab]}]
, this is like the LaTeX2e behaviour). Also all arguments are long by nature unlike xparse
where you'd need to specify that using +
.
You can define a parser using myparserdef{<name>}{<state>}{<meaning>}[<args>]{<code>}
, with <name>
the name of the parser, <state>
the state, <meaning>
the meaning of a token, <args>
an argument string (built from the arguments listed above, up to 9 arguments are supported) and <code>
the code which should be executed for that combination of <name>
, <state>
and <meaning>
. You can switch states using myparserstate{<state>}
, if you switch to final
parsing is ended, the initial state is initial
. All in all this is pretty similar to pgfparser
s way of doing things. The biggest difference is, that my parser ignores blanks by default, however you can define an action for blanks with myparserdef{<name>}{<state>}{blank space}[<args>]{<code>}
. Many consecutive blanks are considered as one.
You can also use a different syntax for myparserdef
namely: myparserdef{<name>}{<state>}<token>[<args>]{<code>}
with <token>
being a single token not surrounded by {}
(spaces are ignored here, it is not checked whether <token>
is really a single token, so be careful). In that case the same will be done as if you'd typed the meaning
of the <token>
, so myparserdef{foo}{bar}a{baz}
does the same as myparserdef{foo}{bar}{the letter a}{baz}
.
A parser is executed using myparserrun{<name>}
. A parser needs at least one rule, else an error is thrown.
This parser system allows me to place coordinates inside of sheet
, which was the main reason why I found pgfparser
insufficient.
I did all of this without using any packages, only LaTeX-Kernel macros, just for procrastinating reasons. Using for example xparse
would shorten the code considerably.
documentclass[tikz,border=7pt]{standalone}
% my parser >>>
makeatletter
% logic helpers >>>
longdefmyfi@An#1fi#2{fi}
longdefmyfi@Ay#1fi#2{fi#2}
longdefmyfi@Byfi#1{fi#1}
longdefmyfi@BTbfi#1#2#3{fi#2}
% <<<
% mynewdef >>>
@ifdefinablemyifundefTF%>>>
{%
defmyifundefTF#1%
{%
ifdefined#1%
myfi@Ay
else
myfi@BTb
fi
{%
ifx#1relax
myfi@BTb
fi
@secondoftwo
}%
}%
}%<<<
@ifdefinablemynewdef%>>>
{%
protecteddefmynewdef{@ifnextchar[{mynewdef@a}{mynewdef@a}}%
}%<<<
@ifdefinablemynewdef@a%>>>
{%
longdefmynewdef@a[#1]#2#3#{mynewdef@b{#1}#2{#3}}%
}%<<<
@ifdefinablemynewdef@b%>>>
{%
protectedlongdefmynewdef@b#1#2#3#4%
{%
myifundefTF#2%
{%
#1def#2#3{#4}%
}
{%
PackageError{my}{Command string#2space already defined}{}%
}
}%
}%<<<
mynewdefmyifcsundefTF#1%>>>
{%
ifcsname #1endcsname
myfi@Ay
else
myfi@BTb
fi
{%
expandafterifxcsname #1endcsnamerelax
myfi@BTb
fi
@secondoftwo
}%
}%<<<
% <<<
% opt arg parsing >>>
mynewdef[long]my@ifmark#1%>>>
{%
ifxmy@mark#1%
myfi@BTb
fi
@secondoftwo
}%<<<
mynewdef[protectedlong]myoarg@oarg#1%>>>
{%
@ifnextchar[{myoarg@oarg@{#1}}{#1{my@mark}}%
}%<<<
mynewdef[long]myoarg@oarg@#1[#2]%>>>
{%
#1{#2}%
}%<<<
mynewdef[protectedlong]myoarg@Oarg#1#2%>>>
{%
@ifnextchar[{myoarg@oarg@{#2}}{#2{#1}}%
}%<<<
mynewdef[protectedlong]myoarg@darg#1#2#3%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#3}}{#3{my@mark}}%
}%<<<
mynewdef[protectedlong]myoarg@Darg#1#2#3#4%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#4}}{#4{#3}}%
}%<<<
% <<<
% macros for string comparison >>>
begingroupdef:{endgroupletmy@sptoken= }:
mynewdefmy@mark{my@mark@if@you@see@this@report@it}
mynewdefmy@stop{my@stop@if@you@see@this@report@it}
mynewdefmyparser@final{final}
mynewdefmyparser@noarg{noarg}
mynewdefmyparser@blankspace{blank space}
% <<<
mynewdef[protected]myparserdef#1#2%>>>
{%
defmyparserdef@twoargs{{#1}{#2}}%
myparserdef@a
}%<<<
mynewdef[protected]myparserdef@a%>>>
{%
futureletmyparserdef@argmyparserdef@b
}%<<<
mynewdefmyparserdef@b%>>>
{%
ifxmyparserdef@argmy@sptoken
myfi@BTb
fi
@secondoftwo
{myparserdef@gobble@space}
{myparserdef@c}%
}%<<<
mynewdef[protected]myparserdef@gobble@space%>>>
{%
afterassignmentmyparserdef@a
letmyparserdef@arg= % space after = to get spaces, too
}%<<<
mynewdef[protected]myparserdef@c#1%>>>
{%
ifxmyparserdef@argbgroup
myfi@BTb
fi
@secondoftwo
{%
defmyparserdef@arg{#1}%
ifxmyparser@blankspacemyparserdef@arg
myfi@BTb
fi
@secondoftwo
{expandaftermyparserdef@dmyparserdef@twoargs{blank space space}}
{expandaftermyparserdef@dmyparserdef@twoargs{#1}}%
}
{expandaftermyparserdef@dmyparserdef@twoargs{meaningmyparserdef@arg}}%
}%<<<
mynewdef[long]myparserdef@d#1#2#3%>>>
{%
myoarg@oarg{myparserdef@e{#1}{#2}{#3}}%
}%<<<
mynewdef[protectedlong]myparserdef@e#1#2#3#4#5%>>>
{%
% if that is the first rule for the parser, define space to be a no-op
myifcsundefTF{myparser@name{#1}}%
{%
expandafterdef
csname myparser@name{#1} all blank space spacespace typeendcsname
{noarg}%
expandafterdef
csname myparser@name{#1} all blank space spacespace codeendcsname
{myparser@getnexttoken}%
% use a macro as a flag that there is at least one parser rule defined
expandafterdefcsnamemyparser@name{#1}endcsname{}%
}
{}%
% check whether the optional argument was used
my@ifmark{#4}
{%
% use a macro as flag that there is a rule for the specified state
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{noarg}%
defmyparserdef@arg@count{0}%
}
{%
edefmyparserdef@arg@count
{thenumexprmyparserdef@argcount#4my@mark}%
ifnummyparserdef@arg@count>9%
PackageError{my}{Too many arguments for parser rule}
{A maximum of 9 parameters is supported.}%
else
ifnummyparserdef@arg@count=0%
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname
{noarg}%
else
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{#4}%
fi
fi
}%
% define what the code does
expandafterdefcsname myparser@name{#1} #2 #3 codeendcsname{}%
expandafterrenewcommandcsname myparser@name{#1} #2 #3 codeendcsname
[myparserdef@arg@count]{#5myparser@getnexttoken}%
}%<<<
mynewdefmyparserdef@argcount#1%>>>
{%
ifxmy@mark#1%
myfi@An
else
myfi@By
fi
{csname myparserdef@argcount@#1endcsname}%
}%<<<
mynewdefmyparserdef@argcount@m%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@o%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@O#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@d#1#2%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@D#1#2#3%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@r#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@t#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparser@name#1%>>>
{%
myparser #1%
}%<<<
mynewdefmyparser@rule#1%>>>
{%
myparser@currentspace #1space meaningmyparser@token
}%<<<
mynewdefmyparser@type#1%>>>
{%
myparser@rule{#1} type%
}%<<<
mynewdefmyparser@code#1%>>>
{%
myparser@rule{#1} code%
}%<<<
mynewdef[protected]myparserrun#1%>>>
{%
myifcsundefTF{myparser@name{#1}}
{PackageError{my}{No parser named '#1' defined}{}}
{%
edefmyparser@current{myparser@name{#1}}%
defmyparser@usersname{#1}%
defmyparser@state{initial}%
myparser@getnexttoken
}%
}%<<<
mynewdef[protected]myparser@getnexttoken%>>>
{%
ifxmyparser@statemyparser@final
myfi@An
else
myfi@By
fi
{%
afterassignmentmyparser@getnexttoken@a
letmyparser@token= % space after = to get spaces, too
}%
}%<<<
mynewdef[protected]myparser@getnexttoken@a%>>>
{%
myifcsundefTF{myparser@type{myparser@state}}
{%
myifcsundefTF{myparser@type{all}}
{%
PackageError{my}
{%
No rule for parser 'myparser@usersname' in state
'myparser@state' for 'meaningmyparser@token'.
Ignoring token%
}
{}%
myparser@getnexttoken
}
{myparser@handle{all}}%
}
{myparser@handle{myparser@state}}%
}%<<<
mynewdef[protected]myparser@handle#1%>>>
{%
expandafterifxcsnamemyparser@type{#1}endcsnamemyparser@noarg
myfi@BTb
fi
@secondoftwo
{csnamemyparser@code{#1}endcsname}
{myparser@handle@args{#1}}%
}%<<<
mynewdef[protected]myparserstate#1%>>>
{%
defmyparser@state{#1}%
}%<<<
mynewdef[protected]myparser@handle@args#1%>>>
{%
defmyparser@stateorall{#1}%
expandafterexpandafterexpandaftermyparser@handle@args@a
csnamemyparser@type{#1}endcsnamemy@markmy@stop
}%<<<
mynewdefmyparser@handle@args@a%>>>
{%
myparser@handle@args@b{}%
}%<<<
mynewdef[long]myparser@handle@args@b#1#2#3my@stop%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{%
myifcsundefTF{myparser@handle@args@#2}
{PackageError{my}{Unknown argument type '#2'}{}}
{csname myparser@handle@args@#2endcsname{#1}{#3}}%
}%
}%<<<
mynewdef[long]myparser@handle@args@c#1#2%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{myparser@handle@args@b{#1}#2my@stop}%
}%<<<
mynewdef[long]myparser@handle@args@m#1#2#3%>>>
{%
myparser@handle@args@c{#1{#3}}{#2}%
}%<<<
mynewdef[protectedlong]myparser@handle@args@o#1#2%>>>
{%
myoarg@oarg{myparser@handle@args@m{#1}{#2}}%
}%<<<
mynewdef[long]myparser@handle@args@O#1#2%>>>
{%
myparser@handle@args@O@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@O@#1#2#3my@stop%>>>
{%
myoarg@Oarg{#2}{myparser@handle@args@m{#1}{#3}}%
}%<<<
mynewdef[long]myparser@handle@args@d#1#2%>>>
{%
myparser@handle@args@d@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@d@#1#2#3#4my@stop%>>>
{%
myoarg@darg{#2}{#3}{myparser@handle@args@m{#1}{#4}}%
}%<<<
mynewdef[long]myparser@handle@args@D#1#2%>>>
{%
myparser@handle@args@D@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@D@#1#2#3#4#5my@stop%>>>
{%
myoarg@Darg{#2}{#3}{#4}{myparser@handle@args@m{#1}{#5}}%
}%<<<
mynewdef[long]myparser@handle@args@r#1#2%>>>
{%
myparser@handle@args@r@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@r@a#1#2#3my@stop%>>>
{%
defmyparser@handle@args@r@b##1#2{myparser@handle@args@c{#1{##1}}{#3}}%
myparser@handle@args@r@b
}%<<<
mynewdef[long]myparser@handle@args@t#1#2%>>>
{%
myparser@handle@args@t@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@t@a#1#2#3my@stop%>>>
{%
@ifnextchar#2%
{@firstoftwo{myparser@handle@args@c{#1{my@mark}}{#3}}}
{myparser@handle@args@c{#1{my@stop}}{#3}}%
}%<<<
letmyIfNoValueTFmy@ifmark
letmyIfBooleanTFmy@ifmark
makeatother
% <<<
% the sheet path parser >>>
makeatletter
definsertpath#1{edefsheetpath@{unexpandedexpandafter{sheetpath@#1}}}
defsheetpathcurve#1#2#3#4%
{%
to[out=0,in=180] ++({.5*sheetlength},{#1*sheetheight})
myIfBooleanTF{#3}{}{node[scale=sheetdotsize,inner sep=0pt]{.}}
myIfNoValueTF{#4}{}{coordinate({#4})}
to[out=0,in=180] ++({.5*sheetlength},{#2*sheetheight})
}
defoutputpath{drawsheetpath@;}
defsheet{defsheetpath@{}myparserrun{sheet path}}
makeatother
myparserdef{sheet path}{initial}{the character (}[r,r)]
{insertpath{({#1},{#2*sheetheight})}myparserstate{started}}
myparserdef{sheet path}{initial}{the character =}
{insertpath{(0,0)--++(sheetlength,0)}myparserstate{started}}
myparserdef{sheet path}{started}{the character (}[r)]
{insertpath{coordinate({#1})}}
myparserdef{sheet path}{started}{the character =}
{insertpath{--++(sheetlength,0)}}
myparserdef{sheet path}{started}{the letter u}[txd()]
{insertpath{sheetpathcurve{.5}{-.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter U}[txd()]
{insertpath{sheetpathcurve{1}{-1}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter d}[txd()]
{insertpath{sheetpathcurve{-.5}{.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter D}[txd()]
{insertpath{sheetpathcurve{-1}{1}{#1}{#2}}}
% making '.' a no-op, that way we can use it to end the search for an optional
% argument for instance
myparserdef{sheet path}{started} .{}
myparserdef{sheet path}{started}{the letter x}[d()]
{%
myIfNoValueTF{#1}
{%
insertpath
{--node[scale=sheetdotsize,inner sep=0pt]{.}++(sheetlength,0)}%
}
{%
insertpath
{%
--node[scale=sheetdotsize,inner sep=0pt]{.}
coordinate({#1})
++(sheetlength,0)
}%
}%
}
myparserdef{sheet path}{all}{the character ;}{myparserstate{final}outputpath}
% <<<
% sheet height and sheet length >>>
pgfset
{
,sheet height/.store in=sheetheight
,sheet height=1
,sheet length/.store in=sheetlength
,sheet length=1
,sheet dot size/.store in=sheetdotsize
,sheet dot size=4
}
%<<<
begin{document}
begin{tikzpicture}
begin{scope}[thick, sheet height=0.75]
sheet === ===d(sp1)=;
sheet (0,-1) ==d(sp2)===ux =;
sheet (0,-2) ==ux ===Dx =;
sheet (0,-3) === ==== =;
sheet (0,-4) === ===U(sp3)=;
sheet (0,-5.5) ==x(b1) ===x(b2) =;
end{scope}
draw[dashed]
(b2) -- (sp1)
(b1) -- (sp2)
;
end{tikzpicture}
end{document}
Edit notes:
spaces can now be handled, but are ignored by default
alternative syntax for
myparserdef
added
Inspired by Kpym's answer but thinking that the possibilities of pgfparser
are insufficient, I wrote my own parser, that should be similar to pgf's, but allows the parsed elements to grab arguments. The following arguments are supported:
m
a mandatory argument
r{<delim>}
a mandatory argument delimited by<delim>
o
an optional argument in brackets (you can test for it withmyIfNoValueTF
)
O{<default>}
an optional argument defaulting to<default>
d<delim1><delim2>
an optional argument delimited by<delim1>
and<delim2>
(you can test for it withmyIfNoValueTF
)
D<delim1><delim2>{<default>}
an optional argument delimited by<delim1>
and<delim2>
defaulting to<default>
t<token>
an optional token (you can test for it withmyIfBooleanTF
)
Despite the fact that the argument names are inspired by xparse
, none of them care for balanced delimiters (e.g., [[ab]]
would not be grabbed as [ab]
, but as [ab
with an orphaned ]
, so you'd have to use [{[ab]}]
, this is like the LaTeX2e behaviour). Also all arguments are long by nature unlike xparse
where you'd need to specify that using +
.
You can define a parser using myparserdef{<name>}{<state>}{<meaning>}[<args>]{<code>}
, with <name>
the name of the parser, <state>
the state, <meaning>
the meaning of a token, <args>
an argument string (built from the arguments listed above, up to 9 arguments are supported) and <code>
the code which should be executed for that combination of <name>
, <state>
and <meaning>
. You can switch states using myparserstate{<state>}
, if you switch to final
parsing is ended, the initial state is initial
. All in all this is pretty similar to pgfparser
s way of doing things. The biggest difference is, that my parser ignores blanks by default, however you can define an action for blanks with myparserdef{<name>}{<state>}{blank space}[<args>]{<code>}
. Many consecutive blanks are considered as one.
You can also use a different syntax for myparserdef
namely: myparserdef{<name>}{<state>}<token>[<args>]{<code>}
with <token>
being a single token not surrounded by {}
(spaces are ignored here, it is not checked whether <token>
is really a single token, so be careful). In that case the same will be done as if you'd typed the meaning
of the <token>
, so myparserdef{foo}{bar}a{baz}
does the same as myparserdef{foo}{bar}{the letter a}{baz}
.
A parser is executed using myparserrun{<name>}
. A parser needs at least one rule, else an error is thrown.
This parser system allows me to place coordinates inside of sheet
, which was the main reason why I found pgfparser
insufficient.
I did all of this without using any packages, only LaTeX-Kernel macros, just for procrastinating reasons. Using for example xparse
would shorten the code considerably.
documentclass[tikz,border=7pt]{standalone}
% my parser >>>
makeatletter
% logic helpers >>>
longdefmyfi@An#1fi#2{fi}
longdefmyfi@Ay#1fi#2{fi#2}
longdefmyfi@Byfi#1{fi#1}
longdefmyfi@BTbfi#1#2#3{fi#2}
% <<<
% mynewdef >>>
@ifdefinablemyifundefTF%>>>
{%
defmyifundefTF#1%
{%
ifdefined#1%
myfi@Ay
else
myfi@BTb
fi
{%
ifx#1relax
myfi@BTb
fi
@secondoftwo
}%
}%
}%<<<
@ifdefinablemynewdef%>>>
{%
protecteddefmynewdef{@ifnextchar[{mynewdef@a}{mynewdef@a}}%
}%<<<
@ifdefinablemynewdef@a%>>>
{%
longdefmynewdef@a[#1]#2#3#{mynewdef@b{#1}#2{#3}}%
}%<<<
@ifdefinablemynewdef@b%>>>
{%
protectedlongdefmynewdef@b#1#2#3#4%
{%
myifundefTF#2%
{%
#1def#2#3{#4}%
}
{%
PackageError{my}{Command string#2space already defined}{}%
}
}%
}%<<<
mynewdefmyifcsundefTF#1%>>>
{%
ifcsname #1endcsname
myfi@Ay
else
myfi@BTb
fi
{%
expandafterifxcsname #1endcsnamerelax
myfi@BTb
fi
@secondoftwo
}%
}%<<<
% <<<
% opt arg parsing >>>
mynewdef[long]my@ifmark#1%>>>
{%
ifxmy@mark#1%
myfi@BTb
fi
@secondoftwo
}%<<<
mynewdef[protectedlong]myoarg@oarg#1%>>>
{%
@ifnextchar[{myoarg@oarg@{#1}}{#1{my@mark}}%
}%<<<
mynewdef[long]myoarg@oarg@#1[#2]%>>>
{%
#1{#2}%
}%<<<
mynewdef[protectedlong]myoarg@Oarg#1#2%>>>
{%
@ifnextchar[{myoarg@oarg@{#2}}{#2{#1}}%
}%<<<
mynewdef[protectedlong]myoarg@darg#1#2#3%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#3}}{#3{my@mark}}%
}%<<<
mynewdef[protectedlong]myoarg@Darg#1#2#3#4%>>>
{%
longdefmyoarg@darg@##1#1##2#2{##1{##2}}%
@ifnextchar#1{myoarg@darg@{#4}}{#4{#3}}%
}%<<<
% <<<
% macros for string comparison >>>
begingroupdef:{endgroupletmy@sptoken= }:
mynewdefmy@mark{my@mark@if@you@see@this@report@it}
mynewdefmy@stop{my@stop@if@you@see@this@report@it}
mynewdefmyparser@final{final}
mynewdefmyparser@noarg{noarg}
mynewdefmyparser@blankspace{blank space}
% <<<
mynewdef[protected]myparserdef#1#2%>>>
{%
defmyparserdef@twoargs{{#1}{#2}}%
myparserdef@a
}%<<<
mynewdef[protected]myparserdef@a%>>>
{%
futureletmyparserdef@argmyparserdef@b
}%<<<
mynewdefmyparserdef@b%>>>
{%
ifxmyparserdef@argmy@sptoken
myfi@BTb
fi
@secondoftwo
{myparserdef@gobble@space}
{myparserdef@c}%
}%<<<
mynewdef[protected]myparserdef@gobble@space%>>>
{%
afterassignmentmyparserdef@a
letmyparserdef@arg= % space after = to get spaces, too
}%<<<
mynewdef[protected]myparserdef@c#1%>>>
{%
ifxmyparserdef@argbgroup
myfi@BTb
fi
@secondoftwo
{%
defmyparserdef@arg{#1}%
ifxmyparser@blankspacemyparserdef@arg
myfi@BTb
fi
@secondoftwo
{expandaftermyparserdef@dmyparserdef@twoargs{blank space space}}
{expandaftermyparserdef@dmyparserdef@twoargs{#1}}%
}
{expandaftermyparserdef@dmyparserdef@twoargs{meaningmyparserdef@arg}}%
}%<<<
mynewdef[long]myparserdef@d#1#2#3%>>>
{%
myoarg@oarg{myparserdef@e{#1}{#2}{#3}}%
}%<<<
mynewdef[protectedlong]myparserdef@e#1#2#3#4#5%>>>
{%
% if that is the first rule for the parser, define space to be a no-op
myifcsundefTF{myparser@name{#1}}%
{%
expandafterdef
csname myparser@name{#1} all blank space spacespace typeendcsname
{noarg}%
expandafterdef
csname myparser@name{#1} all blank space spacespace codeendcsname
{myparser@getnexttoken}%
% use a macro as a flag that there is at least one parser rule defined
expandafterdefcsnamemyparser@name{#1}endcsname{}%
}
{}%
% check whether the optional argument was used
my@ifmark{#4}
{%
% use a macro as flag that there is a rule for the specified state
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{noarg}%
defmyparserdef@arg@count{0}%
}
{%
edefmyparserdef@arg@count
{thenumexprmyparserdef@argcount#4my@mark}%
ifnummyparserdef@arg@count>9%
PackageError{my}{Too many arguments for parser rule}
{A maximum of 9 parameters is supported.}%
else
ifnummyparserdef@arg@count=0%
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname
{noarg}%
else
expandafterdefcsname myparser@name{#1} #2 #3 typeendcsname{#4}%
fi
fi
}%
% define what the code does
expandafterdefcsname myparser@name{#1} #2 #3 codeendcsname{}%
expandafterrenewcommandcsname myparser@name{#1} #2 #3 codeendcsname
[myparserdef@arg@count]{#5myparser@getnexttoken}%
}%<<<
mynewdefmyparserdef@argcount#1%>>>
{%
ifxmy@mark#1%
myfi@An
else
myfi@By
fi
{csname myparserdef@argcount@#1endcsname}%
}%<<<
mynewdefmyparserdef@argcount@m%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@o%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@O#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@d#1#2%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@D#1#2#3%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@r#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparserdef@argcount@t#1%>>>
{%
+1%
myparserdef@argcount
}%<<<
mynewdefmyparser@name#1%>>>
{%
myparser #1%
}%<<<
mynewdefmyparser@rule#1%>>>
{%
myparser@currentspace #1space meaningmyparser@token
}%<<<
mynewdefmyparser@type#1%>>>
{%
myparser@rule{#1} type%
}%<<<
mynewdefmyparser@code#1%>>>
{%
myparser@rule{#1} code%
}%<<<
mynewdef[protected]myparserrun#1%>>>
{%
myifcsundefTF{myparser@name{#1}}
{PackageError{my}{No parser named '#1' defined}{}}
{%
edefmyparser@current{myparser@name{#1}}%
defmyparser@usersname{#1}%
defmyparser@state{initial}%
myparser@getnexttoken
}%
}%<<<
mynewdef[protected]myparser@getnexttoken%>>>
{%
ifxmyparser@statemyparser@final
myfi@An
else
myfi@By
fi
{%
afterassignmentmyparser@getnexttoken@a
letmyparser@token= % space after = to get spaces, too
}%
}%<<<
mynewdef[protected]myparser@getnexttoken@a%>>>
{%
myifcsundefTF{myparser@type{myparser@state}}
{%
myifcsundefTF{myparser@type{all}}
{%
PackageError{my}
{%
No rule for parser 'myparser@usersname' in state
'myparser@state' for 'meaningmyparser@token'.
Ignoring token%
}
{}%
myparser@getnexttoken
}
{myparser@handle{all}}%
}
{myparser@handle{myparser@state}}%
}%<<<
mynewdef[protected]myparser@handle#1%>>>
{%
expandafterifxcsnamemyparser@type{#1}endcsnamemyparser@noarg
myfi@BTb
fi
@secondoftwo
{csnamemyparser@code{#1}endcsname}
{myparser@handle@args{#1}}%
}%<<<
mynewdef[protected]myparserstate#1%>>>
{%
defmyparser@state{#1}%
}%<<<
mynewdef[protected]myparser@handle@args#1%>>>
{%
defmyparser@stateorall{#1}%
expandafterexpandafterexpandaftermyparser@handle@args@a
csnamemyparser@type{#1}endcsnamemy@markmy@stop
}%<<<
mynewdefmyparser@handle@args@a%>>>
{%
myparser@handle@args@b{}%
}%<<<
mynewdef[long]myparser@handle@args@b#1#2#3my@stop%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{%
myifcsundefTF{myparser@handle@args@#2}
{PackageError{my}{Unknown argument type '#2'}{}}
{csname myparser@handle@args@#2endcsname{#1}{#3}}%
}%
}%<<<
mynewdef[long]myparser@handle@args@c#1#2%>>>
{%
my@ifmark{#2}
{csnamemyparser@code{myparser@stateorall}endcsname#1}
{myparser@handle@args@b{#1}#2my@stop}%
}%<<<
mynewdef[long]myparser@handle@args@m#1#2#3%>>>
{%
myparser@handle@args@c{#1{#3}}{#2}%
}%<<<
mynewdef[protectedlong]myparser@handle@args@o#1#2%>>>
{%
myoarg@oarg{myparser@handle@args@m{#1}{#2}}%
}%<<<
mynewdef[long]myparser@handle@args@O#1#2%>>>
{%
myparser@handle@args@O@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@O@#1#2#3my@stop%>>>
{%
myoarg@Oarg{#2}{myparser@handle@args@m{#1}{#3}}%
}%<<<
mynewdef[long]myparser@handle@args@d#1#2%>>>
{%
myparser@handle@args@d@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@d@#1#2#3#4my@stop%>>>
{%
myoarg@darg{#2}{#3}{myparser@handle@args@m{#1}{#4}}%
}%<<<
mynewdef[long]myparser@handle@args@D#1#2%>>>
{%
myparser@handle@args@D@{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@D@#1#2#3#4#5my@stop%>>>
{%
myoarg@Darg{#2}{#3}{#4}{myparser@handle@args@m{#1}{#5}}%
}%<<<
mynewdef[long]myparser@handle@args@r#1#2%>>>
{%
myparser@handle@args@r@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@r@a#1#2#3my@stop%>>>
{%
defmyparser@handle@args@r@b##1#2{myparser@handle@args@c{#1{##1}}{#3}}%
myparser@handle@args@r@b
}%<<<
mynewdef[long]myparser@handle@args@t#1#2%>>>
{%
myparser@handle@args@t@a{#1}#2my@stop
}%<<<
mynewdef[protectedlong]myparser@handle@args@t@a#1#2#3my@stop%>>>
{%
@ifnextchar#2%
{@firstoftwo{myparser@handle@args@c{#1{my@mark}}{#3}}}
{myparser@handle@args@c{#1{my@stop}}{#3}}%
}%<<<
letmyIfNoValueTFmy@ifmark
letmyIfBooleanTFmy@ifmark
makeatother
% <<<
% the sheet path parser >>>
makeatletter
definsertpath#1{edefsheetpath@{unexpandedexpandafter{sheetpath@#1}}}
defsheetpathcurve#1#2#3#4%
{%
to[out=0,in=180] ++({.5*sheetlength},{#1*sheetheight})
myIfBooleanTF{#3}{}{node[scale=sheetdotsize,inner sep=0pt]{.}}
myIfNoValueTF{#4}{}{coordinate({#4})}
to[out=0,in=180] ++({.5*sheetlength},{#2*sheetheight})
}
defoutputpath{drawsheetpath@;}
defsheet{defsheetpath@{}myparserrun{sheet path}}
makeatother
myparserdef{sheet path}{initial}{the character (}[r,r)]
{insertpath{({#1},{#2*sheetheight})}myparserstate{started}}
myparserdef{sheet path}{initial}{the character =}
{insertpath{(0,0)--++(sheetlength,0)}myparserstate{started}}
myparserdef{sheet path}{started}{the character (}[r)]
{insertpath{coordinate({#1})}}
myparserdef{sheet path}{started}{the character =}
{insertpath{--++(sheetlength,0)}}
myparserdef{sheet path}{started}{the letter u}[txd()]
{insertpath{sheetpathcurve{.5}{-.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter U}[txd()]
{insertpath{sheetpathcurve{1}{-1}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter d}[txd()]
{insertpath{sheetpathcurve{-.5}{.5}{#1}{#2}}}
myparserdef{sheet path}{started}{the letter D}[txd()]
{insertpath{sheetpathcurve{-1}{1}{#1}{#2}}}
% making '.' a no-op, that way we can use it to end the search for an optional
% argument for instance
myparserdef{sheet path}{started} .{}
myparserdef{sheet path}{started}{the letter x}[d()]
{%
myIfNoValueTF{#1}
{%
insertpath
{--node[scale=sheetdotsize,inner sep=0pt]{.}++(sheetlength,0)}%
}
{%
insertpath
{%
--node[scale=sheetdotsize,inner sep=0pt]{.}
coordinate({#1})
++(sheetlength,0)
}%
}%
}
myparserdef{sheet path}{all}{the character ;}{myparserstate{final}outputpath}
% <<<
% sheet height and sheet length >>>
pgfset
{
,sheet height/.store in=sheetheight
,sheet height=1
,sheet length/.store in=sheetlength
,sheet length=1
,sheet dot size/.store in=sheetdotsize
,sheet dot size=4
}
%<<<
begin{document}
begin{tikzpicture}
begin{scope}[thick, sheet height=0.75]
sheet === ===d(sp1)=;
sheet (0,-1) ==d(sp2)===ux =;
sheet (0,-2) ==ux ===Dx =;
sheet (0,-3) === ==== =;
sheet (0,-4) === ===U(sp3)=;
sheet (0,-5.5) ==x(b1) ===x(b2) =;
end{scope}
draw[dashed]
(b2) -- (sp1)
(b1) -- (sp2)
;
end{tikzpicture}
end{document}
edited 16 hours ago
answered yesterday
SkillmonSkillmon
23.8k12248
23.8k12248
Wow ! This is a huge work. I'm impressed (like always I'm by the texperts ;)).
– Kpym
4 hours ago
add a comment |
Wow ! This is a huge work. I'm impressed (like always I'm by the texperts ;)).
– Kpym
4 hours ago
Wow ! This is a huge work. I'm impressed (like always I'm by the texperts ;)).
– Kpym
4 hours ago
Wow ! This is a huge work. I'm impressed (like always I'm by the texperts ;)).
– Kpym
4 hours ago
add a comment |
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f481125%2fdrawing-ramified-coverings-with-tikz%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Although it doesn't have
curvy
links,tikz-timing
could be useful to draw this kind of diagrams.– Ignasi
17 hours ago