From 6b20232f9954318f8a023a0835fe453d77af50cd Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 15 Nov 2021 23:55:57 -0500 Subject: [PATCH 1/9] remove nil and noop --- attachments.scad | 16 +++++++++------- shapes3d.scad | 21 --------------------- threading.scad | 1 - 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/attachments.scad b/attachments.scad index c95c460..f3dde57 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1885,7 +1885,8 @@ module show_anchors(s=10, std=true, custom=true) { anchor_arrow(s, color="cyan"); } color("black") - noop($tags="anchor-arrow") { +// tags("anchor-arrow") + { xrot(two_d? 0 : 90) { back(s/3) { yrot_copies(n=2) @@ -1897,13 +1898,14 @@ module show_anchors(s=10, std=true, custom=true) { } } } - color([1, 1, 1, 0.4]) - noop($tags="anchor-arrow") { + color([1, 1, 1, 1]) + tags("anchor-arrow") + { xrot(two_d? 0 : 90) { back(s/3) { - zcopies(s/21) cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true); + cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true); } - } + } } } } @@ -1953,7 +1955,7 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow" // Example: // anchor_arrow2d(s=20); module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tags="anchor-arrow") { - noop() color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2"); + color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2"); } @@ -1971,7 +1973,7 @@ module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tags="anchor-arrow") { // expose_anchors() cube(50, center=true) show_anchors(); module expose_anchors(opacity=0.2) { show("anchor-arrow") - children(); + children(); hide("anchor-arrow") color(is_undef($color)? [0,0,0] : is_string($color)? $color : diff --git a/shapes3d.scad b/shapes3d.scad index 422b3f8..9c5b5c1 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -2304,27 +2304,6 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers // Section: Miscellaneous -// Module: nil() -// -// Description: -// Useful when you MUST pass a child to a module, but you want it to be nothing. -module nil() union(){} - - -// Module: noop() -// -// Description: -// Passes through the children passed to it, with no action at all. Useful while debugging when -// you want to replace a command. This is an attachable non-object. -// -// Arguments: -// 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 noop(spin=0, orient=UP) attachable(CENTER,spin,orient, d=0.01) {nil(); children();} - - - - // Module: interior_fillet() // // Description: diff --git a/threading.scad b/threading.scad index ff40aba..61cbbf5 100644 --- a/threading.scad +++ b/threading.scad @@ -535,7 +535,6 @@ module npt_threaded_rod( higbee=r1*PI/2 ); if (hollow) cylinder(l=l+1, d=size*INCH, center=true); - else nil(); } children(); } From 7dfb99c5bb83403af9b0f9a64813a36c491adb0c Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 16 Nov 2021 06:15:56 -0500 Subject: [PATCH 2/9] fix test --- tests/test_transforms.scad | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/test_transforms.scad b/tests/test_transforms.scad index ec338a1..6a4d14b 100644 --- a/tests/test_transforms.scad +++ b/tests/test_transforms.scad @@ -8,7 +8,7 @@ module test_translate() { assert_equal(translate(val, p=[1,2,3]), [1,2,3]+val); } // Verify that module at least doesn't crash. - translate([-5,-5,-5]) translate([0,0,0]) translate([5,5,5]) nil(); + translate([-5,-5,-5]) translate([0,0,0]) translate([5,5,5]) union(){}; } test_translate(); @@ -21,8 +21,8 @@ module test_move() { assert_equal(move(x=val.x, y=val.y, z=val.z, p=[1,2,3]), [1,2,3]+val); } // Verify that module at least doesn't crash. - move(x=-5) move(y=-5) move(z=-5) move([-5,-5,-5]) nil(); - move(x=5) move(y=5) move(z=5) move([5,5,5]) nil(); + move(x=-5) move(y=-5) move(z=-5) move([-5,-5,-5]) union(){}; + move(x=5) move(y=5) move(z=5) move([5,5,5]) union(){}; } test_move(); @@ -35,7 +35,7 @@ module test_left() { assert_equal(left(0,p=[1,2,3]),[1,2,3]); assert_equal(left(-5,p=[1,2,3]),[6,2,3]); // Verify that module at least doesn't crash. - left(-5) left(0) left(5) nil(); + left(-5) left(0) left(5) union(){}; } test_left(); @@ -48,7 +48,7 @@ module test_right() { assert_equal(right(0,p=[1,2,3]),[1,2,3]); assert_equal(right(5,p=[1,2,3]),[6,2,3]); // Verify that module at least doesn't crash. - right(-5) right(0) right(5) nil(); + right(-5) right(0) right(5) union(){}; } test_right(); @@ -61,7 +61,7 @@ module test_back() { assert_equal(back(0,p=[1,2,3]),[1,2,3]); assert_equal(back(5,p=[1,2,3]),[1,7,3]); // Verify that module at least doesn't crash. - back(-5) back(0) back(5) nil(); + back(-5) back(0) back(5) union(){}; } test_back(); @@ -74,7 +74,7 @@ module test_fwd() { assert_equal(fwd(0,p=[1,2,3]),[1,2,3]); assert_equal(fwd(-5,p=[1,2,3]),[1,7,3]); // Verify that module at least doesn't crash. - fwd(-5) fwd(0) fwd(5) nil(); + fwd(-5) fwd(0) fwd(5) union(){}; } test_fwd(); @@ -87,7 +87,7 @@ module test_down() { assert_equal(down(0,p=[1,2,3]),[1,2,3]); assert_equal(down(-5,p=[1,2,3]),[1,2,8]); // Verify that module at least doesn't crash. - down(-5) down(0) down(5) nil(); + down(-5) down(0) down(5) union(){}; } test_down(); @@ -100,7 +100,7 @@ module test_up() { assert_equal(up(0,p=[1,2,3]),[1,2,3]); assert_equal(up(5,p=[1,2,3]),[1,2,8]); // Verify that module at least doesn't crash. - up(-5) up(0) up(5) nil(); + up(-5) up(0) up(5) union(){}; } test_up(); @@ -112,7 +112,7 @@ module test_scale() { assert_equal(scale(point2d(val)), [[val.x,0,0],[0,val.y,0],[0,0,1]]); assert_equal(scale(val), [[val.x,0,0,0],[0,val.y,0,0],[0,0,val.z,0],[0,0,0,1]]); assert_equal(scale(val, p=[1,2,3]), v_mul([1,2,3], val)); - scale(val) nil(); + scale(val) union(){}; } assert_equal(scale(3), [[3,0,0,0],[0,3,0,0],[0,0,3,0],[0,0,0,1]]); assert_equal(scale(3, p=[1,2,3]), 3*[1,2,3]); @@ -124,7 +124,7 @@ module test_scale() { assert_equal(scale([2,3,4], p=cb), cube([2,3,4])); assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) v_mul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]); // Verify that module at least doesn't crash. - scale(-5) scale(5) nil(); + scale(-5) scale(5) union(){}; } test_scale(); @@ -134,10 +134,10 @@ module test_xscale() { for (val=vals) { assert_equal(xscale(val), [[val,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); assert_equal(xscale(val, p=[1,2,3]), [val*1,2,3]); - xscale(val) nil(); + xscale(val) union(){}; } // Verify that module at least doesn't crash. - xscale(-5) xscale(5) nil(); + xscale(-5) xscale(5) union(){}; } test_xscale(); @@ -147,10 +147,10 @@ module test_yscale() { for (val=vals) { assert_equal(yscale(val), [[1,0,0,0],[0,val,0,0],[0,0,1,0],[0,0,0,1]]); assert_equal(yscale(val, p=[1,2,3]), [1,val*2,3]); - yscale(val) nil(); + yscale(val) union(){}; } // Verify that module at least doesn't crash. - yscale(-5) yscale(5) nil(); + yscale(-5) yscale(5) union(){}; } test_yscale(); @@ -160,10 +160,10 @@ module test_zscale() { for (val=vals) { assert_equal(zscale(val), [[1,0,0,0],[0,1,0,0],[0,0,val,0],[0,0,0,1]]); assert_equal(zscale(val, p=[1,2,3]), [1,2,val*3]); - zscale(val) nil(); + zscale(val) union(){}; } // Verify that module at least doesn't crash. - zscale(-5) zscale(5) nil(); + zscale(-5) zscale(5) union(){}; } test_zscale(); @@ -184,7 +184,7 @@ module test_mirror() { assert_approx(mirror(val), m, str("mirror(",val,")")); assert_approx(mirror(val, p=[1,2,3]), apply(m, [1,2,3]), str("mirror(",val,",p=...)")); // Verify that module at least doesn't crash. - mirror(val) nil(); + mirror(val) union(){}; } } test_mirror(); @@ -194,7 +194,7 @@ module test_xflip() { assert_approx(xflip(), [[-1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]); assert_approx(xflip(p=[1,2,3]), [-1,2,3]); // Verify that module at least doesn't crash. - xflip() nil(); + xflip() union(){}; } test_xflip(); @@ -203,7 +203,7 @@ module test_yflip() { assert_approx(yflip(), [[1,0,0,0],[0,-1,0,0],[0,0,1,0],[0,0,0,1]]); assert_approx(yflip(p=[1,2,3]), [1,-2,3]); // Verify that module at least doesn't crash. - yflip() nil(); + yflip() union(){}; } test_yflip(); @@ -212,7 +212,7 @@ module test_zflip() { assert_approx(zflip(), [[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]]); assert_approx(zflip(p=[1,2,3]), [1,2,-3]); // Verify that module at least doesn't crash. - zflip() nil(); + zflip() union(){}; } test_zflip(); @@ -341,7 +341,7 @@ module test_xrot() { assert_approx(xrot(a, p=path[0]), apply(m, path[0])); assert_approx(xrot(a, p=path), apply(m, path)); // Verify that module at least doesn't crash. - xrot(a) nil(); + xrot(a) union(){}; } } test_xrot(); @@ -356,7 +356,7 @@ module test_yrot() { assert_approx(yrot(a, p=path[0]), apply(m, path[0])); assert_approx(yrot(a, p=path), apply(m, path)); // Verify that module at least doesn't crash. - yrot(a) nil(); + yrot(a) union(){}; } } test_yrot(); @@ -371,7 +371,7 @@ module test_zrot() { assert_approx(zrot(a, p=path[0]), apply(m, path[0])); assert_approx(zrot(a, p=path), apply(m, path)); // Verify that module at least doesn't crash. - zrot(a) nil(); + zrot(a) union(){}; } } test_zrot(); @@ -390,7 +390,7 @@ module test_skew() { assert_approx(skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7), m); assert_approx(skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7, p=[1,2,3]), apply(m,[1,2,3])); // Verify that module at least doesn't crash. - skew(undef,2,3,4,5,6,7) nil(); + skew(undef,2,3,4,5,6,7) union(){}; } test_skew(); From d576526b60a72a3fac8e9e895e3b4ee2653ab249 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 16 Nov 2021 18:49:37 -0500 Subject: [PATCH 3/9] VNF interface update skin.scad and shapes2d.scad --- attachments.scad | 4 +- shapes2d.scad | 90 ++++++++++++++++++++++++++----------- skin.scad | 115 +++++++++++++++++++++++------------------------ vnf.scad | 7 +-- 4 files changed, 128 insertions(+), 88 deletions(-) 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(); } From 45e64f309c08c0f100d112a62f037afb317239ff Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 16 Nov 2021 20:33:51 -0500 Subject: [PATCH 4/9] bug fixes --- shapes2d.scad | 3 ++- skin.scad | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index 08fc969..977e942 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1173,8 +1173,9 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // 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, atype="hull") = + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") 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, diff --git a/skin.scad b/skin.scad index c7c8996..585e781 100644 --- a/skin.scad +++ b/skin.scad @@ -814,7 +814,7 @@ module path_sweep(shape, path, method="incremental", normal, closed=false, twist { vnf = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, symmetry, last_normal, tangent, relaxed, caps, style); - vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, vnf=vnf, atype=atype, cp=cp) + vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp) children(); } @@ -1005,7 +1005,7 @@ module path_sweep2d(profile, path, closed=false, caps, quality=1, style="min_edg anchor="origin", cp="centroid", spin=0, orient=UP, atype="hull") { vnf = path_sweep2d(profile, path, closed, caps, quality, style); - vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, vnf=vnf, atype=atype, cp=cp) + vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp) children(); } From 47bf93e6cb3ebfbc744848334732a9e64dcf3f34 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 16 Nov 2021 20:46:37 -0500 Subject: [PATCH 5/9] more anchoring fixes --- drawing.scad | 4 +++- regions.scad | 6 ++++-- skin.scad | 18 +++++++++--------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drawing.scad b/drawing.scad index 85b686e..dea89a6 100644 --- a/drawing.scad +++ b/drawing.scad @@ -598,6 +598,8 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) { // start = Start angle of arc. // wedge = If true, include centerpoint `cp` in output to form pie slice shape. // endpoint = If false exclude the last point (function only). Default: true +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). (Module only) Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). (Module only) Default: `0` // Examples(2D): // arc(N=4, r=30, angle=30, wedge=true); // arc(r=30, angle=30, wedge=true); @@ -703,7 +705,7 @@ function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, l module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, anchor=CENTER, spin=0) { path = arc(N=N, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge); - attachable(anchor,spin, two_d=true, path=path, extent=true) { + attachable(anchor,spin, two_d=true, path=path, extent=false) { polygon(path); children(); } diff --git a/regions.scad b/regions.scad index d5a2b28..1e453f5 100644 --- a/regions.scad +++ b/regions.scad @@ -288,6 +288,7 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"` // spin = Rotate this many degrees after anchor. See [spin](attachments.scad#spin). Default: `0` // cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 2D point. Default: "centroid" +// atype = Set to "hull" or "intersect to select anchor type. Default: "hull" // Example(2D): Displaying a region // region([circle(d=50), square(25,center=true)]); // Example(2D): Displaying a list of polygons that intersect each other, which is not a region @@ -298,13 +299,14 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // region(rgn); module region(r, anchor="origin", spin=0, cp="centroid") { + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); r = force_region(r); dummy=assert(is_region(r), "Input is not a region"); points = flatten(r); lengths = [for(path=r) len(path)]; starts = [0,each cumsum(lengths)]; paths = [for(i=idx(r)) count(s=starts[i], n=lengths[i])]; - attachable(anchor, spin, two_d=true, region=r, extent=false, cp=cp){ + attachable(anchor, spin, two_d=true, region=r, extent=atype=="hull", cp=cp){ polygon(points=points, paths=paths); children(); } @@ -646,7 +648,7 @@ module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, slices=slices, maxseg=maxseg, style=style ); - attachable(anchor,spin,orient, cp=cp, vnf=vnf, extent=!anchor_isect, anchors=anchors) { + attachable(anchor,spin,orient, cp=cp, region=region, h=height, extent=atype=="hull", anchors=anchors) { vnf_polyhedron(vnf, convexity=convexity); children(); } diff --git a/skin.scad b/skin.scad index 585e781..3880f08 100644 --- a/skin.scad +++ b/skin.scad @@ -16,9 +16,9 @@ // Function&Module: skin() // Usage: As module: -// skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [convexity=], [anchor=],[cp=],[spin=],[orient=],[extent=]) [attachments]; +// skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [convexity=], [anchor=],[cp=],[spin=],[orient=],[atype=]) {attachments}; // Usage: As function: -// vnf = skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=]); +// vnf = skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [anchor=],[cp=],[spin=],[orient=],[atype=]); // Description: // Given a list of two or more path `profiles` in 3d space, produces faces to skin a surface between // the profiles. Optionally the first and last profiles can have endcaps, or the first and last profiles @@ -500,9 +500,9 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // Function&Module: path_sweep() // Usage: As module -// path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [convexity=], [transforms=], [anchor=], [cp=], [spin=], [orient=], [extent=]) [attachments]; -// Usage: As function -// vnf = path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [convexity=], [transforms=]); +// path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [convexity=], [anchor=], [cp=], [spin=], [orient=], [atype=]) {attachments}; +// Usage: As function +// vnf = path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [transforms=], [anchor=], [cp=], [spin=], [orient=], [atype=]) {attachments}; // Description: // Takes as input a 2D polygon path, and a 2d or 3d path and constructs a polyhedron by sweeping the shape along the path. // When run as a module returns the polyhedron geometry. When run as a function returns a VNF by default or if you set `transforms=true` @@ -923,9 +923,9 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi // Function&Module: path_sweep2d() // Usage: as module -// path_sweep2d(shape, path, [closed], [caps], [quality], [style], [convexity=], [anchor=], [spin=], [orient=], [extent=], [cp=]) [attachments]; +// path_sweep2d(shape, path, [closed], [caps], [quality], [style], [convexity=], [anchor=], [spin=], [orient=], [atype=], [cp=]) {attachments}; // Usage: as function -// vnf = path_sweep2d(shape, path, [closed], [caps], [quality], [style]); +// vnf = path_sweep2d(shape, path, [closed], [caps], [quality], [style], [anchor=], [spin=], [orient=], [atype=], [cp=]); // Description: // Takes an input 2D polygon (the shape) and a 2d path and constructs a polyhedron by sweeping the shape along the path. // When run as a module returns the polyhedron geometry. When run as a function returns a VNF. @@ -1044,9 +1044,9 @@ function _ofs_face_edge(face,firstlen,second=false) = // Function&Module: sweep() // Usage: As Module -// sweep(shape, transforms, [closed], [caps], [style], [convexity=], [anchor=], [spin=], [orient=], [extent=]) [attachments]; +// sweep(shape, transforms, [closed], [caps], [style], [convexity=], [anchor=], [spin=], [orient=], [atype=]) [attachments]; // Usage: As Function -// vnf = sweep(shape, transforms, [closed], [caps], [style]); +// vnf = sweep(shape, transforms, [closed], [caps], [style], [anchor=], [spin=], [orient=], [atype=]); // Description: // The input `shape` must be a non-self-intersecting 2D polygon or region, and `transforms` // is a list of 4x4 transformation matrices. The sweep algorithm applies each transformation in sequence From cd664f830c1a8c9bd8aabce0c6d6229d10099d24 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 16 Nov 2021 22:13:00 -0500 Subject: [PATCH 6/9] attachment bugfixes --- regions.scad | 2 +- shapes2d.scad | 3 ++- skin.scad | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/regions.scad b/regions.scad index 1e453f5..253d91b 100644 --- a/regions.scad +++ b/regions.scad @@ -297,7 +297,7 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // [square([60,10], center=true)] // ); // region(rgn); -module region(r, anchor="origin", spin=0, cp="centroid") +module region(r, anchor="origin", spin=0, cp="centroid", atype="hull") { assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); r = force_region(r); diff --git a/shapes2d.scad b/shapes2d.scad index 977e942..ad93b84 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1189,7 +1189,8 @@ function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER 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, 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) { +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, atype="hull") { + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); 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,extent=atype=="hull", two_d=true, path=path) { polygon(path); diff --git a/skin.scad b/skin.scad index 3880f08..cf60b33 100644 --- a/skin.scad +++ b/skin.scad @@ -1130,7 +1130,7 @@ module sweep(shape, transforms, closed=false, caps, style="min_edge", convexity= anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") { vnf = sweep(shape, transforms, closed, caps, style); - vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, vnf=vnf, atype=atype, cp=cp) + vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp) children(); } From 2bfb4e39e7af0d0445a34daae9f6d7d3eb83e461 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 16 Nov 2021 23:12:03 -0500 Subject: [PATCH 7/9] more fixes --- attachments.scad | 19 ++++++++++++------- regions.scad | 2 +- vnf.scad | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/attachments.scad b/attachments.scad index 7991ff2..057a5f4 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1518,15 +1518,20 @@ function _get_cp(geom) = let(cp=select(geom,-3)) is_vector(cp) ? cp : let( + f=echo(type=geom[0]), type = in_list(geom[0],["vnf_extent","vnf_isect"]) ? "vnf" : in_list(geom[0],["rgn_extent","rgn_isect"]) ? "path" + : in_list(geom[0],["xrgn_extent","xrgn_isect"]) ? "xpath" : "other" ) assert(type!="other", "Invalid cp value") - cp=="centroid" ? centroid(geom[1]) + cp=="centroid" ? ( + type=="vnf" && (len(geom[1][0])==0 || len(geom[1][1])==0) ? [0,0,0] : + [each centroid(geom[1]), if (type=="xpath") geom[2]/2] + ) : let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1]))) - cp=="mean" ? mean(points) - : cp=="box" ? mean(pointlist_bounds(points)) + cp=="mean" ? [each mean(points), if (type=="xpath") geom[2]/2] + : cp=="box" ?[each mean(pointlist_bounds(points)), if (type=="xpath") geom[2]/2] : assert(false,"Invalid cp specification"); @@ -1758,7 +1763,7 @@ function _find_anchor(anchor, geom) = pos = point2d(cp) + rot(from=RIGHT, to=anchor, p=[maxx,midy]) ) [anchor, pos, anchor, 0] ) : type == "xrgn_isect"? ( //region - assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.") + assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") let( rgn_raw = move(-point2d(cp), p=geom[1]), l = geom[2], @@ -1788,10 +1793,10 @@ function _find_anchor(anchor, geom) = oang = approx(xyvec, [0,0])? 0 : atan2(xyvec.y, xyvec.x) + 90 ) [anchor, pos, vec, oang] ) : type == "xrgn_extent"? ( //region - assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.") + assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") let( - rgn_raw = geom[1], l = geom[2], - rgn = is_region(rgn_raw)? rgn_raw : [rgn_raw], + rgn = force_region(geom[1]), + l = geom[2], anchor = point3d(anchor), xyanch = point2d(anchor), m = ( diff --git a/regions.scad b/regions.scad index 253d91b..c75dfae 100644 --- a/regions.scad +++ b/regions.scad @@ -637,7 +637,7 @@ function region_parts(region) = // mrgn = union(rgn1,rgn2); // orgn = difference(mrgn,rgn3); // linear_sweep(orgn,height=20,convexity=16) show_anchors(); -module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity, anchor_isect=false, spin=0, orient=UP, cp="centroid", anchor="origin") { +module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity, anchor_isect=false, spin=0, orient=UP, cp="centroid", anchor="origin", atype="hull") { region = force_region(region); dummy=assert(is_region(region),"Input is not a region"); anchor = center ? "zcenter" : anchor; diff --git a/vnf.scad b/vnf.scad index 815534f..3efcf20 100644 --- a/vnf.scad +++ b/vnf.scad @@ -877,7 +877,7 @@ function vnf_area(vnf) = /// The centroid of a tetrahedron is the average of its vertices. /// The centroid of the total is the volume weighted average. function _vnf_centroid(vnf,eps=EPSILON) = - assert(is_vnf(vnf) && len(vnf[0])!=0 ) + assert(is_vnf(vnf) && len(vnf[0])!=0 && len(vnf[1])!=0,"Invalid or empty VNF given to centroid") let( verts = vnf[0], pos = sum([ From 475129fd95821a48423561730ed77a44d1d4fc53 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Wed, 17 Nov 2021 13:38:07 -0500 Subject: [PATCH 8/9] revamp extruded region attachables and apply to linear_sweep. --- attachments.scad | 102 ++++++++++++++--------------------------------- regions.scad | 5 ++- skin.scad | 8 ++-- strings.scad | 2 +- vnf.scad | 2 +- 5 files changed, 40 insertions(+), 79 deletions(-) diff --git a/attachments.scad b/attachments.scad index 057a5f4..daf0cbd 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1515,10 +1515,10 @@ function _attach_transform(anchor, spin, orient, geom, p) = function _get_cp(geom) = + let(cp=select(geom,-3)) is_vector(cp) ? cp : let( - f=echo(type=geom[0]), type = in_list(geom[0],["vnf_extent","vnf_isect"]) ? "vnf" : in_list(geom[0],["rgn_extent","rgn_isect"]) ? "path" : in_list(geom[0],["xrgn_extent","xrgn_isect"]) ? "xpath" @@ -1549,20 +1549,22 @@ function _get_cp(geom) = // anchor = Vector or named anchor string. // geom = The geometry description of the shape. function _find_anchor(anchor, geom) = + is_string(anchor)? ( + anchor=="origin"? [anchor, CENTER, UP, 0] + : let( + anchors = last(geom), + found = search([anchor], anchors, num_returns_per_match=1)[0] + ) + assert(found!=[], str("Unknown anchor: ",anchor)) + anchors[found] + ) : let( cp = _get_cp(geom), offset_raw = select(geom,-2), offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering. - anchors = last(geom), type = geom[0] ) - is_string(anchor)? ( - anchor=="origin"? [anchor, CENTER, UP, 0] - : let(found = search([anchor], anchors, num_returns_per_match=1)[0]) - assert(found!=[], str("Unknown anchor: ",anchor)) - anchors[found] - ) : - assert(is_vector(anchor),str("anchor=",anchor)) + assert(is_vector(anchor),str("Invalid anchor: anchor=",anchor)) let(anchor = point3d(anchor)) anchor==CENTER? [anchor, cp, UP, 0] : let( @@ -1725,8 +1727,7 @@ function _find_anchor(anchor, geom) = ) : type == "rgn_isect"? ( //region assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.") let( - rgn_raw = move(-point2d(cp), p=geom[1]), - rgn = is_region(rgn_raw)? rgn_raw : [rgn_raw], + rgn = force_region(move(-point2d(cp), p=geom[1])), anchor = point2d(anchor), isects = [ for (path=rgn, t=triplet(path,true)) let( @@ -1753,68 +1754,27 @@ function _find_anchor(anchor, geom) = let( rgn = force_region(geom[1]), anchor = point2d(anchor), - m = rot(from=anchor, to=RIGHT) * move(-[cp.x, cp.y, 0]), - rpts = apply(m, flatten(rgn)), + rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)), maxx = max(column(rpts,0)), - idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i], - miny = min([for (i=idxs) rpts[i].y]), - maxy = max([for (i=idxs) rpts[i].y]), - midy = (miny+maxy)/2, - pos = point2d(cp) + rot(from=RIGHT, to=anchor, p=[maxx,midy]) - ) [anchor, pos, anchor, 0] - ) : type == "xrgn_isect"? ( //region - assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") - let( - rgn_raw = move(-point2d(cp), p=geom[1]), - l = geom[2], - rgn = is_region(rgn_raw)? rgn_raw : [rgn_raw], - anchor = point3d(anchor), - xyanch = point2d(anchor) - ) approx(xyanch,[0,0])? [anchor, [0,0,anchor.z*l/2], unit(anchor,UP), 0] : - let( - isects = [ - for (path=rgn, t=triplet(path,true)) let( - seg1 = [t[0],t[1]], - seg2 = [t[1],t[2]], - isect = line_intersection([[0,0],xyanch], seg1, RAY, SEGMENT), - n = is_undef(isect)? [0,1] : - !approx(isect, t[1])? line_normal(seg1) : - unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]), - n2 = vector_angle(xyanch,n)>90? -n : n - ) - if(!is_undef(isect) && !approx(isect,t[0])) - [norm(isect), isect, n2] - ], - maxidx = max_index(column(isects,0)), - isect = isects[maxidx], - pos = point3d(cp) + point3d(isect[1]) + unit([0,0,anchor.z],CENTER)*l/2, - xyvec = unit(isect[2],[0,1]), - vec = unit((point3d(xyvec)+UP*anchor.z)/2,UP), - oang = approx(xyvec, [0,0])? 0 : atan2(xyvec.y, xyvec.x) + 90 - ) [anchor, pos, vec, oang] - ) : type == "xrgn_extent"? ( //region - assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") - let( - rgn = force_region(geom[1]), - l = geom[2], - anchor = point3d(anchor), - xyanch = point2d(anchor), - m = ( - approx(xyanch,[0,0])? [[1,0,0],[0,1,0],[0,0,1]] : - rot(from=xyanch, to=RIGHT, planar=true) - ) * move(-[cp.x, cp.y]), - rpts = apply(m, flatten(rgn)), - maxx = max(column(rpts,0)), - idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i], - ys = [for (i=idxs) rpts[i].y], + ys = [for (pt=rpts) if (approx(pt.x, maxx)) pt.y], midy = (min(ys)+max(ys))/2, - xypos = point2d(cp) + ( - approx(xyanch,[0,0])? [0,0] : - rot(from=RIGHT, to=xyanch, p=[maxx,midy]) - ), - pos = point3d(xypos) + unit([0,0,anchor.z],CENTER)*l/2, - vec = unit((point3d(xyanch)+UP*anchor.z)/2,UP) - ) [anchor, pos, vec, oang] + pos = rot(from=RIGHT, to=anchor, p=[maxx,midy]) + ) [anchor, pos, unit(anchor), 0] + ) : type=="xrgn_extent" || type=="xrgn_isect" ? ( // extruded region + assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") + let( + anchor_xy = point2d(anchor), + L = geom[2] + ) + approx(anchor_xy,[0,0]) ? [anchor, up(anchor.z*L/2,cp), anchor, oang] : + let( + newgeom = list_set(geom, [0,len(geom)-3], [substr(geom[0],1), point2d(cp)]), + result2d = _find_anchor(anchor_xy, newgeom), + pos = point3d(result2d[1], cp.z+anchor.z*L/2), + vec = unit(point3d(result2d[2], anchor.z),UP), + oang = atan2(vec.y,vec.x) + 90 + ) + [anchor, pos, vec, oang] ) : assert(false, "Unknown attachment geometry type."); diff --git a/regions.scad b/regions.scad index c75dfae..6097494 100644 --- a/regions.scad +++ b/regions.scad @@ -612,7 +612,7 @@ function region_parts(region) = // style = The style to use when triangulating the surface of the object. Valid values are `"default"`, `"alt"`, or `"quincunx"`. // convexity = Max number of surfaces any single ray could pass through. Module use only. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"` -// anchor_isect = If true, anchoring it performed by finding where the anchor vector intersects the swept shape. Default: false +// atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" // cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" // 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` @@ -637,7 +637,8 @@ function region_parts(region) = // mrgn = union(rgn1,rgn2); // orgn = difference(mrgn,rgn3); // linear_sweep(orgn,height=20,convexity=16) show_anchors(); -module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity, anchor_isect=false, spin=0, orient=UP, cp="centroid", anchor="origin", atype="hull") { +module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity, + spin=0, orient=UP, cp="centroid", anchor="origin", atype="hull") { region = force_region(region); dummy=assert(is_region(region),"Input is not a region"); anchor = center ? "zcenter" : anchor; diff --git a/skin.scad b/skin.scad index cf60b33..467dba0 100644 --- a/skin.scad +++ b/skin.scad @@ -154,7 +154,7 @@ // 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" +// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" // style = vnf_vertex_array style. Default: "min_edge" // Example: // skin([octagon(4), circle($fn=70,r=2)], z=[0,3], slices=10); @@ -570,7 +570,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // 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" +// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. 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); @@ -949,7 +949,7 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi // 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" +// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. 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); @@ -1073,7 +1073,7 @@ function _ofs_face_edge(face,firstlen,second=false) = // spin = Rotate this many degrees around Z axis after anchor. Default: 0 // orient = Vector to rotate top towards after spin (module only) // atype = Select "hull" or "intersect" anchor types. Default: "hull" -// cp = set centerpoint for anchor computation. Default: "centroid" +// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. 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)]; diff --git a/strings.scad b/strings.scad index 962ea30..57f92ef 100644 --- a/strings.scad +++ b/strings.scad @@ -17,7 +17,7 @@ // Arguments: // str = string to operate on // pos = starting index of substring, or vector of first and last position. Default: 0 -// len = length of substring, or omit it to get the rest of the string. If len is less than zero the emptry string is returned. +// len = length of substring, or omit it to get the rest of the string. If len is zero or less then the emptry string is returned. // Example: // substr("abcdefg",3,3); // Returns "def" // substr("abcdefg",2); // Returns "cdefg" diff --git a/vnf.scad b/vnf.scad index 3efcf20..92b7ce5 100644 --- a/vnf.scad +++ b/vnf.scad @@ -783,7 +783,7 @@ function _slice_3dpolygons(polys, dir, cuts) = // vnf = A VNF structure, or list of VNF structures. // convexity = Max number of times a line could intersect a wall of the shape. // extent = If true, calculate anchors by extents, rather than intersection. Default: true. -// cp = Centerpoint of VNF to use for anchoring when `extent` is false. Default: `[0, 0, 0]` +// cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" // 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` From ba2e1d45f17724de652c90aeca15e794b6d50e77 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Wed, 17 Nov 2021 14:16:22 -0500 Subject: [PATCH 9/9] remove anchor_isect reference --- regions.scad | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/regions.scad b/regions.scad index 6097494..059235d 100644 --- a/regions.scad +++ b/regions.scad @@ -657,7 +657,7 @@ module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, function linear_sweep(region, height=1, center, twist=0, scale=1, slices, - maxseg, style="default", cp="centroid", anchor_isect=false, anchor, spin=0, orient=UP) = + maxseg, style="default", cp="centroid", atype="hull", anchor, spin=0, orient=UP) = let( region = force_region(region) ) @@ -702,7 +702,7 @@ function linear_sweep(region, height=1, center, twist=0, scale=1, slices, for (rgn = regions) vnf_from_region(rgn, ident(4), reverse=true), for (rgn = trgns) vnf_from_region(rgn, up(height), reverse=false) ]) - ) reorient(anchor,spin,orient, cp=cp, vnf=vnf, extent=!anchor_isect, p=vnf, anchors=anchors); + ) reorient(anchor,spin,orient, cp=cp, vnf=vnf, extent=atype=="hull", p=vnf, anchors=anchors);