Here's a more serious example using Asymptote. I would note that for commutative diagrams in a *TeX
document, I still recommend using tikz-cd
(or tikz
directly for sufficiently complicated examples). But I believe this answer is still potentially useful because it solves a couple of Asymptote problems that might come up in other contexts:
- how to compute the bounding box of a label (see the
boundingbox()
function in the example code; note that this would not work if the picture were scaled with size()
rather than unitsize()
)
- how to set up labels to have the same baseline without changing their bounding boxes (make sure they are drawn with a
pen
that has the basealign
option AND that they have alignment N
(north); these must be used together to have the desired effect).
Here's the example code:
settings.outformat="pdf";
real xunit=2cm, yunit=1.4cm;
unitsize(xunit,yunit);
defaultpen(basealign);
picture blank = currentpicture.copy();
usepackage("amssymb");
string[][] nodestext = {{"$\hat{A}$", "$d$", "$A$"},
{"$\sum_i a_i$", "$c$"},
{"$\hat{A}$", "$\displaystyle\prod_{n \in \mathbb{Z}} A_n$", "$\displaystyle\prod_{n \in \mathbb{Z}} A_n$", "$A$"},
{},
{"", "", minipage("node not in math mode",60pt)}};
Label[][] nodes;
for (int r = 0; r < nodestext.length; ++r) {
nodes.push(new Label[nodestext[r].length]);
for (int c = 0; c < nodestext[r].length; ++c) {
nodes[r][c] = Label(nodestext[r][c], position=(c,-r), align=N);
label(nodes[r][c]);
}
}
/*
* This function computes the bounding box of a Label by creating a new blank
* picture with the same sizing information as the old picture, adding the
* Label to that blank picture, and then computing the bounding box of that picture.
*/
path boundingbox(Label L) {
picture currentpic = blank.copy();
label(currentpic, L);
pair min = min(currentpic, user=true); //Without the user=true option, the returned answer would be measured in postscript points.
pair max = max(currentpic, user=true);
return box(min, max);
}
path[][] boundingboxes;
pair[][] centers;
for (int r = 0; r < nodes.length; ++r) {
path[] boundingboxesr;
pair[] centersr;
for (int c = 0; c < nodes[r].length; ++c) {
Label currentnode = nodes[r][c];
pair currentpos = (c,-r);
boundingboxesr.push(boundingbox(currentnode));
centersr.push(currentpos + (0,7pt/yunit));
}
boundingboxes.push(boundingboxesr);
centers.push(centersr);
}
path truncate(path thepath, int sourcerow, int sourcecol, int up=0, int right=0) {
pair source = centers[sourcerow][sourcecol];
int destrow = sourcerow - up;
int destcol = sourcecol + right;
pair dest = centers[destrow][destcol];
path toreturn = thepath;
toreturn = firstcut(toreturn, knife=boundingboxes[sourcerow][sourcecol]).after;
toreturn = lastcut(toreturn, knife=boundingboxes[destrow][destcol]).before;
return toreturn;
}
void cdarrow(int sourcerow, int sourcecol, int up=0, int right=0, Label L="", bool crossingover = false) {
pair source = centers[sourcerow][sourcecol];
int destrow = sourcerow - up;
int destcol = sourcecol + right;
pair dest = centers[destrow][destcol];
path touse = truncate(source -- dest, sourcerow, sourcecol, up, right);
if (crossingover) draw(touse, white+linewidth(3pt));
draw(touse, arrow=Arrow(TeXHead), L=L, margin=Margins);
}
cdarrow(0,0,up=-1,right=1);
cdarrow(1,0,up=1,right=1,crossingover=true, L=Label("$\scriptstyle h$",align=Relative(0.3W),position=Relative(0.65)));
cdarrow(1,0,right=1,L=Label("$\scriptstyle f$",align=Relative(E)));
cdarrow(0,1,right=-1);
cdarrow(2,0,right=1);
cdarrow(2,1,right=1);
cdarrow(2,2,right=1);
path curvedarrow = centers[2][0]{SSE} .. tension 0.75 .. {NE} centers[2][2];
curvedarrow=truncate(curvedarrow, 2, 0, right=2);
draw(curvedarrow, arrow=Arrow(TeXHead), L=Label("$\scriptstyle g$",align=Relative(E)), margin=Margins);
curvedarrow = centers[0][1] {ESE} .. {ENE} centers[0][2];
curvedarrow = truncate(curvedarrow, 0,1, right=1);
draw(curvedarrow, arrow=Arrow(TeXHead), margin=Margins);
curvedarrow = centers[0][1] {ENE} .. {ESE} centers[0][2];
curvedarrow = truncate(curvedarrow, 0,1, right=1);
draw(curvedarrow, arrow=Arrow(TeXHead), margin=Margins);
The result:
