diff --git a/attachments.scad b/attachments.scad index 51ea9ec..2fdea01 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1522,7 +1522,7 @@ function _get_cp(geom) = ) assert(type!="other", "Invalid cp value") cp=="centroid" ? centroid(geom[1]) - : let(points = type=="vnf"?geom[1][0]:geom[1]) + : let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1]))) cp=="mean" ? mean(points) : cp=="box" ? mean(pointlist_bounds(points)) : assert(false,"Invalid cp specification"); @@ -1703,13 +1703,18 @@ function _find_anchor(anchor, geom) = ) [anchor, pos, vec, 0] ) : type == "circle"? ( //r assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.") - let( + let( rr = geom[1], r = is_num(rr)? [rr,rr] : point2d(rr), + pos = approx(anchor.x,0) ? [sign(anchor.x)*r.x,0] + : let( + m = anchor.y/anchor.x, + px = sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y))) + ) + [px,m*px], anchor = unit(point2d(anchor),[0,0]), - pos = point2d(cp) + v_mul(r,anchor) + point2d(offset), - vec = unit(v_mul(r,anchor),[0,1]) - ) [anchor, pos, vec, 0] + vec = [r.y/r.x*pos.x, r.x/r.y*pos.y] + ) [anchor, point2d(cp+offset)+pos, vec, 0] ) : type == "rgn_isect"? ( //region assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.") let( @@ -1727,7 +1732,10 @@ function _find_anchor(anchor, geom) = n2 = vector_angle(anchor,n)>90? -n : n ) if(!is_undef(isect) && !approx(isect,t[0])) [norm(isect), isect, n2] - ], + ] + ) + assert(len(isects)>0, "Anchor vector does not intersect with the shape. Attachment failed.") + let( maxidx = max_index(column(isects,0)), isect = isects[maxidx], pos = point2d(cp) + isect[1], @@ -1736,8 +1744,7 @@ function _find_anchor(anchor, geom) = ) : type == "rgn_extent"? ( //region assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.") let( - rgn_raw = geom[1], - rgn = is_region(rgn_raw)? rgn_raw : [rgn_raw], + 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)), diff --git a/drawing.scad b/drawing.scad index 78e0a02..85b686e 100644 --- a/drawing.scad +++ b/drawing.scad @@ -703,7 +703,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) { + attachable(anchor,spin, two_d=true, path=path, extent=true) { polygon(path); children(); } diff --git a/regions.scad b/regions.scad index 3f7a0de..d5a2b28 100644 --- a/regions.scad +++ b/regions.scad @@ -278,13 +278,16 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // Module: region() // Usage: -// region(r); +// region(r, [anchor], [spin], [cp]) { ... }; // Description: // Creates the 2D polygons described by the given region or list of polygons. This module works on // arbitrary lists of polygons that cross each other and hence do not define a valid region. The // displayed result is the exclusive-or of the polygons listed in the input. // Arguments: // r = region to create as geometry +// 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" // 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 @@ -293,16 +296,18 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // [square([60,10], center=true)] // ); // region(rgn); -module region(r) +module region(r, anchor="origin", spin=0, cp="centroid") { - no_children($children); 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])]; - polygon(points=points, paths=paths); + attachable(anchor, spin, two_d=true, region=r, extent=false, cp=cp){ + polygon(points=points, paths=paths); + children(); + } } @@ -583,7 +588,7 @@ function region_parts(region) = // Function&Module: linear_sweep() // Usage: -// linear_sweep(region, height, [center], [slices], [twist], [scale], [style], [convexity]); +// linear_sweep(region, height, [center], [slices], [twist], [scale], [style], [convexity]) {attachments}; // Description: // If called as a module, creates a polyhedron that is the linear extrusion of the given 2D region or polygon. // If called as a function, returns a VNF that can be used to generate a polyhedron of the linear extrusion @@ -630,7 +635,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, anchor, 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") { region = force_region(region); dummy=assert(is_region(region),"Input is not a region"); anchor = center ? "zcenter" : anchor;