diff --git a/attachments.scad b/attachments.scad index f3dde57..7991ff2 100644 --- a/attachments.scad +++ b/attachments.scad @@ -29,6 +29,8 @@ $parent_geom = undef; $tags_shown = []; $tags_hidden = []; +_ANCHOR_TYPES = ["intersect","hull"]; + // Section: Anchors, Spin, and Orientation // This library adds the concept of anchoring, spin and orientation to the `cube()`, `cylinder()` @@ -1885,7 +1887,7 @@ module show_anchors(s=10, std=true, custom=true) { anchor_arrow(s, color="cyan"); } color("black") -// tags("anchor-arrow") + tags("anchor-arrow") { xrot(two_d? 0 : 90) { back(s/3) { diff --git a/shapes2d.scad b/shapes2d.scad index cfd6c18..08fc969 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -244,15 +244,20 @@ module circle(r, d, anchor=CENTER, spin=0) { // ellipse(d=50, anchor=FRONT, spin=45); // Example(NORENDER): Called as Function // path = ellipse(d=50, anchor=FRONT, spin=45); -module ellipse(r, d, realign=false, circum=false, anchor=CENTER, spin=0) { - r = get_radius(r=r, d=d, dflt=1); - dummy = assert((is_finite(r) || is_vector(r,2)) && all_positive(r), "Invalid radius or diameter for ellipse"); +module ellipse(r, d, realign=false, circum=false, uniform=false, anchor=CENTER, spin=0) +{ + r = force_list(get_radius(r=r, d=d, dflt=1),2); + dummy = assert(is_vector(r,2) && all_positive(r), "Invalid radius or diameter for ellipse"); sides = segs(max(r)); sc = circum? (1 / cos(180/sides)) : 1; - rx = default(r[0],r) * sc; - ry = default(r[1],r) * sc; + rx = r.x * sc; + ry = r.y * sc; attachable(anchor,spin, two_d=true, r=[rx,ry]) { - if (rx < ry) { + if (uniform) { + assert(!circum, "Circum option not allowed when \"uniform\" is true"); + polygon(ellipse(r,realign=realign, circum=circum, uniform=true)); + } + else if (rx < ry) { xscale(rx/ry) { zrot(realign? 180/sides : 0) { circle(r=ry, $fn=sides); @@ -270,14 +275,42 @@ module ellipse(r, d, realign=false, circum=false, anchor=CENTER, spin=0) { } -function ellipse(r, d, realign=false, circum=false, anchor=CENTER, spin=0) = +// Iterative refinement to produce an inscribed polygon +// in an ellipse whose side lengths are all equal +function _ellipse_refine(a,b,N, _theta=[]) = + len(_theta)==0? _ellipse_refine(a,b,N,lerpn(0,360,N,endpoint=false)) + : + let( + pts = [for(t=_theta) [a*cos(t),b*sin(t)]], + lenlist= path_segment_lengths(pts,closed=true), + meanlen = mean(lenlist), + error = lenlist/meanlen + ) + all_equal(error,EPSILON) ? pts + : + let( + dtheta = [each deltas(_theta), + 360-last(_theta)], + newdtheta = [for(i=idx(dtheta)) dtheta[i]/error[i]], + adjusted = [0,each cumsum(list_head(newdtheta / sum(newdtheta) * 360))] + ) + _ellipse_refine(a,b,N,adjusted); + + + +function ellipse(r, d, realign=false, circum=false, uniform=false, anchor=CENTER, spin=0) = + let( + r = force_list(get_radius(r=r, d=d, dflt=1),2), + sides = segs(max(r)) + ) + uniform ? assert(!circum, "Circum option not allowed when \"uniform\" is true") + reorient(anchor,spin,two_d=true,r=[r.x,r.y],p=_ellipse_refine(r.x,r.y,sides)) + : let( - r = get_radius(r=r, d=d, dflt=1), - sides = segs(max(r)), offset = realign? 180/sides : 0, sc = circum? (1 / cos(180/sides)) : 1, - rx = default(r[0],r) * sc, - ry = default(r[1],r) * sc, + rx = r.x * sc, + ry = r.y * sc, pts = [for (i=[0:1:sides-1]) let(a=360-offset-i*360/sides) [rx*cos(a), ry*sin(a)]] ) reorient(anchor,spin, two_d=true, r=[rx,ry], p=pts); @@ -780,6 +813,7 @@ module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER // align_pit = If given as a 2D vector, rotates the whole shape so that the first inner corner is pointed towards that direction. This occurs before spin. // 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` +// atype = Choose "hull" or "intersect" anchor methods. Default: "hull" // Extra Anchors: // "tip0" ... "tip4" = Each tip has an anchor, pointing outwards. // "pit0" ... "pit4" = The inside corner between each tip has an anchor, pointing outwards. @@ -801,7 +835,8 @@ module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER // stroke([[0,0],[0,7]], endcap2="arrow2"); // Example(2D): Called as Function // stroke(closed=true, star(n=5, r=50, ir=25)); -function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, _mat, _anchs) = +function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, atype="hull", _mat, _anchs) = + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") assert(is_undef(align_tip) || is_vector(align_tip)) assert(is_undef(align_pit) || is_vector(align_pit)) assert(is_undef(align_tip) || is_undef(align_pit), "Can only specify one of align_tip and align_pit") @@ -843,10 +878,11 @@ function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit named_anchor(str("midpt",i), pos, unit(pos,BACK), 0), ] ] - ) reorient(anchor,spin, two_d=true, path=path, p=path, anchors=anchors); + ) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull", anchors=anchors); -module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0) { +module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, atype="hull") { + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); assert(is_undef(align_tip) || is_vector(align_tip)); assert(is_undef(align_pit) || is_vector(align_pit)); assert(is_undef(align_tip) || is_undef(align_pit), "Can only specify one of align_tip and align_pit"); @@ -874,7 +910,7 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, ] ]; path = star(n=n, r=r, ir=ir, realign=realign, _mat=mat, _anchs=anchors); - attachable(anchor,spin, two_d=true, path=path, anchors=anchors) { + attachable(anchor,spin, two_d=true, path=path, extent=atype=="hull", anchors=anchors) { polygon(path); children(); } @@ -948,7 +984,7 @@ module jittered_poly(path, dist=1/512) { // Function&Module: teardrop2d() // // Description: -// Makes a 2D teardrop shape. Useful for extruding into 3D printable holes. +// Makes a 2D teardrop shape. Useful for extruding into 3D printable holes. Uses "intersect" style anchoring. // // Usage: As Module // teardrop2d(r/d=, [ang], [cap_h]); @@ -979,7 +1015,7 @@ module jittered_poly(path, dist=1/512) { module teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0) { path = teardrop2d(r=r, d=d, ang=ang, cap_h=cap_h); - attachable(anchor,spin, two_d=true, path=path) { + attachable(anchor,spin, two_d=true, path=path, extent=false) { polygon(path); children(); } @@ -1008,7 +1044,7 @@ function teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0) = ), maxx_idx = max_index(column(path,0)), path2 = list_rotate(path,maxx_idx) - ) reorient(anchor,spin, two_d=true, path=path2, p=path2); + ) reorient(anchor,spin, two_d=true, path=path2, p=path2, extent=false); @@ -1023,7 +1059,7 @@ function teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0) = // See Also: circle(), ellipse() // Description: // When called as a function, returns a 2D path forming a shape of two circles joined by curved waist. -// When called as a module, creates a 2D shape of two circles joined by curved waist. +// When called as a module, creates a 2D shape of two circles joined by curved waist. Uses "hull" style anchoring. // Arguments: // r = The radius of the end circles. // spread = The distance between the centers of the end circles. Default: 10 @@ -1094,6 +1130,8 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // Description: // When called as a function, returns a 2D path for the outline of the [Superformula](https://en.wikipedia.org/wiki/Superformula) shape. // When called as a module, creates a 2D [Superformula](https://en.wikipedia.org/wiki/Superformula) shape. +// Note that the "hull" type anchoring (the default) is more intuitive for concave star-like shapes, but the anchor points do not +// necesarily lie on the line of the anchor vector, which can be confusing, especially for simpler, ellipse-like shapes. // Arguments: // step = The angle step size for sampling the superformula shape. Smaller steps are slower but more accurate. // m1 = The m1 argument for the superformula. Default: 4. @@ -1108,6 +1146,7 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // d = Diameter of the shape. Scale shape to fit in a circle of diameter d. // 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` +// atype = Select "hull" or "intersect" style anchoring. Default: "hull". // Example(2D): // supershape(step=0.5,m1=16,m2=16,n1=0.5,n2=0.5,n3=16,r=50); // Example(2D): Called as Function @@ -1133,8 +1172,9 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // Examples: // linear_extrude(height=0.3, scale=0) supershape(step=1, m1=6, n1=0.4, n2=0, n3=6); // linear_extrude(height=5, scale=0) supershape(step=1, b=3, m1=6, n1=3.8, n2=16, n3=10); -function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0) = +function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0, atype="hull") = let( + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""), r = get_radius(r=r, d=d, dflt=undef), m2 = is_def(m2) ? m2 : m1, n2 = is_def(n2) ? n2 : n1, @@ -1146,11 +1186,11 @@ function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER rads = [for (theta = angs) _superformula(theta=theta,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b)], scale = is_def(r) ? r/max(rads) : 1, path = [for (i = [steps:-1:1]) let(a=angs[i]) scale*rads[i]*[cos(a), sin(a)]] - ) reorient(anchor,spin, two_d=true, path=path, p=path); + ) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull"); module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=undef, d=undef, anchor=CENTER, spin=0) { path = supershape(step=step,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b,r=r,d=d); - attachable(anchor,spin, two_d=true, path=path) { + attachable(anchor,spin,extent=atype=="hull", two_d=true, path=path) { polygon(path); children(); } @@ -1165,7 +1205,7 @@ module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=und // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable // See Also: regular_ngon(), pentagon(), hexagon(), octagon() // Description: -// Creates a 2D Reuleaux Polygon; a constant width shape that is not circular. +// Creates a 2D Reuleaux Polygon; a constant width shape that is not circular. Uses "intersect" type anchoring. // Arguments: // N = Number of "sides" to the Reuleaux Polygon. Must be an odd positive number. Default: 3 // r = Radius of the shape. Scale shape to fit in a circle of radius r. @@ -1192,7 +1232,7 @@ module reuleaux_polygon(N=3, r, d, anchor=CENTER, spin=0) { cp = polar_to_xy(r, ca) ) named_anchor(str("tip",i), cp, unit(cp,BACK), 0), ]; - attachable(anchor,spin, two_d=true, path=path, anchors=anchors) { + attachable(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors) { polygon(path); children(); } @@ -1219,7 +1259,7 @@ function reuleaux_polygon(N=3, r, d, anchor=CENTER, spin=0) = cp = polar_to_xy(r, ca) ) named_anchor(str("tip",i), cp, unit(cp,BACK), 0), ] - ) reorient(anchor,spin, two_d=true, path=path, anchors=anchors, p=path); + ) reorient(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors, p=path); // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/skin.scad b/skin.scad index 61e74a2..c7c8996 100644 --- a/skin.scad +++ b/skin.scad @@ -150,11 +150,11 @@ // method = method for connecting profiles, one of "distance", "fast_distance", "tangent", "direct" or "reindex". Default: "direct". // z = array of height values for each profile if the profiles are 2d // convexity = convexity setting for use with polyhedron. (module only) Default: 10 -// anchor = Translate so anchor point is at the origin. (module only) Default: "origin" -// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0 -// orient = Vector to rotate top towards after spin (module only) -// extent = use extent method for computing anchors. (module only) Default: false -// cp = set centerpoint for anchor computation. (module only) Default: object centroid +// anchor = Translate so anchor point is at the origin. Default: "origin" +// spin = Rotate this many degrees around Z axis after anchor. Default: 0 +// orient = Vector to rotate top towards after spin +// atype = Select "hull" or "intersect anchor types. Default: "hull" +// cp = set centerpoint for anchor computation. Default: "centroid" // style = vnf_vertex_array style. Default: "min_edge" // Example: // skin([octagon(4), circle($fn=70,r=2)], z=[0,3], slices=10); @@ -383,18 +383,17 @@ // } // } module skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge", convexity=10, - anchor="origin",cp,spin=0, orient=UP, extent=false) + anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") { vnf = skin(profiles, slices, refine, method, sampling, caps, closed, z, style=style); - attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf)) - { - vnf_polyhedron(vnf,convexity=convexity); + vnf_polyhedron(vnf,convexity=convexity,spin=spin,anchor=anchor,orient=orient,atype=atype,cp=cp) children(); - } } -function skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge") = +function skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge", + anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") = + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") assert(is_def(slices),"The slices argument must be specified.") assert(is_list(profiles) && len(profiles)>1, "Must provide at least two profiles") let( @@ -490,13 +489,13 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close ". Method ",method[i]," requires equal values")) refine[i] * len(pair[0]) ) - subdivide_and_slice(pair,slices[i], nsamples, method=sampling)] + subdivide_and_slice(pair,slices[i], nsamples, method=sampling)], + vnf=vnf_join( + [for(i=idx(full_list)) + vnf_vertex_array(full_list[i], cap1=i==0 && fullcaps[0], cap2=i==len(full_list)-1 && fullcaps[1], + col_wrap=true, style=style)]) ) - vnf_join( - [for(i=idx(full_list)) - vnf_vertex_array(full_list[i], cap1=i==0 && fullcaps[0], cap2=i==len(full_list)-1 && fullcaps[1], - col_wrap=true, style=style)]); - + reorient(anchor,spin,orient,vnf=vnf,p=vnf,extent=atype=="hull",cp=cp); // Function&Module: path_sweep() @@ -566,13 +565,12 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // caps = Can be a boolean or vector of two booleans. Set to false to disable caps at the two ends. Default: true // style = vnf_vertex_array style. Default: "min_edge" // transforms = set to true to return transforms instead of a VNF. These transforms can be manipulated and passed to sweep(). Default: false. -// convexity = convexity parameter for polyhedron(). Only accepted by the module version. Default: 10 -// anchor = Translate so anchor point is at the origin. (module only) Default: "origin" -// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0 -// orient = Vector to rotate top towards after spin (module only) -// extent = use extent method for computing anchors. (module only) Default: false -// cp = set centerpoint for anchor computation. (module only) Default: object centroid -// +// convexity = convexity parameter for polyhedron(). (module only) Default: 10 +// anchor = Translate so anchor point is at the origin. Default: "origin" +// spin = Rotate this many degrees around Z axis after anchor. Default: 0 +// orient = Vector to rotate top towards after spin +// atype = Select "hull" or "intersect" anchor types. Default: "hull" +// cp = set centerpoint for anchor computation. Default: "centroid" // Example(2D): We'll use this shape in several examples // ushape = [[-10, 0],[-10, 10],[ -7, 10],[ -7, 2],[ 7, 2],[ 7, 7],[ 10, 7],[ 10, 0]]; // polygon(ushape); @@ -812,20 +810,19 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // } module path_sweep(shape, path, method="incremental", normal, closed=false, twist=0, twist_by_length=true, symmetry=1, last_normal, tangent, relaxed=false, caps, style="min_edge", convexity=10, - anchor="origin",cp,spin=0, orient=UP, extent=false) + anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") { vnf = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, symmetry, last_normal, tangent, relaxed, caps, style); - attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf)) - { - vnf_polyhedron(vnf,convexity=convexity); + vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, vnf=vnf, atype=atype, cp=cp) children(); - } } function path_sweep(shape, path, method="incremental", normal, closed=false, twist=0, twist_by_length=true, - symmetry=1, last_normal, tangent, relaxed=false, caps, style="min_edge", transforms=false) = + symmetry=1, last_normal, tangent, relaxed=false, caps, style="min_edge", transforms=false, + anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") = + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") assert(!closed || twist % (360/symmetry)==0, str("For a closed sweep, twist must be a multiple of 360/symmetry = ",360/symmetry)) assert(closed || symmetry==1, "symmetry must be 1 when closed is false") assert(is_integer(symmetry) && symmetry>0, "symmetry must be a positive integer") @@ -919,7 +916,9 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi apply(transform_list[L], rshape)), dummy = ends_match ? 0 : echo("WARNING: ***** The points do not match when closing the model *****") ) - transforms ? transform_list : sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=fullcaps,style=style); + transforms ? transform_list + : sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=fullcaps,style=style, + anchor=anchor,cp=cp,spin=spin,orient=orient,atype=atype); // Function&Module: path_sweep2d() @@ -946,11 +945,11 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi // style = vnf_vertex_array style. Default: "min_edge" // --- // convexity = convexity parameter for polyhedron (module only) Default: 10 -// anchor = Translate so anchor point is at the origin. (module only) Default: "origin" -// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0 -// orient = Vector to rotate top towards after spin (module only) -// extent = use extent method for computing anchors. (module only) Default: false -// cp = set centerpoint for anchor computation. (module only) Default: object centroid +// anchor = Translate so anchor point is at the origin. Default: "origin" +// spin = Rotate this many degrees around Z axis after anchor. Default: 0 +// orient = Vector to rotate top towards after spin +// atype = Select "hull" or "intersect" anchor types. Default: "hull" +// cp = set centerpoint for anchor computation. Default: "centroid" // Example: Sine wave example with self-intersections at each peak. This would fail with path_sweep(). // sinewave = [for(i=[-30:10:360*2+30]) [i/40,3*sin(i)]]; // path_sweep2d(circle(r=3,$fn=15), sinewave); @@ -967,7 +966,8 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi // path_sweep2d(circle(r=3.25, $fn=32), select(ellipse,floor(L*.2),ceil(L*.8)),closed=false); // path_sweep2d(circle(r=3.25, $fn=32), select(ellipse,floor(L*.7),ceil(L*.3)),closed=false); -function path_sweep2d(shape, path, closed=false, caps, quality=1, style="min_edge") = +function path_sweep2d(shape, path, closed=false, caps, quality=1, style="min_edge", + anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") = let( caps = is_def(caps) ? caps : closed ? false : true, @@ -992,23 +992,21 @@ function path_sweep2d(shape, path, closed=false, caps, quality=1, style="min_edg ) select(path3d(ofs[0],pt.y),map) ] - ) + ), + vnf = vnf_vertex_array([ + each proflist, + if (closed) proflist[0] + ],cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style) ) - vnf_vertex_array([ - each proflist, - if (closed) proflist[0] - ],cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style); + reorient(anchor,spin,orient,vnf=vnf,p=vnf,extent=atype=="hull",cp=cp); module path_sweep2d(profile, path, closed=false, caps, quality=1, style="min_edge", convexity=10, - anchor="origin", cp, spin=0, orient=UP, extent=false) + anchor="origin", cp="centroid", spin=0, orient=UP, atype="hull") { vnf = path_sweep2d(profile, path, closed, caps, quality, style); - attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf)) - { - vnf_polyhedron(vnf,convexity=convexity); + vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, vnf=vnf, atype=atype, cp=cp) children(); - } } // Extract vertex mapping from offset face list. The output of this function @@ -1071,11 +1069,11 @@ function _ofs_face_edge(face,firstlen,second=false) = // style = vnf_vertex_array style. Default: "min_edge" // --- // convexity = convexity setting for use with polyhedron. (module only) Default: 10 -// anchor = Translate so anchor point is at the origin. (module only) Default: "origin" -// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0 +// anchor = Translate so anchor point is at the origin. Default: "origin" +// spin = Rotate this many degrees around Z axis after anchor. Default: 0 // orient = Vector to rotate top towards after spin (module only) -// extent = use extent method for computing anchors. (module only) Default: false -// cp = set centerpoint for anchor computation. (module only) Default: object centroid +// atype = Select "hull" or "intersect" anchor types. Default: "hull" +// cp = set centerpoint for anchor computation. Default: "centroid" // Example: This is the "sweep-drop" example from list-comprehension-demos. // function drop(t) = 100 * 0.5 * (1 - cos(180 * t)) * sin(180 * t) + 1; // function path(t) = [0, 0, 80 + 80 * cos(180 * t)]; @@ -1097,7 +1095,8 @@ function _ofs_face_edge(face,firstlen,second=false) = // inside = [for(i=[24:-1:2]) up(i)*rot(i)*scale(1.2*i/24+1)]; // sweep(shape, concat(outside,inside)); -function sweep(shape, transforms, closed=false, caps, style="min_edge") = +function sweep(shape, transforms, closed=false, caps, style="min_edge", + anchor="origin", cp="centroid", spin=0, orient=UP, atype="hull") = assert(is_consistent(transforms, ident(4)), "Input transforms must be a list of numeric 4x4 matrices in sweep") assert(is_path(shape,2) || is_region(shape), "Input shape must be a 2d path or a region.") let( @@ -1128,15 +1127,13 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge") = module sweep(shape, transforms, closed=false, caps, style="min_edge", convexity=10, - anchor="origin",cp,spin=0, orient=UP, extent=false) + anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") { vnf = sweep(shape, transforms, closed, caps, style); - attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf)) - { - vnf_polyhedron(vnf,convexity=convexity); - children(); - } -} + vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, vnf=vnf, atype=atype, cp=cp) + children(); +} + // Section: Functions for resampling and slicing profile lists diff --git a/vnf.scad b/vnf.scad index 5e44e2d..815534f 100644 --- a/vnf.scad +++ b/vnf.scad @@ -787,10 +787,11 @@ function _slice_3dpolygons(polys, dir, cuts) = // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -module vnf_polyhedron(vnf, convexity=2, extent=true, cp=[0,0,0], anchor="origin", spin=0, orient=UP) { +// atype = Select "hull" or "intersect" anchor type. Default: "hull" +module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") { vnf = is_vnf_list(vnf)? vnf_join(vnf) : vnf; - cp = is_def(cp) ? cp : centroid(vnf); - attachable(anchor,spin,orient, vnf=vnf, extent=extent, cp=cp) { + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); + attachable(anchor,spin,orient, vnf=vnf, extent=atype=="hull", cp=cp) { polyhedron(vnf[0], vnf[1], convexity=convexity); children(); }