1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
// 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);
}
|