Fix for #166: tweaks for xcopies, etc.

This commit is contained in:
Garth Minette 2020-11-30 21:09:00 -08:00
parent 021073863a
commit f36fbb60db
4 changed files with 83 additions and 23 deletions

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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, <cp>) ...
// scale([X,Y,Z], <cp>) ...
// Usage: Scale Points
// pts = scale(v, p);
// pts = scale(v, p, <cp>);
// Usage: Get Scaling Matrix
// mat = scale(v);
// mat = scale(v, <cp>);
// 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 )
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()

View file

@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////////////
BOSL_VERSION = [2,0,472];
BOSL_VERSION = [2,0,473];
// Section: BOSL Library Version Functions