diff --git a/comparisons.scad b/comparisons.scad index 3ea5d07..4bc846f 100644 --- a/comparisons.scad +++ b/comparisons.scad @@ -187,7 +187,7 @@ function all_nonnegative(x,eps=0) = // eps = Set to tolerance for approximate equality. Default: 0 function all_equal(vec,eps=0) = eps==0 ? [for(v=vec) if (v!=vec[0]) v] == [] - : [for(v=vec) if (!approx(v,vec[0])) v] == []; + : [for(v=vec) if (!approx(v,vec[0],eps)) v] == []; diff --git a/masks2d.scad b/masks2d.scad new file mode 100644 index 0000000..65121c5 --- /dev/null +++ b/masks2d.scad @@ -0,0 +1,503 @@ +////////////////////////////////////////////////////////////////////// +// LibFile: masks2d.scad +// This file provides 2D masking shapes that you can use with {{edge_profile()}} to mask edges. +// The shapes include the simple roundover and chamfer as well as more elaborate shapes +// like the cove and ogee found in furniture and architecture. You can make the masks +// as geometry or as 2D paths. +// Includes: +// include +////////////////////////////////////////////////////////////////////// + + +// Section: 2D Masking Shapes + +// Function&Module: mask2d_roundover() +// Usage: As Module +// mask2d_roundover(r|d, [inset], [excess]); +// Usage: With Attachments +// mask2d_roundover(r|d, [inset], [excess]) { attachments } +// Usage: As Module +// path = mask2d_roundover(r|d, [inset], [excess]); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) +// See Also: corner_profile(), edge_profile(), face_profile() +// Description: +// Creates a 2D roundover/bead mask shape that is useful for extruding into a 3D mask for a 90° edge. +// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. +// If called as a function, this just returns a 2D path of the outline of the mask shape. +// Arguments: +// r = Radius of the roundover. +// inset = Optional bead inset size. Default: 0 +// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 +// --- +// d = Diameter of the roundover. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): 2D Roundover Mask +// mask2d_roundover(r=10); +// Example(2D): 2D Bead Mask +// mask2d_roundover(r=10,inset=2); +// Example: Masking by Edge Attachment +// diff("mask") +// cube([50,60,70],center=true) +// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) +// mask2d_roundover(r=10, inset=2); +module mask2d_roundover(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) { + path = mask2d_roundover(r=r,d=d,excess=excess,inset=inset); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } +} + +function mask2d_roundover(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) = + assert(is_num(r)||is_num(d)) + assert(is_undef(excess)||is_num(excess)) + assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) + let( + inset = is_list(inset)? inset : [inset,inset], + excess = default(excess,$overlap), + r = get_radius(r=r,d=d,dflt=1), + steps = quantup(segs(r),4)/4, + step = 90/steps, + path = [ + [r+inset.x,-excess], + [-excess,-excess], + [-excess, r+inset.y], + for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step) + ] + ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path); + + +// Function&Module: mask2d_cove() +// Usage: As Module +// mask2d_cove(r|d, [inset], [excess]); +// Usage: With Attachments +// mask2d_cove(r|d, [inset], [excess]) { attachments } +// Usage: As Function +// path = mask2d_cove(r|d, [inset], [excess]); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) +// See Also: corner_profile(), edge_profile(), face_profile() +// Description: +// Creates a 2D cove mask shape that is useful for extruding into a 3D mask for a 90° edge. +// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. +// If called as a function, this just returns a 2D path of the outline of the mask shape. +// Arguments: +// r = Radius of the cove. +// inset = Optional amount to inset code from corner. Default: 0 +// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 +// --- +// d = Diameter of the cove. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): 2D Cove Mask +// mask2d_cove(r=10); +// Example(2D): 2D Inset Cove Mask +// mask2d_cove(r=10,inset=3); +// Example: Masking by Edge Attachment +// diff("mask") +// cube([50,60,70],center=true) +// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) +// mask2d_cove(r=10, inset=2); +module mask2d_cove(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) { + path = mask2d_cove(r=r,d=d,excess=excess,inset=inset); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } +} + +function mask2d_cove(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) = + assert(is_num(r)||is_num(d)) + assert(is_undef(excess)||is_num(excess)) + assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) + let( + inset = is_list(inset)? inset : [inset,inset], + excess = default(excess,$overlap), + r = get_radius(r=r,d=d,dflt=1), + steps = quantup(segs(r),4)/4, + step = 90/steps, + path = [ + [r+inset.x,-excess], + [-excess,-excess], + [-excess, r+inset.y], + for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step) + ] + ) reorient(anchor,spin, two_d=true, path=path, p=path); + + +// Function&Module: mask2d_chamfer() +// Usage: As Module +// mask2d_chamfer(edge, [angle], [inset], [excess]); +// mask2d_chamfer(y, [angle], [inset], [excess]); +// mask2d_chamfer(x, [angle], [inset], [excess]); +// Usage: With Attachments +// mask2d_chamfer(edge, [angle], [inset], [excess]) { attachments } +// Usage: As Function +// path = mask2d_chamfer(edge, [angle], [inset], [excess]); +// path = mask2d_chamfer(y, [angle], [inset], [excess]); +// path = mask2d_chamfer(x, [angle], [inset], [excess]); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) +// See Also: corner_profile(), edge_profile(), face_profile() +// Description: +// Creates a 2D chamfer mask shape that is useful for extruding into a 3D mask for a 90° edge. +// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. +// If called as a function, this just returns a 2D path of the outline of the mask shape. +// Arguments: +// edge = The length of the edge of the chamfer. +// angle = The angle of the chamfer edge, away from vertical. Default: 45. +// inset = Optional amount to inset code from corner. Default: 0 +// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 +// --- +// x = The width of the chamfer. +// y = The height of the chamfer. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): 2D Chamfer Mask +// mask2d_chamfer(x=10); +// Example(2D): 2D Chamfer Mask by Width. +// mask2d_chamfer(x=10, angle=30); +// Example(2D): 2D Chamfer Mask by Height. +// mask2d_chamfer(y=10, angle=30); +// Example(2D): 2D Inset Chamfer Mask +// mask2d_chamfer(x=10, inset=2); +// Example: Masking by Edge Attachment +// diff("mask") +// cube([50,60,70],center=true) +// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) +// mask2d_chamfer(x=10, inset=2); +module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) { + path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset); + attachable(anchor,spin, two_d=true, path=path, extent=true) { + polygon(path); + children(); + } +} + +function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) = + assert(num_defined([x,y,edge])==1) + assert(is_num(first_defined([x,y,edge]))) + assert(is_num(angle)) + assert(is_undef(excess)||is_num(excess)) + assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) + let( + inset = is_list(inset)? inset : [inset,inset], + excess = default(excess,$overlap), + x = !is_undef(x)? x : + !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) : + hyp_ang_to_opp(hyp=edge,ang=angle), + y = opp_ang_to_adj(opp=x,ang=angle), + path = [ + [x+inset.x, -excess], + [-excess, -excess], + [-excess, y+inset.y], + [inset.x, y+inset.y], + [x+inset.x, inset.y] + ] + ) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path); + + +// Function&Module: mask2d_rabbet() +// Usage: As Module +// mask2d_rabbet(size, [excess]); +// Usage: With Attachments +// mask2d_rabbet(size, [excess]) { attachments } +// Usage: As Function +// path = mask2d_rabbet(size, [excess]); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) +// See Also: corner_profile(), edge_profile(), face_profile() +// Description: +// Creates a 2D rabbet mask shape that is useful for extruding into a 3D mask for a 90° edge. +// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. +// If called as a function, this just returns a 2D path of the outline of the mask shape. +// Arguments: +// size = The size of the rabbet, either as a scalar or an [X,Y] list. +// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 +// --- +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): 2D Rabbet Mask +// mask2d_rabbet(size=10); +// Example(2D): 2D Asymmetrical Rabbet Mask +// mask2d_rabbet(size=[5,10]); +// Example: Masking by Edge Attachment +// diff("mask") +// cube([50,60,70],center=true) +// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) +// mask2d_rabbet(size=10); +module mask2d_rabbet(size, excess=0.01, anchor=CENTER,spin=0) { + path = mask2d_rabbet(size=size, excess=excess); + attachable(anchor,spin, two_d=true, path=path, extent=false) { + polygon(path); + children(); + } +} + +function mask2d_rabbet(size, excess=0.01, anchor=CENTER,spin=0) = + assert(is_num(size)||(is_vector(size)&&len(size)==2)) + assert(is_undef(excess)||is_num(excess)) + let( + excess = default(excess,$overlap), + size = is_list(size)? size : [size,size], + path = [ + [size.x, -excess], + [-excess, -excess], + [-excess, size.y], + size + ] + ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path); + + +// Function&Module: mask2d_dovetail() +// Usage: As Module +// mask2d_dovetail(edge, [angle], [inset], [shelf], [excess], ...); +// mask2d_dovetail(x=, [angle=], [inset=], [shelf=], [excess=], ...); +// mask2d_dovetail(y=, [angle=], [inset=], [shelf=], [excess=], ...); +// Usage: With Attachments +// mask2d_dovetail(edge, [angle], [inset], [shelf], ...) { attachments } +// Usage: As Function +// path = mask2d_dovetail(edge, [angle], [inset], [shelf], [excess]); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) +// See Also: corner_profile(), edge_profile(), face_profile() +// Description: +// Creates a 2D dovetail mask shape that is useful for extruding into a 3D mask for a 90° edge. +// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. +// If called as a function, this just returns a 2D path of the outline of the mask shape. +// Arguments: +// edge = The length of the edge of the dovetail. +// angle = The angle of the chamfer edge, away from vertical. Default: 30. +// inset = Optional amount to inset code from corner. Default: 0 +// shelf = The extra height to add to the inside corner of the dovetail. Default: 0 +// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 +// --- +// x = The width of the dovetail. +// y = The height of the dovetail. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): 2D Dovetail Mask +// mask2d_dovetail(x=10); +// Example(2D): 2D Dovetail Mask by Width. +// mask2d_dovetail(x=10, angle=30); +// Example(2D): 2D Dovetail Mask by Height. +// mask2d_dovetail(y=10, angle=30); +// Example(2D): 2D Inset Dovetail Mask +// mask2d_dovetail(x=10, inset=2); +// Example: Masking by Edge Attachment +// diff("mask") +// cube([50,60,70],center=true) +// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) +// mask2d_dovetail(x=10, inset=2); +module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) { + path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } +} + +function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) = + assert(num_defined([x,y,edge])==1) + assert(is_num(first_defined([x,y,edge]))) + assert(is_num(angle)) + assert(is_undef(excess)||is_num(excess)) + assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) + let( + inset = is_list(inset)? inset : [inset,inset], + excess = default(excess,$overlap), + x = !is_undef(x)? x : + !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) : + hyp_ang_to_opp(hyp=edge,ang=angle), + y = opp_ang_to_adj(opp=x,ang=angle), + path = [ + [inset.x,0], + [-excess, 0], + [-excess, y+inset.y+shelf], + inset+[x,y+shelf], + inset+[x,y], + inset + ] + ) reorient(anchor,spin, two_d=true, path=path, p=path); + + +// Function&Module: mask2d_teardrop() +// Usage: As Module +// mask2d_teardrop(r|d, [angle], [excess]); +// Usage: With Attachments +// mask2d_teardrop(r|d, [angle], [excess]) { attachments } +// Usage: As Function +// path = mask2d_teardrop(r|d, [angle], [excess]); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) +// See Also: corner_profile(), edge_profile(), face_profile() +// Description: +// Creates a 2D teardrop mask shape that is useful for extruding into a 3D mask for a 90° edge. +// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. +// If called as a function, this just returns a 2D path of the outline of the mask shape. +// This is particularly useful to make partially rounded bottoms, that don't need support to print. +// Arguments: +// r = Radius of the rounding. +// angle = The maximum angle from vertical. +// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 +// --- +// d = Diameter of the rounding. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): 2D Teardrop Mask +// mask2d_teardrop(r=10); +// Example(2D): Using a Custom Angle +// mask2d_teardrop(r=10,angle=30); +// Example: Masking by Edge Attachment +// diff("mask") +// cube([50,60,70],center=true) +// edge_profile(BOT) +// mask2d_teardrop(r=10, angle=40); +function mask2d_teardrop(r, angle=45, excess=0.01, d, anchor=CENTER, spin=0) = + assert(is_num(angle)) + assert(angle>0 && angle<90) + assert(is_num(excess)) + let( + r = get_radius(r=r, d=d, dflt=1), + n = ceil(segs(r) * angle/360), + cp = [r,r], + tp = cp + polar_to_xy(r,180+angle), + bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0], + step = angle/n, + path = [ + bp, bp-[0,excess], [-excess,-excess], [-excess,r], + for (i=[0:1:n]) cp+polar_to_xy(r,180+i*step) + ] + ) reorient(anchor,spin, two_d=true, path=path, p=path); + +module mask2d_teardrop(r, angle=45, excess=0.01, d, anchor=CENTER, spin=0) { + path = mask2d_teardrop(r=r, d=d, angle=angle, excess=excess); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } +} + +// Function&Module: mask2d_ogee() +// Usage: As Module +// mask2d_ogee(pattern, [excess], ...); +// Usage: With Attachments +// mask2d_ogee(pattern, [excess], ...) { attachments } +// Usage: As Function +// path = mask2d_ogee(pattern, [excess], ...); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) +// See Also: corner_profile(), edge_profile(), face_profile() +// +// Description: +// Creates a 2D Ogee mask shape that is useful for extruding into a 3D mask for a 90° edge. +// This 2D mask is designed to be `difference()`d away from the edge of a shape that is in the first (X+Y+) quadrant. +// Since there are a number of shapes that fall under the name ogee, the shape of this mask is given as a pattern. +// Patterns are given as TYPE, VALUE pairs. ie: `["fillet",10, "xstep",2, "step",[5,5], ...]`. See Patterns below. +// If called as a function, this just returns a 2D path of the outline of the mask shape. +// . +// ### Patterns +// . +// Type | Argument | Description +// -------- | --------- | ---------------- +// "step" | [x,y] | Makes a line to a point `x` right and `y` down. +// "xstep" | dist | Makes a `dist` length line towards X+. +// "ystep" | dist | Makes a `dist` length line towards Y-. +// "round" | radius | Makes an arc that will mask a roundover. +// "fillet" | radius | Makes an arc that will mask a fillet. +// +// Arguments: +// pattern = A list of pattern pieces to describe the Ogee. +// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 +// --- +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// +// Example(2D): 2D Ogee Mask +// mask2d_ogee([ +// "xstep",1, "ystep",1, // Starting shoulder. +// "fillet",5, "round",5, // S-curve. +// "ystep",1, "xstep",1 // Ending shoulder. +// ]); +// Example: Masking by Edge Attachment +// diff("mask") +// cube([50,60,70],center=true) +// edge_profile(TOP) +// mask2d_ogee([ +// "xstep",1, "ystep",1, // Starting shoulder. +// "fillet",5, "round",5, // S-curve. +// "ystep",1, "xstep",1 // Ending shoulder. +// ]); +module mask2d_ogee(pattern, excess=0.01, anchor=CENTER,spin=0) { + path = mask2d_ogee(pattern, excess=excess); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } +} + +function mask2d_ogee(pattern, excess=0.01, anchor=CENTER, spin=0) = + assert(is_list(pattern)) + assert(len(pattern)>0) + assert(len(pattern)%2==0,"pattern must be a list of TYPE, VAL pairs.") + assert(all([for (i = idx(pattern,step=2)) in_list(pattern[i],["step","xstep","ystep","round","fillet"])])) + let( + excess = default(excess,$overlap), + x = concat([0], cumsum([ + for (i=idx(pattern,step=2)) let( + type = pattern[i], + val = pattern[i+1] + ) ( + type=="step"? val.x : + type=="xstep"? val : + type=="round"? val : + type=="fillet"? val : + 0 + ) + ])), + y = concat([0], cumsum([ + for (i=idx(pattern,step=2)) let( + type = pattern[i], + val = pattern[i+1] + ) ( + type=="step"? val.y : + type=="ystep"? val : + type=="round"? val : + type=="fillet"? val : + 0 + ) + ])), + tot_x = last(x), + tot_y = last(y), + data = [ + for (i=idx(pattern,step=2)) let( + type = pattern[i], + val = pattern[i+1], + pt = [x[i/2], tot_y-y[i/2]] + ( + type=="step"? [val.x,-val.y] : + type=="xstep"? [val,0] : + type=="ystep"? [0,-val] : + type=="round"? [val,0] : + type=="fillet"? [0,-val] : + [0,0] + ) + ) [type, val, pt] + ], + path = [ + [tot_x,-excess], + [-excess,-excess], + [-excess,tot_y], + for (pat = data) each + pat[0]=="step"? [pat[2]] : + pat[0]=="xstep"? [pat[2]] : + pat[0]=="ystep"? [pat[2]] : + let( + r = pat[1], + steps = segs(abs(r)), + step = 90/steps + ) [ + for (i=[0:1:steps]) let( + a = pat[0]=="round"? (180+i*step) : (90-i*step) + ) pat[2] + abs(r)*[cos(a),sin(a)] + ] + ], + path2 = deduplicate(path) + ) reorient(anchor,spin, two_d=true, path=path2, p=path2); + + + diff --git a/masks.scad b/masks3d.scad similarity index 99% rename from masks.scad rename to masks3d.scad index f5d783f..df89eb3 100644 --- a/masks.scad +++ b/masks3d.scad @@ -1,6 +1,7 @@ ////////////////////////////////////////////////////////////////////// -// LibFile: masks.scad -// Masking shapes. +// LibFile: masks3d.scad +// This file defines 3D masks for applying chamfers, roundovers, and teardrop roundovers to straight edges and circular +// edges in three dimensions. // Includes: // include ////////////////////////////////////////////////////////////////////// diff --git a/shapes2d.scad b/shapes2d.scad index 6369ac3..8a75c9b 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1,15 +1,13 @@ ////////////////////////////////////////////////////////////////////// // LibFile: shapes2d.scad // This file includes redefinitions of the core modules to -// work with attachment. You can also create regular polygons +// work with attachment, and functional forms of those modules +// that produce paths. You can create regular polygons // with optional rounded corners and alignment features not // available with circle(). The file also provides teardrop2d, -// which is useful for 3d printable holes. Lastly you can use the -// masks to produce edge treatments common in furniture from the -// simple roundover or cove molding to the more elaborate ogee. +// which is useful for 3D printable holes. // Many of the commands have module forms that produce geometry and -// function forms that produce a path. This file defines function -// forms of the core OpenSCAD modules that produce paths. +// function forms that produce a path. // Includes: // include ////////////////////////////////////////////////////////////////////// @@ -1224,498 +1222,4 @@ function reuleaux_polygon(N=3, r, d, anchor=CENTER, spin=0) = ) reorient(anchor,spin, two_d=true, path=path, anchors=anchors, p=path); -// Section: 2D Masking Shapes - -// Function&Module: mask2d_roundover() -// Usage: As Module -// mask2d_roundover(r|d, [inset], [excess]); -// Usage: With Attachments -// mask2d_roundover(r|d, [inset], [excess]) { attachments } -// Usage: As Module -// path = mask2d_roundover(r|d, [inset], [excess]); -// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) -// See Also: corner_profile(), edge_profile(), face_profile() -// Description: -// Creates a 2D roundover/bead mask shape that is useful for extruding into a 3D mask for a 90º edge. -// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. -// If called as a function, this just returns a 2D path of the outline of the mask shape. -// Arguments: -// r = Radius of the roundover. -// inset = Optional bead inset size. Default: 0 -// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 -// --- -// d = Diameter of the roundover. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): 2D Roundover Mask -// mask2d_roundover(r=10); -// Example(2D): 2D Bead Mask -// mask2d_roundover(r=10,inset=2); -// Example: Masking by Edge Attachment -// diff("mask") -// cube([50,60,70],center=true) -// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_roundover(r=10, inset=2); -module mask2d_roundover(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) { - path = mask2d_roundover(r=r,d=d,excess=excess,inset=inset); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } -} - -function mask2d_roundover(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) = - assert(is_num(r)||is_num(d)) - assert(is_undef(excess)||is_num(excess)) - assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) - let( - inset = is_list(inset)? inset : [inset,inset], - excess = default(excess,$overlap), - r = get_radius(r=r,d=d,dflt=1), - steps = quantup(segs(r),4)/4, - step = 90/steps, - path = [ - [r+inset.x,-excess], - [-excess,-excess], - [-excess, r+inset.y], - for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step) - ] - ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path); - - -// Function&Module: mask2d_cove() -// Usage: As Module -// mask2d_cove(r|d, [inset], [excess]); -// Usage: With Attachments -// mask2d_cove(r|d, [inset], [excess]) { attachments } -// Usage: As Function -// path = mask2d_cove(r|d, [inset], [excess]); -// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) -// See Also: corner_profile(), edge_profile(), face_profile() -// Description: -// Creates a 2D cove mask shape that is useful for extruding into a 3D mask for a 90º edge. -// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. -// If called as a function, this just returns a 2D path of the outline of the mask shape. -// Arguments: -// r = Radius of the cove. -// inset = Optional amount to inset code from corner. Default: 0 -// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 -// --- -// d = Diameter of the cove. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): 2D Cove Mask -// mask2d_cove(r=10); -// Example(2D): 2D Inset Cove Mask -// mask2d_cove(r=10,inset=3); -// Example: Masking by Edge Attachment -// diff("mask") -// cube([50,60,70],center=true) -// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_cove(r=10, inset=2); -module mask2d_cove(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) { - path = mask2d_cove(r=r,d=d,excess=excess,inset=inset); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } -} - -function mask2d_cove(r, inset=0, excess=0.01, d, anchor=CENTER,spin=0) = - assert(is_num(r)||is_num(d)) - assert(is_undef(excess)||is_num(excess)) - assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) - let( - inset = is_list(inset)? inset : [inset,inset], - excess = default(excess,$overlap), - r = get_radius(r=r,d=d,dflt=1), - steps = quantup(segs(r),4)/4, - step = 90/steps, - path = [ - [r+inset.x,-excess], - [-excess,-excess], - [-excess, r+inset.y], - for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step) - ] - ) reorient(anchor,spin, two_d=true, path=path, p=path); - - -// Function&Module: mask2d_chamfer() -// Usage: As Module -// mask2d_chamfer(edge, [angle], [inset], [excess]); -// mask2d_chamfer(y, [angle], [inset], [excess]); -// mask2d_chamfer(x, [angle], [inset], [excess]); -// Usage: With Attachments -// mask2d_chamfer(edge, [angle], [inset], [excess]) { attachments } -// Usage: As Function -// path = mask2d_chamfer(edge, [angle], [inset], [excess]); -// path = mask2d_chamfer(y, [angle], [inset], [excess]); -// path = mask2d_chamfer(x, [angle], [inset], [excess]); -// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) -// See Also: corner_profile(), edge_profile(), face_profile() -// Description: -// Creates a 2D chamfer mask shape that is useful for extruding into a 3D mask for a 90º edge. -// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. -// If called as a function, this just returns a 2D path of the outline of the mask shape. -// Arguments: -// edge = The length of the edge of the chamfer. -// angle = The angle of the chamfer edge, away from vertical. Default: 45. -// inset = Optional amount to inset code from corner. Default: 0 -// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 -// --- -// x = The width of the chamfer. -// y = The height of the chamfer. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): 2D Chamfer Mask -// mask2d_chamfer(x=10); -// Example(2D): 2D Chamfer Mask by Width. -// mask2d_chamfer(x=10, angle=30); -// Example(2D): 2D Chamfer Mask by Height. -// mask2d_chamfer(y=10, angle=30); -// Example(2D): 2D Inset Chamfer Mask -// mask2d_chamfer(x=10, inset=2); -// Example: Masking by Edge Attachment -// diff("mask") -// cube([50,60,70],center=true) -// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_chamfer(x=10, inset=2); -module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) { - path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset); - attachable(anchor,spin, two_d=true, path=path, extent=true) { - polygon(path); - children(); - } -} - -function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) = - assert(num_defined([x,y,edge])==1) - assert(is_num(first_defined([x,y,edge]))) - assert(is_num(angle)) - assert(is_undef(excess)||is_num(excess)) - assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) - let( - inset = is_list(inset)? inset : [inset,inset], - excess = default(excess,$overlap), - x = !is_undef(x)? x : - !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) : - hyp_ang_to_opp(hyp=edge,ang=angle), - y = opp_ang_to_adj(opp=x,ang=angle), - path = [ - [x+inset.x, -excess], - [-excess, -excess], - [-excess, y+inset.y], - [inset.x, y+inset.y], - [x+inset.x, inset.y] - ] - ) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path); - - -// Function&Module: mask2d_rabbet() -// Usage: As Module -// mask2d_rabbet(size, [excess]); -// Usage: With Attachments -// mask2d_rabbet(size, [excess]) { attachments } -// Usage: As Function -// path = mask2d_rabbet(size, [excess]); -// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) -// See Also: corner_profile(), edge_profile(), face_profile() -// Description: -// Creates a 2D rabbet mask shape that is useful for extruding into a 3D mask for a 90º edge. -// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. -// If called as a function, this just returns a 2D path of the outline of the mask shape. -// Arguments: -// size = The size of the rabbet, either as a scalar or an [X,Y] list. -// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 -// --- -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): 2D Rabbet Mask -// mask2d_rabbet(size=10); -// Example(2D): 2D Asymmetrical Rabbet Mask -// mask2d_rabbet(size=[5,10]); -// Example: Masking by Edge Attachment -// diff("mask") -// cube([50,60,70],center=true) -// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_rabbet(size=10); -module mask2d_rabbet(size, excess=0.01, anchor=CENTER,spin=0) { - path = mask2d_rabbet(size=size, excess=excess); - attachable(anchor,spin, two_d=true, path=path, extent=false) { - polygon(path); - children(); - } -} - -function mask2d_rabbet(size, excess=0.01, anchor=CENTER,spin=0) = - assert(is_num(size)||(is_vector(size)&&len(size)==2)) - assert(is_undef(excess)||is_num(excess)) - let( - excess = default(excess,$overlap), - size = is_list(size)? size : [size,size], - path = [ - [size.x, -excess], - [-excess, -excess], - [-excess, size.y], - size - ] - ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path); - - -// Function&Module: mask2d_dovetail() -// Usage: As Module -// mask2d_dovetail(edge, [angle], [inset], [shelf], [excess], ...); -// mask2d_dovetail(x=, [angle=], [inset=], [shelf=], [excess=], ...); -// mask2d_dovetail(y=, [angle=], [inset=], [shelf=], [excess=], ...); -// Usage: With Attachments -// mask2d_dovetail(edge, [angle], [inset], [shelf], ...) { attachments } -// Usage: As Function -// path = mask2d_dovetail(edge, [angle], [inset], [shelf], [excess]); -// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) -// See Also: corner_profile(), edge_profile(), face_profile() -// Description: -// Creates a 2D dovetail mask shape that is useful for extruding into a 3D mask for a 90º edge. -// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. -// If called as a function, this just returns a 2D path of the outline of the mask shape. -// Arguments: -// edge = The length of the edge of the dovetail. -// angle = The angle of the chamfer edge, away from vertical. Default: 30. -// inset = Optional amount to inset code from corner. Default: 0 -// shelf = The extra height to add to the inside corner of the dovetail. Default: 0 -// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 -// --- -// x = The width of the dovetail. -// y = The height of the dovetail. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): 2D Dovetail Mask -// mask2d_dovetail(x=10); -// Example(2D): 2D Dovetail Mask by Width. -// mask2d_dovetail(x=10, angle=30); -// Example(2D): 2D Dovetail Mask by Height. -// mask2d_dovetail(y=10, angle=30); -// Example(2D): 2D Inset Dovetail Mask -// mask2d_dovetail(x=10, inset=2); -// Example: Masking by Edge Attachment -// diff("mask") -// cube([50,60,70],center=true) -// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_dovetail(x=10, inset=2); -module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) { - path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } -} - -function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) = - assert(num_defined([x,y,edge])==1) - assert(is_num(first_defined([x,y,edge]))) - assert(is_num(angle)) - assert(is_undef(excess)||is_num(excess)) - assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) - let( - inset = is_list(inset)? inset : [inset,inset], - excess = default(excess,$overlap), - x = !is_undef(x)? x : - !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) : - hyp_ang_to_opp(hyp=edge,ang=angle), - y = opp_ang_to_adj(opp=x,ang=angle), - path = [ - [inset.x,0], - [-excess, 0], - [-excess, y+inset.y+shelf], - inset+[x,y+shelf], - inset+[x,y], - inset - ] - ) reorient(anchor,spin, two_d=true, path=path, p=path); - - -// Function&Module: mask2d_teardrop() -// Usage: As Module -// mask2d_teardrop(r|d, [angle], [excess]); -// Usage: With Attachments -// mask2d_teardrop(r|d, [angle], [excess]) { attachments } -// Usage: As Function -// path = mask2d_teardrop(r|d, [angle], [excess]); -// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) -// See Also: corner_profile(), edge_profile(), face_profile() -// Description: -// Creates a 2D teardrop mask shape that is useful for extruding into a 3D mask for a 90º edge. -// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant. -// If called as a function, this just returns a 2D path of the outline of the mask shape. -// This is particularly useful to make partially rounded bottoms, that don't need support to print. -// Arguments: -// r = Radius of the rounding. -// angle = The maximum angle from vertical. -// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 -// --- -// d = Diameter of the rounding. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): 2D Teardrop Mask -// mask2d_teardrop(r=10); -// Example(2D): Using a Custom Angle -// mask2d_teardrop(r=10,angle=30); -// Example: Masking by Edge Attachment -// diff("mask") -// cube([50,60,70],center=true) -// edge_profile(BOT) -// mask2d_teardrop(r=10, angle=40); -function mask2d_teardrop(r, angle=45, excess=0.01, d, anchor=CENTER, spin=0) = - assert(is_num(angle)) - assert(angle>0 && angle<90) - assert(is_num(excess)) - let( - r = get_radius(r=r, d=d, dflt=1), - n = ceil(segs(r) * angle/360), - cp = [r,r], - tp = cp + polar_to_xy(r,180+angle), - bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0], - step = angle/n, - path = [ - bp, bp-[0,excess], [-excess,-excess], [-excess,r], - for (i=[0:1:n]) cp+polar_to_xy(r,180+i*step) - ] - ) reorient(anchor,spin, two_d=true, path=path, p=path); - -module mask2d_teardrop(r, angle=45, excess=0.01, d, anchor=CENTER, spin=0) { - path = mask2d_teardrop(r=r, d=d, angle=angle, excess=excess); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } -} - -// Function&Module: mask2d_ogee() -// Usage: As Module -// mask2d_ogee(pattern, [excess], ...); -// Usage: With Attachments -// mask2d_ogee(pattern, [excess], ...) { attachments } -// Usage: As Function -// path = mask2d_ogee(pattern, [excess], ...); -// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D) -// See Also: corner_profile(), edge_profile(), face_profile() -// -// Description: -// Creates a 2D Ogee mask shape that is useful for extruding into a 3D mask for a 90º edge. -// This 2D mask is designed to be `difference()`d away from the edge of a shape that is in the first (X+Y+) quadrant. -// Since there are a number of shapes that fall under the name ogee, the shape of this mask is given as a pattern. -// Patterns are given as TYPE, VALUE pairs. ie: `["fillet",10, "xstep",2, "step",[5,5], ...]`. See Patterns below. -// If called as a function, this just returns a 2D path of the outline of the mask shape. -// . -// ### Patterns -// . -// Type | Argument | Description -// -------- | --------- | ---------------- -// "step" | [x,y] | Makes a line to a point `x` right and `y` down. -// "xstep" | dist | Makes a `dist` length line towards X+. -// "ystep" | dist | Makes a `dist` length line towards Y-. -// "round" | radius | Makes an arc that will mask a roundover. -// "fillet" | radius | Makes an arc that will mask a fillet. -// -// Arguments: -// pattern = A list of pattern pieces to describe the Ogee. -// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01 -// --- -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// -// Example(2D): 2D Ogee Mask -// mask2d_ogee([ -// "xstep",1, "ystep",1, // Starting shoulder. -// "fillet",5, "round",5, // S-curve. -// "ystep",1, "xstep",1 // Ending shoulder. -// ]); -// Example: Masking by Edge Attachment -// diff("mask") -// cube([50,60,70],center=true) -// edge_profile(TOP) -// mask2d_ogee([ -// "xstep",1, "ystep",1, // Starting shoulder. -// "fillet",5, "round",5, // S-curve. -// "ystep",1, "xstep",1 // Ending shoulder. -// ]); -module mask2d_ogee(pattern, excess=0.01, anchor=CENTER,spin=0) { - path = mask2d_ogee(pattern, excess=excess); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } -} - -function mask2d_ogee(pattern, excess=0.01, anchor=CENTER, spin=0) = - assert(is_list(pattern)) - assert(len(pattern)>0) - assert(len(pattern)%2==0,"pattern must be a list of TYPE, VAL pairs.") - assert(all([for (i = idx(pattern,step=2)) in_list(pattern[i],["step","xstep","ystep","round","fillet"])])) - let( - excess = default(excess,$overlap), - x = concat([0], cumsum([ - for (i=idx(pattern,step=2)) let( - type = pattern[i], - val = pattern[i+1] - ) ( - type=="step"? val.x : - type=="xstep"? val : - type=="round"? val : - type=="fillet"? val : - 0 - ) - ])), - y = concat([0], cumsum([ - for (i=idx(pattern,step=2)) let( - type = pattern[i], - val = pattern[i+1] - ) ( - type=="step"? val.y : - type=="ystep"? val : - type=="round"? val : - type=="fillet"? val : - 0 - ) - ])), - tot_x = last(x), - tot_y = last(y), - data = [ - for (i=idx(pattern,step=2)) let( - type = pattern[i], - val = pattern[i+1], - pt = [x[i/2], tot_y-y[i/2]] + ( - type=="step"? [val.x,-val.y] : - type=="xstep"? [val,0] : - type=="ystep"? [0,-val] : - type=="round"? [val,0] : - type=="fillet"? [0,-val] : - [0,0] - ) - ) [type, val, pt] - ], - path = [ - [tot_x,-excess], - [-excess,-excess], - [-excess,tot_y], - for (pat = data) each - pat[0]=="step"? [pat[2]] : - pat[0]=="xstep"? [pat[2]] : - pat[0]=="ystep"? [pat[2]] : - let( - r = pat[1], - steps = segs(abs(r)), - step = 90/steps - ) [ - for (i=[0:1:steps]) let( - a = pat[0]=="round"? (180+i*step) : (90-i*step) - ) pat[2] + abs(r)*[cos(a),sin(a)] - ] - ], - path2 = deduplicate(path) - ) reorient(anchor,spin, two_d=true, path=path2, p=path2); - - - - - // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/std.scad b/std.scad index 490ed21..10d4848 100644 --- a/std.scad +++ b/std.scad @@ -17,7 +17,8 @@ include include include include -include +include +include include include include diff --git a/tests/test_comparisons.scad b/tests/test_comparisons.scad index b732521..0168037 100644 --- a/tests/test_comparisons.scad +++ b/tests/test_comparisons.scad @@ -164,6 +164,16 @@ module test_all_zero() { test_all_zero(); +module test_all_equal() { + assert(all_equal([1,1,1,1])); + assert(all_equal([[3,4],[3,4],[3,4]])); + assert(!all_equal([1,2,1,1])); + assert(!all_equal([1,1.001,1,1.001,.999])); + assert(all_equal([1,1.001,1,1.001,.999],eps=.01)); +} +test_all_equal(); + + module test_all_nonzero() { assert(!all_nonzero(0)); assert(!all_nonzero([0,0,0])); diff --git a/tests/test_shapes2d.scad b/tests/test_shapes2d.scad index 6ed09eb..5260810 100644 --- a/tests/test_shapes2d.scad +++ b/tests/test_shapes2d.scad @@ -126,95 +126,5 @@ module test_reuleaux_polygon() { test_reuleaux_polygon(); -module test_mask2d_chamfer() { - assert_approx(mask2d_chamfer(x=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,0]]); - assert_approx(mask2d_chamfer(y=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,0]]); - assert_approx(mask2d_chamfer(edge=10),[[7.07106781187,-0.01],[-0.01,-0.01],[-0.01,7.07106781187],[0,7.07106781187],[7.07106781187,0]]); - assert_approx(mask2d_chamfer(x=10,angle=30),[[10,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,0]]); - assert_approx(mask2d_chamfer(y=10,angle=30),[[5.7735026919,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,0]]); - assert_approx(mask2d_chamfer(edge=10,angle=30),[[5,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,0]]); - assert_approx(mask2d_chamfer(x=10,angle=30,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,18.3205080757],[1,18.3205080757],[11,1]]); - assert_approx(mask2d_chamfer(y=10,angle=30,inset=1),[[6.7735026919,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[6.7735026919,1]]); - assert_approx(mask2d_chamfer(edge=10,angle=30,inset=1),[[6,-0.01],[-0.01,-0.01],[-0.01,9.66025403784],[1,9.66025403784],[6,1]]); - assert_approx(mask2d_chamfer(x=10,angle=30,inset=1,excess=1),[[11,-1],[-1,-1],[-1,18.3205080757],[1,18.3205080757],[11,1]]); - assert_approx(mask2d_chamfer(y=10,angle=30,inset=1,excess=1),[[6.7735026919,-1],[-1,-1],[-1,11],[1,11],[6.7735026919,1]]); - assert_approx(mask2d_chamfer(edge=10,angle=30,inset=1,excess=1),[[6,-1],[-1,-1],[-1,9.66025403784],[1,9.66025403784],[6,1]]); -} -test_mask2d_chamfer(); - - -module test_mask2d_cove() { - $fn = 24; - assert_approx(mask2d_cove(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[2.58819045103,9.65925826289],[5,8.66025403784],[7.07106781187,7.07106781187],[8.66025403784,5],[9.65925826289,2.58819045103],[10,0]]); - assert_approx(mask2d_cove(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[2.58819045103,9.65925826289],[5,8.66025403784],[7.07106781187,7.07106781187],[8.66025403784,5],[9.65925826289,2.58819045103],[10,0]]); - assert_approx(mask2d_cove(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[3.58819045103,10.6592582629],[6,9.66025403784],[8.07106781187,8.07106781187],[9.66025403784,6],[10.6592582629,3.58819045103],[11,1]]); - assert_approx(mask2d_cove(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[3.58819045103,10.6592582629],[6,9.66025403784],[8.07106781187,8.07106781187],[9.66025403784,6],[10.6592582629,3.58819045103],[11,1]]); - assert_approx(mask2d_cove(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[3.58819045103,10.6592582629],[6,9.66025403784],[8.07106781187,8.07106781187],[9.66025403784,6],[10.6592582629,3.58819045103],[11,1]]); - assert_approx(mask2d_cove(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[3.58819045103,10.6592582629],[6,9.66025403784],[8.07106781187,8.07106781187],[9.66025403784,6],[10.6592582629,3.58819045103],[11,1]]); -} -test_mask2d_cove(); - - -module test_mask2d_roundover() { - $fn = 24; - assert_approx(mask2d_roundover(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[2.92893218813,2.92893218813],[5,1.33974596216],[7.41180954897,0.340741737109],[10,0]]); - assert_approx(mask2d_roundover(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[2.92893218813,2.92893218813],[5,1.33974596216],[7.41180954897,0.340741737109],[10,0]]); - assert_approx(mask2d_roundover(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.34074173711,8.41180954897],[2.33974596216,6],[3.92893218813,3.92893218813],[6,2.33974596216],[8.41180954897,1.34074173711],[11,1]]); - assert_approx(mask2d_roundover(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.34074173711,8.41180954897],[2.33974596216,6],[3.92893218813,3.92893218813],[6,2.33974596216],[8.41180954897,1.34074173711],[11,1]]); - assert_approx(mask2d_roundover(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.34074173711,8.41180954897],[2.33974596216,6],[3.92893218813,3.92893218813],[6,2.33974596216],[8.41180954897,1.34074173711],[11,1]]); - assert_approx(mask2d_roundover(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.34074173711,8.41180954897],[2.33974596216,6],[3.92893218813,3.92893218813],[6,2.33974596216],[8.41180954897,1.34074173711],[11,1]]); -} -test_mask2d_roundover(); - - -module test_mask2d_dovetail() { - assert_approx(mask2d_dovetail(x=10),[[0,0],[-0.01,0],[-0.01,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); - assert_approx(mask2d_dovetail(y=10),[[0,0],[-0.01,0],[-0.01,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); - assert_approx(mask2d_dovetail(edge=10),[[0,0],[-0.01,0],[-0.01,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); - assert_approx(mask2d_dovetail(x=10,angle=30),[[0,0],[-0.01,0],[-0.01,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]); - assert_approx(mask2d_dovetail(y=10,angle=30),[[0,0],[-0.01,0],[-0.01,10],[5.7735026919,10],[5.7735026919,10],[0,0]]); - assert_approx(mask2d_dovetail(edge=10,angle=30),[[0,0],[-0.01,0],[-0.01,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]); - assert_approx(mask2d_dovetail(x=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); - assert_approx(mask2d_dovetail(y=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); - assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); - assert_approx(mask2d_dovetail(x=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]); - assert_approx(mask2d_dovetail(y=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]); - assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]); -} -test_mask2d_dovetail(); - - -module test_mask2d_rabbet() { - assert_approx(mask2d_rabbet(10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[10,10]]); - assert_approx(mask2d_rabbet(size=10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[10,10]]); - assert_approx(mask2d_rabbet(size=[10,15]), [[10,-0.01],[-0.01,-0.01],[-0.01,15],[10,15]]); - assert_approx(mask2d_rabbet(size=[10,15],excess=1), [[10,-1],[-1,-1],[-1,15],[10,15]]); -} -test_mask2d_rabbet(); - - -module test_mask2d_teardrop() { - $fn=24; - assert_approx(mask2d_teardrop(r=10), [[5.85786437627,0],[5.85786437627,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[2.92893218813,2.92893218813]]); - assert_approx(mask2d_teardrop(d=20), [[5.85786437627,0],[5.85786437627,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[2.92893218813,2.92893218813]]); - assert_approx(mask2d_teardrop(r=10,angle=30), [[4.2264973081,0],[4.2264973081,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5]]); - assert_approx(mask2d_teardrop(r=10,angle=30,excess=1), [[4.2264973081,0],[4.2264973081,-1],[-1,-1],[-1,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5]]); -} -test_mask2d_teardrop(); - - -module test_mask2d_ogee() { - $fn=24; - assert_approx( - mask2d_ogee([ - "xstep",1, "ystep",1, // Starting shoulder. - "fillet",5, "round",5, // S-curve. - "ystep",1, "xstep",1 // Ending shoulder. - ]), - [[12,-0.01],[-0.01,-0.01],[-0.01,12],[1,12],[1,11],[1.32701564615,10.9892946162],[1.6526309611,10.9572243069],[1.97545161008,10.903926402],[2.29409522551,10.8296291314],[2.60719732652,10.7346506475],[2.91341716183,10.6193976626],[3.2114434511,10.4843637077],[3.5,10.3301270189],[3.7778511651,10.1573480615],[4.04380714504,9.96676670146],[4.2967290755,9.75919903739],[4.53553390593,9.53553390593],[4.75919903739,9.2967290755],[4.96676670146,9.04380714504],[5.15734806151,8.7778511651],[5.33012701892,8.5],[5.48436370766,8.2114434511],[5.61939766256,7.91341716183],[5.73465064748,7.60719732652],[5.82962913145,7.29409522551],[5.90392640202,6.97545161008],[5.95722430687,6.6526309611],[5.98929461619,6.32701564615],[6,6],[6.01070538381,5.67298435385],[6.04277569313,5.3473690389],[6.09607359798,5.02454838992],[6.17037086855,4.70590477449],[6.26534935252,4.39280267348],[6.38060233744,4.08658283817],[6.51563629234,3.7885565489],[6.66987298108,3.5],[6.84265193849,3.2221488349],[7.03323329854,2.95619285496],[7.24080096261,2.7032709245],[7.46446609407,2.46446609407],[7.7032709245,2.24080096261],[7.95619285496,2.03323329854],[8.2221488349,1.84265193849],[8.5,1.66987298108],[8.7885565489,1.51563629234],[9.08658283817,1.38060233744],[9.39280267348,1.26534935252],[9.70590477449,1.17037086855],[10.0245483899,1.09607359798],[10.3473690389,1.04277569313],[10.6729843538,1.01070538381],[11,1],[11,0],[12,0]] - ); -} -test_mask2d_ogee(); - // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap