// Draw path g on frame f with user-constructed pen p. void makedraw(frame f, path g, pen p, int depth=mantissaBits) { if(depth == 0) return; --depth; path n=nib(p); for(int i=0; i < size(g); ++i) fill(f,shift(point(g,i))*n,p); static real epsilon=1000*realEpsilon; int L=length(g); real stop=L-epsilon; int N=length(n); pair first=point(n,0); pair n0=first; for(int i=0; i < N; ++i) { pair n1=point(n,i+1); pair dir=unit(n1-n0); real t=dirtime(g,-dir)-epsilon; if(straight(g,(int) t)) t=ceil(t); if(t > epsilon && t < stop) { makedraw(f,subpath(g,0,t),p,depth); makedraw(f,subpath(g,t,L),p,depth); return; } real t=dirtime(g,dir); if(straight(g,(int) t)) t=ceil(t); if(t > epsilon && t < stop) { makedraw(f,subpath(g,0,t),p,depth); makedraw(f,subpath(g,t,L),p,depth); return; } n0=n1; } n0=first; for(int i=0; i < N; ++i) { pair n1=point(n,i+1); fill(f,shift(n0)*g--shift(n1)*reverse(g)--cycle,p); n0=n1; } } void draw(frame f, path g, pen p=currentpen) { if(size(nib(p)) == 0) _draw(f,g,p); else { begingroup(f); makedraw(f,g,p); endgroup(f); } } void draw(frame f, explicit path[] g, pen p=currentpen) { for(int i=0; i < g.length; ++i) draw(f,g[i],p); } void draw(frame f, guide[] g, pen p=currentpen) { for(int i=0; i < g.length; ++i) draw(f,g[i],p); } void filldraw(frame f, path[] g, pen fillpen=currentpen, pen drawpen=currentpen) { begingroup(f); fill(f,g,fillpen); draw(f,g,drawpen); endgroup(f); } path[] complement(frame f, path[] g) { static pair margin=(0.5,0.5); return box(minbound(min(f),min(g))-margin,maxbound(max(f),max(g))+margin)^^g; } void unfill(frame f, path[] g, bool copy=true) { clip(f,complement(f,g),evenodd,copy); } void filloutside(frame f, path[] g, pen p=currentpen, bool copy=true) { fill(f,complement(f,g),p+evenodd,copy); } struct filltype { typedef void fill2(frame f, path[] g, pen fillpen); fill2 fill2; pen fillpen; pen drawpen; int type; static int Fill=1; static int FillDraw=2; static int Draw=3; static int NoFill=4; static int UnFill=5; void operator init(int type=0, pen fillpen=nullpen, pen drawpen=nullpen, fill2 fill2) { this.type=type; this.fillpen=fillpen; this.drawpen=drawpen; this.fill2=fill2; } void fill(frame f, path[] g, pen p) {fill2(f,g,p);} } path[] margin(path[] g, real xmargin, real ymargin) { if(xmargin != 0 || ymargin != 0) { pair M=max(g); pair m=min(g); real width=M.x-m.x; real height=M.y-m.y; real xfactor=width > 0 ? (width+2xmargin)/width : 1; real yfactor=height > 0 ? (height+2ymargin)/height : 1; g=scale(xfactor,yfactor)*g; g=shift(0.5*(M+m)-0.5*(max(g)+min(g)))*g; } return g; } filltype Fill(real xmargin=0, real ymargin=xmargin, pen p=nullpen) { return filltype(filltype.Fill,p,new void(frame f, path[] g, pen fillpen) { if(p != nullpen) fillpen=p; if(fillpen == nullpen) fillpen=currentpen; fill(f,margin(g,xmargin,ymargin),fillpen); }); } filltype FillDraw(real xmargin=0, real ymargin=xmargin, pen fillpen=nullpen, pen drawpen=nullpen) { return filltype(filltype.FillDraw,fillpen,drawpen, new void(frame f, path[] g, pen Drawpen) { if(drawpen != nullpen) Drawpen=drawpen; pen Fillpen=fillpen == nullpen ? Drawpen : fillpen; if(Fillpen == nullpen) Fillpen=currentpen; if(Drawpen == nullpen) Drawpen=Fillpen; if(cyclic(g[0])) filldraw(f,margin(g,xmargin,ymargin),Fillpen,Drawpen); else draw(f,margin(g,xmargin,ymargin),Drawpen); }); } filltype Draw(real xmargin=0, real ymargin=xmargin, pen p=nullpen) { return filltype(filltype.Draw,drawpen=p, new void(frame f, path[] g, pen drawpen) { pen drawpen=p == nullpen ? drawpen : p; if(drawpen == nullpen) drawpen=currentpen; draw(f,margin(g,xmargin,ymargin),drawpen); }); } filltype NoFill=filltype(filltype.NoFill,new void(frame f, path[] g, pen p) { draw(f,g,p); }); filltype UnFill(real xmargin=0, real ymargin=xmargin) { return filltype(filltype.UnFill,new void(frame f, path[] g, pen) { unfill(f,margin(g,xmargin,ymargin)); }); } filltype FillDraw=FillDraw(), Fill=Fill(), Draw=Draw(), UnFill=UnFill(); // Fill varying radially from penc at the center of the bounding box to // penr at the edge. filltype RadialShade(pen penc, pen penr) { return filltype(new void(frame f, path[] g, pen) { pair c=(min(g)+max(g))/2; radialshade(f,g,penc,c,0,penr,c,abs(max(g)-min(g))/2); }); } filltype RadialShadeDraw(real xmargin=0, real ymargin=xmargin, pen penc, pen penr, pen drawpen=nullpen) { return filltype(new void(frame f, path[] g, pen Drawpen) { if(drawpen != nullpen) Drawpen=drawpen; if(Drawpen == nullpen) Drawpen=penc; pair c=(min(g)+max(g))/2; if(cyclic(g[0])) radialshade(f,margin(g,xmargin,ymargin),penc,c,0,penr,c, abs(max(g)-min(g))/2); draw(f,margin(g,xmargin,ymargin),Drawpen); }); } // Fill the region in frame dest underneath frame src and return the // boundary of src. path fill(frame dest, frame src, filltype filltype=NoFill, real xmargin=0, real ymargin=xmargin) { pair z=(xmargin,ymargin); path g=box(min(src)-z,max(src)+z); filltype.fill(dest,g,nullpen); return g; } // Add frame dest to frame src with optional grouping and background fill. void add(frame dest, frame src, bool group, filltype filltype=NoFill, bool above=true) { if(above) { if(filltype != NoFill) fill(dest,src,filltype); if(group) begingroup(dest); add(dest,src); if(group) endgroup(dest); } else { if(group) { frame f; endgroup(f); prepend(dest,f); } prepend(dest,src); if(group) { frame f; begingroup(f); prepend(dest,f); } if(filltype != NoFill) { frame f; fill(f,src,filltype); prepend(dest,f); } } } void add(frame dest, frame src, filltype filltype, bool above=filltype.type != filltype.UnFill) { if(filltype != NoFill) fill(dest,src,filltype); (above ? add : prepend)(dest,src); }