// 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
{
using fill2=void(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);
}