diff --git a/distributors.scad b/distributors.scad index c2da04b..1cf9dff 100644 --- a/distributors.scad +++ b/distributors.scad @@ -400,19 +400,17 @@ module zdistribute(spacing=10, sizes=undef, l=undef) // Makes a square or hexagonal grid of copies of children. // // Usage: -// grid2d(size, spacing, [stagger], [scale], [in_poly]) ... -// grid2d(size, cols, rows, [stagger], [scale], [in_poly]) ... -// grid2d(spacing, cols, rows, [stagger], [scale], [in_poly]) ... +// grid2d(spacing, size, [stagger], [scale], [in_poly]) ... +// grid2d(n, size, [stagger], [scale], [in_poly]) ... +// grid2d(spacing, n, [stagger], [scale], [in_poly]) ... // grid2d(spacing, in_poly, [stagger], [scale]) ... -// grid2d(cols, rows, in_poly, [stagger], [scale]) ... +// grid2d(n, in_poly, [stagger], [scale]) ... // // Arguments: // size = The [X,Y] size to spread the copies over. // spacing = Distance between copies in [X,Y] or scalar distance. -// cols = How many columns of copies to make. If staggered, count both staggered and unstaggered columns. -// rows = How many rows of copies to make. If staggered, count both staggered and unstaggered rows. +// 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) // stagger = If true, make a staggered (hexagonal) grid. If false, make square grid. If `"alt"`, makes alternate staggered pattern. Default: false -// scale = [X,Y] scaling factors to reshape grid. // in_poly = If given a list of polygon points, only creates copies whose center would be inside the polygon. Polygon can be concave and/or self crossing. // // Side Effects: @@ -421,10 +419,11 @@ module zdistribute(spacing=10, sizes=undef, l=undef) // `$row` is set to the integer row number for each child. // // Examples: -// grid2d(size=50, spacing=10, stagger=false) cylinder(d=10, h=1); -// grid2d(spacing=10, rows=7, cols=13, stagger=true) cylinder(d=6, h=5); -// grid2d(spacing=10, rows=7, cols=13, stagger="alt") cylinder(d=6, h=5); -// grid2d(size=50, rows=11, cols=11, stagger=true) cylinder(d=5, h=1); +// grid2d(size=50, spacing=10) cylinder(d=10, h=1); +// grid2d(size=50, spacing=[10,15]) cylinder(d=10, h=1); +// grid2d(spacing=10, n=[13,7], stagger=true) cylinder(d=6, h=5); +// grid2d(spacing=10, n=[13,7], stagger="alt") cylinder(d=6, h=5); +// grid2d(size=50, n=11, stagger=true) cylinder(d=5, h=1); // // Example: // poly = [[-25,-25], [25,25], [-25,25], [25,-25]]; @@ -433,7 +432,7 @@ module zdistribute(spacing=10, sizes=undef, l=undef) // %polygon(poly); // // Example: Using `$row` and `$col` -// grid2d(spacing=[8,8], cols=8, rows=8) +// grid2d(spacing=8], n=8) // color(($row+$col)%2?"black":"red") // cube([8,8,0.01], center=false); // @@ -449,60 +448,62 @@ module zdistribute(spacing=10, sizes=undef, l=undef) // zrot(180/6) // cylinder(h=20, d=10/cos(180/6)+0.01, $fn=6); // } -module grid2d(size=undef, spacing=undef, cols=undef, rows=undef, stagger=false, scale=[1,1,1], in_poly=undef) +module grid2d(spacing, n, size, stagger=false, in_poly=undef) { assert(in_list(stagger, [false, true, "alt"])); - scl = vmul(scalar_vec3(scale, 1), (stagger!=false? [0.5, sin(60), 1] : [1,1,1])); - if (!is_undef(size)) { - siz = scalar_vec3(size); - if (!is_undef(spacing)) { - spc = vmul(scalar_vec3(spacing), scl); - maxcols = ceil(siz.x/spc.x); - maxrows = ceil(siz.y/spc.y); - grid2d(spacing=spacing, cols=maxcols, rows=maxrows, stagger=stagger, scale=scale, in_poly=in_poly) children(); - } else { - spc = [siz.x/cols, siz.y/rows]; - grid2d(spacing=spc, cols=cols, rows=rows, stagger=stagger, scale=scale, in_poly=in_poly) children(); + bounds = is_undef(in_poly)? undef : pointlist_bounds(in_poly); + size = is_num(size)? [size, size] : + is_vector(size)? assert(len(size)==2) size : + bounds!=undef? [ + for (i=[0:1]) 2*max(abs(bounds[0][i]),bounds[1][i]) + ] : undef; + spacing = is_num(spacing)? ( + stagger!=false? polar_to_xy(spacing,60) : + [spacing,spacing] + ) : + is_vector(spacing)? assert(len(spacing)==2) spacing : + size!=undef? ( + is_num(n)? vdiv(size,(n-1)*[1,1]) : + is_vector(n)? assert(len(n)==2) vdiv(size,n-[1,1]) : + vdiv(size,(stagger==false? [1,1] : [2,2])) + ) : + undef; + n = is_num(n)? [n,n] : + is_vector(n)? assert(len(n)==2) n : + size!=undef && spacing!=undef? vfloor(vdiv(size,spacing))+[1,1] : + [2,2]; + offset = vmul(spacing, n-[1,1])/2; + if (stagger == false) { + for (row = [0:1:n.y-1]) { + for (col = [0:1:n.x-1]) { + pos = vmul([col,row],spacing) - offset; + if (is_undef(in_poly) || point_in_polygon(pos, in_poly)>=0) { + $col = col; + $row = row; + $pos = pos; + translate(pos) children(); + } + } } } else { - spc = is_list(spacing)? point3d(spacing) : vmul(scalar_vec3(spacing), scl); - bounds = !is_undef(in_poly)? pointlist_bounds(in_poly) : undef; - bnds = !is_undef(bounds)? [for (a=[0,1]) 2*max(vabs([ for (i=[0,1]) bounds[i][a] ]))+1 ] : undef; - mcols = !is_undef(cols)? cols : (!is_undef(spc) && !is_undef(bnds))? quantup(ceil(bnds[0]/spc[0])-1, 4)+1 : undef; - mrows = !is_undef(rows)? rows : (!is_undef(spc) && !is_undef(bnds))? quantup(ceil(bnds[1]/spc[1])-1, 4)+1 : undef; - siz = vmul(spc, [mcols-1, mrows-1, 0])+[0,0,0.01]; + // stagger == true or stagger == "alt" staggermod = (stagger == "alt")? 1 : 0; - if (stagger == false) { - for (row = [0:1:mrows-1]) { - for (col = [0:1:mcols-1]) { - pos = [col*spc.x, row*spc.y] - point2d(siz/2); + cols1 = ceil(n.x/2); + cols2 = n.x - cols1; + for (row = [0:1:n.y-1]) { + rowcols = ((row%2) == staggermod)? cols1 : cols2; + if (rowcols > 0) { + for (col = [0:1:rowcols-1]) { + rowdx = (row%2 != staggermod)? spacing.x : 0; + pos = vmul([2*col,row],spacing) + [rowdx,0] - offset; if (is_undef(in_poly) || point_in_polygon(pos, in_poly)>=0) { - $col = col; + $col = col * 2 + ((row%2!=staggermod)? 1 : 0); $row = row; $pos = pos; translate(pos) children(); } } } - } else { - // stagger == true or stagger == "alt" - cols1 = ceil(mcols/2); - cols2 = mcols - cols1; - for (row = [0:1:mrows-1]) { - rowcols = ((row%2) == staggermod)? cols1 : cols2; - if (rowcols > 0) { - for (col = [0:1:rowcols-1]) { - rowdx = (row%2 != staggermod)? spc[0] : 0; - pos = [2*col*spc[0]+rowdx, row*spc[1]] - point2d(siz/2); - if (is_undef(in_poly) || point_in_polygon(pos, in_poly)>=0) { - $col = col * 2 + ((row%2!=staggermod)? 1 : 0); - $row = row; - $pos = pos; - translate(pos) children(); - } - } - } - } } } } diff --git a/tutorials/Distributors.md b/tutorials/Distributors.md new file mode 100644 index 0000000..072e393 --- /dev/null +++ b/tutorials/Distributors.md @@ -0,0 +1,124 @@ +# BOSL2 Distributors Tutorial + +## Distributors + +Distributors are modules that are useful for placing multiple copies of a child +across a line, area, volume, or ring. Many transforms also have one or more +distributive variation. + +Transforms | Related Distributors +----------------------- | --------------------- +`left()`, `right()` | `xcopies()` +`fwd()`, `back()` | `ycopies()` +`down()`, `up()` | `zcopies()` +`move()`, `translate()` | `move_copies()`, `line_of()`, `grid2d()`, `grid3d()` +`xrot()` | `xrot_copies()` +`yrot()` | `yrot_copies()` +`zrot()` | `zrot_copies()` +`rot()`, `rotate()` | `rot_copies()`, `arc_of()` +`xflip()` | `xflip_copy()` +`yflip()` | `yflip_copy()` +`zflip()` | `zflip_copy()` +`mirror()` | `mirror_copy()` + + +### Transform Distributors +Using `xcopies()`, you can make a line of evenly spaced copies of a shape +centered along the X axis. To make a line of 5 spheres, spaced every 20 +units along the X axis, do: +```openscad +xcopies(20, n=5) sphere(d=10); +``` +Note that the first expected argument to `xcopies()` is the spacing argument, +so you do not need to supply the `spacing=` argument name. + +Similarly, `ycopies()` makes a line of evenly spaced copies centered along the +Y axis. To make a line of 5 spheres, spaced every 20 units along the Y +axis, do: +```openscad +ycopies(20, n=5) sphere(d=10); +``` + +And, `zcopies()` makes a line of evenly spaced copies centered along the Z axis. +To make a line of 5 spheres, spaced every 20 units along the Z axis, do: +```openscad +zcopies(20, n=5) sphere(d=10); +``` + +If you don't give the `n=` argument to `xcopies()`, `ycopies()` or `zcopies()`, +then it defaults to 2 (two) copies: +```openscad +xcopies(20) sphere(d=10); +``` + +```openscad +ycopies(20) sphere(d=10); +``` + +```openscad +zcopies(20) sphere(d=10); +``` + +If you don't know the spacing you want, but instead know how long a line you want +the copies distributed over, you can use the `l=` argument instead of the `spacing=` +argument: +```openscad +xcopies(l=100, n=5) sphere(d=10); +``` + +```openscad +ycopies(l=100, n=5) sphere(d=10); +``` + +```openscad +zcopies(l=100, n=5) sphere(d=10); +``` + +If you don't want the line of copies centered on the origin, you can give a starting +point, `sp=`, and the line of copies will start there. For `xcopies()`, the line of +copies will extend to the right of the starting point. +```openscad +xcopies(20, n=5, sp=[0,0,0]) sphere(d=10); +``` + +For `ycopies()`, the line of copies will extend to the back of the starting point. +```openscad +ycopies(20, n=5, sp=[0,0,0]) sphere(d=10); +``` + +For `zcopies()`, the line of copies will extend upwards from the starting point. +```openscad +zcopies(20, n=5, sp=[0,0,0]) sphere(d=10); +``` + +If you need to distribute copies along an arbitrary line, you can use the +`line_of()` command. You can give both the direction vector and the spacing +of the line of copies with the `spacing=` argument: +```openscad +line_of(spacing=(BACK+RIGHT)*20, n=5) sphere(d=10); +``` + +With the `p1=` argument, you can specify the starting point of the line: +```openscad +line_of(spacing=(BACK+RIGHT)*20, n=5, p1=[0,0,0]) sphere(d=10); +``` + +IF you give both `p1=` and `p2=`, you can nail down both the start and endpoints +of the line of copies: +```openscad +line_of(p1=[0,100,0], p2=[100,0,0], n=4) + sphere(d=10); +``` + +You can also spread copies across a 2D area using the `grid2d()` + + +### Rotational Distributors +You can make six copies of a cone, rotated around a center: +```openscad +zrot_copies(n=6) yrot(90) cylinder(h=50,d1=0,d2=20); +``` + +To Be Completed + + diff --git a/tutorials/Transforms.md b/tutorials/Transforms.md index 38e8a80..fe53fb2 100644 --- a/tutorials/Transforms.md +++ b/tutorials/Transforms.md @@ -233,119 +233,3 @@ skew(szy=0.5) cube(10,center=false); ``` -## Distributors - -Distributors are modules that are useful for placing multiple copies of a child -across a line, area, volume, or ring. Many transforms also distributive variation. - -Transforms | Related Distributors ------------------------ | --------------------- -`left()`, `right()` | `xcopies()` -`fwd()`, `back()` | `ycopies()` -`down()`, `up()` | `zcopies()` -`move()`, `translate()` | `move_copies()`, `line_of()`, `grid2d()`, `grid3d()` -`xrot()` | `xrot_copies()` -`yrot()` | `yrot_copies()` -`zrot()` | `zrot_copies()` -`rot()`, `rotate()` | `rot_copies()`, `arc_of()` - - -### Transform Distributors -Using `xcopies()`, you can make a line of evenly spaced copies of a shape -centered along the X axis. To make a line of 5 spheres, spaced every 20 -units along the X axis, do: -```openscad -xcopies(20, n=5) sphere(d=10); -``` -Note that the first expected argument to `xcopies()` is the spacing argument, -so you do not need to supply the `spacing=` argument name. - -Similarly, `ycopies()` makes a line of evenly spaced copies centered along the -Y axis. To make a line of 5 spheres, spaced every 20 units along the Y -axis, do: -```openscad -ycopies(20, n=5) sphere(d=10); -``` - -And, `zcopies()` makes a line of evenly spaced copies centered along the Z axis. -To make a line of 5 spheres, spaced every 20 units along the Z axis, do: -```openscad -zcopies(20, n=5) sphere(d=10); -``` - -If you don't give the `n=` argument to `xcopies()`, `ycopies()` or `zcopies()`, -then it defaults to 2 (two) copies: -```openscad -xcopies(20) sphere(d=10); -``` - -```openscad -ycopies(20) sphere(d=10); -``` - -```openscad -zcopies(20) sphere(d=10); -``` - -If you don't know the spacing you want, but instead know how long a line you want -the copies distributed over, you can use the `l=` argument instead of the `spacing=` -argument: -```openscad -xcopies(l=100, n=5) sphere(d=10); -``` - -```openscad -ycopies(l=100, n=5) sphere(d=10); -``` - -```openscad -zcopies(l=100, n=5) sphere(d=10); -``` - -If you don't want the line of copies centered on the origin, you can give a starting -point, `sp=`, and the line of copies will start there. For `xcopies()`, the line of -copies will extend to the right of the starting point. -```openscad -xcopies(20, n=5, sp=[0,0,0]) sphere(d=10); -``` - -For `ycopies()`, the line of copies will extend to the back of the starting point. -```openscad -ycopies(20, n=5, sp=[0,0,0]) sphere(d=10); -``` - -For `zcopies()`, the line of copies will extend upwards from the starting point. -```openscad -zcopies(20, n=5, sp=[0,0,0]) sphere(d=10); -``` - -If you need to distribute copies along an arbitrary line, you can use the -`line_of()` command. You can give both the direction vector and the spacing -of the line of copies with the `spacing=` argument: -```openscad -line_of(spacing=(BACK+RIGHT)*20, n=5) sphere(d=10); -``` - -With the `p1=` argument, you can specify the starting point of the line: -```openscad -line_of(spacing=(BACK+RIGHT)*20, n=5, p1=[0,0,0]) sphere(d=10); -``` - -IF you give both `p1=` and `p2=`, you can nail down both the start and endpoints -of the line of copies: -```openscad -line_of(p1=[0,100,0], p2=[100,0,0], n=4) - sphere(d=10); -``` - - - -### Rotational Distributors -You can make six copies of a cone, rotated around a center: -```openscad -zrot_copies(n=6) yrot(90) cylinder(h=50,d1=0,d2=20); -``` - -To Be Completed - - diff --git a/vectors.scad b/vectors.scad index ce82094..583cc73 100644 --- a/vectors.scad +++ b/vectors.scad @@ -95,6 +95,17 @@ function vdiv(v1, v2) = [for (i = [0:1:len(v1)-1]) v1[i]/v2[i]]; function vabs(v) = [for (x=v) abs(x)]; +// Function: vfloor() +// Description: +// Returns the given vector after performing a `floor()` on all items. +function vfloor(v) = [for (x=v) floor(x)]; + + +// Function: vceil() +// Description: +// Returns the given vector after performing a `ceil()` on all items. +function vceil(v) = [for (x=v) ceil(x)]; + // Function: unit() // Description: diff --git a/version.scad b/version.scad index 93a8fe9..fd3faef 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,229]; +BOSL_VERSION = [2,0,230]; // Section: BOSL Library Version Functions