Next: , Previous: Data types, Up: Programming


5.2 Guides and paths

guide
an unresolved cubic spline (list of cubic-spline nodes and control points).

This is like a path except that the computation of the cubic spline is deferred until drawing time (when it is resolved into a path); this allows two guides with free endpoint conditions to be joined together smoothly. The solid curve in the following example is built up incrementally as a guide, but only resolved at drawing time:

     size(200);
     
     real mexican(real x) {return (1-8x^2)*exp(-(4x^2));}
     
     int n=30;
     real a=1.5;
     real width=2a/n;
     
     guide hat;
     path solved;
     
     for(int i=0; i < n; ++i) {
       real t=-a+i*width;
       pair z=(t,mexican(t));
       hat=hat..z;
       solved=solved..z;
     }
     
     draw(hat);
     dot(hat,red);
     draw(solved,dashed);
     
mexicanhat.png
path
a cubic spline resolved into a fixed path. In the previous example, the dashed curve is incrementally resolved at each iteration, before the entire set of nodes (shown in red) is known.

The implicit initializer for paths and guides is nullpath, which is useful for building up a path within a loop.

The routine circle(pair c, real r), which returns a Bezier curve approximating a circle of radius r centered on c, is based on unitcircle:

     
     path circle(pair c, real r)
     {
       return shift(c)*scale(r)*unitcircle;
     }
If high accuracy is needed, a true circle may be produced with this routine, defined in the module graph.asy:
     
     path Circle(pair c, real r, int n=400);

A circular arc consistent with the above approximation centered on c with radius r from angle1 to angle2 degrees, drawing counterclockwise if angle2 >= angle1, can be constructed with

     
     guide arc(pair c, real r, real angle1, real angle2);
If r < 0, the complementary arc of radius |r| is constructed. For convenience, an arc centered at c from pair z1 to z2 (assuming |z2-c|=|z1-c|) in the direction CCW (counter-clockwise) or CW (clockwise) may also be constructed with
     
     guide arc(pair c, explicit pair z1, explicit pair z2,
               bool direction=CCW)

If high accuracy is needed, a true arc may be produced with this routine, defined in the module graph.asy:

     
     guide Arc(pair c, real r, real angle1, real angle2,
               int n=400);

An ellipse can be drawn with the routine

     
     guide ellipse(pair c, real a, real b)
     {
       return shift(c)*xscale(a)*yscale(b)*unitcircle;
     }

Here is an example of all five path connectors discussed in Tutorial:

     size(300,0);
     pair[] z=new pair[10];
     
     z[0]=(0,100); z[1]=(50,0); z[2]=(180,0);
     
     for(int n=3; n <= 9; ++n)
       z[n]=z[n-3]+(200,0);
     
     path p=z[0]..z[1]---z[2]::{up}z[3]
          &z[3]..z[4]--z[5]::{up}z[6]
          &z[6]::z[7]---z[8]..{up}z[9];
     
     draw(p,grey+linewidth(4mm));
     
     dot(z);
join.png

Here are some useful functions for paths:

int length(path);
This is the number of (linear or cubic) segments in the path. If the path is cyclic, this is the same as the number of nodes in the path.


int size(path);
This is the number of nodes in the path. If the path is cyclic, this is the same as the path length.


pair point(path p, int n);
If p is cyclic, return the coordinates of node n mod length(p). Otherwise, return the coordinates of node n, unless n < 0 (in which case point(0) is returned) or n > length(p) (in which case point(length(p)) is returned).
pair point(path p, real t);
This returns the coordinates of the point between node floor(t) and floor(t)+1 corresponding to the cubic spline parameter t=t-floor(t) (see Bezier). If t lies outside the range [0,length(p)], it is first reduced modulo length(p) in the case where p is cyclic or else converted to the corresponding endpoint of p.


pair dir(path, int n);
This returns the direction (as a pair) of the tangent to the path at node n. If the path contains only one point, (0,0) is returned.
pair dir(path, real t);
This returns the direction of the tangent to the path at the point between node floor(t) and floor(t)+1 corresponding to the cubic spline parameter t=t-floor(t) (see Bezier). If the path contains only one point, (0,0) is returned.


pair precontrol(path, int n);
This returns the precontrol point of node n.
pair precontrol(path, real t);
This returns the effective precontrol point at parameter t.


pair postcontrol(path, int n);
This returns the postcontrol point of node n.
pair postcontrol(path, real t);
This returns the effective postcontrol point at parameter t.


real arclength(path);
This returns the length (in user coordinates) of the piecewise linear or cubic curve that the path represents.


real arctime(path, real L);
This returns the path "time", a real number between 0 and the length of the path in the sense of point(path, real), at which the cumulative arclength (measured from the beginning of the path) equals L.


real dirtime(path, pair z);
This returns the first "time", a real number between 0 and the length of the path in the sense of point(path, real), at which the tangent to the path has the direction of pair z, or -1 if this never happens.


path reverse(path p);
returns a path running backwards along p.


path subpath(path p, int n, int m);
returns the subpath running from node n to node m. If n < m, the direction of the subpath is reversed.
path subpath(path p, real a, real b);
returns the subpath running from path time a to path time b, in the sense of point(path, real). If a < b, the direction of the subpath is reversed.


pair intersect(path p, path q, real fuzz=0);
If p and q have at least one intersection point, return a pair of times representing the respective path times along p and q, in the sense of point(path, real), for one such intersection point (as chosen by the algorithm described on page 137 of The MetaFontbook). Perform the computations to the absolute error specified by fuzz, or, if fuzz is 0, to machine precision. If the paths do not intersect, return the pair (-1,-1).


pair intersectionpoint(path p, path q, real fuzz=0);
This returns point(p,intersect(p,q,fuzz).x), the actual point of intersection.


slice firstcut(path p, path q);
Return the portions of path p before and after the first intersection of p with path q as a structure slice (if no such intersection exists, the entire path is considered to be `before' the intersection):
          
          struct slice {
            public path before,after;
          }
     

Note that firstcut.after plays the role of the MetaPost cutbefore command.


slice lastcut(path p, path q);
Return the portions of path p before and after the last intersection of p with path q as a slice (if no such intersection exists, the entire path is considered to be `after' the intersection).

Note that lastcut.before plays the role of the MetaPost cutafter command.


path buildcycle(... path[] g);
This returns the path surrounding a region bounded by a list of consecutively intersecting paths, following the behaviour of the MetaPost buildcycle command.


pair min(path);
returns the pair(left,bottom) for the path bounding box.


pair max(path);
returns the pair(right,top) for the path bounding box.


bool cyclic(path);
returns true iff path is cyclic


bool straight(path, int i);
returns true iff the segment between node i and node i+1 is straight.


bool inside(path g, pair z, pen p=currentpen);
returns true iff the point z is inside the region bounded by the cyclic path g according to the fillrule of pen p (see fillrule).

We point out an efficiency distinction in the use of guides and paths:

guide g;
for(int i=0; i < 10; ++i)
  g=g--(i,i);
path p=g;

runs in linear time, whereas

path p;
for(int i=0; i < 10; ++i)
  p=p--(i,i);

runs in quadratic time, as the entire path up to that point is copied at each step of the iteration.

As a technical detail we note that a direction specifier given to nullpath modifies the node on the other side: the paths

a..{up}nullpath..b;
c..nullpath{up}..d;
e..{up}nullpath{down}..f;
are respectively equivalent to
a..nullpath..{up}b;
c{up}..nullpath..d;
e{down}..nullpath..{up}f;