Skip to content

Commit b44fd1c

Browse files
Merge pull request #1888 from adrianVmariano/master
add dir= option to scale()
2 parents a9f31f0 + c02d0ef commit b44fd1c

File tree

2 files changed

+50
-40
lines changed

2 files changed

+50
-40
lines changed

math.scad

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,7 @@ function sum_of_sines(a, sines) =
10991099
// ints = rand_int(0,100,3);
11001100
// int = rand_int(-10,10,1)[0];
11011101
function rand_int(minval, maxval, n, seed=undef) =
1102-
assert( is_finite(minval+maxval+n) && (is_undef(seed) || is_finite(seed) ), "\nInput must be finite numbers.")
1102+
assert( is_vector([minval,maxval,n]) && (is_undef(seed) || is_finite(seed) ), "\nInput must be finite numbers.")
11031103
assert(maxval >= minval, "\nMax value cannot be smaller than minval.")
11041104
let (rvect = is_def(seed) ? rands(minval,maxval+1,n,seed) : rands(minval,maxval+1,n))
11051105
[for(entry = rvect) floor(entry)];

transforms.scad

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//////////////////////////////////////////////////////////////////////
22
// LibFile: transforms.scad
33
// Functions and modules that provide shortcuts for translation,
4-
// rotation and mirror operations. Also provided are skew and frame_map
4+
// rotation and mirror operations. Also provided are skew and frame_map,
55
// which remaps the coordinate axes. The shortcuts can act on
66
// geometry, like the usual OpenSCAD rotate() and translate(). They
77
// also work as functions that operate on lists of points in various
@@ -57,8 +57,8 @@ _BOSL2_TRANSFORMS = is_undef(_BOSL2_STD) && (is_undef(BOSL2_NO_STD_WARNING) || !
5757
// transformation to apply to the data. For example, it could be a rotation or scaling, or combination of both.
5858
// The 3x1 column at the top right gives the translation to apply. The bottom row should be `[0,0,0,1]`. That
5959
// bottom row is only present to enable
60-
// the matrices to be multiplied together. OpenSCAD ignores it and in fact `multmatrix` will
61-
// accept a 3x4 matrix, where that row is missing. In order for a matrix to act on a point you have to
60+
// the matrices to be multiplied together. OpenSCAD ignores it and in fact `multmatrix`
61+
// accepts a 3x4 matrix, where that row is missing. In order for a matrix to act on a point you have to
6262
// augment the point with an extra 1, making it a length 4 vector. In OpenSCAD you can then compute the
6363
// the affine transformed point as `tran_point = M * point`. However, this syntax hides a complication that
6464
// arises if you have a list of points. A list of points like `[[1,2,3,1],[4,5,6,1],[7,8,9,1]]` has the augmented points
@@ -160,7 +160,7 @@ function translate(v=[0,0,0], p=_NO_ARG) = move(v=v, p=p);
160160

161161
// Function&Module: left()
162162
//
163-
// Synopsis: Translates children leftwards (X-).
163+
// Synopsis: Translates children leftward (X-).
164164
// SynTags: Trans, Path, VNF, Mat
165165
// Topics: Affine, Matrices, Transforms, Translation
166166
// See Also: move(), right(), fwd(), back(), down(), up()
@@ -173,7 +173,7 @@ function translate(v=[0,0,0], p=_NO_ARG) = move(v=v, p=p);
173173
// mat = left(x);
174174
//
175175
// Description:
176-
// Moves geometry or data to the left (in the X- direction) by the specified amount. (If `x` is negative motion will be to the right.)
176+
// Moves geometry or data to the left (in the X- direction) by the specified amount. (If `x` is negative motion is to the right.)
177177
// * If called as a module, moves all children left.
178178
// * If called as a function with the `p` argument, returns the translated version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
179179
// * If called as a function without the `p` argument, returns a 4x4 transformation matrix.
@@ -206,7 +206,7 @@ function left(x=0, p=_NO_ARG) =
206206
// Function&Module: right()
207207
// Aliases: xmove()
208208
//
209-
// Synopsis: Translates children rightwards (X+).
209+
// Synopsis: Translates children rightward (X+).
210210
// SynTags: Trans, Path, VNF, Mat
211211
// Topics: Affine, Matrices, Transforms, Translation
212212
// See Also: move(), left(), fwd(), back(), down(), up()
@@ -219,7 +219,7 @@ function left(x=0, p=_NO_ARG) =
219219
// mat = right(x);
220220
//
221221
// Description:
222-
// Moves geometry or data to the right (in the X+ direction) by the specified amount. (If `x` is negative motion will be to the left.)
222+
// Moves geometry or data to the right (in the X+ direction) by the specified amount. (If `x` is negative motion is to the left.)
223223
// * If called as a module, moves all children right.
224224
// * If called as a function with the `p` argument, returns the translated version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
225225
// * If called as a function without the `p` argument, returns a 4x4 tranformation matrix.
@@ -264,7 +264,7 @@ function xmove(x=0, p=_NO_ARG) =
264264

265265
// Function&Module: fwd()
266266
//
267-
// Synopsis: Translates children forwards (Y-).
267+
// Synopsis: Translates children forward (Y-).
268268
// SynTags: Trans, Path, VNF, Mat
269269
// Topics: Affine, Matrices, Transforms, Translation
270270
// See Also: move(), left(), right(), back(), down(), up()
@@ -277,7 +277,7 @@ function xmove(x=0, p=_NO_ARG) =
277277
// mat = fwd(y);
278278
//
279279
// Description:
280-
// Moves geometry or data forward (in the Y- direction) by the specified amount. (If `y` is negative motion will be to the back.)
280+
// Moves geometry or data forward (in the Y- direction) by the specified amount. (If `y` is negative motion is to the back.)
281281
// * If called as a module, moves all children forward.
282282
// * If called as a function with the `p` argument, returns the translated version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
283283
// * If called as a function without the `p` argument, returns a 4x4 transformation matrix.
@@ -309,7 +309,7 @@ function fwd(y=0, p=_NO_ARG) =
309309
// Function&Module: back()
310310
// Aliases: ymove()
311311
//
312-
// Synopsis: Translates children backwards (Y+).
312+
// Synopsis: Translates children backward (Y+).
313313
// SynTags: Trans, Path, VNF, Mat
314314
// Topics: Affine, Matrices, Transforms, Translation
315315
// See Also: move(), left(), right(), fwd(), down(), up()
@@ -322,7 +322,7 @@ function fwd(y=0, p=_NO_ARG) =
322322
// mat = back(y);
323323
//
324324
// Description:
325-
// Moves geometry or data forward (in the Y+ direction) by the specified amount. (If `y` is negative motion will be forward.)
325+
// Moves geometry or data forward (in the Y+ direction) by the specified amount. (If `y` is negative motion is forward.)
326326
// * If called as a module, moves all children forward.
327327
// * If called as a function with the `p` argument, returns the translated version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
328328
// * If called as a function without the `p` argument, returns a 4x4 transformation matrix.
@@ -367,7 +367,7 @@ function ymove(y=0,p=_NO_ARG) =
367367

368368
// Function&Module: down()
369369
//
370-
// Synopsis: Translates children downwards (Z-).
370+
// Synopsis: Translates children downward (Z-).
371371
// SynTags: Trans, Path, VNF, Mat
372372
// Topics: Affine, Matrices, Transforms, Translation
373373
// See Also: move(), left(), right(), fwd(), back(), up()
@@ -380,7 +380,7 @@ function ymove(y=0,p=_NO_ARG) =
380380
// mat = down(z);
381381
//
382382
// Description:
383-
// Moves geometry or data dwon (in the Z- direction) by the specified amount. (If `z` is negative motion will be upward.)
383+
// Moves geometry or data dwon (in the Z- direction) by the specified amount. (If `z` is negative motion is upward.)
384384
// * If called as a module, moves all children down.
385385
// * If called as a function with the `p` argument, returns the translated version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
386386
// * If called as a function without the `p` argument, returns a 4x4 transformation matrix.
@@ -411,7 +411,7 @@ function down(z=0, p=_NO_ARG) =
411411
// Function&Module: up()
412412
// Aliases: zmove()
413413
//
414-
// Synopsis: Translates children upwards (Z+).
414+
// Synopsis: Translates children upward (Z+).
415415
// SynTags: Trans, Path, VNF, Mat
416416
// Topics: Affine, Matrices, Transforms, Translation
417417
// See Also: move(), left(), right(), fwd(), back(), down()
@@ -424,7 +424,7 @@ function down(z=0, p=_NO_ARG) =
424424
// mat = up(z);
425425
//
426426
// Description:
427-
// Moves geometry or data up (in the Z+ direction) by the specified amount. (If `z` is negative motion will be downward.)
427+
// Moves geometry or data up (in the Z+ direction) by the specified amount. (If `z` is negative motion is downward.)
428428
// * If called as a module, moves all children up
429429
// * If called as a function with the `p` argument, returns the translated version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
430430
// * If called as a function without the `p` argument, returns a 4x4 transformation matrix.
@@ -496,7 +496,7 @@ function zmove(z=0, p=_NO_ARG) =
496496
// M = rot(from=, to=, [a=], [reverse=]);
497497
//
498498
// Description:
499-
// This is an expanded version of the built-in `rotate()`, which behaves identically to the built-in with the same arguments,
499+
// This is an expanded version of the built-in `rotate()` that behaves identically to the built-in with the same arguments,
500500
// but offers additional capabilities.
501501
// You can specify the rotation to perform in one of several ways:
502502
// * `rot(30)` or `rot(a=30)` rotates 30 degrees around the Z axis.
@@ -505,7 +505,7 @@ function zmove(z=0, p=_NO_ARG) =
505505
// * `rot(from=[0,0,1], to=[1,0,0])` rotates the `from` vector to line up with the `to` vector, in this case the top to the right and hence equivalent to `rot(a=90,v=[0,1,0]`. The axis of rotation is perpendicular to the two given vectors.
506506
// * `rot(from=[0,1,1], to=[1,1,0], a=45)` rotates 45 degrees around the `from` vector ([0,1,1]) and then rotates the `from` vector to align with the `to` vector. Equivalent to `rot(from=[0,1,1],to=[1,1,0]) rot(a=45,v=[0,1,1])`. You can also regard `a` as as post-rotation around the `to` vector. For this form, `a` must be a scalar.
507507
// * If the `cp` centerpoint argument is given, then rotations are performed around that centerpoint. So `rot(args...,cp=[1,2,3])` is equivalent to `move(-[1,2,3])rot(args...)move([1,2,3])`.
508-
// * If the `reverse` argument is true, then the rotations performed will be exactly reversed.
508+
// * If the `reverse` argument is true, then the rotations performed is exactly reversed.
509509
// .
510510
// The behavior and return value varies depending on how `rot()` is called:
511511
// * If called as a module, rotates all children.
@@ -715,7 +715,7 @@ function zrot(a=0, p=_NO_ARG, cp) = rot(a, cp=cp, p=p);
715715

716716
// Function&Module: tilt()
717717
//
718-
// Synopsis: Tilts children towards a direction
718+
// Synopsis: Tilts children toward a direction
719719
// SynTags: Trans, Path, VNF, Mat
720720
// Topics: Affine, Matrices, Transforms, Rotation
721721
// See Also: rot(), xrot(), yrot(), zrot()
@@ -728,9 +728,9 @@ function zrot(a=0, p=_NO_ARG, cp) = rot(a, cp=cp, p=p);
728728
// M = tilt(to, [reverse=], [cp=]);
729729
//
730730
// Description:
731-
// This is shorthand for `rot(from=UP,to=x)` and operates similarly. It tilts that which is pointing UP until it is pointing at the given direction vector.
731+
// This is shorthand for `rot(from=UP,to=x)` and operates similarly. It tilts things that point UP until they point in the direction of the given `to` vector.
732732
// * If the `cp` centerpoint argument is given, then the tilt/rotation is performed around that centerpoint. So `tilt(...,cp=[1,2,3])` is equivalent to `move([1,2,3]) tilt(...) move([-1,-2,-3])`.
733-
// * If the `reverse` argument is true, then the tilt/rotation will be reversed.
733+
// * If the `reverse` argument is true, then the tilt/rotation is reversed.
734734
// .
735735
// The behavior and return value varies depending on how `tilt()` is called:
736736
// * Ifc called as a module, tilts all children.
@@ -786,16 +786,19 @@ function tilt(to, p=_NO_ARG, cp, reverse=false) =
786786
// scale(SCALAR) CHILDREN;
787787
// scale([X,Y,Z]) CHILDREN;
788788
// Usage: As module with center point (BOSL2 extension)
789-
// scale(v, cp) CHILDREN;
789+
// scale(v, cp=) CHILDREN;
790+
// Usage: As module with with scaling direction vector (BOSL2 extension)
791+
// scale(v, dir=, [cp=]) CHILDREN;
790792
// Usage: Scale Points
791-
// pts = scale(v, p, [cp=]);
793+
// pts = scale(v, p, [cp=], [dir=]);
792794
// Usage: Get Scaling Matrix
793-
// mat = scale(v, [cp=]);
795+
// mat = scale(v, [cp=], [dir=]);
794796
//
795797
// Description:
796798
// 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.
797799
// If `v` has fewer than three dimensions, it is padded with 1 values, so scaling by [4] is the same as [4,1,1]. If you give `cp` a
798-
// vector value then the scaling is done relative to that specified center point.
800+
// vector value then the scaling is done relative to that specified center point. If you give the vector `dir` then the scale factor, `v`, must
801+
// be a scalar and the scaling occurs along the direction of `dir`. (The sign and magnitude of `dir` don't matter.)
799802
// * If called as the built-in module, scales all children.
800803
// * If called as a function with the `p` argument, returns the scaled version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
801804
// * If called as a function without a `p` argument, returns a 4x4 transformation matrix.
@@ -805,6 +808,7 @@ function tilt(to, p=_NO_ARG, cp, reverse=false) =
805808
// p = (function only) A point, list of points, Bezier patch or VNF to scale.
806809
// ---
807810
// cp = If given, centers the scaling on the point `cp`.
811+
// dir = If given as a vector, scale along the direction of that vector.
808812
//
809813
// Example(NORENDER):
810814
// pt1 = scale(3, [3,1,4]); // Returns: [9,3,12]
@@ -817,11 +821,17 @@ function tilt(to, p=_NO_ARG, cp, reverse=false) =
817821
// path = circle(d=50,$fn=12);
818822
// #stroke(path,closed=true);
819823
// stroke(scale([1.5,3],path),closed=true);
820-
function scale(v=1, p=_NO_ARG, cp=[0,0,0]) =
824+
function scale(v=1, p=_NO_ARG, cp=[0,0,0],dir) =
821825
assert(is_num(v) || is_vector(v),"Invalid scale")
822826
assert(p==_NO_ARG || is_list(p),"Invalid point list")
827+
assert(is_undef(dir) || is_vector(dir),"Invalid dir vector")
823828
assert(is_vector(cp))
824-
let(
829+
is_def(dir)? assert(is_num(v),"v must be a scalar when dir is given")
830+
assert(len(dir)<=3 && norm(dir)>0, "dir must be a nonzero vector with 3 or fewer entries")
831+
rot(from=dir,to=RIGHT,reverse=true)
832+
* xscale(v,p=p,cp=rot(from=dir,to=RIGHT,p=cp))
833+
* rot(from=dir,to=RIGHT)
834+
: let(
825835
v = is_num(v)? [v,v,v] : v,
826836
m = cp==[0,0,0]
827837
? affine3d_scale(v)
@@ -1250,17 +1260,18 @@ function zflip(p=_NO_ARG, z=0) =
12501260
// Description:
12511261
// Maps one coordinate frame to another. You must specify two or
12521262
// three of `x`, `y`, and `z`. The specified axes are mapped to the vectors you supplied, so if you
1253-
// specify x=[1,1] then the x axis will be mapped to the line y=x. If you
1263+
// specify x=[1,1] then the x axis is mapped to the line y=x. If you
12541264
// give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand
1255-
// coordinate system. If the vectors you give are orthogonal the result will be a rotation and the
1256-
// `reverse` parameter will supply the inverse map, which enables you to map two arbitrary
1265+
// coordinate system. If the vectors you give are orthogonal the result is a rotation and the
1266+
// `reverse` parameter supplies the inverse map, which enables you to map two arbitrary
12571267
// coordinate systems to each other by using the canonical coordinate system as an intermediary.
12581268
// You cannot use the `reverse` option with non-orthogonal inputs. Note that only the direction
1259-
// of the specified vectors matters: the transformation will not apply scaling, though it can
1269+
// of the specified vectors matters: the transformation does not apply scaling, though it can
12601270
// skew if your provide non-orthogonal axes.
12611271
// .
1262-
// You can use `frame_map()` as a module, or as a function. In the functional form, you will usually
1263-
// need to provide the points to be transformed with the `p=` named argument, unless you give all three of `x`, `y`, and `z`.
1272+
// You can use `frame_map()` as a module, or as a function. In the functional form, you
1273+
// need to provide the points to be transformed with the `p=` named argument (except in the
1274+
// less common situation where you give all three of `x`, `y`, and `z`).
12641275
// The functional form with no `p=` argument returns a 4x4 transformation matrix.
12651276
// Arguments:
12661277
// x = Destination 3D vector for x axis.
@@ -1353,7 +1364,7 @@ module frame_map(x,y,z,p,reverse=false)
13531364
// * If called as a function with the `p` argument, returns the skewed version of that `p` argument. The `p` argument can be a point, list of points, [bezier patch](beziers.scad) or [VNF structure](vnf.scad).
13541365
// * Called as a function without a `p` argument, returns the 4x4 transformation skew matrix.
13551366
// .
1356-
// Each skew factor is a multiplier. For example, if `sxy=2`, then it will skew along the X axis by 2x the value of the Y axis.
1367+
// Each skew factor is a multiplier. For example, if `sxy=2`, then it skews along the X axis by 2x the value of the Y axis.
13571368
// Arguments:
13581369
// p = (function only) The point, path, Bezier patch, or VNF to skew.
13591370
// ---
@@ -1449,7 +1460,7 @@ function skew(p=_NO_ARG, sxy, sxz, syx, syz, szx, szy, axy, axz, ayx, ayz, azx,
14491460
/// See Also: is_affine(), is_matrix()
14501461
/// Description:
14511462
/// Checks if the input is a 3D transform that does not act on the z coordinate, except possibly
1452-
/// for a simple scaling of z. Note that an input which is only a zscale returns false.
1463+
/// for a simple scaling of Z. An input that is a pure scaling of Z returns false.
14531464
/// Arguments:
14541465
/// t = The transformation matrix to check.
14551466
/// Example:
@@ -1482,7 +1493,7 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][
14821493
// except possibly by scaling it. When points contains 2D data you can also supply the transform as
14831494
// a 3x3 affine transformation matrix or the corresponding 2x3 matrix with the last row deleted.
14841495
// .
1485-
// Any other combination of matrices will produce an error, including acting with a 2D matrix (3x3) on 3D data.
1496+
// Any other combination of matrices produces an error, including acting with a 2D matrix (3x3) on 3D data.
14861497
// The output of apply is always the same dimension as the input&mdash;projections are not supported.
14871498
// .
14881499
// Note that a matrix with a negative determinant such as any mirror reflection flips the orientation of faces.
@@ -1579,11 +1590,10 @@ module rotate(a,v)
15791590
_rotate(a=a,v=v) children();
15801591
}
15811592

1582-
module scale(v,cp)
1593+
module scale(v,cp=[0,0,0],dir)
15831594
{
1584-
if (!is_undef(cp)){
1585-
multmatrix(scale(v,cp=cp)) children();
1586-
}
1595+
if (cp!=[0,0,0] || is_def(dir))
1596+
multmatrix(scale(v,cp=cp,dir=dir)) children();
15871597
else {
15881598
s3 = is_finite(v) ? affine3d_scale([v,v,v])
15891599
: is_vector(v) ? affine3d_scale(v)

0 commit comments

Comments
 (0)