diff --git a/attachments.scad b/attachments.scad index 772e81d0..ecb121df 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1567,7 +1567,7 @@ function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; // Usage: Spheroid/Ovoid Geometry // geom = attach_geom(r=|d=, ...); // Usage: Extruded 2D Path/Polygon/Region Geometry -// geom = attach_geom(region=, l=|h=, [extent=], ...); +// geom = attach_geom(region=, l=|h=, [extent=], [shift=], [scale=], [twist=], ...); // Usage: VNF Geometry // geom = attach_geom(vnf=, [extent=], ...); // @@ -1583,6 +1583,8 @@ function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; // size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length. // size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape. // shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift. +// scale = If given as number or a 2D vector, scales the top of the shape, relative to the bottom. Default: `[1,1]` +// twist = If given as number, rotates the top of the shape by the given number of degrees clockwise, relative to the bottom. Default: `0` // r = Radius of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. // d = Diameter of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. // r1 = Radius of the bottom of the conical volume. Can be a scalar, or a list of sizes per axis. @@ -1663,7 +1665,8 @@ function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; // geom = attach_geom(region=region, l=length, extent=false); // function attach_geom( - size, size2, shift, + size, size2, + shift, scale, twist, r,r1,r2, d,d1,d2, l,h, vnf, region, extent=true, @@ -1713,9 +1716,17 @@ function attach_geom( ? ["rgn_extent", region, cp, offset, anchors] : ["rgn_isect", region, cp, offset, anchors] : assert(is_finite(l)) + let( + shift = default(shift, [0,0]), + scale = is_num(scale)? [scale,scale] : default(scale, [1,1]), + twist = default(twist, 0) + ) + assert(is_vector(shift,2)) + assert(is_vector(scale,2)) + assert(is_num(twist)) extent==true - ? ["xrgn_extent", region, l, cp, offset, anchors] - : ["xrgn_isect", region, l, cp, offset, anchors] + ? ["xrgn_extent", region, l, twist, scale, shift, cp, offset, anchors] + : ["xrgn_isect", region, l, twist, scale, shift, cp, offset, anchors] ) : let( r1 = get_radius(r1=r1,d1=d1,r=r,d=d,dflt=undef) @@ -1922,7 +1933,7 @@ function _get_cp(geom) = assert(type!="other", "Invalid cp value") 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] + [each centroid(geom[1]), if (type=="xpath") 0] ) : let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1]))) cp=="mean" ? [each mean(points), if (type=="xpath") geom[2]/2] @@ -2160,11 +2171,21 @@ function _find_anchor(anchor, geom) = 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] + rgn = geom[1], + L = geom[2], + twist = geom[3], + scale = geom[4], + shift = geom[5], + u = (anchor.z + 1) / 2, + shmat = move(lerp([0,0], shift, u)), + scmat = scale(lerp([1,1], scale, u)), + twmat = zrot(lerp(0, -twist, u)), + mat = shmat * scmat * twmat ) - approx(anchor_xy,[0,0]) ? [anchor, up(anchor.z*L/2,cp), anchor, oang] : + approx(anchor_xy,[0,0]) ? [anchor, apply(mat, up(anchor.z*L/2,cp)), anchor, oang] : let( - newgeom = list_set(geom, [0,len(geom)-3], [substr(geom[0],1), point2d(cp)]), + newrgn = apply(mat, rgn), + newgeom = attach_geom(two_d=true, region=newrgn, extent=type=="xrgn_extent", cp=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), diff --git a/skin.scad b/skin.scad index 59e84abd..9370b86b 100644 --- a/skin.scad +++ b/skin.scad @@ -523,15 +523,14 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // maxseg = If given, then any long segments of the region will be subdivided to be shorter than this length. This can refine twisting flat faces a lot. Default: `undef` (no subsampling) // 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. -// cp = Centerpoint for determining intersection anchors or centering the shape. Determines the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: `[0,0,0]` +// cp = Centerpoint for determining intersection anchors or centering the shape. Determines the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: `"centroid"` // atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Extra Anchors: -// centroid_top = The centroid of the top of the shape, oriented UP. -// centroid = The centroid of the center of the shape, oriented UP. -// centroid_bot = The centroid of the bottom of the shape, oriented DOWN. +// "origin" = Centers the extruded shape vertically only, but keeps the original path positions in the X and Y. Oriented UP. +// "original_base" = Keeps the original path positions in the X and Y, but at the bottom of the extrusion. Oriented UP. // Example: Extruding a Compound Region. // rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)]; // rgn2 = [square(30,center=false)]; @@ -574,27 +573,27 @@ module linear_sweep( region, height, center, twist=0, scale=1, shift=[0,0], slices, maxseg, style="default", convexity, - cp=[0,0,0], atype="hull", h, + cp, atype="hull", h, anchor, spin=0, orient=UP ) { h = first_defined([h, height, 1]); region = force_region(region); check = assert(is_region(region),"Input is not a region"); - anchor = get_anchor(anchor, center, BOT, BOT); + anchor = center==true? "origin" : + center == false? "original_base" : + default(anchor, "original_base"); vnf = linear_sweep( region, height=h, style=style, twist=twist, scale=scale, shift=shift, slices=slices, maxseg=maxseg, - anchor=CTR + anchor="origin" ); - cent = centroid(region); anchors = [ - named_anchor("centroid_top", point3d(cent, h/2), UP), - named_anchor("centroid", point3d(cent), UP), - named_anchor("centroid_bot", point3d(cent,-h/2), DOWN) + named_anchor("original_base", [0,0,-h/2], UP) ]; - geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, anchors=anchors) : - atype=="intersect"? attach_geom(cp=cp, region=region, h=h, extent=false, anchors=anchors) : + cp = default(cp, "centroid"); + geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, shift=shift, scale=scale, twist=twist, anchors=anchors) : + atype=="intersect"? attach_geom(cp=cp, region=region, h=h, extent=false, shift=shift, scale=scale, twist=twist, anchors=anchors) : assert(in_list(atype, ["hull", "intersect"])); attachable(anchor,spin,orient, geom=geom) { vnf_polyhedron(vnf, convexity=convexity); @@ -607,7 +606,7 @@ function linear_sweep( region, height, center, twist=0, scale=1, shift=[0,0], slices, maxseg, style="default", - cp=[0,0,0], atype="hull", h, + cp, atype="hull", h, anchor, spin=0, orient=UP ) = let( region = force_region(region) ) @@ -616,7 +615,9 @@ function linear_sweep( assert(is_vector(shift, 2), str(shift)) let( h = first_defined([h, height, 1]), - anchor = get_anchor(anchor, center, BOT, BOT), + anchor = center==true? "origin" : + center == false? "original_base" : + default(anchor, "original_base"), regions = region_parts(region), slices = default(slices, max(1,ceil(abs(twist)/5))), scale = is_num(scale)? [scale,scale] : point2d(scale), @@ -655,14 +656,12 @@ function linear_sweep( for (rgn = regions) vnf_from_region(rgn, down(h/2), reverse=true), for (rgn = trgns) vnf_from_region(rgn, up(h/2), reverse=false) ]), - cent = centroid(region), anchors = [ - named_anchor("centroid_top", point3d(cent, h/2), UP), - named_anchor("centroid", point3d(cent), UP), - named_anchor("centroid_bot", point3d(cent,-h/2), DOWN) + named_anchor("original_base", [0,0,-h/2], UP) ], - geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, anchors=anchors) : - atype=="intersect"? attach_geom(cp=cp, region=region, h=h, extent=false, anchors=anchors) : + cp = default(cp, "centroid"), + geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, shift=shift, scale=scale, twist=twist, anchors=anchors) : + atype=="intersect"? attach_geom(cp=cp, region=region, h=h, extent=false, shift=shift, scale=scale, twist=twist, anchors=anchors) : assert(in_list(atype, ["hull", "intersect"])) ) reorient(anchor,spin,orient, geom=geom, p=vnf);