diff --git a/attachments.scad b/attachments.scad index 3989124..fd3eab8 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1628,7 +1628,7 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // Module: attachable() // // Usage: Square/Trapezoid Geometry -// attachable(anchor, spin, two_d=true, size=, [size2=], [shift=], ...) {OBJECT; children();} +// attachable(anchor, spin, two_d=true, size=, [size2=], [shift=], [override=], ...) {OBJECT; children();} // Usage: Circle/Oval Geometry // attachable(anchor, spin, two_d=true, r=|d=, ...) {OBJECT; children();} // Usage: 2D Path/Polygon Geometry @@ -1708,6 +1708,7 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // anchors = If given as a list of anchor points, allows named anchor points. // two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) // axis = The vector pointing along the axis of a geometry. Default: UP +// override = Function that takes an anchor and returns a pair `[position,direction]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction]]` entries. If the direction/position that is returned is undef then the default will be used. // geom = If given, uses the pre-defined (via {{attach_geom()}} geometry. // // Side Effects: @@ -1892,7 +1893,7 @@ module attachable( offset=[0,0,0], anchors=[], two_d=false, - axis=UP, + axis=UP,override, geom ) { dummy1 = @@ -1913,7 +1914,7 @@ module attachable( d=d, d1=d1, d2=d2, l=l, vnf=vnf, region=region, extent=extent, cp=cp, offset=offset, anchors=anchors, - two_d=two_d, axis=axis + two_d=two_d, axis=axis, override=override ); m = _attach_transform(anchor,spin,orient,geom); multmatrix(m) { @@ -2032,7 +2033,7 @@ function reorient( cp=[0,0,0], anchors=[], two_d=false, - axis=UP, + axis=UP, override, geom, p=undef ) = @@ -2056,7 +2057,7 @@ function reorient( d=d, d1=d1, d2=d2, l=l, vnf=vnf, region=region, extent=extent, cp=cp, offset=offset, anchors=anchors, - two_d=two_d, axis=axis + two_d=two_d, axis=axis, override=override ), $attach_to = undef ) _attach_transform(anchor,spin,orient,geom,p); @@ -2130,6 +2131,7 @@ function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; // anchors = If given as a list of anchor points, allows named anchor points. // two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) // axis = The vector pointing along the axis of a geometry. Default: UP +// override = Function that takes an anchor and returns a pair `[position,direction]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction]]` entries. If the direction/position that is returned is undef then the default will be used. // // Example(NORENDER): Null/Point Shape // geom = attach_geom(); @@ -2177,7 +2179,7 @@ function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; // geom = attach_geom(two_d=true, size=size); // // Example(NORENDER): 2D Trapezoidal Shape -// geom = attach_geom(two_d=true, size=[x1,y], size2=x2, shift=shift); +// geom = attach_geom(two_d=true, size=[x1,y], size2=x2, shift=shift, override=override); // // Example(NORENDER): 2D Circular Shape // geom = attach_geom(two_d=true, r=r); @@ -2197,6 +2199,13 @@ function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; // Example(NORENDER): Extruded Region, Anchored by Intersection // geom = attach_geom(region=region, l=length, extent=false); // + +function _local_struct_val(struct, key)= + assert(is_def(key),"key is missing") + let(ind = search([key],struct)[0]) + ind == [] ? undef : struct[ind][1]; + + function attach_geom( size, size2, shift, scale, twist, @@ -2207,7 +2216,7 @@ function attach_geom( offset=[0,0,0], anchors=[], two_d=false, - axis=UP + axis=UP, override ) = assert(is_bool(extent)) assert(is_vector(cp) || is_string(cp)) @@ -2219,12 +2228,15 @@ function attach_geom( two_d? ( let( size2 = default(size2, size.x), - shift = default(shift, 0) + shift = default(shift, 0), + over_f = is_undef(override) ? function(anchor) [undef,undef] + : is_func(override) ? override + : function(anchor) _local_struct_val(override,anchor) ) assert(is_vector(size,2)) assert(is_num(size2)) assert(is_num(shift)) - ["trapezoid", point2d(size), size2, shift, cp, offset, anchors] + ["trapezoid", point2d(size), size2, shift, over_f, cp, offset, anchors] ) : ( let( size2 = default(size2, point2d(size)), @@ -2637,7 +2649,7 @@ function _find_anchor(anchor, geom) = mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep, pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt) ) [anchor, pos, anchor, oang] - ) : type == "trapezoid"? ( //size, size2, shift + ) : type == "trapezoid"? ( //size, size2, shift, override let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1") let( @@ -2646,9 +2658,12 @@ function _find_anchor(anchor, geom) = u = (anchor.y+1)/2, // 0<=u<=1 frpt = [size.x/2*anchor.x, -size.y/2], bkpt = [size2/2*anchor.x+shift, size.y/2], - pos = point2d(cp) + lerp(frpt, bkpt, u) + point2d(offset), + override = geom[4](anchor), + pos = default(override[0],point2d(cp) + lerp(frpt, bkpt, u) + point2d(offset)), svec = point3d(line_normal(bkpt,frpt)*anchor.x), - vec = anchor.y < 0? ( + vec = is_def(override[1]) ? override[1] + : + anchor.y < 0? ( anchor.x == 0? FWD : size.x == 0? unit(-[shift,size.y], FWD) : unit((point3d(svec) + FWD) / 2, FWD) @@ -2658,6 +2673,7 @@ function _find_anchor(anchor, geom) = anchor.x == 0? BACK : size2 == 0? unit([shift,size.y], BACK) : unit((point3d(svec) + BACK) / 2, BACK) + ) ) [anchor, pos, vec, 0] ) : type == "ellipse"? ( //r diff --git a/geometry.scad b/geometry.scad index 216e161..a3e8841 100644 --- a/geometry.scad +++ b/geometry.scad @@ -2296,24 +2296,27 @@ module hull_points(points, fast=false) { no_children($children); check = assert(is_path(points)) assert(len(points)>=3, "Point list must contain 3 points"); - if (len(points[0])==2) - hull() polygon(points=points); - else { - if (fast) { - extra = len(points)%3; - faces = [ - [for(i=[0:1:extra+2])i], // If vertex count not divisible by 3, combine extras with first 3 - for(i=[extra+3:3:len(points)-3])[i,i+1,i+2] - ]; - hull() polyhedron(points=points, faces=faces); - } else { - faces = hull(points); - if (is_num(faces[0])){ - if (len(faces)<=2) echo("Hull contains only two points"); - else polyhedron(points=points, faces=[faces]); + attachable(){ + if (len(points[0])==2) + hull() polygon(points=points); + else { + if (fast) { + extra = len(points)%3; + faces = [ + [for(i=[0:1:extra+2])i], // If vertex count not divisible by 3, combine extras with first 3 + for(i=[extra+3:3:len(points)-3])[i,i+1,i+2] + ]; + hull() polyhedron(points=points, faces=faces); + } else { + faces = hull(points); + if (is_num(faces[0])){ + if (len(faces)<=2) echo("Hull contains only two points"); + else polyhedron(points=points, faces=[faces]); + } + else polyhedron(points=points, faces=faces); } - else polyhedron(points=points, faces=faces); } + union(); } } diff --git a/shapes2d.scad b/shapes2d.scad index 77d8116..75b3429 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -112,6 +112,9 @@ module square(size=1, center, anchor, spin) { // Example(2D): "perim" Anchors // rect([40,30], rounding=10, atype="perim") // show_anchors(); +// Example(2D): "perim" Anchors +// rect([40,30], rounding=[-10,-8,-3,-7], atype="perim") +// show_anchors(); // Example(2D): Mixed Chamferring and Rounding // rect([40,30],rounding=[5,0,10,0],chamfer=[0,8,0,15],$fa=1,$fs=1); // Example(2D): Called as Function @@ -120,52 +123,49 @@ module square(size=1, center, anchor, spin) { // move_copies(path) color("blue") circle(d=2,$fn=8); module rect(size=1, rounding=0, atype="box", chamfer=0, anchor=CENTER, spin=0) { errchk = assert(in_list(atype, ["box", "perim"])); - size = is_num(size)? [size,size] : point2d(size); + size = force_list(size,2); if (rounding==0 && chamfer==0) { attachable(anchor, spin, two_d=true, size=size) { square(size, center=true); children(); } } else { - pts = rect(size=size, rounding=rounding, chamfer=chamfer); - if (atype == "perim") { - attachable(anchor, spin, two_d=true, path=pts) { + pts_over = rect(size=size, rounding=rounding, chamfer=chamfer, atype=atype, _return_override=true); + pts = pts_over[0]; + override = pts_over[1]; + attachable(anchor, spin, two_d=true, size=size,override=override) { polygon(pts); children(); - } - } else { - attachable(anchor, spin, two_d=true, size=size) { - polygon(pts); - children(); - } } } } -function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0) = - assert(is_num(size) || is_vector(size)) - assert(is_num(chamfer) || len(chamfer)==4) - assert(is_num(rounding) || len(rounding)==4) +function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0, _return_override) = + assert(is_num(size) || is_vector(size,2)) + assert(is_num(chamfer) || is_vector(chamfer,4)) + assert(is_num(rounding) || is_vector(rounding,4)) assert(in_list(atype, ["box", "perim"])) let( anchor=point2d(anchor), - size = is_num(size)? [size,size] : point2d(size), - complex = rounding!=0 || chamfer!=0 + size = force_list(size,2), + chamfer = force_list(chamfer,4), + rounding = force_list(rounding,4) ) - (rounding==0 && chamfer==0)? let( - path = [ - [ size.x/2, -size.y/2], - [-size.x/2, -size.y/2], - [-size.x/2, size.y/2], - [ size.x/2, size.y/2] - ] - ) - rot(spin, p=move(-v_mul(anchor,size/2), p=path)) : + all_zero(concat(chamfer,rounding),0) ? + let( + path = [ + [ size.x/2, -size.y/2], + [-size.x/2, -size.y/2], + [-size.x/2, size.y/2], + [ size.x/2, size.y/2] + ] + ) + rot(spin, p=move(-v_mul(anchor,size/2), p=path)) + : + assert(all_zero(v_mul(chamfer,rounding),0), "Cannot specify chamfer and rounding at the same corner") let( - chamfer = is_list(chamfer)? chamfer : [for (i=[0:3]) chamfer], - rounding = is_list(rounding)? rounding : [for (i=[0:3]) rounding], quadorder = [3,2,1,0], quadpos = [[1,1],[-1,1],[-1,-1],[1,-1]], eps = 1e-9, @@ -176,7 +176,7 @@ function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0) assert(insets_x <= size.x, "Requested roundings and/or chamfers exceed the rect width.") assert(insets_y <= size.y, "Requested roundings and/or chamfers exceed the rect height.") let( - path = [ + corners = [ for(i = [0:3]) let( quad = quadorder[i], @@ -191,13 +191,20 @@ function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0) abs(qround) >= eps? [for (j=[0:1:cverts]) let(a=90-j*step) v_mul(polar_to_xy(abs(qinset),a),[sign(qinset),1])] : [[0,0]], qfpts = [for (p=qpts) v_mul(p,qpos)], - qrpts = qpos.x*qpos.y < 0? reverse(qfpts) : qfpts - ) - each move(cp, p=qrpts) - ] - ) complex && atype=="perim"? - reorient(anchor,spin, two_d=true, path=path, p=path) : - reorient(anchor,spin, two_d=true, size=size, p=path); + qrpts = qpos.x*qpos.y < 0? reverse(qfpts) : qfpts, + cornerpt = atype=="box" || (qround==0 && qchamf==0) ? undef + : qround<0 || qchamf<0 ? [[0,-qpos.y*min(qround,qchamf)]] + : [for(seg=pair(qrpts)) let(isect=line_intersection(seg, [[0,0],qpos],SEGMENT,LINE)) if (is_def(isect) && isect!=seg[0]) isect] + ) + assert(is_undef(cornerpt) || len(cornerpt)==1,"Cannot find corner point to anchor") + [move(cp, p=qrpts), is_undef(cornerpt)? undef : move(cp,p=cornerpt[0])] + ], + path = flatten(column(corners,0)), + override = [for(i=[0:3]) + let(quad=quadorder[i]) + if (is_def(corners[i][1])) [quadpos[quad], [corners[i][1], min(chamfer[quad],rounding[quad])<0 ? [quadpos[quad].x,0] : undef]]] + ) _return_override ? [reorient(anchor,spin, two_d=true, size=size, p=path, override=override), override] + : reorient(anchor,spin, two_d=true, size=size, p=path, override=override); // Function&Module: circle() @@ -868,8 +875,12 @@ module right_triangle(size=[1,1], center, anchor, spin=0) { // rounding = The rounding radius for the corners. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding) // chamfer = The Length of the chamfer faces at the corners. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer) // flip = If true, negative roundings and chamfers will point forward and back instead of left and right. Default: `false`. +// atype = The type of anchoring to use with `anchor=`. Valid opptions are "box" and "perim". This lets you choose between putting anchors on the rounded or chamfered perimeter, or on the square bounding box of the shape. Default: "box" // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// Anchor Types: +// box = Anchor is with respect to the rectangular bounding box of the shape. +// perim = Anchors are placed along the rounded or chamfered perimeter of the shape. // Examples(2D): // trapezoid(h=30, w1=40, w2=20); // trapezoid(h=25, w1=20, w2=35); @@ -893,9 +904,17 @@ module right_triangle(size=[1,1], center, anchor, spin=0) { // trapezoid(h=30, w1=60, w2=40, rounding=-5, flip=true); // Example(2D): Mixed Chamfering and Rounding // trapezoid(h=30, w1=60, w2=40, rounding=[5,0,-10,0],chamfer=[0,8,0,-15],$fa=1,$fs=1); +// Example(2D): default anchors for roundings +// trapezoid(h=30, w1=100, ang=[66,44],rounding=5) show_anchors(); +// Example(2D): default anchors for negative roundings are still at the trapezoid corners +// trapezoid(h=30, w1=100, ang=[66,44],rounding=-5) show_anchors(); +// Example(2D): "perim" anchors are at the tips of negative roundings +// trapezoid(h=30, w1=100, ang=[66,44],rounding=-5, atype="perim") show_anchors(); +// Example(2D): They point the other direction if you flip them +// trapezoid(h=30, w1=100, ang=[66,44],rounding=-5, atype="perim",flip=true) show_anchors(); // Example(2D): Called as Function // stroke(closed=true, trapezoid(h=30, w1=40, w2=20)); -function trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anchor=CENTER, spin=0, angle) = +function trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anchor=CENTER, spin=0, ,atype="box", _return_override, angle) = assert(is_undef(angle), "The angle parameter has been replaced by ang, which specifies trapezoid interior angle") assert(is_undef(h) || is_finite(h)) assert(is_undef(w1) || is_finite(w1)) @@ -919,11 +938,12 @@ function trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anc w1 = is_def(w1)? w1 : w2 + x1 + x2, w2 = is_def(w2)? w2 : w1 - x1 - x2, shift = first_defined([shift,(x1-x2)/2]), - chamfs = is_num(chamfer)? [for (i=[0:3]) chamfer] : - assert(len(chamfer)==4) chamfer, - rounds = is_num(rounding)? [for (i=[0:3]) rounding] : - assert(len(rounding)==4) rounding, - srads = [for (i=[0:3]) rounds[i]? rounds[i] : chamfs[i]], + chamfer = force_list(chamfer,4), + rounding = force_list(rounding,4) + ) + assert(all_zero(v_mul(chamfer,rounding),0), "Cannot specify chamfer and rounding at the same corner") + let( + srads = chamfer+rounding, rads = v_abs(srads) ) assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.") @@ -947,65 +967,70 @@ function trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anc b = a + [hyps[i] * qdirs[i].x * (srads[i]<0 && !flip? 1 : -1), 0] ) b ], - cpath = [ - each ( + corners = [ + ( let(i = 0) - rads[i] == 0? [base[i]] : - srads[i] > 0? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i], 90], r=rads[i]) : - flip? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i],-90], r=rads[i]) : - arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],90], r=rads[i]) + rads[i] == 0? [base[i]] + : srads[i] > 0? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i], 90], r=rads[i]) + : flip? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i],-90], r=rads[i]) + : arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],90], r=rads[i]) ), - each ( + ( let(i = 1) - rads[i] == 0? [base[i]] : - srads[i] > 0? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[90,180+angs[i]], r=rads[i]) : - flip? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[270,180+angs[i]], r=rads[i]) : - arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[90,angs[i]], r=rads[i]) + rads[i] == 0? [base[i]] + : srads[i] > 0? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[90,180+angs[i]], r=rads[i]) + : flip? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[270,180+angs[i]], r=rads[i]) + : arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[90,angs[i]], r=rads[i]) ), - each ( + ( let(i = 2) - rads[i] == 0? [base[i]] : - srads[i] > 0? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],270], r=rads[i]) : - flip? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],90], r=rads[i]) : - arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i],-90], r=rads[i]) + rads[i] == 0? [base[i]] + : srads[i] > 0? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],270], r=rads[i]) + : flip? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],90], r=rads[i]) + : arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i],-90], r=rads[i]) ), - each ( + ( let(i = 3) - rads[i] == 0? [base[i]] : - srads[i] > 0? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[-90,angs[i]], r=rads[i]) : - flip? arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[90,angs[i]], r=rads[i]) : - arc(n=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[270,180+angs[i]], r=rads[i]) + rads[i] == 0? [base[i]] + : srads[i] > 0? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[-90,angs[i]], r=rads[i]) + : flip? arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[90,angs[i]], r=rads[i]) + : arc(n=rounding[i]?undef:2, cp=base[i]+offs[i], angle=[270,180+angs[i]], r=rads[i]) ), ], - path = reverse(cpath) - ) true //simple // force regular anchoring - ? reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path) - : reorient(anchor,spin, two_d=true, path=path, p=path); + path = reverse(flatten(corners)), + override = [for(i=[0:3]) + if (atype!="box" && srads[i]!=0) + srads[i]>0? + let(dir = unit(base[i]-select(base,i-1)) + unit(base[i]-select(base,i+1)), + pt=[for(seg=pair(corners[i])) let(isect=line_intersection(seg, [base[i],base[i]+dir],SEGMENT,LINE)) + if (is_def(isect) && isect!=seg[0]) isect] + ) + [qdirs[i], [pt[0], undef]] + : flip? + let( dir=unit(base[i] - select(base,i+(i%2==0?-1:1)))) + [qdirs[i], [select(corners[i],i%2==0?0:-1), dir]] + : let( dir = [qdirs[i].x,0]) + [qdirs[i], [select(corners[i],i%2==0?-1:0), dir]]] + ) _return_override ? [reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path, override=override),override] + : reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path, override=override); -module trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anchor=CENTER, spin=0, angle) { - path = trapezoid(h=h, w1=w1, w2=w2, ang=ang, shift=shift, chamfer=chamfer, rounding=rounding, flip=flip, angle=angle); - union() { - simple = true; //chamfer==0 && rounding==0; // force "normal" anchoring for now - ang = force_list(ang,2); - h = is_def(h)? h : (w1-w2) * sin(ang[0]) * sin(ang[1]) / sin(ang[0]+ang[1]); - x1 = is_undef(ang[0]) || ang[0]==90 ? 0 : h/tan(ang[0]); - x2 = is_undef(ang[1]) || ang[1]==90 ? 0 : h/tan(ang[1]); - w1 = is_def(w1)? w1 : w2 + x1 + x2; - w2 = is_def(w2)? w2 : w1 - x1 - x2; - shift = first_defined([shift,(x1-x2)/2]); - if (simple) { - attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift) { - polygon(path); - children(); - } - } else { - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } - } + +module trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anchor=CENTER, spin=0, atype="box", angle) { + path_over = trapezoid(h=h, w1=w1, w2=w2, ang=ang, shift=shift, chamfer=chamfer, rounding=rounding, flip=flip, angle=angle,atype=atype,_return_override=true); + path=path_over[0]; + override = path_over[1]; + ang = force_list(ang,2); + h = is_def(h)? h : (w1-w2) * sin(ang[0]) * sin(ang[1]) / sin(ang[0]+ang[1]); + x1 = is_undef(ang[0]) || ang[0]==90 ? 0 : h/tan(ang[0]); + x2 = is_undef(ang[1]) || ang[1]==90 ? 0 : h/tan(ang[1]); + w1 = is_def(w1)? w1 : w2 + x1 + x2; + w2 = is_def(w2)? w2 : w1 - x1 - x2; + shift = first_defined([shift,(x1-x2)/2]); + attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, override=override) { + polygon(path); + children(); } }