From f36fbb60dbf85665fd573e28b0f4f9c3ce0274a0 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 30 Nov 2020 21:09:00 -0800 Subject: [PATCH] Fix for #166: tweaks for xcopies, etc. --- distributors.scad | 9 ++-- tests/test_transforms.scad | 9 ++++ transforms.scad | 86 +++++++++++++++++++++++++++++--------- version.scad | 2 +- 4 files changed, 83 insertions(+), 23 deletions(-) diff --git a/distributors.scad b/distributors.scad index 3d101118..ff3482e5 100644 --- a/distributors.scad +++ b/distributors.scad @@ -152,7 +152,7 @@ module line_of(spacing, n, l, p1, p2) // spacing = spacing between copies. (Default: 1.0) // n = Number of copies to spread out. (Default: 2) // l = Length to spread copies over. -// sp = If given, copies will be spread on a line to the right of starting position `sp`. If not given, copies will be spread along a line that is centered at [0,0,0]. +// sp = If given as a point, copies will be spread on a line to the right of starting position `sp`. If given as a scalar, copies will be spread on a line to the right of starting position `[sp,0,0]`. If not given, copies will be spread along a line that is centered at [0,0,0]. // // Side Effects: // `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually. @@ -170,6 +170,7 @@ module line_of(spacing, n, l, p1, p2) // } module xcopies(spacing, n, l, sp) { + sp = is_finite(sp)? [sp,0,0] : sp; line_of(l=l*RIGHT, spacing=spacing*RIGHT, n=n, p1=sp) children(); } @@ -187,7 +188,7 @@ module xcopies(spacing, n, l, sp) // spacing = spacing between copies. (Default: 1.0) // n = Number of copies to spread out. (Default: 2) // l = Length to spread copies over. -// sp = If given, copies will be spread on a line back from starting position `sp`. If not given, copies will be spread along a line that is centered at [0,0,0]. +// sp = If given as a point, copies will be spread on a line back from starting position `sp`. If given as a scalar, copies will be spread on a line back from starting position `[0,sp,0]`. If not given, copies will be spread along a line that is centered at [0,0,0]. // // Side Effects: // `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually. @@ -205,6 +206,7 @@ module xcopies(spacing, n, l, sp) // } module ycopies(spacing, n, l, sp) { + sp = is_finite(sp)? [0,sp,0] : sp; line_of(l=l*BACK, spacing=spacing*BACK, n=n, p1=sp) children(); } @@ -222,7 +224,7 @@ module ycopies(spacing, n, l, sp) // spacing = spacing between copies. (Default: 1.0) // n = Number of copies to spread out. (Default: 2) // l = Length to spread copies over. -// sp = If given, copies will be spread on a line up from starting position `sp`. If not given, copies will be spread along a line that is centered at [0,0,0]. +// sp = If given as a point, copies will be spread on a line up from starting position `sp`. If given as a scalar, copies will be spread on a line up from starting position `[0,0,sp]`. If not given, copies will be spread along a line that is centered at [0,0,0]. // // Side Effects: // `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually. @@ -240,6 +242,7 @@ module ycopies(spacing, n, l, sp) // } module zcopies(spacing, n, l, sp) { + sp = is_finite(sp)? [0,0,sp] : sp; line_of(l=l*UP, spacing=spacing*UP, n=n, p1=sp) children(); } diff --git a/tests/test_transforms.scad b/tests/test_transforms.scad index 3fb044bf..2681b480 100644 --- a/tests/test_transforms.scad +++ b/tests/test_transforms.scad @@ -106,14 +106,23 @@ test_up(); module test_scale() { + cb = cube(1); vals = [[-1,-2,-3],[1,1,1],[3,6,2],[1,2,3],[243,75,147]]; for (val=vals) { + assert_equal(scale(point2d(val)), [[val.x,0,0],[0,val.y,0],[0,0,1]]); assert_equal(scale(val), [[val.x,0,0,0],[0,val.y,0,0],[0,0,val.z,0],[0,0,0,1]]); assert_equal(scale(val, p=[1,2,3]), vmul([1,2,3], val)); scale(val) nil(); } assert_equal(scale(3), [[3,0,0,0],[0,3,0,0],[0,0,3,0],[0,0,0,1]]); assert_equal(scale(3, p=[1,2,3]), 3*[1,2,3]); + assert_equal(scale(3, p=cb), cube(3)); + assert_equal(scale(2, p=square(1)), square(2)); + assert_equal(scale(2, cp=[1,1], p=square(1)), square(2, center=true)); + assert_equal(scale([2,3], p=square(1)), square([2,3])); + assert_equal(scale([2,2], cp=[0.5,0.5], p=square(1)), move([-0.5,-0.5], p=square([2,2]))); + assert_equal(scale([2,3,4], p=cb), cube([2,3,4])); + assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) vmul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]); // Verify that module at least doesn't crash. scale(-5) scale(5) nil(); } diff --git a/transforms.scad b/transforms.scad index 43cb257a..1be45143 100644 --- a/transforms.scad +++ b/transforms.scad @@ -524,12 +524,12 @@ function zrot(a=0, cp=undef, p=undef) = rot(a, cp=cp, p=p); // Function&Module: scale() // Usage: As Module -// scale(SCALAR) ... -// scale([X,Y,Z]) ... +// scale(SCALAR, ) ... +// scale([X,Y,Z], ) ... // Usage: Scale Points -// pts = scale(v, p); +// pts = scale(v, p, ); // Usage: Get Scaling Matrix -// mat = scale(v); +// mat = scale(v, ); // Description: // Scales by the [X,Y,Z] scaling factors given in `v`. If `v` is given as a scalar number, all axes are scaled uniformly by that amount. // * Called as the built-in module, scales all children. @@ -541,6 +541,7 @@ function zrot(a=0, cp=undef, p=undef) = rot(a, cp=cp, p=p); // * Called as a function without a `p` argument, and a 3D list of scaling factors in `v`, returns an affine3d scaling matrix. // Arguments: // v = Either a numeric uniform scaling factor, or a list of [X,Y,Z] scaling factors. Default: 1 +// cp = If given, centers the scaling on the point `cp`. // p = If called as a function, the point or list of points to scale. // Example(NORENDER): // pt1 = scale(3, p=[3,1,4]); // Returns: [9,3,12] @@ -552,20 +553,33 @@ function zrot(a=0, cp=undef, p=undef) = rot(a, cp=cp, p=p); // path = circle(d=50,$fn=12); // #stroke(path,closed=true); // stroke(scale([1.5,3],p=path),closed=true); -function scale(v=1, p=undef) = +function scale(v=1, cp=[0,0,0], p=undef) = assert(is_num(v) || is_vector(v)) assert(is_undef(p) || is_list(p)) - let(v = is_num(v)? [v,v,v] : v) + let( v = is_num(v)? [v,v,v] : v ) is_undef(p)? ( - len(v)==2? affine2d_scale(v) : affine3d_scale(point3d(v)) + len(v)==2? ( + cp==[0,0,0] || cp == [0,0] ? affine2d_scale(v) : ( + affine2d_translate(point2d(cp)) * + affine2d_scale(v) * + affine2d_translate(point2d(-cp)) + ) + ) : ( + cp==[0,0,0] ? affine3d_scale(v) : ( + affine3d_translate(point3d(cp)) * + affine3d_scale(v) * + affine3d_translate(point3d(-cp)) + ) + ) ) : ( assert(is_list(p)) - is_vector(p)? ( len(p)==2? vmul(p,point2d(v)) : vmul(p,point3d(v,1)) ) : + let( mat = scale(v=v, cp=cp) ) + is_vector(p)? apply(mat, p) : is_vnf(p)? let(inv=product([for (x=v) x<0? -1 : 1])) [ - scale(v=v, p=p[0]), + apply(mat, p[0]), inv>=0? p[1] : [for (l=p[1]) reverse(l)] ] : - [ for (pp=p) scale(v=v, p=pp) ] + apply(mat, p) ); @@ -591,7 +605,8 @@ function scale(v=1, p=undef) = // // Arguments: // x = Factor to scale by, along the X axis. -// p = A point or path to scale, when called as a function. +// cp = If given as a point, centers the scaling on the point `cp`. If given as a scalar, centers scaling on the point `[cp,0,0]` +// p = A point, path, bezier patch, or VNF to scale, when called as a function. // planar = If true, and `p` is not given, then the matrix returned is an affine2d matrix instead of an affine3d matrix. // // Example: As Module @@ -601,9 +616,20 @@ function scale(v=1, p=undef) = // path = circle(d=50,$fn=12); // #stroke(path,closed=true); // stroke(xscale(2,p=path),closed=true); -module xscale(x=1) scale([x,1,1]) children(); +module xscale(x=1, cp=0) { + cp = is_num(cp)? [cp,0,0] : cp; + if (cp == [0,0,0]) { + scale([x,1,1]) children(); + } else { + translate(cp) scale([x,1,1]) translate(-cp) children(); + } +} -function xscale(x=1, p=undef, planar=false) = (planar || (!is_undef(p) && len(p)==2))? scale([x,1],p=p) : scale([x,1,1],p=p); +function xscale(x=1, cp=0, p, planar=false) = + let( cp = is_num(cp)? [cp,0,0] : cp ) + (planar || (!is_undef(p) && len(p)==2)) + ? scale([x,1], cp=cp, p=p) + : scale([x,1,1], cp=cp, p=p); // Function&Module: yscale() @@ -627,7 +653,8 @@ function xscale(x=1, p=undef, planar=false) = (planar || (!is_undef(p) && len(p) // // Arguments: // y = Factor to scale by, along the Y axis. -// p = A point or path to scale, when called as a function. +// cp = If given as a point, centers the scaling on the point `cp`. If given as a scalar, centers scaling on the point `[0,cp,0]` +// p = A point, path, bezier patch, or VNF to scale, when called as a function. // planar = If true, and `p` is not given, then the matrix returned is an affine2d matrix instead of an affine3d matrix. // // Example: As Module @@ -637,9 +664,20 @@ function xscale(x=1, p=undef, planar=false) = (planar || (!is_undef(p) && len(p) // path = circle(d=50,$fn=12); // #stroke(path,closed=true); // stroke(yscale(2,p=path),closed=true); -module yscale(y=1) scale([1,y,1]) children(); +module yscale(y=1, cp=0) { + cp = is_num(cp)? [0,cp,0] : cp; + if (cp == [0,0,0]) { + scale([1,y,1]) children(); + } else { + translate(cp) scale([1,y,1]) translate(-cp) children(); + } +} -function yscale(y=1, p=undef, planar=false) = (planar || (!is_undef(p) && len(p)==2))? scale([1,y],p=p) : scale([1,y,1],p=p); +function yscale(y=1, cp=0, p, planar=false) = + let( cp = is_num(cp)? [0,cp,0] : cp ) + (planar || (!is_undef(p) && len(p)==2)) + ? scale([1,y],p=p) + : scale([1,y,1],p=p); // Function&Module: zscale() @@ -663,7 +701,8 @@ function yscale(y=1, p=undef, planar=false) = (planar || (!is_undef(p) && len(p) // // Arguments: // z = Factor to scale by, along the Z axis. -// p = A point or path to scale, when called as a function. +// cp = If given as a point, centers the scaling on the point `cp`. If given as a scalar, centers scaling on the point `[0,0,cp]` +// p = A point, path, bezier patch, or VNF to scale, when called as a function. // planar = If true, and `p` is not given, then the matrix returned is an affine2d matrix instead of an affine3d matrix. // // Example: As Module @@ -673,9 +712,18 @@ function yscale(y=1, p=undef, planar=false) = (planar || (!is_undef(p) && len(p) // path = xrot(90,p=path3d(circle(d=50,$fn=12))); // #trace_path(path); // trace_path(zscale(2,p=path)); -module zscale(z=1) scale([1,1,z]) children(); +module zscale(z=1, cp=0) { + cp = is_num(cp)? [0,0,cp] : cp; + if (cp == [0,0,0]) { + scale([1,1,z]) children(); + } else { + translate(cp) scale([1,1,z]) translate(-cp) children(); + } +} -function zscale(z=1, p=undef) = scale([1,1,z],p=p); +function zscale(z=1, cp=0, p) = + let( cp = is_num(cp)? [0,0,cp] : cp ) + scale([1,1,z], cp=cp, p=p); // Function&Module: mirror() diff --git a/version.scad b/version.scad index be986f38..1744eebd 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,472]; +BOSL_VERSION = [2,0,473]; // Section: BOSL Library Version Functions