# What does the `align` argument do in this Asymptote code?

The following code produces the output

picture pic1;
size(pic1,10);
fill(pic1,(0,0)--(5,10)--(10,0)--cycle,red+opacity(0.5)); // triangle

picture pic2;
size(pic2,10);
fill(pic2,unitcircle,green+opacity(0.5)); // circle

picture pic;

However, if I change the line add(pic,pic1.fit(),position = (0,0)); to add(pic,pic1.fit(),position = (0,0),align=N); I get the output

I expected the red-triangle to have shifted slightly north from its position in the first picture. Where am I going wrong in understanding the functions behavior?

• This is AFAIK the alignment anchor, whereby unfortunately the meaning is sort of opposite to what it is in TikZ. In the first picture, the triangle's left corner is at (0,0) because of the way you draw it, i.e. in the pic (0,0) is the left corner. In the second pic the southern anchor is at at (0,0), so roughly speaking TikZ's anchor=south corresponds to align=N (and to PStricks' \rput[b]{...}).
– user121799
Commented Apr 14, 2019 at 15:51
• I am not sure I follow. Can you please explain it a little more? By anchor do you mean the edges of the bounding box of the figure? Commented Apr 14, 2019 at 17:45

Hy

In fact I started to write some test examples for align a few months ago (due to your question What does the .fit() method of a picture do?). There was also a discussion on Asymptote forum help due to some behavior. Please find some explanations, codes, pictures. I hope it will help Asymptote users.

Tests 1 : simple pic.fit() no align argument

From left to right and top to bottom. A picture pic1 is created (with keepAspect=false) and is added with various pic.fit(x.size, y.size,keepAspect).

case 1 : pic1 is added at (1.2,.5) coordinates point. No extra argument

case 2 : pic1.fit(2*xs,ys,keepAspect=true). Due to keepAspect=true pic1 is not scaled (the minimum is chosen)

case 3 : pic1 is enlarged by 2 pic1.fit(2*xs,ys). Since keepAspect=false (precised for pic1) the y coordinate is not scaled

case 3 : pic1.fit(2*xs,3*ys,keepAspect=true) pic1 is scaled (x and `y by 2=min(2,3))

In cases 3 and 4 it is worth noting that the fixed size object (label, linewidth) are not scaled. Please also notice that the (0,0) coordinate of pic1 is the left bottom.

size(10cm);

picture pic1;
size(pic1,2cm,keepAspect=false);
label(pic1,"T",(.5,.5));
label(pic1,"O",(1,1),SW);
draw(pic1,unitsquare);
draw(pic1,(.5,.5)--(1,1));

frame f1=pic1.fit();
dot((.5,1.2),blue);

real xs=pic1.xsize;
real ys=pic1.ysize;

frame f2=pic1.fit(2*xs,ys,keepAspect=true);
dot((1.5,1.2),blue);

frame f3=pic1.fit(2*xs,ys);
dot((.5,.5),blue);

frame f4=pic1.fit(2*xs,3*ys,keepAspect=true);
dot((1.5,.5),blue);

Tests 2 : pic.fit() and align arguments

With respect to the first tests, the (0,0) coordinate of pic1 is not the left-bottom (the rectangle is shift). As mentionned in the Asymptote documentation pic1 will be aligned so that the origin is at position (here (.5,1)) of the current picture. (see the cases 1 and 2)

Case 1: no option is given. The origin of pic1 is at position (.5,1) of the current picture.

Case 2: pair align option is given : (0,0). Then pic1 is aligned so that the center of pic1 is at position (.9,1) of the current picture.

Case 3: From the documentation, the constant Align can be used to align the bottom-left corner of the label at position. Align is a pair, so that using +/-Align.x, +/-Align.y we can align pic1 at {Left/Mid/Right}/{Top/Mid/Bottom} point. Here pair align=(Align.x,0) and position=(.5,.7). It means that pic1 will be aligned in the direction of (Align.x,0) (which is equivalent to (1,0)) and displaced from position=(.5,.7) by the PostScript offset (Align.x,0)*labelmargin(p).

Case 4: another illustration of case 3, align to a point of the border of pic2. add(f4,(1,.9),(-2*Align.x,-5*Align.y)). The green line is added to pic1 to obtain pic2, pair l=truepoint(pic2,(2,5)) allows one to place the point on the border. Following the documentation truepoint(picture pic=currentpicture, pair dir, bool user=true) is a convenient way of determining the point on the bounding box of pic in the direction dir relative to its center (and here including the fixed-size objects).

Case 5: Align.x value is 1.05367121277235e-08. What happens with larger value ? Here we choose add(f5,(.5,.7),(10,15)). Following the documentation it will be aligned in the direction of (10,15) and displaced from (.5,.7) (the currend picture) by the PostScript offset (10,15)*labelmargin(p). To illustrate the obtained boundary point of pic2, a line is added with draw((.5,.7),(0,0)--(10,15),linecap(0)+red) : allows one to draw a fixed-sized object (in PostScript coordinates, the (0,0)--(10,15) line) about the user coordinate origin (.5,.7). It is worth noting that the red and blue lines have a common end.

Case 6: similar to case 5 with a position in cm. You can verify that the distance from the dot to the left-bottom corner is equal to 2cm.

size(9cm);

picture pic1;
size(pic1,3cm);
label(pic1,"T",(.5,.5));
label(pic1,"O",(1,1),SW);
draw(pic1,shift(0,.2)*scale(2,1)*unitsquare);
draw(pic1,(.5,.5)--(1,1));

frame f1=pic1.fit();

dot((.5,1));

dot((.9,1.05));

dot((.5,.9));

picture pic2;
size(pic2,3cm);
pair l=truepoint(pic2,(2,5));
draw(pic2,l--(l.x,.5),green+linecap(0));
frame f4=pic2.fit();
dot((1,.95));

pair l=truepoint(pic1,(-10,-15));
draw(pic1,l--(l.x,.5),blue+linecap(0));
frame f5=pic1.fit();
dot((.5,.7));
write(labelmargin(currentpen));
draw((.5,.7),(0,0)--(10,15),linecap(0)+red);

dot((.75,.7));
draw((.75,.7),(0,0)--1cm*unit((1,1)),linecap(0)+red);

Case 1: no option no position. add(pic1) is equivalent to the apply the draw command of pic1 to the current picture

Case 2: no option is given, only the position (4,.5). Surprise ! The adding picture is tiny ! In fact as mentioned in the documentation, add(pic1,(4,.5)) will add pic1 specified in Postcript coordinates to the currentpicture.

Case 3: the standard pic1.fit at position (0,2).

Case 4: by scaling pic1 into pic2 with an appropriate value, we have an equivalent of pic1.fit, at position (3,-2). It is worth noting that fixed scaled object (Labels) are not scaled.

Case 5: scaling by a factor 3 in the x direction with respect to the y direction the picture pic1. Fixed scaled object (Labels) are not scaled.

Case 6: scaling by a factor 3 in the x direction pic1.fit(). Fixed scaled object (Labels, linewidth) are x-scaled. */

size(8cm);

//label("Picture test",(1,1));
draw(shift(3,-2)*scale(8)*shift((-.5,-.5))*unitsquare);

/*
Last test: the difference between scaling/adding picture to picture and frame to picture.
*/

picture pic1;
size(pic1,2cm);
label(pic1,"T",(.5,.5));
label(pic1,"O",(1,1),SW);
draw(pic1,scale(2,1)*unitsquare);
draw(pic1,(.5,.5)--(1,1));

/* is equivalent to
label("T",(.5,.5));
label("O",(1,1),SW);
draw(scale(2,1)*unitsquare);
draw((.5,.5)--(1,1));
*/
dot((0,0),blue); // is at the origin of `add(pic1)`

add(pic1,(4,.5)); // so small

frame f1=pic1.fit();

picture pic2;
pic2=(scale(size(pic1).x/size(pic1,user=true).x)*pic1);