From af53674c4e2df2c0acffe4c1855b7fc6ed25c742 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 24 Mar 2023 19:50:52 -0400 Subject: [PATCH 1/7] Add angle definition to prismoid --- shapes2d.scad | 54 ++++++++---- shapes3d.scad | 225 +++++++++++++++++++++++++------------------------- 2 files changed, 152 insertions(+), 127 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index 1561c7b..24b452f 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -914,6 +914,30 @@ module right_triangle(size=[1,1], center, anchor, spin=0) { // 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_dims(h,w1,w2,shift,ang) = + let( + h = is_def(h)? h + : num_defined([w1,w2,each ang])==4 ? (w1-w2) * sin(ang[0]) * sin(ang[1]) / sin(ang[0]+ang[1]) + : undef + ) + is_undef(h) ? [h] + : + let( + 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 + : is_def(w2) && is_def(ang[0]) ? w2 + x1 + x2 + : undef, + w2 = is_def(w2)? w2 + : is_def(w1) && is_def(ang[0]) ? w1 - x1 - x2 + : undef, + shift = first_defined([shift,(x1-x2)/2]) + ) + [h,w1,w2,shift]; + + + 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)) @@ -927,17 +951,15 @@ function trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anc assert(is_finite(rounding) || is_vector(rounding,4)) let( ang = force_list(ang,2), - angOK = ang==[undef,undef] || (all_positive(ang) && ang[0]<180 && ang[1]<180) + angOK = len(ang)==2 && (ang==[undef,undef] || (all_positive(ang) && ang[0]<180 && ang[1]<180)) ) - assert(angOK, "trapezoid angles must be strictly between 0 and 180") + assert(angOK, "trapezoid angles must be scalar or 2-vector, strictly between 0 and 180") let( - simple = chamfer==0 && rounding==0, - 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]), + h_w1_w2_shift = _trapezoid_dims(h,w1,w2,shift,ang), + h = h_w1_w2_shift[0], + w1 = h_w1_w2_shift[1], + w2 = h_w1_w2_shift[2], + shift = h_w1_w2_shift[3], chamfer = force_list(chamfer,4), rounding = force_list(rounding,4) ) @@ -1018,16 +1040,16 @@ function trapezoid(h, w1, w2, ang, shift, chamfer=0, rounding=0, flip=false, anc 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_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]); + h_w1_w2_shift = _trapezoid_dims(h,w1,w2,shift,ang); + h = h_w1_w2_shift[0]; + w1 = h_w1_w2_shift[1]; + w2 = h_w1_w2_shift[2]; + shift = h_w1_w2_shift[3]; attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, override=override) { polygon(path); children(); diff --git a/shapes3d.scad b/shapes3d.scad index 4516d81..0f60c95 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -565,30 +565,32 @@ function cuboid( // Function&Module: prismoid() // -// Usage: Typical Prismoids -// prismoid(size1, size2, h|l, [shift], ...) [ATTACHMENTS]; -// Usage: Chamfered Prismoids -// prismoid(size1, size2, h|l, [chamfer=], ...) [ATTACHMENTS]; -// prismoid(size1, size2, h|l, [chamfer1=], [chamfer2=], ...) [ATTACHMENTS]; -// Usage: Rounded Prismoids -// prismoid(size1, size2, h|l, [rounding=], ...) [ATTACHMENTS]; -// prismoid(size1, size2, h|l, [rounding1=], [rounding2=], ...) [ATTACHMENTS]; +// Usage: +// prismoid(size1, size2, [h|l|height|length], [shift], [xang=], [yang=], ...) [ATTACHMENTS]; +// Usage: Chamfered and/or Rounded Prismoids +// prismoid(size1, size2, h|l|height|length, [chamfer=], [rounding=]...) [ATTACHMENTS]; +// prismoid(size1, size2, h|l|height|length, [chamfer1=], [chamfer2=], [rounding1=], [rounding2=], ...) [ATTACHMENTS]; // Usage: As Function -// vnf = prismoid(size1, size2, h|l, [shift], [rounding], [chamfer]); -// vnf = prismoid(size1, size2, h|l, [shift], [rounding1], [rounding2], [chamfer1], [chamfer2]); -// +// vnf = prismoid(...); // Description: // Creates a rectangular prismoid shape with optional roundovers and chamfering. // You can only round or chamfer the vertical(ish) edges. For those edges, you can // specify rounding and/or chamferring per-edge, and for top and bottom separately. // If you want to round the bottom or top edges see {{rounded_prism()}}. -// +// . +// Specification of the prismoid is similar to specification for {{trapezoid()}}. You can specify the dimensions of the +// bottom and top and its height to get a symmetric prismoid. You can use the shift argument to shift the top face around. +// You can also specify base angles either in the X direction, Y direction or both. In order to avoid overspecification, +// you may need to specify a parameter such as size2 as a list of two values, one of which is undef. For example, +// specifying `size2=[100,undef]` sets the size in the X direction but allows the size in the Y direction to be computed based on yang. // Arguments: // size1 = [width, length] of the bottom end of the prism. // size2 = [width, length] of the top end of the prism. -// h/l = Height of the prism. +// h/l/height/length = Height of the prism. // shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end. // --- +// xang = base angle in the X direction. Can be a scalar or list of two values, one of which may be undef +// yang = base angle in the Y direction. Can be a scalar or list of two values, one of which may be undef // rounding = The roundover radius for the vertical-ish edges of the prismoid. 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) // rounding1 = The roundover radius for the bottom of the vertical-ish edges of the prismoid. 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-]. // rounding2 = The roundover radius for the top of the vertical-ish edges of the prismoid. 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-]. @@ -599,14 +601,14 @@ function cuboid( // 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` // -// See Also: cuboid(), rounded_prism() +// See Also: cuboid(), rounded_prism(), trapezoid() // +// Example: Truncated Pyramid +// prismoid(size1=[35,50], size2=[20,30], h=20); // Example: Rectangular Pyramid // prismoid([40,40], [0,0], h=20); // Example: Prism // prismoid(size1=[40,40], size2=[0,40], h=20); -// Example: Truncated Pyramid -// prismoid(size1=[35,50], size2=[20,30], h=20); // Example: Wedge // prismoid(size1=[60,35], size2=[30,0], h=30); // Example: Truncated Tetrahedron @@ -617,9 +619,19 @@ function cuboid( // prismoid(size1=[30,60], size2=[0,60], shift=[-15,0], h=30); // Example(FlatSpin,VPD=160,VPT=[0,0,10]): Shifting/Skewing // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]); +// Example: Specifying bottom, height and angle +// prismoid(size1=[100,75], h=30, xang=50, yang=70); +// Example: Specifying top, height and angle, with asymmetric angles +// prismoid(size2=[100,75], h=30, xang=[50,60], yang=[70,40]); +// Example: Specifying top, bottom and angle for X and using that to define height. Note that giving yang here would likely give a conflicting height calculation, which is not allowed. +// prismoid(size1=[100,75], size2=[75,35], xang=50); +// Example: The same as the previous example but we give a shift in Y. Note that shift.x must be undef because you cannot give combine an angle with a shift, so a shift.x value would conflict with xang being defined. +// prismoid(size1=[100,75], size2=[75,35], xang=50, shift=[undef,20]); +// Example: The X dimensions defined by the base length, angle and height; the Y dimensions defined by the top length, angle, and height. +// prismoid(size1=[100,undef], size2=[undef,75], h=30, xang=[20,90], yang=30); // Example: Rounding // prismoid(100, 80, rounding=10, h=30); -// Example: Outer Chamfer Only +// Example: Chamfers // prismoid(100, 80, chamfer=5, h=30); // Example: Gradiant Rounding // prismoid(100, 80, rounding1=10, rounding2=0, h=30); @@ -644,46 +656,25 @@ function cuboid( // show_anchors(); module prismoid( - size1, size2, h, shift=[0,0], + size1=undef, size2=undef, h, shift=[undef,undef], + xang, yang, rounding=0, rounding1, rounding2, chamfer=0, chamfer1, chamfer2, l, height, length, center, anchor, spin=0, orient=UP -) { - checks = - assert(is_num(size1) || is_vector(size1,2)) - assert(is_num(size2) || is_vector(size2,2)) - assert(is_num(h) || is_num(l)) - assert(is_vector(shift,2)) - assert(is_num(rounding) || is_vector(rounding,4), "Bad rounding argument.") - assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "Bad rounding1 argument.") - assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "Bad rounding2 argument.") - assert(is_num(chamfer) || is_vector(chamfer,4), "Bad chamfer argument.") - assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "Bad chamfer1 argument.") - assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "Bad chamfer2 argument."); - eps = pow(2,-14); - size1 = is_num(size1)? [size1,size1] : size1; - size2 = is_num(size2)? [size2,size2] : size2; - checks2 = - assert(all_nonnegative(size1)) - assert(all_nonnegative(size2)) - assert(size1.x + size2.x > 0) - assert(size1.y + size2.y > 0); - s1 = [max(size1.x, eps), max(size1.y, eps)]; - s2 = [max(size2.x, eps), max(size2.y, eps)]; - rounding1 = default(rounding1, rounding); - rounding2 = default(rounding2, rounding); - chamfer1 = default(chamfer1, chamfer); - chamfer2 = default(chamfer2, chamfer); - anchor = get_anchor(anchor, center, BOT, BOT); - vnf = prismoid( +) +{ + vnf_s1_s2_shift = prismoid( size1=size1, size2=size2, h=h, shift=shift, + xang=xang, yang=yang, + rounding=rounding, chamfer=chamfer, rounding1=rounding1, rounding2=rounding2, chamfer1=chamfer1, chamfer2=chamfer2, - l=l, center=CENTER + l=l, height=height, length=length, center=CENTER, _return_dim=true ); - attachable(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift) { - vnf_polyhedron(vnf, convexity=4); + anchor = get_anchor(anchor, center, BOT, BOT); + attachable(anchor,spin,orient, size=vnf_s1_s2_shift[1], size2=vnf_s1_s2_shift[2], shift=vnf_s1_s2_shift[3]) { + vnf_polyhedron(vnf_s1_s2_shift[0], convexity=4); children(); } } @@ -693,78 +684,90 @@ function prismoid( rounding=0, rounding1, rounding2, chamfer=0, chamfer1, chamfer2, l, height, length, center, - anchor=DOWN, spin=0, orient=UP + anchor=DOWN, spin=0, orient=UP, xang, yang, + _return_dim=false + ) = - assert(is_vector(size1,2)) - assert(is_vector(size2,2)) - assert(is_num(h) || is_num(l)) - assert(is_vector(shift,2)) - assert( - (is_num(rounding) && rounding>=0) || - (is_vector(rounding,4) && all_nonnegative(rounding)), - "Bad rounding argument." + assert(is_undef(shift) || is_num(shift) || len(shift)==2, "shift must be a number or list of length 2") + assert(is_undef(size1) || is_num(size1) || len(size1)==2, "size1 must be a number or list of length 2") + assert(is_undef(size2) || is_num(size2) || len(size2)==2, "size2 must be a number or list of length 2") + let( + xang = force_list(xang,2), + yang = force_list(yang,2), + yangOK = len(yang)==2 && (yang==[undef,undef] || (all_positive(yang) && yang[0]<180 && yang[1]<180)), + xangOK = len(xang)==2 && (xang==[undef,undef] || (all_positive(xang) && xang[0]<180 && xang[1]<180)), + size1=force_list(size1,2), + size2=force_list(size2,2), + h=first_defined([l,h,length,height]), + shift = force_list(shift,2), + + fff=echo(size1=size1,size2=size2) ) - assert( - is_undef(rounding1) || (is_num(rounding1) && rounding1>=0) || - (is_vector(rounding1,4) && all_nonnegative(rounding1)), - "Bad rounding1 argument." + assert(xangOK, "prismoid angles must be scalar or 2-vector, strictly between 0 and 180") + assert(yangOK, "prismoid angles must be scalar or 2-vector, strictly between 0 and 180") + assert(xang==[undef,undef] || shift.x==undef, "Cannot specify xang and a shift.x value together") + assert(yang==[undef,undef] || shift.y==undef, "Cannot specify yang and a shift.y value together") + assert(all_positive([h]) || is_undef(h), "h must be a positive value") + let( + hx = _trapezoid_dims(h,size1.x,size2.x,shift.x,xang)[0], + hy = _trapezoid_dims(h,size1.y,size2.y,shift.y,yang)[0] + ,eerr=echo(hx=hx,hy=hy,xang=xang,yang=yang) ) - assert( - is_undef(rounding2) || (is_num(rounding2) && rounding2>=0) || - (is_vector(rounding2,4) && all_nonnegative(rounding2)), - "Bad rounding2 argument." - ) - assert( - (is_num(chamfer) && chamfer>=0) || - (is_vector(chamfer,4) && all_nonnegative(chamfer)), - "Bad chamfer argument." - ) - assert( - is_undef(chamfer1) || (is_num(chamfer1) && chamfer1>=0) || - (is_vector(chamfer1,4) && all_nonnegative(chamfer1)), - "Bad chamfer1 argument." - ) - assert( - is_undef(chamfer2) || (is_num(chamfer2) && chamfer2>=0) || - (is_vector(chamfer2,4) && all_nonnegative(chamfer2)), - "Bad chamfer2 argument." + assert(num_defined([hx,hy])>0, "Height not given and specification does not determine prismoid height") + assert(hx==undef || hy==undef || approx(hx,hy), + str("X and Y angle specifications give rise to conflicting height values ",hx," and ",hy)) + let( + h = first_defined([hx,hy]), + x_h_w1_w2_shift = _trapezoid_dims(h,size1.x,size2.x,shift.x,xang), + y_h_w1_w2_shift = _trapezoid_dims(h,size1.y,size2.y,shift.y,yang) ) let( - eps = pow(2,-14), - h = one_defined([h,l,length,height],"h,l,length,height",dflt=1), - shiftby = point3d(point2d(shift)), - s1 = [max(size1.x, eps), max(size1.y, eps)], - s2 = [max(size2.x, eps), max(size2.y, eps)], + s1 = [x_h_w1_w2_shift[1], y_h_w1_w2_shift[1]], + s2 = [x_h_w1_w2_shift[2], y_h_w1_w2_shift[2]], + shift = [x_h_w1_w2_shift[3], y_h_w1_w2_shift[3]] + ) + assert(is_vector(s1,2), "Insufficient information to define prismoid") + assert(is_vector(s2,2), "Insufficient information to define prismoid") + assert(all_nonnegative(concat(s1,s2)),"Degenerate prismoid geometry") + assert(s1.x+s2.x>0 && s1.y+s2.y>0, "Degenerate prismoid geometry") + assert(is_num(rounding) || is_vector(rounding,4), "rounding must be a number or 4-vector") + assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "rounding1 must be a number or 4-vector") + assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "rounding2 must be a number or 4-vector") + assert(is_num(chamfer) || is_vector(chamfer,4), "chamfer must be a number or 4-vector") + assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "chamfer1 must be a number or 4-vector") + assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "chamfer2 must be a number or 4-vector") + let( + chamfer1=force_list(default(chamfer1,chamfer),4), + chamfer2=force_list(default(chamfer2,chamfer),4), + rounding1=force_list(default(rounding1,rounding),4), + rounding2=force_list(default(rounding2,rounding),4) + ) + assert(all_nonnegative(chamfer1), "chamfer/chamfer1 must be non-negative") + assert(all_nonnegative(chamfer2), "chamfer/chamfer2 must be non-negative") + assert(all_nonnegative(rounding1), "rounding/rounding1 must be non-negative") + assert(all_nonnegative(rounding2), "rounding/rounding2 must be non-negative") + assert(all_zero(v_mul(rounding1,chamfer1),0), + "rounding1 and chamfer1 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner") + assert(all_zero(v_mul(rounding2,chamfer2),0), + "rounding2 and chamfer2 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner") + let( + ffda=echo(s1=s1,s2=s2,h=h,shift=shift), rounding1 = default(rounding1, rounding), rounding2 = default(rounding2, rounding), chamfer1 = default(chamfer1, chamfer), chamfer2 = default(chamfer2, chamfer), anchor = get_anchor(anchor, center, BOT, BOT), - vnf = (rounding1==0 && rounding2==0 && chamfer1==0 && chamfer2==0)? ( - let( - corners = [[1,1],[1,-1],[-1,-1],[-1,1]] * 0.5, - points = [ - for (p=corners) point3d(v_mul(s2,p), +h/2) + shiftby, - for (p=corners) point3d(v_mul(s1,p), -h/2) - ], - faces=[ - [0,1,2], [0,2,3], [0,4,5], [0,5,1], - [1,5,6], [1,6,2], [2,6,7], [2,7,3], - [3,7,4], [3,4,0], [4,7,6], [4,6,5], - ] - ) [points, faces] - ) : ( - let( - path1 = rect(size1, rounding=rounding1, chamfer=chamfer1, anchor=CTR), - path2 = rect(size2, rounding=rounding2, chamfer=chamfer2, anchor=CTR), - points = [ + path1 = rect(s1, rounding=rounding1, chamfer=chamfer1, anchor=CTR), + path2 = rect(s2, rounding=rounding2, chamfer=chamfer2, anchor=CTR), + points = [ each path3d(path1, -h/2), - each path3d(move(shiftby, p=path2), +h/2), - ], - faces = hull(points) - ) [points, faces] - ) - ) reorient(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift, p=vnf); + each path3d(move(shift, path2), +h/2), + ], + faces = hull(points), + vnf = [points, faces] + ) + _return_dim ? [reorient(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift, p=vnf),point3d(s1,h),s2,shift] + : reorient(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift, p=vnf); // Function&Module: octahedron() @@ -1009,7 +1012,7 @@ module rect_tube( assert(is_undef(ichamfer) || is_num(ichamfer) || (is_list(ichamfer) && len(ichamfer)==4), "ichamfer must be a number or 4-vector") assert(is_undef(ichamfer1) || is_num(ichamfer1) || (is_list(ichamfer1) && len(ichamfer1)==4), "ichamfer1 must be a number or 4-vector") assert(is_undef(ichamfer2) || is_num(ichamfer2) || (is_list(ichamfer2) && len(ichamfer2)==4), "ichamfer2 must be a number or 4-vector"); - chamfer1=force_list( is_def(chamfer1)?chamfer1 : default(chamfer1,chamfer),4); + chamfer1=force_list(default(chamfer1,chamfer),4); chamfer2=force_list(default(chamfer2,chamfer),4); rounding1=force_list(default(rounding1,rounding),4); rounding2=force_list(default(rounding2,rounding),4); From 83b09c259f4c35432bec645607a007396449a7f9 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sat, 25 Mar 2023 01:09:55 -0400 Subject: [PATCH 2/7] fix tests to agree with code --- tests/test_shapes3d.scad | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_shapes3d.scad b/tests/test_shapes3d.scad index 368eaee..5fd6229 100644 --- a/tests/test_shapes3d.scad +++ b/tests/test_shapes3d.scad @@ -34,12 +34,12 @@ test_sphere(); module test_prismoid() { $fn=24; - assert_approx(prismoid([100,80],[50,40],h=50), [[[25,20,50],[25,-20,50],[-25,-20,50],[-25,20,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); - assert_approx(prismoid([100,80],[50,40],h=50,anchor=BOT), [[[25,20,50],[25,-20,50],[-25,-20,50],[-25,20,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); - assert_approx(prismoid([100,80],[0,0],h=50,anchor=BOT), [[[0.000030517578125,0.000030517578125,50],[0.000030517578125,-0.000030517578125,50],[-0.000030517578125,-0.000030517578125,50],[-0.000030517578125,0.000030517578125,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); - assert_approx(prismoid([100,80],[40,50],h=50,anchor=BOT), [[[20,25,50],[20,-25,50],[-20,-25,50],[-20,25,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); - assert_approx(prismoid([100,80],[40,50],h=50,anchor=TOP+RIGHT), [[[0,25,0],[0,-25,0],[-40,-25,0],[-40,25,0],[30,40,-50],[30,-40,-50],[-70,-40,-50],[-70,40,-50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); - assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5]), [[[30,30,50],[30,-20,50],[-10,-20,50],[-10,30,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); + assert_approx(prismoid([100,80],[50,40],h=50),[[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[25,-20,50],[-25,-20,50],[-25,20,50],[25,20,50]],[[3,4,7],[4,6,7],[6,3,7],[4,5,6],[5,2,6],[2,3,6],[4,1,5],[1,2,5],[4,0,1],[0,2,1],[0,3,2],[4,3,0]]]); + assert_approx(prismoid([100,80],[50,40],h=50,anchor=BOT),[[[50, -40, 0], [-50, -40, 0], [-50, 40, 0], [50, 40, 0], [25, -20, 50], [-25, -20, 50], [-25, 20, 50], [25, 20, 50]], [[3, 4, 7], [4, 6, 7], [6, 3, 7], [4, 5, 6], [5, 2, 6], [2, 3, 6], [4, 1, 5], [1, 2, 5], [4, 0, 1], [0, 2, 1], [0, 3, 2], [4, 3, 0]]]); + assert_approx(prismoid([100,80],[0,0],h=50,anchor=BOT),[[[50, -40, 0], [-50, -40, 0], [-50, 40, 0], [50, 40, 0], [0, 0, 50], [0, 0, 50], [0, 0, 50], [0, 0, 50]], [[2, 4, 1], [4, 0, 1], [0, 2, 1], [0, 3, 2], [4, 3, 0], [3, 4, 2]]]); + assert_approx(prismoid([100,80],[40,50],h=50,anchor=BOT),[[[50, -40, 0], [-50, -40, 0], [-50, 40, 0], [50, 40, 0], [20, -25, 50], [-20, -25, 50], [-20, 25, 50], [20, 25, 50]], [[3, 4, 7], [4, 6, 7], [6, 3, 7], [4, 5, 6], [5, 2, 6], [2, 3, 6], [4, 1, 5], [1, 2, 5], [4, 0, 1], [0, 2, 1], [0, 3, 2], [4, 3, 0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,anchor=TOP+RIGHT), [[[30, -40, -50], [-70, -40, -50], [-70, 40, -50], [30, 40, -50], [0, -25, 0], [-40, -25, 0], [-40, 25, 0], [0, 25, 0]], [[3, 4, 7], [4, 6, 7], [6, 3, 7], [4, 5, 6], [5, 2, 6], [2, 3, 6], [4, 1, 5], [1, 2, 5], [4, 0, 1], [0, 2, 1], [0, 3, 2], [4, 3, 0]]]); + assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5]), [[[50, -40, 0], [-50, -40, 0], [-50, 40, 0], [50, 40, 0], [30, -20, 50], [-10, -20, 50], [-10, 30, 50], [30, 30, 50]], [[2, 7, 6], [7, 5, 6], [5, 2, 6], [7, 4, 5], [4, 1, 5], [1, 2, 5], [7, 0, 4], [0, 1, 4], [0, 7, 3], [7, 2, 3], [2, 0, 3], [2, 1, 0]]]); assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[14,7,15],[7,8,15],[8,14,15],[5,14,13],[14,12,13],[12,5,13],[14,11,12],[11,4,12],[4,5,12],[14,10,11],[3,4,11],[10,3,11],[2,3,10],[9,2,10],[14,9,10],[14,8,9],[1,2,9],[8,1,9],[7,0,8],[0,1,8],[14,6,7],[6,0,7],[14,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]); assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[11,9,10],[9,4,10],[4,5,10],[5,11,10],[2,3,9],[8,2,9],[11,8,9],[3,4,9],[1,2,8],[11,0,8],[0,1,8],[0,11,7],[11,6,7],[6,0,7],[11,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]); assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[10,3,11],[3,4,11],[4,10,11],[2,10,9],[10,8,9],[8,2,9],[10,7,8],[7,2,8],[1,2,7],[6,1,7],[10,6,7],[10,5,6],[5,1,6],[10,4,5],[0,1,5],[4,0,5],[3,0,4],[10,2,3],[2,0,3],[2,1,0]]]); From d3eb60ad0e5c91ad6ed295494d1bddbc5ad9f18c Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sat, 25 Mar 2023 08:10:51 -0400 Subject: [PATCH 3/7] remove debug echoes --- shapes3d.scad | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/shapes3d.scad b/shapes3d.scad index 0f60c95..95c2843 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -699,9 +699,7 @@ function prismoid( size1=force_list(size1,2), size2=force_list(size2,2), h=first_defined([l,h,length,height]), - shift = force_list(shift,2), - - fff=echo(size1=size1,size2=size2) + shift = force_list(shift,2) ) assert(xangOK, "prismoid angles must be scalar or 2-vector, strictly between 0 and 180") assert(yangOK, "prismoid angles must be scalar or 2-vector, strictly between 0 and 180") @@ -711,7 +709,6 @@ function prismoid( let( hx = _trapezoid_dims(h,size1.x,size2.x,shift.x,xang)[0], hy = _trapezoid_dims(h,size1.y,size2.y,shift.y,yang)[0] - ,eerr=echo(hx=hx,hy=hy,xang=xang,yang=yang) ) assert(num_defined([hx,hy])>0, "Height not given and specification does not determine prismoid height") assert(hx==undef || hy==undef || approx(hx,hy), @@ -751,7 +748,6 @@ function prismoid( assert(all_zero(v_mul(rounding2,chamfer2),0), "rounding2 and chamfer2 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner") let( - ffda=echo(s1=s1,s2=s2,h=h,shift=shift), rounding1 = default(rounding1, rounding), rounding2 = default(rounding2, rounding), chamfer1 = default(chamfer1, chamfer), From 63da84dcf6df98e85c4b750b98b61bb11fb4dcc9 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Sun, 26 Mar 2023 17:00:05 +0100 Subject: [PATCH 4/7] Fix BOSL2logo.scad: tag syntax changed Online demo of fix: https://ochafik.com/openscad2/#%7B%22params%22%3A%7B%22sourcePath%22%3A%22%2Fhome%2Fplayground.scad%22%2C%22source%22%3A%22include%20%3CBOSL2%2Fstd.scad%3E%5Cninclude%20%3CBOSL2%2Fgears.scad%3E%5Cninclude%20%3CBOSL2%2Fbeziers.scad%3E%5Cninclude%20%3CBOSL2%2Fscrews.scad%3E%5Cninclude%20%3CBOSL2%2Fcubetruss.scad%3E%5Cn%5Cn%24fa%3D1%3B%5Cn%24fs%3D1%3B%5Cn%5Cnxdistribute(50)%20%7B%5Cn%5Ctrecolor(%5C%22%23f77%5C%22)%5Cn%5Ctdiff(%5C%22hole%5C%22)%5Cn%5Ctcuboid(%5B45%2C45%2C10%5D%2C%20chamfer%3D10%2C%20edges%3D%5BRIGHT%2BBACK%2CRIGHT%2BFRONT%5D%2C%20anchor%3DFRONT)%20%7B%5Cn%5Ct%5Cttag(%5C%22hole%5C%22)cuboid(%5B30%2C30%2C11%5D%2C%20chamfer%3D5%2C%20edges%3D%5BRIGHT%2BBACK%2CRIGHT%2BFRONT%5D)%3B%5Cn%5Ct%5Ctattach(FRONT%2CBACK%2C%20overlap%3D5)%20%7B%5Cn%5Ct%5Ct%5Ctdiff(%5C%22hole2%5C%22)%5Cn%5Ct%5Ct%5Ctcuboid(%5B45%2C45%2C10%5D%2C%20rounding%3D15%2C%20edges%3D%5BRIGHT%2BBACK%2CRIGHT%2BFRONT%5D)%20%7B%5Cn%5Ct%5Ct%5Ct%5Cttag(%5C%22hole2%5C%22)cuboid(%5B30%2C30%2C11%5D%2C%20rounding%3D10%2C%20edges%3D%5BRIGHT%2BBACK%2CRIGHT%2BFRONT%5D)%3B%5Cn%5Ct%5Ct%5Ct%7D%5Cn%5Ct%5Ct%7D%5Cn%5Ct%7D%5Cn%5Cn%20%20%20%20recolor(%5C%22%237f7%5C%22)%5Cn%5Ctbevel_gear(pitch%3D8%2C%20teeth%3D20%2C%20face_width%3D12%2C%20shaft_diam%3D25%2C%20pitch_angle%3D45%2C%20slices%3D12%2C%20spiral_angle%3D30)%3B%5Cn%5Cn%5Ctx%20%3D%2018%3B%5Cn%5Cty%20%3D%2020%3B%5Cn%5Cts1%20%3D%2025%3B%5Cn%5Cts2%20%3D%2020%3B%5Cn%5Ctsbez%20%3D%20%5B%5Cn%5Ct%5Ct%20%20%20%20%20%20%20%20%20%20%20%20%5B-x%2C-y%5D%2C%20%5B-x%2C-y-s1%5D%2C%5Cn%5Ct%5Ct%5B%20x%2C-y-s1%5D%2C%20%5B%20x%2C-y%5D%2C%20%5B%20x%2C-y%2Bs2%5D%2C%5Cn%5Ct%5Ct%5B-x%2C%20y-s2%5D%2C%20%5B-x%2C%20y%5D%2C%20%5B-x%2C%20y%2Bs1%5D%2C%5Cn%5Ct%5Ct%5B%20x%2C%20y%2Bs1%5D%2C%20%5B%20x%2C%20y%5D%5Cn%5Ct%5D%3B%5Cn%5Ctrecolor(%5C%22%2399f%5C%22)%5Cn%5Ctpath_sweep(regular_ngon(n%3D3%2Cd%3D10%2Cspin%3D90)%2C%20bezpath_curve(sbez))%3B%5Cn%5Cn%5Ctrecolor(%5C%22%230bf%5C%22)%5Cn%5Cttranslate(%5B-15%2C-35%2C0%5D)%5Cn%5Ctcubetruss_corner(size%3D10%2C%20strut%3D1%2C%20h%3D1%2C%20bracing%3Dfalse%2C%20extents%3D%5B3%2C8%2C0%2C0%2C0%5D%2C%20clipthick%3D0)%3B%5Cn%5Cn%5Ctrecolor(%5C%22%23777%5C%22)%5Cn%5Ctxdistribute(24)%20%7B%5Cn%5Ct%5Ctscrew(%5C%22M12%2C70%5C%22%2C%20head%3D%5C%22hex%5C%22%2C%20anchor%3D%5C%22origin%5C%22%2C%20orient%3DBACK)%5Cn%5Ct%5Ct%5Ctattach(BOT%2CCENTER)%5Cn%5Ct%5Ct%5Ct%5Ctnut(%5C%22M12%5C%22%2C%20thickness%3D10%2C%20diameter%3D20)%3B%5Cn%5Ct%5Ctscrew(%5C%22M12%2C70%5C%22%2C%20head%3D%5C%22hex%5C%22%2C%20anchor%3D%5C%22origin%5C%22%2C%20orient%3DBACK)%5Cn%5Ct%5Ct%5Ctattach(BOT%2CCENTER)%5Cn%5Ct%5Ct%5Ct%5Ctnut(%5C%22M12%5C%22%2C%20thickness%3D10%2C%20diameter%3D20)%3B%5Cn%5Ct%7D%5Cn%7D%5Cn%22%2C%22features%22%3A%5B%22manifold%22%2C%22fast-csg%22%2C%22lazy-union%22%5D%7D%2C%22view%22%3A%7B%22layout%22%3A%7B%22mode%22%3A%22multi%22%2C%22focus%22%3Afalse%2C%22editor%22%3Atrue%2C%22viewer%22%3Atrue%2C%22customizer%22%3Afalse%7D%2C%22logs%22%3Atrue%7D%7D --- examples/BOSL2logo.scad | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/BOSL2logo.scad b/examples/BOSL2logo.scad index a24785a..c533e80 100644 --- a/examples/BOSL2logo.scad +++ b/examples/BOSL2logo.scad @@ -11,11 +11,11 @@ xdistribute(50) { recolor("#f77") diff("hole") cuboid([45,45,10], chamfer=10, edges=[RIGHT+BACK,RIGHT+FRONT], anchor=FRONT) { - cuboid([30,30,11], chamfer=5, edges=[RIGHT+BACK,RIGHT+FRONT], $tags="hole"); + tag("hole")cuboid([30,30,11], chamfer=5, edges=[RIGHT+BACK,RIGHT+FRONT]); attach(FRONT,BACK, overlap=5) { - diff("hole") + diff("hole2") cuboid([45,45,10], rounding=15, edges=[RIGHT+BACK,RIGHT+FRONT]) { - cuboid([30,30,11], rounding=10, edges=[RIGHT+BACK,RIGHT+FRONT], $tags="hole"); + tag("hole2")cuboid([30,30,11], rounding=10, edges=[RIGHT+BACK,RIGHT+FRONT]); } } } @@ -50,4 +50,3 @@ xdistribute(50) { nut("M12", thickness=10, diameter=20); } } - From ee3e2466bd99da87b2b3a677835ffcc3db6d418f Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sun, 26 Mar 2023 17:35:58 -0700 Subject: [PATCH 5/7] Fix reorient() for CTR anchors on prismoid/trapezoid geometry. --- attachments.scad | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/attachments.scad b/attachments.scad index 846ed0e..af62c38 100644 --- a/attachments.scad +++ b/attachments.scad @@ -2031,10 +2031,7 @@ function reorient( orient = default(orient, UP), region = !is_undef(region)? region : !is_undef(path)? [path] : - undef - ) - (anchor==CENTER && spin==0 && orient==UP && p!=undef)? p : - let( + undef, geom = is_def(geom)? geom : attach_geom( size=size, size2=size2, shift=shift, From 2f1266e8c3116ce63a601ebf0f3136528a8c2666 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Mon, 27 Mar 2023 02:21:26 -0700 Subject: [PATCH 6/7] Corrected prismoid geometry corner anchor vectors. --- attachments.scad | 25 +++++++++++++++++++------ vectors.scad | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/attachments.scad b/attachments.scad index af62c38..5361964 100644 --- a/attachments.scad +++ b/attachments.scad @@ -2542,12 +2542,25 @@ function _find_anchor(anchor, geom) = bot = point3d(v_mul(point2d(size )/2, axy), -h/2), top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2), pos = point3d(cp) + lerp(bot,top,u) + offset, - vecs = [ - if (anch.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,h], p=[axy.x,0,0]), UP), - if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,h], p=[0,axy.y,0]), UP), - if (anch.z!=0) anch==CENTER? UP : unit([0,0,anch.z],UP) - ], - vec = anchor==CENTER? UP : rot(from=UP, to=axis, p=unit(sum(vecs) / len(vecs))), + vecs = anchor==CENTER? [UP] + : [ + if (anch.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,h], p=[axy.x,0,0]), UP), + if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,h], p=[0,axy.y,0]), UP), + if (anch.z!=0) unit([0,0,anch.z],UP) + ], + vec2 = anchor==CENTER? UP + : len(vecs)==1? unit(vecs[0],UP) + : len(vecs)==2? vector_bisect(vecs[0],vecs[1]) + : let( + v1 = vector_bisect(vecs[0],vecs[2]), + v2 = vector_bisect(vecs[1],vecs[2]), + p1 = plane_from_normal(yrot(90,p=v1)), + p2 = plane_from_normal(xrot(-90,p=v2)), + line = plane_intersection(p1,p2), + v3 = unit(line[1]-line[0],UP) * anch.z + ) + unit(v3,UP), + vec = rot(from=UP, to=axis, p=vec2), pos2 = rot(from=UP, to=axis, p=pos) ) [anchor, pos2, vec, oang] ) : type == "conoid"? ( //r1, r2, l, shift diff --git a/vectors.scad b/vectors.scad index a654716..f588b82 100644 --- a/vectors.scad +++ b/vectors.scad @@ -284,6 +284,26 @@ function vector_axis(v1,v2=undef,v3=undef) = ) unit(cross(w1,w3)); +// Function: vector_bisect() +// Usage: +// newv = vector_bisect(v1,v2); +// Description: +// Returns a unit vector that exactly bisects the minor angle between two given vectors. +// If given two vectors that are directly opposed, returns `undef`. +function vector_bisect(v1,v2) = + assert(is_vector(v1)) + assert(is_vector(v2)) + assert(!approx(norm(v1),0), "Zero length vector.") + assert(!approx(norm(v2),0), "Zero length vector.") + assert(len(v1)==len(v2), "Vectors are of different sizes.") + let( v1 = unit(v1), v2 = unit(v2) ) + approx(v1,-v2)? undef : + let( + axis = vector_axis(v1,v2), + ang = vector_angle(v1,v2), + v3 = rot(ang/2, v=axis, p=v1) + ) v3; + // Section: Vector Searching From 1478a2e348e324fa0123898db247161fc5afb368 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Mon, 27 Mar 2023 02:22:17 -0700 Subject: [PATCH 7/7] Corrected prismoid anchoring. --- shapes3d.scad | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shapes3d.scad b/shapes3d.scad index 95c2843..c1222be 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -670,11 +670,12 @@ module prismoid( rounding=rounding, chamfer=chamfer, rounding1=rounding1, rounding2=rounding2, chamfer1=chamfer1, chamfer2=chamfer2, - l=l, height=height, length=length, center=CENTER, _return_dim=true + l=l, height=height, length=length, anchor=BOT, _return_dim=true ); anchor = get_anchor(anchor, center, BOT, BOT); attachable(anchor,spin,orient, size=vnf_s1_s2_shift[1], size2=vnf_s1_s2_shift[2], shift=vnf_s1_s2_shift[3]) { - vnf_polyhedron(vnf_s1_s2_shift[0], convexity=4); + down(vnf_s1_s2_shift[1].z/2) + vnf_polyhedron(vnf_s1_s2_shift[0], convexity=4); children(); } }