From fdac4545e856670a6f1ee4a791d92e230adccab7 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Wed, 4 Oct 2023 22:33:53 -0700 Subject: [PATCH 1/4] Added generic_airplane(), show_transform_list() --- attachments.scad | 88 ++++++++++++++++++++++++++++++++++++- examples/spring_handle.scad | 45 +++++++++++++++++++ 2 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 examples/spring_handle.scad diff --git a/attachments.scad b/attachments.scad index 3657741..838b77c 100644 --- a/attachments.scad +++ b/attachments.scad @@ -3587,7 +3587,7 @@ module show_anchors(s=10, std=true, custom=true) { // Synopsis: Shows a 3d anchor orientation arrow. // SynTags: Geom // Topics: Attachments -// See Also: anchor_arrow2d(), show_anchors(), expose_anchors(), frame_ref() +// See Also: anchor_arrow2d(), show_anchors(), expose_anchors(), frame_ref(), generic_airplane() // Usage: // anchor_arrow([s], [color], [flag], [anchor=], [orient=], [spin=]) [ATTACHMENTS]; // Description: @@ -3628,7 +3628,7 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tag="anchor-arrow", // Topics: Attachments // See Also: anchor_arrow(), show_anchors(), expose_anchors(), frame_ref() // Usage: -// anchor_arrow2d([s], [color], [flag]); +// anchor_arrow2d([s], [color]); // Description: // Show an anchor orientation arrow. // Arguments: @@ -3668,6 +3668,90 @@ module expose_anchors(opacity=0.2) { +// Module: show_transform_list() +// Synopsis: Shows a list of transforms and how they connect. +// SynTags: Geom +// Topics: Attachments +// See Also: generic_airplane(), anchor_arrow(), show_anchors(), expose_anchors(), frame_ref() +// Usage: +// show_transform_list(tlist, [s]); +// show_transform_list(tlist) {CHILDREN}; +// Description: +// Given a list of transformation matrices, shows the position and orientation of each one. +// A line is drawn from each transform position to the next one, and an orientation indicator is +// shown at each position. If a child is passed, that child will be used as the orientation indicator. +// By default, a {{generic_airplane()}} is used as the orientation indicator. +// Arguments: +// s = Length of the {{generic_airplane()}}. Default: 5 +// Example: +// tlist = [ +// zrot(90), +// zrot(90) * fwd(30) * zrot(30), +// zrot(90) * fwd(30) * zrot(30) * +// fwd(35) * xrot(-30), +// zrot(90) * fwd(30) * zrot(30) * +// fwd(35) * xrot(-30) * fwd(40) * yrot(15), +// ]; +// show_transform_list(tlist, s=20); +// Example: +// tlist = [ +// zrot(90), +// zrot(90) * fwd(30) * zrot(30), +// zrot(90) * fwd(30) * zrot(30) * +// fwd(35) * xrot(-30), +// zrot(90) * fwd(30) * zrot(30) * +// fwd(35) * xrot(-30) * fwd(40) * yrot(15), +// ]; +// show_transform_list(tlist) frame_ref(); +module show_transform_list(tlist, s=5) { + path = [for (m = tlist) apply(m, [0,0,0])]; + stroke(path, width=s*0.03); + for (m = tlist) { + multmatrix(m) { + if ($children>0) children(); + else generic_airplane(s=s); + } + } +} + + +// Module: generic_airplane() +// Synopsis: Shows a generic airplane shape, useful for viewing orientations. +// SynTags: Geom +// Topics: Attachments +// See Also: anchor_arrow(), show_anchors(), expose_anchors(), frame_ref() +// Usage: +// generic_airplane([s]); +// Description: +// Creates a generic airplane shape. This can be useful for viewing the orientation of 3D transforms. +// Arguments: +// s = Length of the airplane. Default: 5 +// Example: +// generic_airplane(s=20); +module generic_airplane(s=5) { + $fn = max(segs(0.05*s), 12); + color("#ddd") + fwd(s*0.05) + ycyl(l=0.7*s, d=0.1*s) { + attach(FWD) top_half(s=s) zscale(2) sphere(d=0.1*s); + attach(BACK,FWD) ycyl(l=0.2*s, d1=0.1*s, d2=0.05*s) { + yrot_copies([-90,0,90]) + prismoid(s*[0.01,0.2], s*[0.01,0.05], + h=0.2*s, shift=s*[0,0.15], anchor=BOT); + } + yrot_copies([-90,90]) + prismoid(s*[0.01,0.2], s*[0.01,0.05], + h=0.5*s, shift=s*[0,0.15], anchor=BOT); + } + color("#777") zcopies(0.1*s) sphere(d=0.02*s); + back(0.09*s) { + color("#f00") right(0.46*s) sphere(d=0.04*s); + color("#0f0") left(0.46*s) sphere(d=0.04*s); + } +} + + + // Module: frame_ref() // Synopsis: Shows axis orientation arrows. // SynTags: Geom diff --git a/examples/spring_handle.scad b/examples/spring_handle.scad new file mode 100644 index 0000000..1299894 --- /dev/null +++ b/examples/spring_handle.scad @@ -0,0 +1,45 @@ +include + +$fn = 45; +wire_d = 2; +spring_l = 100; +spring_d = 20; +rod_d = 10; +loops = 17; +tight_loops=3; + +tpart = tight_loops/loops; +lpart = wire_d * tight_loops / 100; +r_table = [ + [0.00, 0], + [0+tpart, 0], + [0.5, 1], + [1-tpart, 0], + [1.00, 0], +]; +l_table = [ + [0.00, -0.50], + [0+tpart, -0.5+lpart], + [1-tpart, +0.5-lpart], + [1.00, +0.50], +]; +lsteps = 45; +tsteps = loops * lsteps; +path = [ + for (i = [0:1:tsteps]) + let( + u = i / tsteps, + a = u * 360 * loops, + r = lookup(u, r_table) * spring_d/2 + wire_d/2 + rod_d/2, + z = lookup(u, l_table) * spring_l, + pt = [r*cos(a), r*sin(a), z] + ) pt +]; +yrot(90) { + color("lightblue") + path_sweep(circle(d=wire_d), path); + cylinder(d=rod_d, h=spring_l+10, center=true); +} + + +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap From d37917c67124d7ac8164412404054db6c6b5e2c7 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Tue, 10 Oct 2023 14:43:16 -0700 Subject: [PATCH 2/4] Enabled various shapes to not barf on zero or negative sizes. --- attachments.scad | 35 +++++++++++++-------- distributors.scad | 55 +++++++++++++++----------------- shapes2d.scad | 80 ++++++++++++++++++++++++++++++----------------- shapes3d.scad | 7 +++-- 4 files changed, 103 insertions(+), 74 deletions(-) diff --git a/attachments.scad b/attachments.scad index ae6d814..4f04551 100644 --- a/attachments.scad +++ b/attachments.scad @@ -3248,7 +3248,11 @@ function _find_anchor(anchor, geom) = let( size=geom[1], size2=geom[2], shift=point2d(geom[3]), axis=point3d(geom[4]), - override = geom[5](anchor), + override = geom[5](anchor) + ) + let( + size = [for (c = size) max(0,c)], + size2 = [for (c = size2) max(0,c)], anch = rot(from=axis, to=UP, p=anchor), offset = rot(from=axis, to=UP, p=offset), h = size.z, @@ -3259,8 +3263,8 @@ function _find_anchor(anchor, geom) = pos = point3d(cp) + lerp(bot,top,u) + offset, 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.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,max(0.01,h)], p=[axy.x,0,0]), UP), + if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,max(0.01,h)], p=[0,axy.y,0]), UP), if (anch.z!=0) unit([0,0,anch.z],UP) ], vec2 = anchor==CENTER? UP @@ -3385,10 +3389,12 @@ function _find_anchor(anchor, geom) = size=geom[1], size2=geom[2], shift=geom[3], 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], + bkpt = [size2/2*anchor.x+shift, size.y/2], override = geom[4](anchor), - pos = default(override[0],point2d(cp) + lerp(frpt, bkpt, u) + point2d(offset)), - svec = point3d(line_normal(bkpt,frpt)*anchor.x), + pos = override[0] != undef? override[0] : + point2d(cp) + lerp(frpt, bkpt, u) + point2d(offset), + svec = approx(bkpt,frpt)? [anchor.x,0,0] : + point3d(line_normal(bkpt,frpt)*anchor.x), vec = is_def(override[1]) ? override[1] : anchor.y == 0? ( anchor.x == 0? BACK : svec ) : anchor.x == 0? [0,anchor.y,0] @@ -3398,13 +3404,16 @@ function _find_anchor(anchor, geom) = let( anchor = unit(_force_anchor_2d(anchor),[0,0]), r = force_list(geom[1],2), - pos = approx(anchor.x,0) ? [0,sign(anchor.y)*r.y] - : let( - m = anchor.y/anchor.x, - px = sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y))) - ) - [px,m*px], - vec = unit([r.y/r.x*pos.x, r.x/r.y*pos.y],BACK) + pos = approx(anchor.x,0) + ? [0,sign(anchor.y)*r.y] + : let( + m = anchor.y/anchor.x, + px = approx(min(r),0)? 0 : + sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y))) + ) + [px,m*px], + vec = approx(min(r),0)? (approx(norm(anchor),0)? BACK : anchor) : + unit([r.y/r.x*pos.x, r.x/r.y*pos.y],BACK) ) [anchor, point2d(cp+offset)+pos, vec, 0] ) : type == "rgn_isect"? ( //region let( diff --git a/distributors.scad b/distributors.scad index d343c78..eddcf3d 100644 --- a/distributors.scad +++ b/distributors.scad @@ -151,16 +151,16 @@ function move_copies(a=[[0,0,0]],p=_NO_ARG) = // See Also: move_copies(), ycopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // // Usage: -// xcopies(spacing, [n], [sp]) CHILDREN; -// xcopies(l, [n], [sp]) CHILDREN; +// xcopies(spacing, [n], [sp=]) CHILDREN; +// xcopies(l=, [n=], [sp=]) CHILDREN; // xcopies(LIST) CHILDREN; // Usage: As a function to translate points, VNF, or Bezier patches -// copies = xcopies(spacing, [n], [sp], p=); -// copies = xcopies(l, [n], [sp], p=); +// copies = xcopies(spacing, [n], [sp=], p=); +// copies = xcopies(l=, [n=], [sp=], p=); // copies = xcopies(LIST, p=); // Usage: Get Translation Matrices -// mats = xcopies(spacing, [n], [sp]); -// mats = xcopies(l, [n], [sp]); +// mats = xcopies(spacing, [n], [sp=]); +// mats = xcopies(l=, [n=], [sp=]); // mats = xcopies(LIST); // Description: // When called as a module, places `n` copies of the children along a line on the X axis. @@ -168,10 +168,10 @@ function move_copies(a=[[0,0,0]],p=_NO_ARG) = // When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`. // // Arguments: -// --- // spacing = Given a scalar, specifies a uniform spacing between copies. Given a list of scalars, each one gives a specific position along the line. (Default: 1.0) // n = Number of copies to place. (Default: 2) -// l = Length to place copies over. +// --- +// l = If given, the length to place copies over. // sp = If given as a point, copies will be placed on a line to the right of starting position `sp`. If given as a scalar, copies will be placed on a line segment to the right of starting position `[sp,0,0]`. If not given, copies will be placed along a line segment that is centered at [0,0,0]. // p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function. // @@ -179,7 +179,6 @@ function move_copies(a=[[0,0,0]],p=_NO_ARG) = // `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually. // `$idx` is set to the index number of each child being copied. // -// // Examples: // xcopies(20) sphere(3); // xcopies(20, n=3) sphere(3); @@ -237,16 +236,16 @@ function xcopies(spacing, n, l, sp, p=_NO_ARG) = // See Also: move_copies(), xcopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // // Usage: -// ycopies(spacing, [n], [sp]) CHILDREN; -// ycopies(l, [n], [sp]) CHILDREN; +// ycopies(spacing, [n], [sp=]) CHILDREN; +// ycopies(l=, [n=], [sp=]) CHILDREN; // ycopies(LIST) CHILDREN; // Usage: As a function to translate points, VNF, or Bezier patches -// copies = ycopies(spacing, [n], [sp], p=); -// copies = ycopies(l, [n], [sp], p=); +// copies = ycopies(spacing, [n], [sp=], p=); +// copies = ycopies(l=, [n=], [sp=], p=); // copies = ycopies(LIST, p=); // Usage: Get Translation Matrices -// mats = ycopies(spacing, [n], [sp]); -// mats = ycopies(l, [n], [sp]); +// mats = ycopies(spacing, [n], [sp=]); +// mats = ycopies(l=, [n=], [sp=]); // mats = ycopies(LIST); // Description: // When called as a module, places `n` copies of the children along a line on the Y axis. @@ -254,10 +253,10 @@ function xcopies(spacing, n, l, sp, p=_NO_ARG) = // When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`. // // Arguments: -// --- // spacing = Given a scalar, specifies a uniform spacing between copies. Given a list of scalars, each one gives a specific position along the line. (Default: 1.0) // n = Number of copies to place on the line. (Default: 2) -// l = Length to place copies over. +// --- +// l = If given, the length to place copies over. // sp = If given as a point, copies will be place on a line back from starting position `sp`. If given as a scalar, copies will be placed on a line back from starting position `[0,sp,0]`. If not given, copies will be placed along a line that is centered at [0,0,0]. // p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function. // @@ -265,7 +264,6 @@ function xcopies(spacing, n, l, sp, p=_NO_ARG) = // `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually. // `$idx` is set to the index number of each child being copied. // -// // Examples: // ycopies(20) sphere(3); // ycopies(20, n=3) sphere(3); @@ -323,16 +321,16 @@ function ycopies(spacing, n, l, sp, p=_NO_ARG) = // See Also: move_copies(), xcopies(), ycopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // // Usage: -// zcopies(spacing, [n], [sp]) CHILDREN; -// zcopies(l, [n], [sp]) CHILDREN; +// zcopies(spacing, [n], [sp=]) CHILDREN; +// zcopies(l=, [n=], [sp=]) CHILDREN; // zcopies(LIST) CHILDREN; // Usage: As a function to translate points, VNF, or Bezier patches -// copies = zcopies(spacing, [n], [sp], p=); -// copies = zcopies(l, [n], [sp], p=); +// copies = zcopies(spacing, [n], [sp=], p=); +// copies = zcopies(l=, [n=], [sp=], p=); // copies = zcopies(LIST, p=); // Usage: Get Translation Matrices -// mats = zcopies(spacing, [n], [sp]); -// mats = zcopies(l, [n], [sp]); +// mats = zcopies(spacing, [n], [sp=]); +// mats = zcopies(l=, [n=], [sp=]); // mats = zcopies(LIST); // Description: // When called as a module, places `n` copies of the children along a line on the Z axis. @@ -340,10 +338,10 @@ function ycopies(spacing, n, l, sp, p=_NO_ARG) = // When called as a function, *with* a `p=` argument, returns a list of transformed copies of `p=`. // // Arguments: -// --- // spacing = Given a scalar, specifies a uniform spacing between copies. Given a list of scalars, each one gives a specific position along the line. (Default: 1.0) // n = Number of copies to place. (Default: 2) -// l = Length to place copies over. +// --- +// l = If given, the length to place copies over. // sp = If given as a point, copies will be placed on a line up from starting position `sp`. If given as a scalar, copies will be placed on a line up from starting position `[0,0,sp]`. If not given, copies will be placed on a line that is centered at [0,0,0]. // p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function. // @@ -351,7 +349,6 @@ function ycopies(spacing, n, l, sp, p=_NO_ARG) = // `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually. // `$idx` is set to the index number of each child being copied. // -// // Examples: // zcopies(20) sphere(3); // zcopies(20, n=3) sphere(3); @@ -478,7 +475,6 @@ function zcopies(spacing, n, l, sp, p=_NO_ARG) = // `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually. // `$idx` is set to the index number of each child being copied. // -// // Examples: // line_copies(10) sphere(d=1.5); // line_copies(10, n=5) sphere(d=3); @@ -592,7 +588,6 @@ function line_copies(spacing, n, l, p1, p2, p=_NO_ARG) = // `$col` is set to the integer column number for each child. // `$row` is set to the integer row number for each child. // -// // Examples: // grid_copies(size=50, spacing=10) cylinder(d=10, h=1); // grid_copies(size=50, spacing=[10,15]) cylinder(d=10, h=1); @@ -851,7 +846,7 @@ function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_ // rot_copies(n=6, v=DOWN+BACK, delta=[20,0,0], subrot=false) // yrot(90) cylinder(h=20, r1=5, r2=0); // color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0); -module rot_copies(rots=[], v=undef, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true) +module rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], subrot=true) { req_children($children); sang = sa + offset; diff --git a/shapes2d.scad b/shapes2d.scad index c914c7c..4f7c34b 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -51,21 +51,26 @@ use function square(size=1, center, anchor, spin=0) = let( anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]), - size = is_num(size)? [size,size] : point2d(size), + size = is_num(size)? [size,size] : point2d(size) + ) + assert(all_positive(size), "All components of size must be positive.") + let( path = [ [ size.x,-size.y], [-size.x,-size.y], [-size.x, size.y], - [ size.x, size.y] + [ size.x, size.y], ] / 2 ) reorient(anchor,spin, two_d=true, size=size, p=path); module square(size=1, center, anchor, spin) { anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]); - size = is_num(size)? [size,size] : point2d(size); + rsize = is_num(size)? [size,size] : point2d(size); + size = [for (c = rsize) max(0,c)]; attachable(anchor,spin, two_d=true, size=size) { - _square(size, center=true); + if (all_positive(size)) + _square(size, center=true); children(); } } @@ -127,8 +132,13 @@ 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 = force_list(size,2); - if (rounding==0 && chamfer==0) { + size = [for (c = force_list(size,2)) max(0,c)]; + if (!all_positive(size)) { + attachable(anchor,spin, two_d=true, size=size) { + union(); + children(); + } + } else if (rounding==0 && chamfer==0) { attachable(anchor, spin, two_d=true, size=size) { square(size, center=true); children(); @@ -138,8 +148,8 @@ module rect(size=1, rounding=0, atype="box", chamfer=0, anchor=CENTER, spin=0) { pts = pts_over[0]; override = pts_over[1]; attachable(anchor, spin, two_d=true, size=size,override=override) { - polygon(pts); - children(); + polygon(pts); + children(); } } } @@ -153,18 +163,19 @@ function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0, assert(in_list(atype, ["box", "perim"])) let( anchor=_force_anchor_2d(anchor), - size = force_list(size,2), + size = [for (c = force_list(size,2)) max(0,c)], chamfer = force_list(chamfer,4), rounding = force_list(rounding,4) ) + assert(all_positive(size), "All components of size must be positive") 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] - ] + [ 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)) : @@ -278,6 +289,9 @@ function circle(r, d, points, corner, anchor=CENTER, spin=0) = ) [cp, r], cp = data[0], r = data[1], + ) + assert(r>0, "Radius/diameter must be positive") + let( sides = segs(r), path = [for (i=[0:1:sides-1]) let(a=360-i*360/sides) r*[cos(a),sin(a)]+cp] ) reorient(anchor,spin, two_d=true, r=r, p=path); @@ -290,7 +304,7 @@ module circle(r, d, points, corner, anchor=CENTER, spin=0) { r = c[1]; translate(cp) { attachable(anchor,spin, two_d=true, r=r) { - _circle(r=r); + if (r>0) _circle(r=r); children(); } } @@ -301,14 +315,14 @@ module circle(r, d, points, corner, anchor=CENTER, spin=0) { cp = c[0]; translate(cp) { attachable(anchor,spin, two_d=true, r=r) { - _circle(r=r); + if (r>0) _circle(r=r); children(); } } } else { r = get_radius(r=r, d=d, dflt=1); attachable(anchor,spin, two_d=true, r=r) { - _circle(r=r); + if (r>0) _circle(r=r); children(); } } @@ -485,18 +499,26 @@ function ellipse(r, d, realign=false, circum=false, uniform=false, anchor=CENTER r = force_list(get_radius(r=r, d=d, dflt=1),2), sides = segs(max(r)) ) - uniform ? assert(!circum, "Circum option not allowed when \"uniform\" is true") - reorient(anchor,spin,two_d=true,r=[r.x,r.y], - p=realign ? reverse(_ellipse_refine_realign(r.x,r.y,sides)) - : reverse_polygon(_ellipse_refine(r.x,r.y,sides))) - : - let( - offset = realign? 180/sides : 0, - sc = circum? (1 / cos(180/sides)) : 1, - rx = r.x * sc, - ry = r.y * sc, - pts = [for (i=[0:1:sides-1]) let(a=360-offset-i*360/sides) [rx*cos(a), ry*sin(a)]] - ) reorient(anchor,spin, two_d=true, r=[rx,ry], p=pts); + assert(all_positive(r), "All components of the radius must be positive.") + uniform + ? assert(!circum, "Circum option not allowed when \"uniform\" is true") + reorient(anchor,spin, + two_d=true, r=[r.x,r.y], + p=realign + ? reverse(_ellipse_refine_realign(r.x,r.y,sides)) + : reverse_polygon(_ellipse_refine(r.x,r.y,sides)) + ) + : let( + offset = realign? 180/sides : 0, + sc = circum? (1 / cos(180/sides)) : 1, + rx = r.x * sc, + ry = r.y * sc, + pts = [ + for (i=[0:1:sides-1]) + let (a = 360-offset-i*360/sides) + [rx*cos(a), ry*sin(a)] + ] + ) reorient(anchor,spin, two_d=true, r=[rx,ry], p=pts); // Section: Polygons diff --git a/shapes3d.scad b/shapes3d.scad index f45c296..f087587 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -67,7 +67,10 @@ module cube(size=1, center, anchor, spin=0, orient=UP) function cube(size=1, center, anchor, spin=0, orient=UP) = let( - siz = scalar_vec3(size), + siz = scalar_vec3(size) + ) + assert(all_positive(siz), "All size components must be positive.") + let( anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]), unscaled = [ [-1,-1,-1],[1,-1,-1],[1,1,-1],[-1,1,-1], @@ -332,7 +335,7 @@ module cuboid( rounding = approx(rounding,0) ? undef : rounding; checks = assert(is_vector(size,3)) - assert(all_positive(size)) + assert(all_nonnegative(size), "All components of size= must be >=0") assert(is_undef(chamfer) || is_finite(chamfer),"chamfer must be a finite value") assert(is_undef(rounding) || is_finite(rounding),"rounding must be a finite value") assert(is_undef(rounding) || is_undef(chamfer), "Cannot specify nonzero value for both chamfer and rounding") From a6e60b5993d4544cb5d3dc83f06b16dd6e5cfc75 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Tue, 10 Oct 2023 14:54:37 -0700 Subject: [PATCH 3/4] Once again, removed extraneous comma. --- shapes2d.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shapes2d.scad b/shapes2d.scad index 4f7c34b..d2070c7 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -288,7 +288,7 @@ function circle(r, d, points, corner, anchor=CENTER, spin=0) = r = get_radius(r=r, d=d, dflt=1) ) [cp, r], cp = data[0], - r = data[1], + r = data[1] ) assert(r>0, "Radius/diameter must be positive") let( From 0d6d5140bf1d93e3210e79f0acdfbbd4e8525cd0 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Tue, 10 Oct 2023 15:16:50 -0700 Subject: [PATCH 4/4] Allow zero-size rect() to allow prismoid() pyramids. --- shapes2d.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shapes2d.scad b/shapes2d.scad index d2070c7..bbcda77 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -167,7 +167,7 @@ function rect(size=1, rounding=0, chamfer=0, atype="box", anchor=CENTER, spin=0, chamfer = force_list(chamfer,4), rounding = force_list(rounding,4) ) - assert(all_positive(size), "All components of size must be positive") + assert(all_nonnegative(size), "All components of size must be >=0") all_zero(concat(chamfer,rounding),0) ? let( path = [