From 475129fd95821a48423561730ed77a44d1d4fc53 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Wed, 17 Nov 2021 13:38:07 -0500 Subject: [PATCH] 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`