1//////////////////////////////////////////////////////////////////////
2// LibFile: masks2d.scad
3// This file provides 2D masking shapes that you can use with {{edge_profile()}} to mask edges.
4// The shapes include the simple roundover and chamfer as well as more elaborate shapes
5// like the cove and ogee found in furniture and architecture. You can make the masks
6// as geometry or as 2D paths.
7// Includes:
8// include <BOSL2/std.scad>
9// FileGroup: Basic Modeling
10// FileSummary: 2D masking shapes for edge profiling: including roundover, cove, teardrop, ogee.
11// FileFootnotes: STD=Included in std.scad
12//////////////////////////////////////////////////////////////////////
13
14
15// Section: 2D Masking Shapes
16
17// Function&Module: mask2d_roundover()
18// Usage: As module
19// mask2d_roundover(r|d=, [inset], [excess]) [ATTACHMENTS];
20// Usage: As function
21// path = mask2d_roundover(r|d=, [inset], [excess]);
22// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
23// See Also: corner_profile(), edge_profile(), face_profile()
24// Description:
25// Creates a 2D roundover/bead mask shape that is useful for extruding into a 3D mask for a 90° edge.
26// Conversely, you can use that same extruded shape to make an interior fillet between two walls at a 90º angle.
27// As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
28// If called as a function, this just returns a 2D path of the outline of the mask shape.
29// Arguments:
30// r = Radius of the roundover.
31// inset = Optional bead inset size. Default: 0
32// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
33// ---
34// d = Diameter of the roundover.
35// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
36// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
37// Example(2D): 2D Roundover Mask
38// mask2d_roundover(r=10);
39// Example(2D): 2D Bead Mask
40// mask2d_roundover(r=10,inset=2);
41// Example: Masking by Edge Attachment
42// diff()
43// cube([50,60,70],center=true)
44// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
45// mask2d_roundover(r=10, inset=2);
46// Example: Making an interior fillet
47// %render() difference() {
48// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
49// cube(310, anchor=BOT+LEFT);
50// }
51// xrot(90)
52// linear_extrude(height=30, center=true)
53// mask2d_roundover(r=10);
54module mask2d_roundover(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) {
55 path = mask2d_roundover(r=r,d=d,excess=excess,inset=inset);
56 attachable(anchor,spin, two_d=true, path=path) {
57 polygon(path);
58 children();
59 }
60}
61
62function mask2d_roundover(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) =
63 assert(is_finite(r)||is_finite(d))
64 assert(is_finite(excess))
65 assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
66 let(
67 inset = is_list(inset)? inset : [inset,inset],
68 r = get_radius(r=r,d=d,dflt=1),
69 steps = quantup(segs(r),4)/4,
70 step = 90/steps,
71 path = [
72 [r+inset.x,-excess],
73 [-excess,-excess],
74 [-excess, r+inset.y],
75 for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step)
76 ]
77 ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
78
79
80// Function&Module: mask2d_cove()
81// Usage: As module
82// mask2d_cove(r|d=, [inset], [excess]) [ATTACHMENTS];
83// Usage: As function
84// path = mask2d_cove(r|d=, [inset], [excess]);
85// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
86// See Also: corner_profile(), edge_profile(), face_profile()
87// Description:
88// Creates a 2D cove mask shape that is useful for extruding into a 3D mask for a 90° edge.
89// Conversely, you can use that same extruded shape to make an interior rounded shelf decoration between two walls at a 90º angle.
90// As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
91// If called as a function, this just returns a 2D path of the outline of the mask shape.
92// Arguments:
93// r = Radius of the cove.
94// inset = Optional amount to inset code from corner. Default: 0
95// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
96// ---
97// d = Diameter of the cove.
98// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
99// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
100// Example(2D): 2D Cove Mask
101// mask2d_cove(r=10);
102// Example(2D): 2D Inset Cove Mask
103// mask2d_cove(r=10,inset=3);
104// Example: Masking by Edge Attachment
105// diff()
106// cube([50,60,70],center=true)
107// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
108// mask2d_cove(r=10, inset=2);
109// Example: Making an interior rounded shelf
110// %render() difference() {
111// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
112// cube(310, anchor=BOT+LEFT);
113// }
114// xrot(90)
115// linear_extrude(height=30, center=true)
116// mask2d_cove(r=5, inset=5);
117module mask2d_cove(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) {
118 path = mask2d_cove(r=r,d=d,excess=excess,inset=inset);
119 attachable(anchor,spin, two_d=true, path=path) {
120 polygon(path);
121 children();
122 }
123}
124
125function mask2d_cove(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) =
126 assert(is_finite(r)||is_finite(d))
127 assert(is_finite(excess))
128 assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
129 let(
130 inset = is_list(inset)? inset : [inset,inset],
131 r = get_radius(r=r,d=d,dflt=1),
132 steps = quantup(segs(r),4)/4,
133 step = 90/steps,
134 path = [
135 [r+inset.x,-excess],
136 [-excess,-excess],
137 [-excess, r+inset.y],
138 for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step)
139 ]
140 ) reorient(anchor,spin, two_d=true, path=path, p=path);
141
142
143// Function&Module: mask2d_chamfer()
144// Usage: As Module
145// mask2d_chamfer(edge, [angle], [inset], [excess]) [ATTACHMENTS];
146// mask2d_chamfer(y=, [angle=], [inset=], [excess=]) [ATTACHMENTS];
147// mask2d_chamfer(x=, [angle=], [inset=], [excess=]) [ATTACHMENTS];
148// Usage: As Function
149// path = mask2d_chamfer(edge, [angle], [inset], [excess]);
150// path = mask2d_chamfer(y=, [angle=], [inset=], [excess=]);
151// path = mask2d_chamfer(x=, [angle=], [inset=], [excess=]);
152// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
153// See Also: corner_profile(), edge_profile(), face_profile()
154// Description:
155// Creates a 2D chamfer mask shape that is useful for extruding into a 3D mask for a 90° edge.
156// Conversely, you can use that same extruded shape to make an interior chamfer between two walls at a 90º angle.
157// As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
158// If called as a function, this just returns a 2D path of the outline of the mask shape.
159// The edge parameter specifies the length of the chamfer's slanted edge. Alternatively you can give x or y to
160// specify the width or height. Only one of x, y, or width is permitted.
161// Arguments:
162// edge = The length of the edge of the chamfer.
163// angle = The angle of the chamfer edge, away from vertical. Default: 45.
164// inset = Optional amount to inset code from corner. Default: 0
165// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
166// ---
167// x = The width of the chamfer.
168// y = The height of the chamfer.
169// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
170// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
171// Example(2D): 2D Chamfer Mask
172// mask2d_chamfer(x=10);
173// Example(2D): 2D Chamfer Mask by Width.
174// mask2d_chamfer(x=10, angle=30);
175// Example(2D): 2D Chamfer Mask by Height.
176// mask2d_chamfer(y=10, angle=30);
177// Example(2D): 2D Inset Chamfer Mask
178// mask2d_chamfer(x=10, inset=2);
179// Example: Masking by Edge Attachment
180// diff()
181// cube([50,60,70],center=true)
182// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
183// mask2d_chamfer(x=10, inset=2);
184// Example: Making an interior chamfer
185// %render() difference() {
186// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
187// cube(310, anchor=BOT+LEFT);
188// }
189// xrot(90)
190// linear_extrude(height=30, center=true)
191// mask2d_chamfer(edge=10);
192module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) {
193 path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset);
194 attachable(anchor,spin, two_d=true, path=path, extent=true) {
195 polygon(path);
196 children();
197 }
198}
199
200function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) =
201 let(dummy=one_defined([x,y,edge],["x","y","edge"]))
202 assert(is_finite(angle))
203 assert(is_finite(excess))
204 assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
205 let(
206 inset = is_list(inset)? inset : [inset,inset],
207 x = is_def(x)? x :
208 is_def(y)? adj_ang_to_opp(adj=y,ang=angle) :
209 hyp_ang_to_opp(hyp=edge,ang=angle),
210 y = opp_ang_to_adj(opp=x,ang=angle),
211 path = [
212 [x+inset.x, -excess],
213 [-excess, -excess],
214 [-excess, y+inset.y],
215 [inset.x, y+inset.y],
216 [x+inset.x, inset.y]
217 ]
218 ) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path);
219
220
221// Function&Module: mask2d_rabbet()
222// Usage: As Module
223// mask2d_rabbet(size, [excess]) [ATTACHMENTS];
224// Usage: As Function
225// path = mask2d_rabbet(size, [excess]);
226// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
227// See Also: corner_profile(), edge_profile(), face_profile()
228// Description:
229// Creates a 2D rabbet mask shape that is useful for extruding into a 3D mask for a 90° edge.
230// Conversely, you can use that same extruded shape to make an interior shelf decoration between two walls at a 90º angle.
231// As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
232// If called as a function, this just returns a 2D path of the outline of the mask shape.
233// Arguments:
234// size = The size of the rabbet, either as a scalar or an [X,Y] list.
235// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
236// ---
237// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
238// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
239// Example(2D): 2D Rabbet Mask
240// mask2d_rabbet(size=10);
241// Example(2D): 2D Asymmetrical Rabbet Mask
242// mask2d_rabbet(size=[5,10]);
243// Example: Masking by Edge Attachment
244// diff()
245// cube([50,60,70],center=true)
246// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
247// mask2d_rabbet(size=10);
248// Example: Making an interior shelf
249// %render() difference() {
250// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
251// cube(310, anchor=BOT+LEFT);
252// }
253// xrot(90)
254// linear_extrude(height=30, center=true)
255// mask2d_rabbet(size=[5,10]);
256module mask2d_rabbet(size, excess=0.01, anchor=CENTER,spin=0) {
257 path = mask2d_rabbet(size=size, excess=excess);
258 attachable(anchor,spin, two_d=true, path=path, extent=false) {
259 polygon(path);
260 children();
261 }
262}
263
264function mask2d_rabbet(size, excess=0.01, anchor=CENTER,spin=0) =
265 assert(is_finite(size)||(is_vector(size)&&len(size)==2))
266 assert(is_finite(excess))
267 let(
268 size = is_list(size)? size : [size,size],
269 path = [
270 [size.x, -excess],
271 [-excess, -excess],
272 [-excess, size.y],
273 size
274 ]
275 ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
276
277
278// Function&Module: mask2d_dovetail()
279// Usage: As Module
280// mask2d_dovetail(edge, [angle], [inset], [shelf], [excess], ...) [ATTACHMENTS];
281// mask2d_dovetail(x=, [angle=], [inset=], [shelf=], [excess=], ...) [ATTACHMENTS];
282// mask2d_dovetail(y=, [angle=], [inset=], [shelf=], [excess=], ...) [ATTACHMENTS];
283// Usage: As Function
284// path = mask2d_dovetail(edge, [angle], [inset], [shelf], [excess]);
285// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
286// See Also: corner_profile(), edge_profile(), face_profile()
287// Description:
288// Creates a 2D dovetail mask shape that is useful for extruding into a 3D mask for a 90° edge.
289// Conversely, you can use that same extruded shape to make an interior dovetail between two walls at a 90º angle.
290// As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
291// If called as a function, this just returns a 2D path of the outline of the mask shape.
292// Arguments:
293// edge = The length of the edge of the dovetail.
294// angle = The angle of the chamfer edge, away from vertical. Default: 30.
295// inset = Optional amount to inset code from corner. Default: 0
296// shelf = The extra height to add to the inside corner of the dovetail. Default: 0
297// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
298// ---
299// x = The width of the dovetail.
300// y = The height of the dovetail.
301// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
302// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
303// Example(2D): 2D Dovetail Mask
304// mask2d_dovetail(x=10);
305// Example(2D): 2D Dovetail Mask by Width.
306// mask2d_dovetail(x=10, angle=30);
307// Example(2D): 2D Dovetail Mask by Height.
308// mask2d_dovetail(y=10, angle=30);
309// Example(2D): 2D Inset Dovetail Mask
310// mask2d_dovetail(x=10, inset=2);
311// Example: Masking by Edge Attachment
312// diff()
313// cube([50,60,70],center=true)
314// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
315// mask2d_dovetail(x=10, inset=2);
316// Example: Making an interior dovetail
317// %render() difference() {
318// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
319// cube(310, anchor=BOT+LEFT);
320// }
321// xrot(90)
322// linear_extrude(height=30, center=true)
323// mask2d_dovetail(x=10);
324module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) {
325 path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess);
326 attachable(anchor,spin, two_d=true, path=path) {
327 polygon(path);
328 children();
329 }
330}
331
332function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) =
333 assert(num_defined([x,y,edge])==1)
334 assert(is_finite(first_defined([x,y,edge])))
335 assert(is_finite(angle))
336 assert(is_finite(excess))
337 assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
338 let(
339 inset = is_list(inset)? inset : [inset,inset],
340 x = !is_undef(x)? x :
341 !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
342 hyp_ang_to_opp(hyp=edge,ang=angle),
343 y = opp_ang_to_adj(opp=x,ang=angle),
344 path = [
345 [inset.x,0],
346 [-excess, 0],
347 [-excess, y+inset.y+shelf],
348 inset+[x,y+shelf],
349 inset+[x,y],
350 inset
351 ]
352 ) reorient(anchor,spin, two_d=true, path=path, p=path);
353
354
355// Function&Module: mask2d_teardrop()
356// Usage: As Module
357// mask2d_teardrop(r|d=, [angle], [excess]) [ATTACHMENTS];
358// Usage: As Function
359// path = mask2d_teardrop(r|d=, [angle], [excess]);
360// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
361// See Also: corner_profile(), edge_profile(), face_profile()
362// Description:
363// Creates a 2D teardrop mask shape that is useful for extruding into a 3D mask for a 90° edge.
364// Conversely, you can use that same extruded shape to make an interior teardrop fillet between two walls at a 90º angle.
365// As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
366// If called as a function, this just returns a 2D path of the outline of the mask shape.
367// This is particularly useful to make partially rounded bottoms, that don't need support to print.
368// Arguments:
369// r = Radius of the rounding.
370// angle = The maximum angle from vertical.
371// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
372// ---
373// d = Diameter of the rounding.
374// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
375// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
376// Example(2D): 2D Teardrop Mask
377// mask2d_teardrop(r=10);
378// Example(2D): Using a Custom Angle
379// mask2d_teardrop(r=10,angle=30);
380// Example: Masking by Edge Attachment
381// diff()
382// cube([50,60,70],center=true)
383// edge_profile(BOT)
384// mask2d_teardrop(r=10, angle=40);
385// Example: Making an interior teardrop fillet
386// %render() difference() {
387// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
388// cube(310, anchor=BOT+LEFT);
389// }
390// xrot(90)
391// linear_extrude(height=30, center=true)
392// mask2d_teardrop(r=10);
393function mask2d_teardrop(r, angle=45, excess=0.01, d, anchor=CENTER, spin=0) =
394 assert(is_finite(angle))
395 assert(angle>0 && angle<90)
396 assert(is_finite(excess))
397 let(
398 r = get_radius(r=r, d=d, dflt=1),
399 n = ceil(segs(r) * angle/360),
400 cp = [r,r],
401 tp = cp + polar_to_xy(r,180+angle),
402 bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0],
403 step = angle/n,
404 path = [
405 bp, bp-[0,excess], [-excess,-excess], [-excess,r],
406 for (i=[0:1:n]) cp+polar_to_xy(r,180+i*step)
407 ]
408 ) reorient(anchor,spin, two_d=true, path=path, p=path);
409
410module mask2d_teardrop(r, angle=45, excess=0.01, d, anchor=CENTER, spin=0) {
411 path = mask2d_teardrop(r=r, d=d, angle=angle, excess=excess);
412 attachable(anchor,spin, two_d=true, path=path) {
413 polygon(path);
414 children();
415 }
416}
417
418// Function&Module: mask2d_ogee()
419// Usage: As Module
420// mask2d_ogee(pattern, [excess], ...) [ATTAHCMENTS];
421// Usage: As Function
422// path = mask2d_ogee(pattern, [excess], ...);
423// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
424// See Also: corner_profile(), edge_profile(), face_profile()
425//
426// Description:
427// Creates a 2D Ogee mask shape that is useful for extruding into a 3D mask for a 90° edge.
428// Conversely, you can use that same extruded shape to make an interior ogee decoration between two walls at a 90º angle.
429// As a 2D mask, this is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
430// Since there are a number of shapes that fall under the name ogee, the shape of this mask is given as a pattern.
431// Patterns are given as TYPE, VALUE pairs. ie: `["fillet",10, "xstep",2, "step",[5,5], ...]`. See Patterns below.
432// If called as a function, this just returns a 2D path of the outline of the mask shape.
433// .
434// ### Patterns
435// .
436// Type | Argument | Description
437// -------- | --------- | ----------------
438// "step" | [x,y] | Makes a line to a point `x` right and `y` down.
439// "xstep" | dist | Makes a `dist` length line towards X+.
440// "ystep" | dist | Makes a `dist` length line towards Y-.
441// "round" | radius | Makes an arc that will mask a roundover.
442// "fillet" | radius | Makes an arc that will mask a fillet.
443//
444// Arguments:
445// pattern = A list of pattern pieces to describe the Ogee.
446// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
447// ---
448// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
449// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
450//
451// Example(2D): 2D Ogee Mask
452// mask2d_ogee([
453// "xstep",1, "ystep",1, // Starting shoulder.
454// "fillet",5, "round",5, // S-curve.
455// "ystep",1, "xstep",1 // Ending shoulder.
456// ]);
457// Example: Masking by Edge Attachment
458// diff()
459// cube([50,60,70],center=true)
460// edge_profile(TOP)
461// mask2d_ogee([
462// "xstep",1, "ystep",1, // Starting shoulder.
463// "fillet",5, "round",5, // S-curve.
464// "ystep",1, "xstep",1 // Ending shoulder.
465// ]);
466// Example: Making an interior ogee
467// %render() difference() {
468// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
469// cube(310, anchor=BOT+LEFT);
470// }
471// xrot(90)
472// linear_extrude(height=30, center=true)
473// mask2d_ogee([
474// "xstep", 1, "round",5,
475// "ystep",1, "fillet",5,
476// "xstep", 1, "ystep", 1,
477// ]);
478module mask2d_ogee(pattern, excess=0.01, anchor=CENTER,spin=0) {
479 path = mask2d_ogee(pattern, excess=excess);
480 attachable(anchor,spin, two_d=true, path=path) {
481 polygon(path);
482 children();
483 }
484}
485
486function mask2d_ogee(pattern, excess=0.01, anchor=CENTER, spin=0) =
487 assert(is_list(pattern))
488 assert(len(pattern)>0)
489 assert(len(pattern)%2==0,"pattern must be a list of TYPE, VAL pairs.")
490 assert(all([for (i = idx(pattern,step=2)) in_list(pattern[i],["step","xstep","ystep","round","fillet"])]))
491 let(
492 x = concat([0], cumsum([
493 for (i=idx(pattern,step=2)) let(
494 type = pattern[i],
495 val = pattern[i+1]
496 ) (
497 type=="step"? val.x :
498 type=="xstep"? val :
499 type=="round"? val :
500 type=="fillet"? val :
501 0
502 )
503 ])),
504 y = concat([0], cumsum([
505 for (i=idx(pattern,step=2)) let(
506 type = pattern[i],
507 val = pattern[i+1]
508 ) (
509 type=="step"? val.y :
510 type=="ystep"? val :
511 type=="round"? val :
512 type=="fillet"? val :
513 0
514 )
515 ])),
516 tot_x = last(x),
517 tot_y = last(y),
518 data = [
519 for (i=idx(pattern,step=2)) let(
520 type = pattern[i],
521 val = pattern[i+1],
522 pt = [x[i/2], tot_y-y[i/2]] + (
523 type=="step"? [val.x,-val.y] :
524 type=="xstep"? [val,0] :
525 type=="ystep"? [0,-val] :
526 type=="round"? [val,0] :
527 type=="fillet"? [0,-val] :
528 [0,0]
529 )
530 ) [type, val, pt]
531 ],
532 path = [
533 [tot_x,-excess],
534 [-excess,-excess],
535 [-excess,tot_y],
536 for (pat = data) each
537 pat[0]=="step"? [pat[2]] :
538 pat[0]=="xstep"? [pat[2]] :
539 pat[0]=="ystep"? [pat[2]] :
540 let(
541 r = pat[1],
542 steps = segs(abs(r)),
543 step = 90/steps
544 ) [
545 for (i=[0:1:steps]) let(
546 a = pat[0]=="round"? (180+i*step) : (90-i*step)
547 ) pat[2] + abs(r)*[cos(a),sin(a)]
548 ]
549 ],
550 path2 = deduplicate(path)
551 ) reorient(anchor,spin, two_d=true, path=path2, p=path2);
552
553
554
555// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap