diff --git a/color.scad b/color.scad index 28d46a1..f9b0652 100644 --- a/color.scad +++ b/color.scad @@ -15,7 +15,7 @@ use // Module: recolor() // Usage: -// recolor([c]) {...} +// recolor([c]) children; // Topics: Attachments // See Also: color_this() // Description: @@ -34,6 +34,7 @@ use // attach(TOP,BOT) cuboid([4,4,2]); module recolor(c="default") { + req_children($children); $color=c; children(); } @@ -41,7 +42,7 @@ module recolor(c="default") // Module: color_this() // Usage: -// color_this([c]) {...} +// color_this([c]) children; // Topics: Attachments // See Also: recolor() // Description: @@ -61,6 +62,7 @@ module recolor(c="default") // attach(TOP,BOT) cuboid([4,4,2]); module color_this(c="default") { + req_children($children); $save_color=default($color,"default"); $color=c; children(); @@ -69,7 +71,7 @@ module color_this(c="default") // Module: rainbow() // Usage: -// rainbow(list) ... +// rainbow(list,[stride],[maxhues],[shuffle],[seed]) children; // Description: // Iterates the list, displaying children in different colors for each list item. The color // is set using the color() module, so this module is not compatible with {{recolor()}} or @@ -91,6 +93,7 @@ module color_this(c="default") // rainbow(rgn) stroke($item, closed=true); module rainbow(list, stride=1, maxhues, shuffle=false, seed) { + req_children($children); ll = len(list); maxhues = first_defined([maxhues,ll]); huestep = 360 / maxhues; @@ -107,7 +110,7 @@ module rainbow(list, stride=1, maxhues, shuffle=false, seed) // Function&Module: hsl() // Usage: -// hsl(h,[s],[l],[a]) ... +// hsl(h,[s],[l],[a]) children; // rgb = hsl(h,[s],[l],[a]); // Description: // When called as a function, returns the [R,G,B] color for the given hue `h`, saturation `s`, and lightness `l` from the HSL colorspace. If you supply @@ -133,12 +136,16 @@ function hsl(h,s=1,l=0.5,a) = if (is_def(a)) a ]; -module hsl(h,s=1,l=0.5,a=1) color(hsl(h,s,l),a) children(); +module hsl(h,s=1,l=0.5,a=1) +{ + req_children($children); + color(hsl(h,s,l),a) children(); +} // Function&Module: hsv() // Usage: -// hsv(h,[s],[v],[a]) ... +// hsv(h,[s],[v],[a]) children; // rgb = hsv(h,[s],[v],[a]); // Description: // When called as a function, returns the [R,G,B] color for the given hue `h`, saturation `s`, and value `v` from the HSV colorspace. If you supply @@ -175,7 +182,11 @@ function hsv(h,s=1,v=1,a) = is_def(a) ? point4d(add_scalar(rgbprime,m),a) : add_scalar(rgbprime,m); -module hsv(h,s=1,v=1,a=1) color(hsv(h,s,v),a) children(); +module hsv(h,s=1,v=1,a=1) +{ + req_children($children); + color(hsv(h,s,v),a) children(); +} diff --git a/distributors.scad b/distributors.scad index 64c6de9..6f248e3 100644 --- a/distributors.scad +++ b/distributors.scad @@ -20,7 +20,7 @@ // Translates copies of all children to each given translation offset. // // Usage: -// move_copies(a) ... +// move_copies(a) children; // // Arguments: // a = Array of XYZ offset vectors. Default `[[0,0,0]]` @@ -34,6 +34,7 @@ // move_copies([[-25,-25,0], [25,-25,0], [0,0,50], [0,25,0]]) sphere(r=10); module move_copies(a=[[0,0,0]]) { + req_children($children); assert(is_list(a)); for ($idx = idx(a)) { $pos = a[$idx]; @@ -46,15 +47,15 @@ module move_copies(a=[[0,0,0]]) // Function&Module: line_of() // // Usage: Spread `n` copies by a given spacing -// line_of(spacing, [n], [p1=]) ... +// line_of(spacing, [n], [p1=]) children; // Usage: Spread copies every given spacing along the line -// line_of(spacing, [l=], [p1=]) ... +// line_of(spacing, [l=], [p1=]) children; // Usage: Spread `n` copies along the length of the line -// line_of([n=], [l=], [p1=]) ... +// line_of([n=], [l=], [p1=]) children; // Usage: Spread `n` copies along the line from `p1` to `p2` -// line_of([n=], [p1=], [p2=]) ... +// line_of([n=], [p1=], [p2=]) children; // Usage: Spread copies every given spacing, centered along the line from `p1` to `p2` -// line_of([spacing], [p1=], [p2=]) ... +// line_of([spacing], [p1=], [p2=]) children; // Usage: As a function // pts = line_of([spacing], [n], [p1=]); // pts = line_of([spacing], [l=], [p1=]); @@ -117,6 +118,7 @@ module move_copies(a=[[0,0,0]]) // move_copies(pts) circle(d=2); module line_of(spacing, n, l, p1, p2) { + req_children($children); pts = line_of(spacing=spacing, n=n, l=l, p1=p1, p2=p2); for (i=idx(pts)) { $idx = i; @@ -155,9 +157,9 @@ function line_of(spacing, n, l, p1, p2) = // Spreads out `n` copies of the children along a line on the X axis. // // Usage: -// xcopies(spacing, [n], [sp]) ... -// xcopies(l, [n], [sp]) ... -// xcopies(LIST) ... +// xcopies(spacing, [n], [sp]) children; +// xcopies(l, [n], [sp]) children; +// xcopies(LIST) children; // // 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) @@ -183,6 +185,7 @@ function line_of(spacing, n, l, p1, p2) = // xcopies([1,2,3,5,7]) sphere(d=1); module xcopies(spacing, n, l, sp) { + req_children($children); dir = RIGHT; sp = is_finite(sp)? (sp*dir) : sp; if (is_vector(spacing)) { @@ -207,9 +210,9 @@ module xcopies(spacing, n, l, sp) // Spreads out `n` copies of the children along a line on the Y axis. // // Usage: -// ycopies(spacing, [n], [sp]) ... -// ycopies(l, [n], [sp]) ... -// ycopies(LIST) ... +// ycopies(spacing, [n], [sp]) children; +// ycopies(l, [n], [sp]) children; +// ycopies(LIST) children; // // 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) @@ -235,6 +238,7 @@ module xcopies(spacing, n, l, sp) // ycopies([1,2,3,5,7]) sphere(d=1); module ycopies(spacing, n, l, sp) { + req_children($children); dir = BACK; sp = is_finite(sp)? (sp*dir) : sp; if (is_vector(spacing)) { @@ -259,9 +263,10 @@ module ycopies(spacing, n, l, sp) // Spreads out `n` copies of the children along a line on the Z axis. // // Usage: -// zcopies(spacing, [n], [sp]) ... -// zcopies(l, [n], [sp]) ... -// zcopies(LIST) ... + +// zcopies(spacing, [n], [sp]) children; +// zcopies(l, [n], [sp]) children; +// zcopies(LIST) children; // // 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) @@ -301,6 +306,7 @@ module ycopies(spacing, n, l, sp) // zcopies([1,2,3,5,7]) sphere(d=1); module zcopies(spacing, n, l, sp) { + req_children($children); dir = UP; sp = is_finite(sp)? (sp*dir) : sp; if (is_vector(spacing)) { @@ -328,16 +334,16 @@ module zcopies(spacing, n, l, sp) // Makes a square or hexagonal grid of copies of children, with an optional masking polygon or region. // // Usage: -// grid2d(spacing, size, [stagger], [scale], [inside]) ... -// grid2d(n, size, [stagger], [scale], [inside]) ... -// grid2d(spacing, n, [stagger], [scale], [inside]) ... -// grid2d(spacing, inside, [stagger], [scale]) ... -// grid2d(n, inside, [stagger], [scale]) ... +// grid2d(spacing, size=, [stagger=], [scale=], [inside=]) children; +// grid2d(n=, size=, [stagger=], [scale=], [inside=]) children; +// grid2d(spacing, [n], [stagger=], [scale=], [inside=]) children; +// grid2d(n=, inside=, [stagger], [scale]) children; // // Arguments: -// size = The [X,Y] size to spread the copies over. // spacing = Distance between copies in [X,Y] or scalar distance. // n = How many columns and rows of copies to make. Can be given as `[COLS,ROWS]`, or just as a scalar that specifies both. If staggered, count both staggered and unstaggered columns and rows. Default: 2 (3 if staggered) +// size = The [X,Y] size to spread the copies over. +// --- // stagger = If true, make a staggered (hexagonal) grid. If false, make square grid. If `"alt"`, makes alternate staggered pattern. Default: false // inside = If given a list of polygon points, or a region, only creates copies whose center would be inside the polygon or region. Polygon can be concave and/or self crossing. // nonzero = If inside is set to a polygon with self-crossings then use the nonzero method for deciding if points are in the polygon. Default: false @@ -379,7 +385,7 @@ module zcopies(spacing, n, l, sp) // } module grid2d(spacing, n, size, stagger=false, inside=undef, nonzero) { - + req_children($children); assert(in_list(stagger, [false, true, "alt"])); bounds = is_undef(inside)? undef : is_path(inside)? pointlist_bounds(inside) : @@ -453,7 +459,6 @@ module grid2d(spacing, n, size, stagger=false, inside=undef, nonzero) } - ////////////////////////////////////////////////////////////////////// // Section: Rotating copies of all children ////////////////////////////////////////////////////////////////////// @@ -472,14 +477,15 @@ module grid2d(spacing, n, size, stagger=false, inside=undef, nonzero) // The first (unrotated) copy will be placed at the relative starting angle `sa`. // // Usage: -// rot_copies(rots, [cp], [sa], [delta], [subrot]) ... -// rot_copies(rots, v, [cp], [sa], [delta], [subrot]) ... -// rot_copies(n, [v], [cp], [sa], [delta], [subrot]) ... +// rot_copies(rots, [cp=], [sa=], [delta=], [subrot=]) children; +// rot_copies(rots, v, [cp=], [sa=], [delta=], [subrot=]) children; +// rot_copies(n=, [v=], [cp=], [sa=], [delta=], [subrot=]) children; // // Arguments: // rots = A list of [X,Y,Z] rotation angles in degrees. If `v` is given, this will be a list of scalar angles in degrees to rotate around `v`. // v = If given, this is the vector of the axis to rotate around. // cp = Centerpoint to rotate around. Default: `[0,0,0]` +// --- // n = Optional number of evenly distributed copies, rotated around the axis. // sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise. Default: 0 // delta = [X,Y,Z] amount to move away from cp before rotating. Makes rings of copies. Default: `[0,0,0]` @@ -520,6 +526,7 @@ module grid2d(spacing, n, size, stagger=false, inside=undef, nonzero) // color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0); module rot_copies(rots=[], v=undef, cp=[0,0,0], n=undef, sa=0, offset=0, delta=[0,0,0], subrot=true) { + req_children($children); sang = sa + offset; angs = !is_undef(n)? (n<=0? [] : [for (i=[0:1:n-1]) i/n*360+sang]) : @@ -548,8 +555,8 @@ module rot_copies(rots=[], v=undef, cp=[0,0,0], n=undef, sa=0, offset=0, delta=[ // Module: xrot_copies() // // Usage: -// xrot_copies(rots, [r], [cp], [sa], [subrot]) ... -// xrot_copies(n, [r], [cp], [sa], [subrot]) ... +// xrot_copies(rots, [cp], [r=], [sa=], [subrot=]) children; +// xrot_copies(n=, [cp=], [r=], [sa=], [subrot=]) children; // // Description: // Given an array of angles, rotates copies of the children to each of those angles around the X axis. @@ -562,6 +569,7 @@ module rot_copies(rots=[], v=undef, cp=[0,0,0], n=undef, sa=0, offset=0, delta=[ // Arguments: // rots = Optional array of rotation angles, in degrees, to make copies at. // cp = Centerpoint to rotate around. +// -- // n = Optional number of evenly distributed copies to be rotated around the ring. // sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from Y+, when facing the origin from X+. First unrotated copy is placed at that angle. // r = Radius to move children back (Y+), away from cp, before rotating. Makes rings of copies. @@ -598,6 +606,7 @@ module rot_copies(rots=[], v=undef, cp=[0,0,0], n=undef, sa=0, offset=0, delta=[ // color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0, center=true); module xrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) { + req_children($children); rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot) children(); } @@ -605,8 +614,8 @@ module xrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // Module: yrot_copies() // // Usage: -// yrot_copies(rots, [r], [cp], [sa], [subrot]) ... -// yrot_copies(n, [r], [cp], [sa], [subrot]) ... +// yrot_copies(rots, [cp], [r=], [sa=], [subrot=]) children; +// yrot_copies(n=, [cp=], [r=], [sa=], [subrot=]) children; // // Description: // Given an array of angles, rotates copies of the children to each of those angles around the Y axis. @@ -619,6 +628,7 @@ module xrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // Arguments: // rots = Optional array of rotation angles, in degrees, to make copies at. // cp = Centerpoint to rotate around. +// --- // n = Optional number of evenly distributed copies to be rotated around the ring. // sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X-, when facing the origin from Y+. // r = Radius to move children left (X-), away from cp, before rotating. Makes rings of copies. @@ -655,6 +665,7 @@ module xrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true); module yrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) { + req_children($children); rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot) children(); } @@ -662,8 +673,8 @@ module yrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // Module: zrot_copies() // // Usage: -// zrot_copies(rots, [r], [cp], [sa], [subrot]) ... -// zrot_copies(n, [r], [cp], [sa], [subrot]) ... +// zrot_copies(rots, [cp], [r=], [sa=], [subrot=]) children; +// zrot_copies(n=, [cp=], [r=], [sa=], [subrot=]) children; // // Description: // Given an array of angles, rotates copies of the children to each of those angles around the Z axis. @@ -676,6 +687,7 @@ module yrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // Arguments: // rots = Optional array of rotation angles, in degrees, to make copies at. // cp = Centerpoint to rotate around. Default: [0,0,0] +// --- // n = Optional number of evenly distributed copies to be rotated around the ring. // sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X+, when facing the origin from Z+. Default: 0 // r = Radius to move children right (X+), away from cp, before rotating. Makes rings of copies. Default: 0 @@ -722,12 +734,13 @@ module zrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // Evenly distributes n duplicate children around an ovoid arc on the XY plane. // // Usage: -// arc_of(r|d, n, [sa], [ea], [rot] -// arc_of(rx|dx, ry|dy, n, [sa], [ea], [rot] +// arc_of(n, r|d=, [sa=], [ea=], [rot=]) children; +// arc_of(n, rx=|dx=, ry=|dy=, [sa=], [ea=], [rot=]) children; // // Arguments: // n = number of copies to distribute around the circle. (Default: 6) // r = radius of circle (Default: 1) +// --- // rx = radius of ellipse on X axis. Used instead of r. // ry = radius of ellipse on Y axis. Used instead of r. // d = diameter of circle. (Default: 2) @@ -757,13 +770,19 @@ module zrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // Example: // #cube(size=[10,3,3],center=true); // arc_of(rx=20, ry=10, n=8) cube(size=[10,3,3],center=true); +// Example(2D): Using `$idx` to alternate shapes +// arc_of(r=50, n=19, sa=0, ea=180) +// if ($idx % 2 == 0) rect(6); +// else circle(d=6); module arc_of( n=6, - r=undef, rx=undef, ry=undef, + r=undef, + rx=undef, ry=undef, d=undef, dx=undef, dy=undef, sa=0, ea=360, rot=true ) { + req_children($children); rx = get_radius(r1=rx, r=r, d1=dx, d=d, dflt=1); ry = get_radius(r1=ry, r=r, d1=dy, d=d, dflt=1); sa = posmod(sa, 360); @@ -789,12 +808,13 @@ module arc_of( // Spreads children semi-evenly over the surface of a sphere. // // Usage: -// ovoid_spread(r|d, n, [cone_ang], [scale], [perp]) ... +// ovoid_spread(n, r|d=, [cone_ang=], [scale=], [perp=]) children; // // Arguments: -// r = Radius of the sphere to distribute over -// d = Diameter of the sphere to distribute over // n = How many copies to evenly spread over the surface. +// r = Radius of the sphere to distribute over +// --- +// d = Diameter of the sphere to distribute over // cone_ang = Angle of the cone, in degrees, to limit how much of the sphere gets covered. For full sphere coverage, use 180. Measured pre-scaling. Default: 180 // scale = The [X,Y,Z] scaling factors to reshape the sphere being covered. // perp = If true, rotate children to be perpendicular to the sphere surface. Default: true @@ -814,8 +834,9 @@ module arc_of( // ovoid_spread(n=500, d=100, cone_ang=180) // color(unit(point3d(v_abs($pos)))) // cylinder(d=8, h=10, center=false); -module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=true) +module ovoid_spread(n=100, r=undef, d=undef, cone_ang=90, scale=[1,1,1], perp=true) { + req_children($children); r = get_radius(r=r, d=d, dflt=50); cnt = ceil(n / (cone_ang/180)); @@ -853,13 +874,15 @@ module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=tr // If you specify `sp` then the copies will start at `sp`. // // Usage: -// path_spread(path), [n], [spacing], [sp], [rotate_children], [closed]) ... +// path_spread(path, [n], [spacing], [sp], [rotate_children], [closed]) children; // // Arguments: -// path = the path where children are placed +// path = path or 1-region where children are placed // n = number of copies // spacing = space between copies // sp = if given, copies will start distance sp from the path start and spread beyond that point +// rotate_children = if true, rotate children to line up with curve normal. Default: true +// closed = If true treat path as a closed curve. Default: false // // Side Effects: // `$pos` is set to the center of each copy @@ -927,8 +950,12 @@ module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=tr // color("blue") cyl(h=3,r=.2, anchor=BOTTOM); // z-aligned cylinder // color("red") xcyl(h=10,r=.2, anchor=FRONT+LEFT); // x-aligned cylinder // } -module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=false) +module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed) { + req_children($children); + is_1reg = is_1region(path); + path = is_1reg ? path[0] : path; + closed = default(closed, is_1reg); length = path_length(path,closed); distances = is_def(sp)? ( // Start point given @@ -983,7 +1010,7 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=fals // Makes a copy of the children, mirrored across the given plane. // // Usage: -// mirror_copy(v, [cp], [offset]) ... +// mirror_copy(v, [cp], [offset]) children; // // Arguments: // v = The normal vector of the plane to mirror across. @@ -1007,6 +1034,7 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=fals // color("blue",0.25) translate([0,-5,-5]) rot(from=UP, to=BACK+UP) cube([15,15,0.01], center=true); module mirror_copy(v=[0,0,1], offset=0, cp) { + req_children($children); cp = is_vector(v,4)? plane_normal(v) * v[3] : is_vector(cp)? cp : is_num(cp)? cp*unit(v) : @@ -1037,7 +1065,7 @@ module mirror_copy(v=[0,0,1], offset=0, cp) // Makes a copy of the children, mirrored across the X axis. // // Usage: -// xflip_copy([x], [offset]) ... +// xflip_copy([offset], [x]) children; // // Arguments: // offset = Distance to offset children right, before copying. @@ -1060,6 +1088,7 @@ module mirror_copy(v=[0,0,1], offset=0, cp) // color("blue",0.25) left(5) cube([0.01,15,15], center=true); module xflip_copy(offset=0, x=0) { + req_children($children); mirror_copy(v=[1,0,0], offset=offset, cp=[x,0,0]) children(); } @@ -1070,7 +1099,7 @@ module xflip_copy(offset=0, x=0) // Makes a copy of the children, mirrored across the Y axis. // // Usage: -// yflip_copy([y], [offset]) ... +// yflip_copy([offset], [y]) children; // // Arguments: // offset = Distance to offset children back, before copying. @@ -1093,6 +1122,7 @@ module xflip_copy(offset=0, x=0) // color("blue",0.25) fwd(5) cube([15,0.01,15], center=true); module yflip_copy(offset=0, y=0) { + req_children($children); mirror_copy(v=[0,1,0], offset=offset, cp=[0,y,0]) children(); } @@ -1103,7 +1133,7 @@ module yflip_copy(offset=0, y=0) // Makes a copy of the children, mirrored across the Z axis. // // Usage: -// zflip_copy([z], [offset]) ... +// zflip_copy([offset], [z]) children; // // Arguments: // offset = Distance to offset children up, before copying. @@ -1126,6 +1156,7 @@ module yflip_copy(offset=0, y=0) // color("blue",0.25) down(5) cube([15,15,0.01], center=true); module zflip_copy(offset=0, z=0) { + req_children($children); mirror_copy(v=[0,0,1], offset=offset, cp=[0,0,z]) children(); } @@ -1142,13 +1173,13 @@ module zflip_copy(offset=0, z=0) // where you only really care about the spacing between them. // // Usage: -// distribute(spacing, dir, [sizes]) ... -// distribute(l, dir, [sizes]) ... +// distribute(spacing, sizes, dir) children; +// distribute(l=, [sizes=], [dir=]) children; // // Arguments: // spacing = Spacing to add between each child. (Default: 10.0) // sizes = Array containing how much space each child will need. -// dir = Vector direction to distribute copies along. +// dir = Vector direction to distribute copies along. Default: RIGHT // l = Length to distribute copies along. // // Side Effects: @@ -1163,6 +1194,7 @@ module zflip_copy(offset=0, z=0) // } module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef) { + req_children($children); gaps = ($children < 2)? [0] : !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : [for (i=[0:1:$children-2]) 0]; @@ -1187,8 +1219,8 @@ module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef) // where you only really care about the spacing between them. // // Usage: -// xdistribute(spacing, [sizes]) ... -// xdistribute(l, [sizes]) ... +// xdistribute(spacing, [sizes]) children; +// xdistribute(l=, [sizes=]) children; // // Arguments: // spacing = spacing between each child. (Default: 10.0) @@ -1207,6 +1239,7 @@ module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef) // } module xdistribute(spacing=10, sizes=undef, l=undef) { + req_children($children); dir = RIGHT; gaps = ($children < 2)? [0] : !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : @@ -1232,8 +1265,8 @@ module xdistribute(spacing=10, sizes=undef, l=undef) // where you only really care about the spacing between them. // // Usage: -// ydistribute(spacing, [sizes]) -// ydistribute(l, [sizes]) +// ydistribute(spacing, [sizes]) children; +// ydistribute(l=, [sizes=]) children; // // Arguments: // spacing = spacing between each child. (Default: 10.0) @@ -1252,6 +1285,7 @@ module xdistribute(spacing=10, sizes=undef, l=undef) // } module ydistribute(spacing=10, sizes=undef, l=undef) { + req_children($children); dir = BACK; gaps = ($children < 2)? [0] : !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : @@ -1277,8 +1311,8 @@ module ydistribute(spacing=10, sizes=undef, l=undef) // where you only really care about the spacing between them. // // Usage: -// zdistribute(spacing, [sizes]) -// zdistribute(l, [sizes]) +// zdistribute(spacing, [sizes]) children; +// zdistribute(l=, [sizes=]) children; // // Arguments: // spacing = spacing between each child. (Default: 10.0) @@ -1297,6 +1331,7 @@ module ydistribute(spacing=10, sizes=undef, l=undef) // } module zdistribute(spacing=10, sizes=undef, l=undef) { + req_children($children); dir = UP; gaps = ($children < 2)? [0] : !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : diff --git a/paths.scad b/paths.scad index dff9991..35884c7 100644 --- a/paths.scad +++ b/paths.scad @@ -322,7 +322,7 @@ function _sum_preserving_round(data, index=0) = // Function: subdivide_path() // See Also: subdivide_and_slice(), resample_path(), jittered_poly() // Usage: -// newpath = subdivide_path(path, [n|refine|maxlen], [method], [closed], [exact]); +// newpath = subdivide_path(path, n|refine=|maxlen=, [method=], [closed=], [exact=]); // Description: // Takes a path as input (closed or open) and subdivides the path to produce a more // finely sampled path. You control the subdivision process by using the `maxlen` arg @@ -463,7 +463,7 @@ function subdivide_path(path, n, refine, maxlen, closed=true, exact, method) = // Function: resample_path() // Usage: -// newpath = resample_path(path, n|spacing, [closed]); +// newpath = resample_path(path, n|spacing=, [closed=]); // Description: // Compute a uniform resampling of the input path. If you specify `n` then the output path will have n // points spaced uniformly (by linear interpolation along the input path segments). The only points of the @@ -553,7 +553,7 @@ function is_path_simple(path, closed, eps=EPSILON) = // Function: path_closest_point() // Usage: -// path_closest_point(path, pt); +// index_pt = path_closest_point(path, pt); // Description: // Finds the closest path segment, and point on that segment to the given point. // Returns `[SEGNUM, POINT]` @@ -828,7 +828,7 @@ function _path_cuts_dir(path, cuts, closed=false, eps=1e-2) = // Topics: Paths // See Also: split_path_at_self_crossings() // Usage: -// path_list = path_cut(path, cutdist, [closed=]); +// path_list = path_cut(path, cutdist, [closed]); // Description: // Given a list of distances in `cutdist`, cut the path into // subpaths at those lengths, returning a list of paths. diff --git a/regions.scad b/regions.scad index b31a940..1e2f5f9 100644 --- a/regions.scad +++ b/regions.scad @@ -44,7 +44,7 @@ // Function: is_region() // Usage: -// is_region(x); +// bool = is_region(x); // Description: // Returns true if the given item looks like a region. A region is a list of non-crossing simple polygons. This test just checks // that the argument is a list whose first entry is a path. @@ -281,7 +281,7 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // Module: region() // Usage: -// region(r, [anchor], [spin], [cp]) { ... }; +// region(r, [anchor], [spin=], [cp=], [atype=]) [attachments]; // 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 @@ -289,6 +289,7 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // Arguments: // r = region to create as geometry // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"` +// --- // spin = Rotate this many degrees after anchor. See [spin](attachments.scad#subsection-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" // atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" @@ -356,7 +357,7 @@ function _point_in_region(point, region, eps=EPSILON, i=0, cnt=0) = // Function: region_area() // Usage: -// area=region_area(region); +// area = region_area(region); // Description: // Computes the area of the specified valid region. (If the region is invalid and has self intersections // the result is meaningless.) @@ -377,14 +378,13 @@ function _clockwise_region(r) = [for(p=r) clockwise_polygon(p)]; // Function: are_regions_equal() // Usage: -// b = are_regions_equal(region1, region2, [eps]) +// b = are_regions_equal(region1, region2, [either_winding]) // Description: -// Returns true if the components of region1 and region2 are the same polygons (in any order) -// within given epsilon tolerance. +// Returns true if the components of region1 and region2 are the same polygons (in any order). // Arguments: // region1 = first region // region2 = second region -// eps = tolerance for comparison +// either_winding = if true then two shapes test equal if they wind in opposite directions. Default: false function are_regions_equal(region1, region2, either_winding=false) = let( region1=force_region(region1), @@ -707,8 +707,8 @@ function _point_dist(path,pathseg_unit,pathseg_len,pt) = // Function: offset() // Usage: -// offsetpath = offset(path, [r|delta], [chamfer], [closed], [check_valid], [quality]) -// path_faces = offset(path, return_faces=true, [r|delta], [chamfer], [closed], [check_valid], [quality], [firstface_index], [flip_faces]) +// offsetpath = offset(path, [r=|delta=], [chamfer=], [closed=], [check_valid=], [quality=]) +// path_faces = offset(path, return_faces=true, [r=|delta=], [chamfer=], [closed=], [check_valid=], [quality=], [firstface_index=], [flip_faces=]) // Description: // Takes a 2D input path, polygon or region and returns a path offset by the specified amount. As with the built-in // offset() module, you can use `r` to specify rounded offset and `delta` to specify offset with @@ -983,7 +983,7 @@ function _list_three(a,b,c) = // Function&Module: union() // Usage: -// union() {...} +// union() children; // region = union(regions); // region = union(REGION1,REGION2); // region = union(REGION1,REGION2,REGION3); @@ -1013,7 +1013,7 @@ function union(regions=[],b=undef,c=undef,eps=EPSILON) = // Function&Module: difference() // Usage: -// difference() {...} +// difference() children; // region = difference(regions); // region = difference(REGION1,REGION2); // region = difference(REGION1,REGION2,REGION3); @@ -1045,7 +1045,7 @@ function difference(regions=[],b=undef,c=undef,eps=EPSILON) = // Function&Module: intersection() // Usage: -// intersection() {...} +// intersection() children; // region = intersection(regions); // region = intersection(REGION1,REGION2); // region = intersection(REGION1,REGION2,REGION3); @@ -1076,7 +1076,7 @@ function intersection(regions=[],b=undef,c=undef,eps=EPSILON) = // Function&Module: exclusive_or() // Usage: -// exclusive_or() {...} +// exclusive_or() children; // region = exclusive_or(regions); // region = exclusive_or(REGION1,REGION2); // region = exclusive_or(REGION1,REGION2,REGION3); diff --git a/rounding.scad b/rounding.scad index c3d7d25..871e611 100644 --- a/rounding.scad +++ b/rounding.scad @@ -340,12 +340,13 @@ include // Example(2D): Two passes to apply chamfers first, and then round the unchamfered corners. Chamfers always add one point, so it's not hard to keep track of the vertices // $fn=32; // shape = square(10); -// chamfered = round_corners(shape, method="chamfer", cut=[2,0,2,0]); +// chamfered = round_corners(shape, method="chamfer", +// cut=[2,0,2,0]); // rounded = round_corners(chamfered, -// cut = [0, 0, // first original veretex, chamfered -// 1.5, // second original vertex -// 0, 0, // third original vertex, chamfered -// 2.5]); // last original vertex +// cut = [0, 0, // 1st original vertex, chamfered +// 1.5, // 2nd original vertex +// 0, 0, // 3rd original vertex, chamfered +// 2.5]); // 4th original vertex // polygon(rounded); // Example(2D): Another example of mixing chamfers and roundings with two passes // path = star(5, step=2, d=100); @@ -1706,7 +1707,7 @@ function os_mask(mask, out=false, extra,check_valid, quality, offset) = // Module: convex_offset_extrude() // Usage: Basic usage. See below for full options -// convex_offset_extrude(height, [bottom], [top], ...) {2D children}; +// convex_offset_extrude(height, [bottom], [top], ...) 2D-children; // Description: // Extrudes 2d children with layers formed from the convex hull of the offset of each child according to a sequence of offset values. // Like `offset_sweep` this module can use built-in offset profiles to provide treatments such as roundovers or chamfers but unlike `offset_sweep()` it @@ -1811,6 +1812,7 @@ module convex_offset_extrude( joint=undef, k=0.75, angle=45, convexity=10, thickness = 1/1024 ) { + req_children($children); argspec = [ ["for", ""], ["r",r], @@ -2347,7 +2349,7 @@ function _circle_mask(r) = // rot(-90) { // $fn=128; // difference(){ -// tube(or=r, wall=2, h=45); +// tube(or=r, wall=2, h=35, anchor=BOT); // bent_cutout_mask(r-1, 2.1, back(5,p=square([18,18]))); // } // } @@ -2356,7 +2358,7 @@ function _circle_mask(r) = // rot(-90) { // $fn=128; // difference(){ -// tube(or=r, wall=2, h=45); +// tube(or=r, wall=2, h=35, anchor=BOT); // bent_cutout_mask(r-1, 2.1, // subdivide_path(back(5,p=square([18,18])),64,closed=true)); // } @@ -2366,7 +2368,7 @@ function _circle_mask(r) = // rot(-90) { // $fn=128; // difference(){ -// tube(or=r, wall=2, h=45); +// tube(or=r, wall=2, h=35, anchor=BOT); // bent_cutout_mask(r-1, 2.1, // apply(back(15), // subdivide_path( diff --git a/shapes3d.scad b/shapes3d.scad index 7e9b319..c80dfe5 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -2450,7 +2450,7 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) = // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"baseline"` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// See Also: attachable() +// See Also: path_text() // Extra Anchors: // "baseline" = Anchors at the baseline of the text, at the start of the string. // str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector. @@ -2460,14 +2460,6 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) = // text3d("Foobar", h=2, anchor=CENTER); // text3d("Foobar", h=2, anchor=str("baseline",CENTER)); // text3d("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT)); -// Example: Using line_of() distributor -// txt = "This is the string."; -// line_of(spacing=[10,-5],n=len(txt)) -// text3d(txt[$idx], size=10, anchor=CENTER); -// Example: Using arc_of() distributor -// txt = "This is the string"; -// arc_of(r=50, n=len(txt), sa=0, ea=180) -// text3d(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90); module text3d(text, h=1, size=10, font="Helvetica", halign, valign, spacing=1.0, direction="ltr", language="em", script="latin", anchor="baseline[-1,0,-1]", spin=0, orient=UP) { no_children($children); dummy1 = diff --git a/tests/test_transforms.scad b/tests/test_transforms.scad index 2721d56..737f1e6 100644 --- a/tests/test_transforms.scad +++ b/tests/test_transforms.scad @@ -18,11 +18,10 @@ module test_move() { for (val=vals) { assert_equal(move(val), [[1,0,0,val.x],[0,1,0,val.y],[0,0,1,val.z],[0,0,0,1]]); assert_equal(move(val, p=[1,2,3]), [1,2,3]+val); - assert_equal(move(x=val.x, y=val.y, z=val.z, p=[1,2,3]), [1,2,3]+val); } // Verify that module at least doesn't crash. - move(x=-5) move(y=-5) move(z=-5) move([-5,-5,-5]) union(){}; - move(x=5) move(y=5) move(z=5) move([5,5,5]) union(){}; + move([-5,-5,-5]) union(){}; + move([5,5,5]) union(){}; sq = square(10); assert_equal(move("centroid", sq), move(-centroid(sq),sq)); assert_equal(move("mean", vals), move(-mean(vals), vals)); diff --git a/transforms.scad b/transforms.scad index 085bd05..0929257 100644 --- a/transforms.scad +++ b/transforms.scad @@ -75,15 +75,12 @@ _NO_ARG = [true,[123232345],false]; // Aliases: translate() // // Usage: As Module -// move([x=], [y=], [z=]) ... -// move(v) ... +// move(v) children; // Usage: As a function to translate points, VNF, or Bezier patch // pts = move(v, p); -// pts = move([x=], [y=], [z=], p=); // pts = move(STRING, p); // Usage: Get Translation Matrix // mat = move(v); -// mat = move([x=], [y=], [z=]); // // Topics: Affine, Matrices, Transforms, Translation // See Also: left(), right(), fwd(), back(), down(), up(), spherical_to_xyz(), altaz_to_xyz(), cylindrical_to_xyz(), polar_to_xy() @@ -97,28 +94,22 @@ _NO_ARG = [true,[123232345],false]; // * Called as a function with a [VNF structure](vnf.scad) in the `p` argument, returns the translated VNF. // * Called as a function with the `p` argument, returns the translated point or list of points. // * Called as a function with the `p` argument set to a VNF or a polygon and `v` set to "centroid", "mean" or "box", translates the argument to the centroid, mean, or bounding box center respectively. -// * Called as a function without a `p` argument, with a 2D offset vector `v`, returns an affine2d translation matrix. -// * Called as a function without a `p` argument, with a 3D offset vector `v`, returns an affine3d translation matrix. +// * Called as a function without a `p` argument, returns a 4x4 translation matrix for operating on 3D data. // // Arguments: -// v = An [X,Y,Z] vector to translate by. For function form with `p` is a point list or VNF, can be "centroid", "mean" or "box". +// v = An [X,Y,Z] vector to translate by. For function form with `p` a point list or VNF, can be "centroid", "mean" or "box". // p = Either a point, or a list of points to be translated when used as a function. -// --- -// x = X axis translation. -// y = Y axis translation. -// z = Z axis translation. // // Example: // #sphere(d=10); // move([0,20,30]) sphere(d=10); // -// Example: +// Example: You can move a 3D object with a 2D vector. The Z component is treated at zero. // #sphere(d=10); -// move(y=20) sphere(d=10); +// move([-10,-5]) sphere(d=10); // -// Example: -// #sphere(d=10); -// move(x=-10, y=-5) sphere(d=10); +// Example(2D): Move to centroid +// polygon(move("centroid", right_triangle([10,4]))); // // Example(FlatSpin): Using Altitude-Azimuth Coordinates // #sphere(d=10); @@ -135,19 +126,18 @@ _NO_ARG = [true,[123232345],false]; // // Example(NORENDER): // pt1 = move([0,20,30], p=[15,23,42]); // Returns: [15, 43, 72] -// pt2 = move(y=10, p=[15,23,42]); // Returns: [15, 33, 42] -// pt3 = move([0,3,1], p=[[1,2,3],[4,5,6]]); // Returns: [[1,5,4], [4,8,7]] -// pt4 = move(y=11, p=[[1,2,3],[4,5,6]]); // Returns: [[1,13,3], [4,16,6]] +// pt2 = move([0,3,1], p=[[1,2,3],[4,5,6]]); // Returns: [[1,5,4], [4,8,7]] // mat2d = move([2,3]); // Returns: [[1,0,2],[0,1,3],[0,0,1]] // mat3d = move([2,3,4]); // Returns: [[1,0,0,2],[0,1,0,3],[0,0,1,4],[0,0,0,1]] -module move(v=[0,0,0], p, x=0, y=0, z=0) { +module move(v=[0,0,0], p) { + req_children($children); assert(!is_string(v),"Module form of `move()` does not accept string `v` arguments"); assert(is_undef(p), "Module form `move()` does not accept p= argument."); assert(is_vector(v) && (len(v)==3 || len(v)==2), "Invalid value for `v`") - translate(point3d(v)+[x,y,z]) children(); + translate(point3d(v)) children(); } -function move(v=[0,0,0], p=_NO_ARG, x=0, y=0, z=0) = +function move(v=[0,0,0], p=_NO_ARG) = is_string(v) ? ( assert(is_vnf(p) || is_path(p),"String movements only work with point lists and VNFs") let( @@ -156,12 +146,12 @@ function move(v=[0,0,0], p=_NO_ARG, x=0, y=0, z=0) = : v=="box" ? mean(pointlist_bounds(p)) : assert(false,str("Unknown string movement ",v)) ) - move(-center,p=p, x=x,y=y,z=z) + move(-center,p=p) ) : assert(is_vector(v) && (len(v)==3 || len(v)==2), "Invalid value for `v`") let( - m = affine3d_translate(point3d(v)+[x,y,z]) + m = affine3d_translate(point3d(v)) ) p==_NO_ARG ? m : apply(m, p); @@ -171,7 +161,7 @@ function translate(v=[0,0,0], p=_NO_ARG) = move(v=v, p=p); // Function&Module: left() // // Usage: As Module -// left(x) ... +// left(x) children; // Usage: Translate Points // pts = left(x, p); // Usage: Get Translation Matrix @@ -199,6 +189,7 @@ function translate(v=[0,0,0], p=_NO_ARG) = move(v=v, p=p); // pt3 = left(3, p=[[1,2,3],[4,5,6]]); // Returns: [[-2,2,3], [1,5,6]] // mat3d = left(4); // Returns: [[1,0,0,-4],[0,1,0,0],[0,0,1,0],[0,0,0,1]] module left(x=0, p) { + req_children($children); assert(is_undef(p), "Module form `left()` does not accept p= argument."); assert(is_finite(x), "Invalid number") translate([-x,0,0]) children(); @@ -213,7 +204,7 @@ function left(x=0, p=_NO_ARG) = // Aliases: xmove() // // Usage: As Module -// right(x) ... +// right(x) children; // Usage: Translate Points // pts = right(x, p); // Usage: Get Translation Matrix @@ -241,6 +232,7 @@ function left(x=0, p=_NO_ARG) = // pt3 = right(3, p=[[1,2,3],[4,5,6]]); // Returns: [[4,2,3], [7,5,6]] // mat3d = right(4); // Returns: [[1,0,0,4],[0,1,0,0],[0,0,1,0],[0,0,0,1]] module right(x=0, p) { + req_children($children); assert(is_undef(p), "Module form `right()` does not accept p= argument."); assert(is_finite(x), "Invalid number") translate([x,0,0]) children(); @@ -251,6 +243,7 @@ function right(x=0, p=_NO_ARG) = move([x,0,0],p=p); module xmove(x=0, p) { + req_children($children); assert(is_undef(p), "Module form `xmove()` does not accept p= argument."); assert(is_finite(x), "Invalid number") translate([x,0,0]) children(); @@ -264,7 +257,7 @@ function xmove(x=0, p=_NO_ARG) = // Function&Module: fwd() // // Usage: As Module -// fwd(y) ... +// fwd(y) children; // Usage: Translate Points // pts = fwd(y, p); // Usage: Get Translation Matrix @@ -292,6 +285,7 @@ function xmove(x=0, p=_NO_ARG) = // pt3 = fwd(3, p=[[1,2,3],[4,5,6]]); // Returns: [[1,-1,3], [4,2,6]] // mat3d = fwd(4); // Returns: [[1,0,0,0],[0,1,0,-4],[0,0,1,0],[0,0,0,1]] module fwd(y=0, p) { + req_children($children); assert(is_undef(p), "Module form `fwd()` does not accept p= argument."); assert(is_finite(y), "Invalid number") translate([0,-y,0]) children(); @@ -306,7 +300,7 @@ function fwd(y=0, p=_NO_ARG) = // Aliases: ymove() // // Usage: As Module -// back(y) ... +// back(y) children; // Usage: Translate Points // pts = back(y, p); // Usage: Get Translation Matrix @@ -334,6 +328,7 @@ function fwd(y=0, p=_NO_ARG) = // pt3 = back(3, p=[[1,2,3],[4,5,6]]); // Returns: [[1,5,3], [4,8,6]] // mat3d = back(4); // Returns: [[1,0,0,0],[0,1,0,4],[0,0,1,0],[0,0,0,1]] module back(y=0, p) { + req_children($children); assert(is_undef(p), "Module form `back()` does not accept p= argument."); assert(is_finite(y), "Invalid number") translate([0,y,0]) children(); @@ -344,6 +339,7 @@ function back(y=0,p=_NO_ARG) = move([0,y,0],p=p); module ymove(y=0, p) { + req_children($children); assert(is_undef(p), "Module form `ymove()` does not accept p= argument."); assert(is_finite(y), "Invalid number") translate([0,y,0]) children(); @@ -357,7 +353,7 @@ function ymove(y=0,p=_NO_ARG) = // Function&Module: down() // // Usage: As Module -// down(z) ... +// down(z) children; // Usage: Translate Points // pts = down(z, p); // Usage: Get Translation Matrix @@ -384,6 +380,7 @@ function ymove(y=0,p=_NO_ARG) = // pt2 = down(3, p=[[1,2,3],[4,5,6]]); // Returns: [[1,2,0], [4,5,3]] // mat3d = down(4); // Returns: [[1,0,0,0],[0,1,0,0],[0,0,1,-4],[0,0,0,1]] module down(z=0, p) { + req_children($children); assert(is_undef(p), "Module form `down()` does not accept p= argument."); translate([0,0,-z]) children(); } @@ -397,7 +394,7 @@ function down(z=0, p=_NO_ARG) = // Aliases: zmove() // // Usage: As Module -// up(z) ... +// up(z) children; // Usage: Translate Points // pts = up(z, p); // Usage: Get Translation Matrix @@ -424,6 +421,7 @@ function down(z=0, p=_NO_ARG) = // pt2 = up(3, p=[[1,2,3],[4,5,6]]); // Returns: [[1,2,6], [4,5,9]] // mat3d = up(4); // Returns: [[1,0,0,0],[0,1,0,0],[0,0,1,4],[0,0,0,1]] module up(z=0, p) { + req_children($children); assert(is_undef(p), "Module form `up()` does not accept p= argument."); assert(is_finite(z), "Invalid number"); translate([0,0,z]) children(); @@ -434,6 +432,7 @@ function up(z=0, p=_NO_ARG) = move([0,0,z],p=p); module zmove(z=0, p) { + req_children($children); assert(is_undef(p), "Module form `zmove()` does not accept p= argument."); assert(is_finite(z), "Invalid number"); translate([0,0,z]) children(); @@ -453,10 +452,10 @@ function zmove(z=0, p=_NO_ARG) = // Function&Module: rot() // // Usage: As a Module -// rot(a, [cp], [reverse]) {...} -// rot([X,Y,Z], [cp], [reverse]) {...} -// rot(a, v, [cp], [reverse]) {...} -// rot(from, to, [a], [reverse]) {...} +// rot(a, [cp=], [reverse=]) children; +// rot([X,Y,Z], [cp=], [reverse=]) children; +// rot(a, v, [cp=], [reverse=]) children; +// rot(from=, to=, [a=], [reverse=]) children; // Usage: As a Function to transform data in `p` // pts = rot(a, p=, [cp=], [reverse=]); // pts = rot([X,Y,Z], p=, [cp=], [reverse=]); @@ -519,6 +518,7 @@ function zmove(z=0, p=_NO_ARG) = // stroke(rot(30,p=path), closed=true); module rot(a=0, v, cp, from, to, reverse=false) { + req_children($children); m = rot(a=a, v=v, cp=cp, from=from, to=to, reverse=reverse); multmatrix(m) children(); } @@ -556,7 +556,7 @@ function rot(a=0, v, cp, from, to, reverse=false, p=_NO_ARG, _m) = // Function&Module: xrot() // // Usage: As Module -// xrot(a, [cp=]) ... +// xrot(a, [cp=]) children; // Usage: As a function to rotate points // rotated = xrot(a, p, [cp=]); // Usage: As a function to return rotation matrix @@ -585,6 +585,7 @@ function rot(a=0, v, cp, from, to, reverse=false, p=_NO_ARG, _m) = // xrot(90) cylinder(h=50, r=10, center=true); module xrot(a=0, p, cp) { + req_children($children); assert(is_undef(p), "Module form `xrot()` does not accept p= argument."); if (a==0) { children(); // May be slightly faster? @@ -601,7 +602,7 @@ function xrot(a=0, p=_NO_ARG, cp) = rot([a,0,0], cp=cp, p=p); // Function&Module: yrot() // // Usage: As Module -// yrot(a, [cp=]) ... +// yrot(a, [cp=]) children; // Usage: Rotate Points // rotated = yrot(a, p, [cp=]); // Usage: Get Rotation Matrix @@ -630,6 +631,7 @@ function xrot(a=0, p=_NO_ARG, cp) = rot([a,0,0], cp=cp, p=p); // yrot(90) cylinder(h=50, r=10, center=true); module yrot(a=0, p, cp) { + req_children($children); assert(is_undef(p), "Module form `yrot()` does not accept p= argument."); if (a==0) { children(); // May be slightly faster? @@ -646,7 +648,7 @@ function yrot(a=0, p=_NO_ARG, cp) = rot([0,a,0], cp=cp, p=p); // Function&Module: zrot() // // Usage: As Module -// zrot(a, [cp=]) ... +// zrot(a, [cp=]) children; // Usage: As Function to rotate points // rotated = zrot(a, p, [cp=]); // Usage: As Function to return rotation matrix @@ -675,6 +677,7 @@ function yrot(a=0, p=_NO_ARG, cp) = rot([0,a,0], cp=cp, p=p); // zrot(90) cube(size=[60,20,40], center=true); module zrot(a=0, p, cp) { + req_children($children); assert(is_undef(p), "Module form `zrot()` does not accept p= argument."); if (a==0) { children(); // May be slightly faster? @@ -696,8 +699,8 @@ function zrot(a=0, p=_NO_ARG, cp) = rot(a, cp=cp, p=p); // Function&Module: scale() // Usage: As Module -// scale(SCALAR) ... -// scale([X,Y,Z]) ... +// scale(SCALAR) children; +// scale([X,Y,Z]) children; // Usage: Scale Points // pts = scale(v, p, [cp=]); // Usage: Get Scaling Matrix @@ -747,7 +750,7 @@ function scale(v=1, p=_NO_ARG, cp=[0,0,0]) = // // // Usage: As Module -// xscale(x, [cp=]) ... +// xscale(x, [cp=]) children; // Usage: Scale Points // scaled = xscale(x, p, [cp=]); // Usage: Get Affine Matrix @@ -780,6 +783,7 @@ function scale(v=1, p=_NO_ARG, cp=[0,0,0]) = // #stroke(path,closed=true); // stroke(xscale(2,p=path),closed=true); module xscale(x=1, p, cp=0) { + req_children($children); assert(is_undef(p), "Module form `xscale()` does not accept p= argument."); cp = is_num(cp)? [cp,0,0] : cp; if (cp == [0,0,0]) { @@ -800,7 +804,7 @@ function xscale(x=1, p=_NO_ARG, cp=0) = // Function&Module: yscale() // // Usage: As Module -// yscale(y, [cp=]) ... +// yscale(y, [cp=]) children; // Usage: Scale Points // scaled = yscale(y, p, [cp=]); // Usage: Get Affine Matrix @@ -833,6 +837,7 @@ function xscale(x=1, p=_NO_ARG, cp=0) = // #stroke(path,closed=true); // stroke(yscale(2,p=path),closed=true); module yscale(y=1, p, cp=0) { + req_children($children); assert(is_undef(p), "Module form `yscale()` does not accept p= argument."); cp = is_num(cp)? [0,cp,0] : cp; if (cp == [0,0,0]) { @@ -853,7 +858,7 @@ function yscale(y=1, p=_NO_ARG, cp=0) = // Function&Module: zscale() // // Usage: As Module -// zscale(z, [cp=]) ... +// zscale(z, [cp=]) children; // Usage: Scale Points // scaled = zscale(z, p, [cp=]); // Usage: Get Affine Matrix @@ -886,6 +891,7 @@ function yscale(y=1, p=_NO_ARG, cp=0) = // #stroke(path,closed=true); // stroke(zscale(2,path),closed=true); module zscale(z=1, p, cp=0) { + req_children($children); assert(is_undef(p), "Module form `zscale()` does not accept p= argument."); cp = is_num(cp)? [0,0,cp] : cp; if (cp == [0,0,0]) { @@ -909,7 +915,7 @@ function zscale(z=1, p=_NO_ARG, cp=0) = // Function&Module: mirror() // Usage: As Module -// mirror(v) ... +// mirror(v) children; // Usage: As Function // pt = mirror(v, p); // Usage: Get Reflection/Mirror Matrix @@ -979,11 +985,11 @@ function mirror(v, p=_NO_ARG) = // Function&Module: xflip() // // Usage: As Module -// xflip([x]) ... +// xflip([x=]) children; // Usage: As Function // pt = xflip(p, [x]); // Usage: Get Affine Matrix -// pt = xflip([x]); +// mat = xflip([x=]); // // Topics: Affine, Matrices, Transforms, Reflection, Mirroring // See Also: mirror(), yflip(), zflip() @@ -998,8 +1004,8 @@ function mirror(v, p=_NO_ARG) = // * Called as a function without a `p` argument, returns the affine3d 4x4 mirror matrix. // // Arguments: -// x = The X coordinate of the plane of reflection. Default: 0 // p = If given, the point, path, patch, or VNF to mirror. Function use only. +// x = The X coordinate of the plane of reflection. Default: 0 // // Example: // xflip() yrot(90) cylinder(d1=10, d2=0, h=20); @@ -1011,6 +1017,7 @@ function mirror(v, p=_NO_ARG) = // color("blue", 0.25) left(5) cube([0.01,15,15], center=true); // color("red", 0.333) yrot(90) cylinder(d1=10, d2=0, h=20); module xflip(p, x=0) { + req_children($children); assert(is_undef(p), "Module form `zflip()` does not accept p= argument."); translate([x,0,0]) mirror([1,0,0]) @@ -1032,11 +1039,11 @@ function xflip(p=_NO_ARG, x=0) = // Function&Module: yflip() // // Usage: As Module -// yflip([y]) ... +// yflip([y=]) children; // Usage: As Function // pt = yflip(p, [y]); // Usage: Get Affine Matrix -// pt = yflip([y]); +// mat = yflip([y=]); // // Topics: Affine, Matrices, Transforms, Reflection, Mirroring // See Also: mirror(), xflip(), zflip() @@ -1064,6 +1071,7 @@ function xflip(p=_NO_ARG, x=0) = // color("blue", 0.25) back(5) cube([15,0.01,15], center=true); // color("red", 0.333) xrot(90) cylinder(d1=10, d2=0, h=20); module yflip(p, y=0) { + req_children($children); assert(is_undef(p), "Module form `yflip()` does not accept p= argument."); translate([0,y,0]) mirror([0,1,0]) @@ -1085,11 +1093,11 @@ function yflip(p=_NO_ARG, y=0) = // Function&Module: zflip() // // Usage: As Module -// zflip([z]) ... +// zflip([z=]) children; // Usage: As Function // pt = zflip(p, [z]); // Usage: Get Affine Matrix -// pt = zflip([z]); +// mat = zflip([z=]); // // Topics: Affine, Matrices, Transforms, Reflection, Mirroring // See Also: mirror(), xflip(), yflip() @@ -1117,6 +1125,7 @@ function yflip(p=_NO_ARG, y=0) = // color("blue", 0.25) down(5) cube([15,15,0.01], center=true); // color("red", 0.333) cylinder(d1=10, d2=0, h=20); module zflip(p, z=0) { + req_children($children); assert(is_undef(p), "Module form `zflip()` does not accept p= argument."); translate([0,0,z]) mirror([0,0,1]) @@ -1137,7 +1146,7 @@ function zflip(p=_NO_ARG, z=0) = // Function&Module: frame_map() // Usage: As module -// frame_map(v1, v2, v3, [reverse=]) { ... } +// frame_map(v1, v2, v3, [reverse=]) children; // Usage: As function to remap points // transformed = frame_map(v1, v2, v3, p=points, [reverse=]); // Usage: As function to return a transformation matrix: @@ -1216,6 +1225,7 @@ function frame_map(x,y,z, p=_NO_ARG, reverse=false) = module frame_map(x,y,z,p,reverse=false) { + req_children($children); assert(is_undef(p), "Module form `frame_map()` does not accept p= argument."); multmatrix(frame_map(x,y,z,reverse=reverse)) children(); @@ -1224,7 +1234,7 @@ module frame_map(x,y,z,p,reverse=false) // Function&Module: skew() // Usage: As Module -// skew([sxy=], [sxz=], [syx=], [syz=], [szx=], [szy=]) ... +// skew([sxy=], [sxz=], [syx=], [syz=], [szx=], [szy=]) children; // Usage: As Function // pts = skew(p, [sxy=], [sxz=], [syx=], [syz=], [szx=], [szy=]); // Usage: Get Affine Matrix @@ -1276,6 +1286,7 @@ module frame_map(x,y,z,p,reverse=false) // stroke(pts,closed=true,dots=true,dots_color="blue"); module skew(p, sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0) { + req_children($children); assert(is_undef(p), "Module form `skew()` does not accept p= argument.") multmatrix( affine3d_skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy) @@ -1299,7 +1310,7 @@ function skew(p=_NO_ARG, sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0) = /// Internal Function: is_2d_transform() /// Usage: -/// x = is_2d_transform(t); +/// bool = is_2d_transform(t); /// Topics: Affine, Matrices, Transforms, Type Checking /// See Also: is_affine(), is_matrix() /// Description: diff --git a/tutorials/Transforms.md b/tutorials/Transforms.md index c7aa541..ca5ad66 100644 --- a/tutorials/Transforms.md +++ b/tutorials/Transforms.md @@ -50,21 +50,13 @@ include right(30) sphere(d=20); ``` -There is also a more generic `move()` command that can work just like `translate()`, or you can -specify the motion on each axis more clearly: +There is also a more generic `move()` command that can work just like `translate()`: ```openscad include #sphere(d=20); move([30,-10]) sphere(d=20); ``` -```openscad -include -#sphere(d=20); -move(x=30,y=10) sphere(d=20); -``` - - ## Scaling The `scale()` command is also fairly simple: ```openscad diff --git a/utility.scad b/utility.scad index 978f538..5a92dfb 100644 --- a/utility.scad +++ b/utility.scad @@ -731,7 +731,7 @@ function segs(r) = // Usage: // no_children($children); // Topics: Error Checking -// See Also: no_function(), no_module() +// See Also: no_function(), no_module(), req_children() // Description: // Assert that the calling module does not support children. Prints an error message to this effect and fails if children are present, // as indicated by its argument. @@ -749,6 +749,28 @@ module no_children(count) { } +// Module: req_children() +// Usage: +// req_children($children); +// Topics: Error Checking +// See Also: no_function(), no_module() +// Description: +// Assert that the calling module requires children. Prints an error message and fails if no +// children are present as indicated by its argument. +// Arguments: +// $children = number of children the module has. +// Example: +// module foo() { +// req_children($children); +// } +module req_children(count) { + assert($children==0, "Module no_children() does not support child modules"); + if ($parent_modules>0) { + assert(count>0, str("Module ",parent_module(1),"() requires children")); + } +} + + // Function: no_function() // Usage: // dummy = no_function(name) diff --git a/vnf.scad b/vnf.scad index 6a90db7..05ffcc3 100644 --- a/vnf.scad +++ b/vnf.scad @@ -27,7 +27,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // Function: vnf_vertex_array() // Usage: -// vnf = vnf_vertex_array(points, [caps], [cap1], [cap2], [style], [reverse], [col_wrap], [row_wrap]); +// vnf = vnf_vertex_array(points, [caps=], [cap1=], [cap2=], [style=], [reverse=], [col_wrap=], [row_wrap=]); // Description: // Creates a VNF structure from a rectangular vertex list, by dividing the vertices into columns and rows, // adding faces to tile the surface. You can optionally have faces added to wrap the last column @@ -662,7 +662,7 @@ function vnf_merge_points(vnf,eps=EPSILON) = // Function: vnf_drop_unused_points() // Usage: -// clean_vnf=vnf_drop_unused_points(vnf); +// clean_vnf = vnf_drop_unused_points(vnf); // Description: // Remove all unreferenced vertices from a VNF. Note that in most // cases unreferenced vertices cause no harm, and this function may @@ -1087,7 +1087,7 @@ function _triangulate_planar_convex_polygons(polys) = // Function: vnf_bend() // Usage: -// bentvnf = vnf_bend(vnf,r,d,[axis]); +// bentvnf = vnf_bend(vnf,r|d=,[axis=]); // Description: // Bend a VNF around the X, Y or Z axis, splitting up faces as necessary. Returns the bent // VNF. For bending around the Z axis the input VNF must not cross the Y=0 plane. For bending @@ -1292,7 +1292,7 @@ module _show_faces(vertices, faces, size=1) { // Module: debug_vnf() // Usage: -// debug_vnf(vnfs, [faces], [vertices], [opacity], [size], [convexity]); +// debug_vnf(vnfs, [faces=], [vertices=], [opacity=], [size=], [convexity=]); // Description: // A drop-in module to replace `vnf_polyhedron()` to help debug vertices and faces. // Draws all the vertices at their 3D position, numbered in blue by their @@ -1332,7 +1332,7 @@ module debug_vnf(vnf, faces=true, vertices=true, opacity=0.5, size=1, convexity= // Usage: As Function // fails = vnf_validate(vnf); // Usage: As Module -// vnf_validate(vnf, [size]); +// vnf_validate(vnf, [size], [check_isects]); // Description: // When called as a function, returns a list of non-manifold errors with the given VNF. // Each error has the format `[ERR_OR_WARN,CODE,MESG,POINTS,COLOR]`. @@ -1358,6 +1358,8 @@ module debug_vnf(vnf, faces=true, vertices=true, opacity=0.5, size=1, convexity= // Arguments: // vnf = The VNF to validate. // size = The width of the lines and diameter of points used to highlight edges and vertices. Module only. Default: 1 +// -- +// show_warns = If true show warnings for non-triangular faces. Default: true // check_isects = If true, performs slow checks for intersecting faces. Default: false // Example: BIG_FACE Warnings; Faces with More Than 3 Vertices. CGAL often will fail to accept that a face is planar after a rotation, if it has more than 3 vertices. // vnf = skin([ @@ -1628,6 +1630,7 @@ function _edge_not_reported(edge, varr, reports) = module vnf_validate(vnf, size=1, show_warns=true, check_isects=false) { + no_children($children); faults = vnf_validate( vnf, show_warns=show_warns, check_isects=check_isects