real arrowlength=0.75cm;
real arrowfactor=15;
real arrowangle=15;
real arcarrowfactor=0.5*arrowfactor;
real arcarrowangle=2*arrowangle;
real arrowsizelimit=0.5;
real arrow2sizelimit=1/3;
real arrowdir=5;
real arrowbarb=3;
real arrowhookfactor=1.5;
real arrowtexfactor=1;
real barfactor=arrowfactor;
real arrowsize(pen p=currentpen)
{
return arrowfactor*linewidth(p);
}
real arcarrowsize(pen p=currentpen)
{
return arcarrowfactor*linewidth(p);
}
real barsize(pen p=currentpen)
{
return barfactor*linewidth(p);
}
struct arrowhead
{
path head(path g, position position=EndPoint, pen p=currentpen,
real size=0, real angle=arrowangle);
real size(pen p)=arrowsize;
real arcsize(pen p)=arcarrowsize;
filltype defaultfilltype(pen) {return FillDraw;}
}
// Add to picture an estimate of the bounding box contribution of arrowhead
// using the local slope at endpoint and ignoring margin.
void addArrow(picture pic, arrowhead arrowhead, path g, pen p, real size,
real angle, filltype filltype, real position)
{
if(filltype == null) filltype=arrowhead.defaultfilltype(p);
pair z=point(g,position);
path g=z-(size+linewidth(p))*dir(g,position)--z;
frame f;
filltype.fill(f,arrowhead.head(g,position,p,size,angle),p);
pic.addBox(z,z,min(f)-z,max(f)-z);
}
arrowbar BeginArrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arrowangle,
filltype filltype=null, position position=BeginPoint)
{
return new bool(picture pic, path g, pen p, margin margin) {
add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,forwards=false,
margin));
return false;
};
}
arrowbar Arrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arrowangle,
filltype filltype=null, position position=EndPoint)
{
return new bool(picture pic, path g, pen p, margin margin) {
add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,margin));
return false;
};
}
arrowbar EndArrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arrowangle,
filltype filltype=null, position position=EndPoint)=Arrow;
arrowbar MidArrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arrowangle, filltype filltype=null)
{
return new bool(picture pic, path g, pen p, margin margin) {
add(pic,arrow(arrowhead,g,p,size,angle,filltype,MidPoint,margin,
center=true));
return false;
};
}
arrowbar Arrows(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arrowangle,
filltype filltype=null)
{
return new bool(picture pic, path g, pen p, margin margin) {
add(pic,arrow2(arrowhead,g,p,size,angle,filltype,margin));
return false;
};
}
arrowbar BeginArcArrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arcarrowangle,
filltype filltype=null, position position=BeginPoint)
{
return new bool(picture pic, path g, pen p, margin margin) {
real size=size == 0 ? arrowhead.arcsize(p) : size;
add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,
forwards=false,margin));
return false;
};
}
arrowbar ArcArrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arcarrowangle,
filltype filltype=null, position position=EndPoint)
{
return new bool(picture pic, path g, pen p, margin margin) {
real size=size == 0 ? arrowhead.arcsize(p) : size;
add(pic,arrow(arrowhead,g,p,size,angle,filltype,position,margin));
return false;
};
}
arrowbar EndArcArrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arcarrowangle,
filltype filltype=null,
position position=EndPoint)=ArcArrow;
arrowbar MidArcArrow(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arcarrowangle,
filltype filltype=null)
{
return new bool(picture pic, path g, pen p, margin margin) {
real size=size == 0 ? arrowhead.arcsize(p) : size;
add(pic,arrow(arrowhead,g,p,size,angle,filltype,MidPoint,margin,
center=true));
return false;
};
}
arrowbar ArcArrows(arrowhead arrowhead=DefaultHead,
real size=0, real angle=arcarrowangle,
filltype filltype=null)
{
return new bool(picture pic, path g, pen p, margin margin) {
real size=size == 0 ? arrowhead.arcsize(p) : size;
add(pic,arrow2(arrowhead,g,p,size,angle,filltype,margin));
return false;
};
}
void draw(picture pic=currentpicture, Label L=null, path g,
align align=NoAlign, pen p=currentpen, arrowbar arrow=None,
arrowbar bar=None, margin margin=NoMargin, Label legend=null,
marker marker=nomarker)
{
// These if statements are ordered in such a way that the most common case
// (with just a path and a pen) executes the least bytecode.
if (marker == nomarker)
{
if (arrow == None && bar == None)
{
if (margin == NoMargin && size(nib(p)) == 0)
{
pic.addExactAbove(
new void(frame f, transform t, transform T, pair, pair) {
_draw(f,t*T*g,p);
});
pic.addPath(g,p);
// Jumping over else clauses takes time, so test if we can return
// here.
if (L == null && legend == null)
return;
}
else // With margin or polygonal pen.
{
_draw(pic, g, p, margin);
}
}
else /* arrow or bar */
{
// Note we are using & instead of && as both arrow and bar need to be
// called.
if (arrow(pic, g, p, margin) & bar(pic, g, p, margin))
_draw(pic, g, p, margin);
}
// Note we are using & instead of && as both arrow and bar need to be
// called.
if ((arrow == None || arrow(pic, g, p, margin)) &
(bar == None || bar(pic, g, p, margin)))
{
_draw(pic, g, p, margin);
}
// Draw a fixed-size line about the user-coordinate 'origin'.
void draw(pair origin, picture pic=currentpicture, Label L=null, path g,
align align=NoAlign, pen p=currentpen, arrowbar arrow=None,
arrowbar bar=None, margin margin=NoMargin, Label legend=null,
marker marker=nomarker)
{
picture opic;
draw(opic,L,g,align,p,arrow,bar,margin,legend,marker);
add(pic,opic,origin);
}
void draw(picture pic=currentpicture, explicit path[] g, pen p=currentpen,
Label legend=null, marker marker=nomarker)
{
// This could be optimized to size and draw the entire array as a batch.
for(int i=0; i < g.length-1; ++i)
draw(pic,g[i],p,marker);
if(g.length > 0) draw(pic,g[g.length-1],p,legend,marker);
}
// Align an arrow pointing to b from the direction dir. The arrow is
// 'length' PostScript units long.
void arrow(picture pic=currentpicture, Label L=null, pair b, pair dir,
real length=arrowlength, align align=NoAlign,
pen p=currentpen, arrowbar arrow=Arrow, margin margin=EndMargin)
{
if(L != null && L.s != "") {
L=L.copy();
if(L.defaultposition) L.position(0);
L.align(L.align,dir);
L.p(p);
}
marginT margin=margin(b--b,p); // Extract margin.begin and margin.end
pair a=(margin.begin+length+margin.end)*unit(dir);
draw(b,pic,L,a--(0,0),align,p,arrow,margin);
}
// Fit an array of pictures simultaneously using the sizing of picture all.
frame[] fit2(picture[] pictures, picture all)
{
frame[] out;
if(!all.empty2()) {
transform t=all.calculateTransform();
pair m=all.min(t);
pair M=all.max(t);
for(picture pic : pictures) {
frame f=pic.fit(t);
draw(f,m,nullpen);
draw(f,M,nullpen);
out.push(f);
}
}
return out;
}
// Fit an array of pictures simultaneously using the size of the first picture.
// TODO: Remove unused arguments.
frame[] fit(string prefix="", picture[] pictures, string format="",
bool view=true, string options="", string script="",
projection P=currentprojection)
{
if(pictures.length == 0)
return new frame[];