我对流行病中经典 SIR 模型的变体很感兴趣。使用 TikZ 的插图简单易用node
。
\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}
\def\a{1.5}
\path[nodes={draw,circle,minimum size=8mm,fill=yellow,draw=blue}]
(210:\a) node (S) {$S$}
(-30:\a) node (I) {$I$}
(90:\a) node (R) {$R$}
;
\draw[->] (S)--(I);
\draw[->] (I)--(R);
\draw[->] (R)--(S);
\draw[->] (R.0) to[out=0,in=60] (I.60);
\end{tikzpicture}
\end{document}
对我来说,使用 Asymptote 来说明上面的简单图表相当复杂。Asymptote 功能强大,我认为它应该涵盖 TikZ 的优美功能node
(inner sep
、minimum width
、、、、等)。以下是我迄今为止尝试过的。shape=circle
shape=ellipse
anchor
.center
S.30
第一次尝试控制箭头尖、圆形和标签大小失败。
// http://asymptote.ualberta.ca/
unitsize(1.5cm);
path R=dir(90), S=dir(210), I=dir(-30);
Label LS = Label("$S$", FillDraw(yellow,blue));
Label LI = Label("$I$", FillDraw(yellow,blue));
Label LR = Label("$R$", FillDraw(yellow,blue));
draw(S--I--R--cycle);
path pRI=arc((0,0),1,90,-30);
draw(pRI);
label(LS,S);
label(LI,I);
label(LR,R);
第二次尝试可以制作圆形、箭头尖端,但无法控制倾斜连接器、标签大小、用颜色(黄色、蓝色)填充和绘制形状。
// http://asymptote.ualberta.ca/
unitsize(1.5cm);
import flowchart;
pair R=dir(90), S=dir(210), I=dir(-30);
block blockS=circle("S",S);
block blockI=circle("I",I);
block blockR=circle("R",R);
draw(blockS,blue);
draw(blockI);
draw(blockR);
add(new void(picture pic, transform t) {
blockconnector operator --=blockconnector(pic,t);
blockS--Arrow(TeXHead)--blockI;
blockI--Arrow(TeXHead)--blockR;
});
总结一下,我在用 Asymptote 绘图时有 2 个问题。
问题 1:如何绘制从 S 到 I、从 I 到 R、从 R 到 I 的箭头,使得箭头尖触及蓝色边界?
问题2:如何制作标签 S、I、R 周围的圆形/椭圆形?
2020 年 12 月 24 日更新看来 Asymptote 的object
s 可以完成这项工作。在我第三次尝试时,箭头尖端触及了蓝色边界,但所有形状都是椭圆形,而不是圆形(参见I
),我必须手动指定边界点(参见point(LI,dir(120),t)
)。代码摘自Asymptote 的画廊,所以我的疑问更加合理。
unitsize(1.5cm);
pair R=dir(90), S=dir(210), I=dir(-30);
real margin=3mm;
object LS=draw("$S$",ellipse,S,margin,FillDraw(yellow,blue));
object LI=draw("$I$",ellipse,I,margin,FillDraw(yellow,blue));
object LR=draw("$R$",ellipse,R,margin,FillDraw(yellow,blue));
add(new void(frame f, transform t) {
picture pic;
draw(pic,point(LS,E,t)--point(LI,W,t),red,Arrow(TeXHead),PenMargin);
draw(pic,point(LI,dir(120),t)--point(LR,dir(-60),t),red,Arrow(TeXHead),PenMargin);
draw(pic,point(LR,dir(240),t)--point(LS,dir(60),t),red,Arrow(TeXHead),PenMargin);
pair R1=point(LR,NE,t), I1=point(LI,NE,t);
path pRI=R1 .. controls R1+30*dir(45) and I1+40*dir(35).. I1;
draw(pic,pRI,red,Arrow(TeXHead),PenMargin);
add(f,pic.fit());
});
shipout(bbox(5mm));
答案1
可以用普通的 Asymptote 绘制带箭头的图表,几乎和用普通的 TikZ 一样简单。
我对这个解决方案很满意,但仍然愿意接受更好的解决方案。我的方法是使用普通的 Asymptote:不需要其他命令/模块。
注意,圆是大小相等的椭圆。通过组合object
和label
,我们可以得到circle
形状,从而使所有圆形具有统一的大小(类似于minimum width
TikZ 的节点)。
object
S=draw("",ellipse,(0,0),xmargin=3mm,FillDraw(yellow,blue)),
I=draw("",ellipse,(4cm,0),xmargin=3mm,FillDraw(yellow,blue)),
R=draw("",ellipse,(2cm,3cm),xmargin=3mm,FillDraw(yellow,blue));
label("S",(0,0));
label("I",(4cm,0));
label("R",(2cm,3cm));
pair
S1=point(S,E), I1=point(I,W),
I2=point(I,dir(120)), R1=point(R,dir(-60)),
R2=point(R,dir(-120)), S2=point(S,dir(60)),
R3=point(R,dir(-20)), I3=point(I,dir(70))
;
DefaultHead=TeXHead;
draw(S1--I1,red,Arrow());
draw(I2--R1,red,Arrow());
draw(R2--S2,red,Arrow());
draw(R3 .. controls R3+1cm*dir(0) and I3+1cm*dir(70) .. I3,red,Arrow());
到目前为止,通常的 TikZnode
涉及选项(inner sep
,inner xsep
形状inner ysep
、、、、、、、、、、、)都可以轻松转换为Asymptote 。我不会测试其他一些功能(fill
文本、对齐节点等)。draw
minimum size
shorten >
anchor
shorten <
circle
ellipse
rectangle
loop
scale
sloped
rotate
text color
anchor
// Diagrams with arrows can be drawn with plain Asymptote
object
S=draw("",ellipse,(0,0),xmargin=6mm,FillDraw(yellow,blue)),
I=draw("",ellipse,(4cm,0),xmargin=3mm,FillDraw(yellow,blue)),
R=draw("",ellipse,(2cm,3cm),xmargin=3mm,FillDraw(yellow,blue));
label(scale(3)*"S",(0,0));
label(scale(.5)*"i",(4cm,0));
label("R",(2cm,3cm));
pair
S1=point(S,E), I1=point(I,W),
I2=point(I,dir(120)), R1=point(R,dir(-60)),
R2=point(R,dir(-120)), S2=point(S,dir(60)),
R3=point(R,dir(-20)), I3=point(I,dir(70)),
I4=point(I,dir(-45))
;
DefaultHead=TeXHead;
draw("$\alpha$",S1--I1,purple,Arrow(),margin=TrueMargin(.5mm, 2mm));
draw(Label("$\beta$",align=SW),I2--R1,red,Arrow(),margin=TrueMargin(1mm));
draw(Label("gamma",Rotate(R2-S2)),R2--S2,red,Arrow());
draw("$\delta$",R3{0} .. {-60}I3,red,Arrow(),margin=TrueMargin(2mm));
draw(Label("go to Heaven",align=plain.S,EndPoint),I4--I4+1cm*dir(-45),cyan,Arrow);
add(bbox(5mm));
有时 TikZ 会通过 、 等表格使 LaTeX 更加美观,就像 tikzpicture 一样matrix of nodes
。matrix of math nodes
在我看来,由于每个图形都是独立的,所以 Asymptote 不应该有这些选项,而 Asymptote 的优势在于真正的 3D。我并不认为 TikZ 是绘图的标准,但它是关心 Asymptote 能帮助我/我们以视觉方式表达想法的起点。
答案2
这是一个很长的评论,但仍然不是一个简洁的答案。
我发现2009年链接import node;
在我看来,语法node.asy
仍然不太方便。另请参阅Jim Heffron 的回答从 2014 年开始。
object
从上面的链接,我进行了第四次尝试。首先回想一下from的语法plain_boxes.asy
object draw(picture pic=currentpicture, Label L, envelope e,
pair position=(0,0), real xmargin=0, real ymargin=xmargin,
pen p=currentpen, filltype filltype=NoFill);
有box
(矩形)的形状ellipse
,,,roundbox
但是没有circle
形状我们可以控制Draw
,Fill
和(xmargin
就像ymargin
TikZ 的);我们可以获取定义的inner sep
边界(TikZ 的 )内的任意点。anchor
object
object
cat = draw("cat", box, (0,0), Draw),
dog = draw("dog", ellipse, (2cm,0), Fill(olive)),
elephant = draw("elephant", roundbox, (0,-2cm),xmargin=2mm,FillDraw(lightblue, darkblue));
pair C=point(cat,E),Ct=point(cat,S);
pair D=point(dog,-90), Dt=point(dog,SW);
pair E=point(elephant,N), Et=point(elephant,NNE);
draw(C--D,red,Arrow(TeXHead));
draw(Et--Dt,red,Arrow(TeXHead));
draw(E--Ct,red,Arrow(TeXHead));
然后我想到这个,简单的代码,看起来相当不错,除了所有形状都是椭圆形,而不是圆形。
object
S=draw("S",ellipse,(0,0),xmargin=3mm,FillDraw(yellow,blue)),
I=draw("I",ellipse,(4cm,0),xmargin=3mm,FillDraw(yellow,blue)),
R=draw("R",ellipse,(2cm,3cm),xmargin=3mm,FillDraw(yellow,blue));
pair
S1=point(S,E), I1=point(I,W),
I2=point(I,dir(120)), R1=point(R,dir(-60)),
R2=point(R,dir(-120)), S2=point(S,dir(60)),
R3=point(R,dir(-20)), I3=point(I,dir(70))
;
DefaultHead=TeXHead;
draw(S1--I1,red,Arrow());
draw(I2--R1,red,Arrow());
draw(R2--S2,red,Arrow());
draw(R3 .. controls R3+1cm*dir(0) and I3+1cm*dir(70) .. I3,red,Arrow());
更新:通过放置circle
形状(@Nguyen Van Chi 模仿了roundbox
中的形状plain_boxes.asy
),效果很好(改变ellipse
为circle
)^^ 我认为现在在 Asymptote 中定义一个新形状(梯形、平行四边形、星形……)是一项舒适的任务(甚至比在 TikZ 中定义新形状更舒服node
)
//////////////////////////////////////////////////////
// code for the `circle` shape from Nguyen Van Chi,
// inspired by the `roundbox` shape (see `plain_boxes.asy`)
path circle(frame dest, frame src=dest, real xmargin=0, real ymargin=xmargin,
pen p=currentpen, filltype filltype=NoFill, bool above=true)
{
pair m=min(src);
pair M=max(src);
pair bound=M-m;
real a=bound.x+2*xmargin;
real b=bound.y+2*ymargin;
path g=shift(m-(xmargin,ymargin))*(circle((a/2,b/2),max(a,b)/2));
frame F;
if(above == false) {
filltype.fill(F,g,p);
prepend(dest,F);
} else filltype.fill(dest,g,p);
return g;
}
path circle(frame f, Label L, real xmargin=0, real ymargin=xmargin,
pen p=currentpen, filltype filltype=NoFill, bool above=true)
{
add(f,L);
return circle(f,xmargin,ymargin,p,filltype,above);
}
//////////////////////////////////////////////////////
object
S=draw("S",circle,(0,0),xmargin=3mm,FillDraw(yellow,blue)),
I=draw("I",circle,(4cm,0),xmargin=3mm,FillDraw(yellow,blue)),
R=draw("R",circle,(2cm,3cm),xmargin=3mm,FillDraw(yellow,blue));
pair
S1=point(S,E), I1=point(I,W),
I2=point(I,dir(120)), R1=point(R,dir(-60)),
R2=point(R,dir(-120)), S2=point(S,dir(60)),
R3=point(R,dir(-20)), I3=point(I,dir(70))
;
DefaultHead=TeXHead;
draw(S1--I1,red,Arrow());
draw(I2--R1,red,Arrow());
draw(R2--S2,red,Arrow());
draw(R3 .. controls R3+1cm*dir(0) and I3+1cm*dir(70) .. I3,red,Arrow());
add(bbox(5mm));