6.6 Deferred drawing

It is sometimes desirable to have elements of a fixed absolute size, independent of the picture scaling from user to PostScript coordinates. For example, normally one would want the size of a dot created with dot, the size of the arrowheads created with arrow (see arrows), and labels to be drawn independent of the scaling.

However, because of Asymptote’s automatic scaling feature (see Figure size), the translation between user coordinate and PostScript coordinate is not determined until shipout time:

size(1cm);
dot((0,0));
dot((1,1));
shipout("x"); // at this point, 1 unit coordinate = 1cm
dot((2,2));
shipout("y"); // at this point, 1 unit coordinate = 0.5cm

It is therefore necessary to defer the drawing of these elements until shipout time.

For example, a frame can be added at a specified user coordinate of a picture with the deferred drawing routine add(picture dest=currentpicture, frame src, pair position):

frame f;
fill(f,circle((0,0),1.5pt));
add(f,position=(1,1),align=(0,0));

A deferred drawing routine is an object of type drawer, which is a function with signature void(frame f, transform t). For example, if the drawing routine

void d(frame f, transform t){
  fill(f,shift(3cm,0)*circle(t*(1,1),2pt));
}

is added to currentpicture with

currentpicture.add(d);

a filled circle of radius 2pt will be drawn on currentpicture centered 3cm to the right of user coordinate (1,1). The parameter t is the affine transformation from user coordinates to PostScript coordinates.

Even though the actual drawing is deferred, you still need to specify to the Asymptote scaling routines that ultimately a fixed-size circle of radius 2pt will be drawn 3cm to the right of user-coordinate (1,1), by adding a frame about (1,1) that extends beyond (1,1) from (3cm-2pt,-2pt)+min(currentpen) to (3cm+2pt,2pt)+max(currentpen), where we have even accounted for the pen linewidth. The following example will then produce a PDF file 10 cm wide:

settings.outformat="pdf";
size(10cm);
dot((0,0));
dot((1,1),red);
add(new void(frame f, transform t) {
  fill(f,shift(3cm,0)*circle(t*(1,1),2pt));
});
pair trueMin=(3cm-2pt,-2pt)+min(currentpen);
pair trueMax=(3cm+2pt,2pt)+max(currentpen);
currentpicture.addPoint((1,1),trueMin);
currentpicture.addPoint((1,1),trueMax);

Here we specified the minimum and maximum user and truesize (fixed) coordinates of the drawer with the picture routine

void addPoint(pair user, pair truesize);

Alternatively, one can use

void addBox(pair userMin, pair userMax, pair trueMin=0, pair trueMax=0) {

to specify a bounding box with bottom-left corner t*(1,1)+trueMin and top-right corner t*(1,1)+trueMax, where t is the transformation that transforms from user coordinates to PostScript coordinates.

For more details about deferred drawing, see “Asymptote: A vector graphics language,” John C. Bowman and Andy Hammerlindl, TUGBOAT: The Communications of the TeX Users Group, 29:2, 288-294 (2008),

https://www.math.ualberta.ca/~bowman/publications/asyTUG.pdf.