mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-06 04:09:47 +00:00
Merge branch 'master' of github.com:revarbat/BOSL2 into revarbat_dev
This commit is contained in:
commit
2e9c303abb
7 changed files with 127 additions and 81 deletions
|
@ -404,6 +404,8 @@ function affine3d_rot_from_to(from, to) =
|
||||||
from = unit(point3d(from)),
|
from = unit(point3d(from)),
|
||||||
to = unit(point3d(to))
|
to = unit(point3d(to))
|
||||||
) approx(from,to)? affine3d_identity() :
|
) approx(from,to)? affine3d_identity() :
|
||||||
|
from.z==0 && to.z==0 ? affine3d_zrot(v_theta(point2d(to)) - v_theta(point2d(from)))
|
||||||
|
:
|
||||||
let(
|
let(
|
||||||
u = vector_axis(from,to),
|
u = vector_axis(from,to),
|
||||||
ang = vector_angle(from,to),
|
ang = vector_angle(from,to),
|
||||||
|
|
|
@ -988,8 +988,8 @@ function _turtle_command(command, parm, parm2, state, index) =
|
||||||
command=="jump" ? list_set(state, path, concat(state[path],[parm])):
|
command=="jump" ? list_set(state, path, concat(state[path],[parm])):
|
||||||
command=="xjump" ? list_set(state, path, concat(state[path],[[parm,lastpt.y]])):
|
command=="xjump" ? list_set(state, path, concat(state[path],[[parm,lastpt.y]])):
|
||||||
command=="yjump" ? list_set(state, path, concat(state[path],[[lastpt.x,parm]])):
|
command=="yjump" ? list_set(state, path, concat(state[path],[[lastpt.x,parm]])):
|
||||||
command=="turn" || command=="left" ? list_set(state, step, rot(default(parm,state[angle]),p=state[step],planar=true)) :
|
command=="turn" || command=="left" ? list_set(state, step, rot(default(parm,state[angle]),p=state[step])) :
|
||||||
command=="right" ? list_set(state, step, rot(-default(parm,state[angle]),p=state[step],planar=true)) :
|
command=="right" ? list_set(state, step, rot(-default(parm,state[angle]),p=state[step])) :
|
||||||
command=="angle" ? list_set(state, angle, parm) :
|
command=="angle" ? list_set(state, angle, parm) :
|
||||||
command=="setdir" ? (
|
command=="setdir" ? (
|
||||||
is_vector(parm) ?
|
is_vector(parm) ?
|
||||||
|
@ -1020,7 +1020,7 @@ function _turtle_command(command, parm, parm2, state, index) =
|
||||||
list_set(
|
list_set(
|
||||||
state, [path,step], [
|
state, [path,step], [
|
||||||
concat(state[path], list_tail(arcpath)),
|
concat(state[path], list_tail(arcpath)),
|
||||||
rot(lrsign * myangle,p=state[step],planar=true)
|
rot(lrsign * myangle,p=state[step])
|
||||||
]
|
]
|
||||||
) :
|
) :
|
||||||
command=="arcleftto" || command=="arcrightto" ?
|
command=="arcleftto" || command=="arcrightto" ?
|
||||||
|
@ -1046,7 +1046,7 @@ function _turtle_command(command, parm, parm2, state, index) =
|
||||||
list_set(
|
list_set(
|
||||||
state, [path,step], [
|
state, [path,step], [
|
||||||
concat(state[path], list_tail(arcpath)),
|
concat(state[path], list_tail(arcpath)),
|
||||||
rot(delta_angle,p=state[step],planar=true)
|
rot(delta_angle,p=state[step])
|
||||||
]
|
]
|
||||||
) :
|
) :
|
||||||
assert(false,str("Unknown turtle command \"",command,"\" at index",index))
|
assert(false,str("Unknown turtle command \"",command,"\" at index",index))
|
||||||
|
|
|
@ -94,11 +94,11 @@ module echo_matrix(M,description,sig=4,eps=1e-9)
|
||||||
// Topics: Matrices, List Handling
|
// Topics: Matrices, List Handling
|
||||||
// See Also: select(), slice()
|
// See Also: select(), slice()
|
||||||
// Description:
|
// Description:
|
||||||
// Extracts entry i from each list in M, or equivalently column i from the matrix M, and returns it as a vector.
|
// Extracts entry `i` from each list in M, or equivalently column i from the matrix M, and returns it as a vector.
|
||||||
// This function will return `undef` at all entry positions indexed by i not found in M.
|
// This function will return `undef` at all entry positions indexed by i not found in M.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// M = The given list of lists.
|
// M = The given list of lists.
|
||||||
// idx = The index, list of indices, or range of indices to fetch.
|
// i = The index to fetch
|
||||||
// Example:
|
// Example:
|
||||||
// M = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]];
|
// M = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]];
|
||||||
// a = column(M,2); // Returns [3, 7, 11, 15]
|
// a = column(M,2); // Returns [3, 7, 11, 15]
|
||||||
|
@ -114,7 +114,6 @@ function column(M, i) =
|
||||||
[for(row=M) row[i]];
|
[for(row=M) row[i]];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: submatrix()
|
// Function: submatrix()
|
||||||
// Usage:
|
// Usage:
|
||||||
// mat = submatrix(M, idx1, idx2);
|
// mat = submatrix(M, idx1, idx2);
|
||||||
|
@ -463,7 +462,7 @@ function matrix_inverse(A) =
|
||||||
// B = rot_inverse(A)
|
// B = rot_inverse(A)
|
||||||
// Description:
|
// Description:
|
||||||
// Inverts a 2d (3x3) or 3d (4x4) rotation matrix. The matrix can be a rotation around any center,
|
// Inverts a 2d (3x3) or 3d (4x4) rotation matrix. The matrix can be a rotation around any center,
|
||||||
// so it may include a translation.
|
// so it may include a translation. This is faster and likely to be more accurate than using `matrix_inverse()`.
|
||||||
function rot_inverse(T) =
|
function rot_inverse(T) =
|
||||||
assert(is_matrix(T,square=true),"Matrix must be square")
|
assert(is_matrix(T,square=true),"Matrix must be square")
|
||||||
let( n = len(T))
|
let( n = len(T))
|
||||||
|
|
|
@ -344,12 +344,12 @@ module regular_polyhedron(
|
||||||
$center = -mean(facepts);
|
$center = -mean(facepts);
|
||||||
cfacepts = move($center, p=facepts);
|
cfacepts = move($center, p=facepts);
|
||||||
$face = rotate_children
|
$face = rotate_children
|
||||||
? path2d(rot(from=face_normals[i], to=[0,0,1], p=cfacepts))
|
? path2d(frame_map(z=face_normals[i], x=facepts[0]-facepts[1], reverse=true, p=cfacepts))
|
||||||
: cfacepts;
|
: cfacepts;
|
||||||
$faceindex = i;
|
$faceindex = i;
|
||||||
translate(-$center)
|
translate(-$center)
|
||||||
if (rotate_children) {
|
if (rotate_children) {
|
||||||
rot(from=[0,0,1], to=face_normals[i])
|
frame_map(z=face_normals[i], x=facepts[0]-facepts[1])
|
||||||
children(i % $children);
|
children(i % $children);
|
||||||
} else {
|
} else {
|
||||||
children(i % $children);
|
children(i % $children);
|
||||||
|
|
|
@ -459,10 +459,10 @@ function regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false
|
||||||
let(
|
let(
|
||||||
inset = opp_ang_to_hyp(rounding, (180-360/n)/2),
|
inset = opp_ang_to_hyp(rounding, (180-360/n)/2),
|
||||||
mat = !is_undef(_mat) ? _mat :
|
mat = !is_undef(_mat) ? _mat :
|
||||||
( realign? rot(-180/n, planar=true) : affine2d_identity() ) * (
|
( realign? zrot(-180/n) : ident(4)) * (
|
||||||
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip), planar=true) :
|
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip)) :
|
||||||
!is_undef(align_side)? rot(from=RIGHT, to=point2d(align_side), planar=true) * rot(180/n, planar=true) :
|
!is_undef(align_side)? rot(from=RIGHT, to=point2d(align_side)) * zrot(180/n) :
|
||||||
affine2d_identity()
|
1
|
||||||
),
|
),
|
||||||
path4 = rounding==0? ellipse(r=r, $fn=n) : (
|
path4 = rounding==0? ellipse(r=r, $fn=n) : (
|
||||||
let(
|
let(
|
||||||
|
@ -504,10 +504,10 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false,
|
||||||
side = is_finite(side)? side/2/sin(180/n) : undef;
|
side = is_finite(side)? side/2/sin(180/n) : undef;
|
||||||
r = get_radius(r1=ir, r2=or, r=r, d1=id, d2=od, d=d, dflt=side);
|
r = get_radius(r1=ir, r2=or, r=r, d1=id, d2=od, d=d, dflt=side);
|
||||||
assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side.");
|
assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side.");
|
||||||
mat = ( realign? rot(-180/n, planar=true) : affine2d_identity() ) * (
|
mat = ( realign? zrot(-180/n) : ident(4) ) * (
|
||||||
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip), planar=true) :
|
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip)) :
|
||||||
!is_undef(align_side)? rot(from=RIGHT, to=point2d(align_side), planar=true) * rot(180/n, planar=true) :
|
!is_undef(align_side)? rot(from=RIGHT, to=point2d(align_side)) * zrot(180/n) :
|
||||||
affine2d_identity()
|
1
|
||||||
);
|
);
|
||||||
inset = opp_ang_to_hyp(rounding, (180-360/n)/2);
|
inset = opp_ang_to_hyp(rounding, (180-360/n)/2);
|
||||||
anchors = [
|
anchors = [
|
||||||
|
@ -930,10 +930,10 @@ function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit
|
||||||
: str("Parameter 'step' must be between 2 and ",floor(n/2-1/2)," for ",n," point stars"))
|
: str("Parameter 'step' must be between 2 and ",floor(n/2-1/2)," for ",n," point stars"))
|
||||||
let(
|
let(
|
||||||
mat = !is_undef(_mat) ? _mat :
|
mat = !is_undef(_mat) ? _mat :
|
||||||
( realign? rot(-180/n, planar=true) : affine2d_identity() ) * (
|
( realign? zrot(-180/n) : ident(4) ) * (
|
||||||
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip), planar=true) :
|
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip)) :
|
||||||
!is_undef(align_pit)? rot(from=RIGHT, to=point2d(align_pit), planar=true) * rot(180/n, planar=true) :
|
!is_undef(align_pit)? rot(from=RIGHT, to=point2d(align_pit)) * zrot(180/n) :
|
||||||
affine2d_identity()
|
1
|
||||||
),
|
),
|
||||||
stepr = is_undef(step)? r : r*cos(180*step/n)/cos(180*(step-1)/n),
|
stepr = is_undef(step)? r : r*cos(180*step/n)/cos(180*(step-1)/n),
|
||||||
ir = get_radius(r=ir, d=id, dflt=stepr),
|
ir = get_radius(r=ir, d=id, dflt=stepr),
|
||||||
|
@ -967,10 +967,10 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit,
|
||||||
r = get_radius(r1=or, d1=od, r=r, d=d, dflt=undef);
|
r = get_radius(r1=or, d1=od, r=r, d=d, dflt=undef);
|
||||||
stepr = is_undef(step)? r : r*cos(180*step/n)/cos(180*(step-1)/n);
|
stepr = is_undef(step)? r : r*cos(180*step/n)/cos(180*(step-1)/n);
|
||||||
ir = get_radius(r=ir, d=id, dflt=stepr);
|
ir = get_radius(r=ir, d=id, dflt=stepr);
|
||||||
mat = ( realign? rot(-180/n, planar=true) : affine2d_identity() ) * (
|
mat = ( realign? zrot(-180/n) : ident(4) ) * (
|
||||||
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip), planar=true) :
|
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip)) :
|
||||||
!is_undef(align_pit)? rot(from=RIGHT, to=point2d(align_pit), planar=true) * rot(180/n, planar=true) :
|
!is_undef(align_pit)? rot(from=RIGHT, to=point2d(align_pit)) * zrot(180/n) :
|
||||||
affine2d_identity()
|
1
|
||||||
);
|
);
|
||||||
anchors = [
|
anchors = [
|
||||||
for (i = [0:1:n-1]) let(
|
for (i = [0:1:n-1]) let(
|
||||||
|
|
|
@ -109,7 +109,7 @@ module test_scale() {
|
||||||
cb = cube(1);
|
cb = cube(1);
|
||||||
vals = [[-1,-2,-3],[1,1,1],[3,6,2],[1,2,3],[243,75,147]];
|
vals = [[-1,-2,-3],[1,1,1],[3,6,2],[1,2,3],[243,75,147]];
|
||||||
for (val=vals) {
|
for (val=vals) {
|
||||||
assert_equal(scale(point2d(val)), [[val.x,0,0],[0,val.y,0],[0,0,1]]);
|
assert_equal(scale(point2d(val)), [[val.x,0,0,0],[0,val.y,0,0],[0,0,1,0],[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), [[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]), v_mul([1,2,3], val));
|
assert_equal(scale(val, p=[1,2,3]), v_mul([1,2,3], val));
|
||||||
scale(val) union(){};
|
scale(val) union(){};
|
||||||
|
|
153
transforms.scad
153
transforms.scad
|
@ -20,15 +20,57 @@
|
||||||
// FileFootnotes: STD=Included in std.scad
|
// FileFootnotes: STD=Included in std.scad
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Section: Affine Transformations
|
||||||
|
// OpenSCAD provides various built-in modules to transform geometry by
|
||||||
|
// translation, scaling, rotation, and mirroring. All of these operations
|
||||||
|
// are affine transformations. A three-dimensional affine transformation
|
||||||
|
// can be represented by a 4x4 matrix. The transformation shortcuts in this
|
||||||
|
// file generally have three modes of operation. They can operate
|
||||||
|
// directly on geometry like their OpenSCAD built-in equivalents. For example,
|
||||||
|
// `left(10) cube()`. They can operate on a list of points (or various other
|
||||||
|
// types of geometric data). For example, operating on a list of points: `points = left(10, [[1,2,3],[4,5,6]])`.
|
||||||
|
// The third option is that the shortcut can return the transformation matrix
|
||||||
|
// corresponding to its action. For example, `M=left(10)`.
|
||||||
|
// .
|
||||||
|
// This capability allows you to store and manipulate transformations, and can
|
||||||
|
// be useful in more advanced modeling. You can multiply these matrices
|
||||||
|
// together, analogously to applying a sequence of operations with the
|
||||||
|
// built-in transformations. So you can write `zrot(37)left(5)cube()`
|
||||||
|
// to perform two operations on a cube. You can also store
|
||||||
|
// that same transformation by multiplying the matrices together: `M = zrot(37) * left(5)`.
|
||||||
|
// Note that the order is exactly the same as the order used to apply the transformation.
|
||||||
|
// .
|
||||||
|
// Suppose you have constructed `M` as above. What now? You can use
|
||||||
|
// the OpensCAD built-in `multmatrix` to apply it to some geometry: `multmatrix(M) cube()`.
|
||||||
|
// Alternative you can use the BOSL2 function `apply` to apply `M` to a point, a list
|
||||||
|
// of points, a bezier patch, or a VNF. For example, `points = apply(M, [[3,4,5],[5,6,7]])`.
|
||||||
|
// Note that the `apply` function can work on both 2D and 3D data, but if you want to
|
||||||
|
// operate on 2D data, you must choose transformations that don't modify z
|
||||||
|
// .
|
||||||
|
// You can use matrices as described above without understanding the details, just
|
||||||
|
// treating a matrix as a box that stores a transformation. The OpenSCAD manual section for multmatrix
|
||||||
|
// gives some details of how this works. We'll elaborate a bit more below. An affine transformation
|
||||||
|
// matrix for three dimensional data is a 4x4 matrix. The top left 3x3 portion gives the linear
|
||||||
|
// transformation to apply to the data. For example, it could be a rotation or scaling, or combination of both.
|
||||||
|
// The 3x1 column at the top right gives the translation to apply. The bottom row should be `[0,0,0,1]`. That
|
||||||
|
// bottom row is only present to enable
|
||||||
|
// the matrices to be multiplied together. OpenSCAD ignores it and in fact `multmatrix` will
|
||||||
|
// accept a 3x4 matrix, where that row is missing. In order for a matrix to act on a point you have to
|
||||||
|
// augment the point with an extra 1, making it a length 4 vector. In OpenSCAD you can then compute the
|
||||||
|
// the affine transformed point as `tran_point = M * point`. However, this syntax hides a complication that
|
||||||
|
// 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
|
||||||
|
// as row vectors on the list. In order to transform such a list, it needs to be muliplied on the right
|
||||||
|
// side, not the left side.
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Section: Translations
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
_NO_ARG = [true,[123232345],false];
|
_NO_ARG = [true,[123232345],false];
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Section: Translations
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Function&Module: move()
|
// Function&Module: move()
|
||||||
// Aliases: translate()
|
// Aliases: translate()
|
||||||
//
|
//
|
||||||
|
@ -98,13 +140,14 @@ _NO_ARG = [true,[123232345],false];
|
||||||
// mat3d = move([2,3,4]); // Returns: [[1,0,0,2],[0,1,0,3],[0,0,1,4],[0,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, x=0, y=0, z=0) {
|
||||||
assert(is_undef(p), "Module form `move()` does not accept p= argument.");
|
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)+[x,y,z]) 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, x=0, y=0, z=0) =
|
||||||
|
assert(is_vector(v) && (len(v)==3 || len(v)==2), "Invalid value for `v`")
|
||||||
let(
|
let(
|
||||||
m = len(v)==2? affine2d_translate(v+[x,y]) :
|
m = affine3d_translate(point3d(v)+[x,y,z])
|
||||||
affine3d_translate(point3d(v)+[x,y,z])
|
|
||||||
)
|
)
|
||||||
p==_NO_ARG ? m : apply(m, p);
|
p==_NO_ARG ? m : apply(m, p);
|
||||||
|
|
||||||
|
@ -143,10 +186,12 @@ function translate(v=[0,0,0], p=_NO_ARG) = move(v=v, p=p);
|
||||||
// mat3d = left(4); // Returns: [[1,0,0,-4],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
|
// 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) {
|
module left(x=0, p) {
|
||||||
assert(is_undef(p), "Module form `left()` does not accept p= argument.");
|
assert(is_undef(p), "Module form `left()` does not accept p= argument.");
|
||||||
|
assert(is_finite(x), "Invalid number")
|
||||||
translate([-x,0,0]) children();
|
translate([-x,0,0]) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
function left(x=0, p=_NO_ARG) = move([-x,0,0],p=p);
|
function left(x=0, p=_NO_ARG) = assert(is_finite(x), "Invalid number")
|
||||||
|
move([-x,0,0],p=p);
|
||||||
|
|
||||||
|
|
||||||
// Function&Module: right()
|
// Function&Module: right()
|
||||||
|
@ -181,10 +226,12 @@ function left(x=0, p=_NO_ARG) = move([-x,0,0],p=p);
|
||||||
// mat3d = right(4); // Returns: [[1,0,0,4],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
|
// 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) {
|
module right(x=0, p) {
|
||||||
assert(is_undef(p), "Module form `right()` does not accept p= argument.");
|
assert(is_undef(p), "Module form `right()` does not accept p= argument.");
|
||||||
|
assert(is_finite(x), "Invalid number")
|
||||||
translate([x,0,0]) children();
|
translate([x,0,0]) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
function right(x=0, p=_NO_ARG) = move([x,0,0],p=p);
|
function right(x=0, p=_NO_ARG) = assert(is_finite(x), "Invalid number")
|
||||||
|
move([x,0,0],p=p);
|
||||||
|
|
||||||
|
|
||||||
// Function&Module: fwd()
|
// Function&Module: fwd()
|
||||||
|
@ -219,10 +266,12 @@ function right(x=0, p=_NO_ARG) = move([x,0,0],p=p);
|
||||||
// mat3d = fwd(4); // Returns: [[1,0,0,0],[0,1,0,-4],[0,0,1,0],[0,0,0,1]]
|
// 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) {
|
module fwd(y=0, p) {
|
||||||
assert(is_undef(p), "Module form `fwd()` does not accept p= argument.");
|
assert(is_undef(p), "Module form `fwd()` does not accept p= argument.");
|
||||||
|
assert(is_finite(y), "Invalid number")
|
||||||
translate([0,-y,0]) children();
|
translate([0,-y,0]) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
function fwd(y=0, p=_NO_ARG) = move([0,-y,0],p=p);
|
function fwd(y=0, p=_NO_ARG) = assert(is_finite(y), "Invalid number")
|
||||||
|
move([0,-y,0],p=p);
|
||||||
|
|
||||||
|
|
||||||
// Function&Module: back()
|
// Function&Module: back()
|
||||||
|
@ -257,10 +306,12 @@ function fwd(y=0, p=_NO_ARG) = move([0,-y,0],p=p);
|
||||||
// mat3d = back(4); // Returns: [[1,0,0,0],[0,1,0,4],[0,0,1,0],[0,0,0,1]]
|
// 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) {
|
module back(y=0, p) {
|
||||||
assert(is_undef(p), "Module form `back()` does not accept p= argument.");
|
assert(is_undef(p), "Module form `back()` does not accept p= argument.");
|
||||||
|
assert(is_finite(y), "Invalid number")
|
||||||
translate([0,y,0]) children();
|
translate([0,y,0]) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
function back(y=0,p=_NO_ARG) = move([0,y,0],p=p);
|
function back(y=0,p=_NO_ARG) = assert(is_finite(y), "Invalid number")
|
||||||
|
move([0,y,0],p=p);
|
||||||
|
|
||||||
|
|
||||||
// Function&Module: down()
|
// Function&Module: down()
|
||||||
|
@ -331,10 +382,12 @@ function down(z=0, p=_NO_ARG) = move([0,0,-z],p=p);
|
||||||
// mat3d = up(4); // Returns: [[1,0,0,0],[0,1,0,0],[0,0,1,4],[0,0,0,1]]
|
// 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) {
|
module up(z=0, p) {
|
||||||
assert(is_undef(p), "Module form `up()` does not accept p= argument.");
|
assert(is_undef(p), "Module form `up()` does not accept p= argument.");
|
||||||
|
assert(is_finite(z), "Invalid number");
|
||||||
translate([0,0,z]) children();
|
translate([0,0,z]) children();
|
||||||
}
|
}
|
||||||
|
|
||||||
function up(z=0, p=_NO_ARG) = move([0,0,z],p=p);
|
function up(z=0, p=_NO_ARG) = assert(is_finite(z), "Invalid number")
|
||||||
|
move([0,0,z],p=p);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -440,20 +493,21 @@ function rot(a=0, v, cp, from, to, reverse=false, planar=false, p=_NO_ARG, _m) =
|
||||||
v_theta(from)
|
v_theta(from)
|
||||||
),
|
),
|
||||||
m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)),
|
m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)),
|
||||||
m3 = reverse? matrix_inverse(m2) : m2
|
m3 = reverse? rot_inverse(m2) : m2
|
||||||
) m3 : let(
|
) m3 : let(
|
||||||
from = is_undef(from)? undef : point3d(from),
|
from = is_undef(from)? undef : point3d(from),
|
||||||
to = is_undef(to)? undef : point3d(to),
|
to = is_undef(to)? undef : point3d(to),
|
||||||
cp = is_undef(cp)? undef : point3d(cp),
|
cp = is_undef(cp)? undef : point3d(cp),
|
||||||
m1 = !is_undef(from)? (
|
m1 = !is_undef(from) ?
|
||||||
assert(is_num(a))
|
assert(is_num(a))
|
||||||
affine3d_rot_from_to(from,to) * affine3d_rot_by_axis(from,a)
|
affine3d_rot_from_to(from,to) * affine3d_rot_by_axis(from,a)
|
||||||
) :
|
: !is_undef(v)?
|
||||||
!is_undef(v)? assert(is_num(a)) affine3d_rot_by_axis(v,a) :
|
assert(is_num(a))
|
||||||
is_num(a)? affine3d_zrot(a) :
|
affine3d_rot_by_axis(v,a)
|
||||||
affine3d_zrot(a.z) * affine3d_yrot(a.y) * affine3d_xrot(a.x),
|
: is_num(a) ? affine3d_zrot(a)
|
||||||
|
: affine3d_zrot(a.z) * affine3d_yrot(a.y) * affine3d_xrot(a.x),
|
||||||
m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)),
|
m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)),
|
||||||
m3 = reverse? matrix_inverse(m2) : m2
|
m3 = reverse? rot_inverse(m2) : m2
|
||||||
) m3
|
) m3
|
||||||
)
|
)
|
||||||
p==_NO_ARG ? m : apply(m, p);
|
p==_NO_ARG ? m : apply(m, p);
|
||||||
|
@ -645,19 +699,11 @@ function scale(v=1, p=_NO_ARG, cp=[0,0,0]) =
|
||||||
assert(is_vector(cp))
|
assert(is_vector(cp))
|
||||||
let(
|
let(
|
||||||
v = is_num(v)? [v,v,v] : v,
|
v = is_num(v)? [v,v,v] : v,
|
||||||
m = len(v)==2? (
|
m = cp==[0,0,0]
|
||||||
cp==[0,0,0] || cp == [0,0] ? affine2d_scale(v) : (
|
? affine3d_scale(v)
|
||||||
affine2d_translate(point2d(cp)) *
|
: affine3d_translate(point3d(cp))
|
||||||
affine2d_scale(v) *
|
* affine3d_scale(v)
|
||||||
affine2d_translate(point2d(-cp))
|
* affine3d_translate(point3d(-cp))
|
||||||
)
|
|
||||||
) : (
|
|
||||||
cp==[0,0,0] ? affine3d_scale(v) : (
|
|
||||||
affine3d_translate(point3d(cp)) *
|
|
||||||
affine3d_scale(v) *
|
|
||||||
affine3d_translate(point3d(-cp))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
p==_NO_ARG? m : apply(m, p) ;
|
p==_NO_ARG? m : apply(m, p) ;
|
||||||
|
|
||||||
|
@ -1278,15 +1324,17 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][
|
||||||
// pts = apply(transform, points);
|
// pts = apply(transform, points);
|
||||||
// Topics: Affine, Matrices, Transforms
|
// Topics: Affine, Matrices, Transforms
|
||||||
// Description:
|
// Description:
|
||||||
// Applies the specified transformation matrix to a point, pointlist, bezier patch or VNF.
|
// Applies the specified transformation matrix `transform` to a point, point list, bezier patch or VNF.
|
||||||
// Both inputs can be 2D or 3D, and it is also allowed to supply 3D transformations with 2D
|
// When `points` contains 2D or 3D points the transform matrix may be a 4x4 affine matrix or a 3x4 matrix---
|
||||||
// data as long as the the only action on the z coordinate is a simple scaling.
|
// the 4x4 matrix with its final row removed. When the data is 2D the matrix must not operate on the Z axis,
|
||||||
|
// except possibly by scaling it. When points contains 2D data you can also supply the transform as
|
||||||
|
// a 3x3 affine transformation matrix or the corresponding 2x3 matrix with the last row deleted.
|
||||||
// .
|
// .
|
||||||
// If you construct your own matrices you can also use a transform that acts like a projection
|
// Any other combination of matrices will produce an error, including acting with a 2D matrix (3x3) on 3D data.
|
||||||
// with fewer rows to produce lower dimensional output.
|
// The output of apply is always the same dimension as the input---projections are not supported.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// transform = The 2D or 3D transformation matrix to apply to the point/points.
|
// transform = The 2D (3x3 or 2x3) or 3D (4x4 or 3x4) transformation matrix to apply.
|
||||||
// points = The point, pointlist, bezier patch, or VNF to apply the transformation to.
|
// points = The point, point list, bezier patch, or VNF to apply the transformation to.
|
||||||
// Example(3D):
|
// Example(3D):
|
||||||
// path1 = path3d(circle(r=40));
|
// path1 = path3d(circle(r=40));
|
||||||
// tmat = xrot(45);
|
// tmat = xrot(45);
|
||||||
|
@ -1319,30 +1367,27 @@ function apply(transform,points) =
|
||||||
: _apply(transform,points);
|
: _apply(transform,points);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _apply(transform,points) =
|
function _apply(transform,points) =
|
||||||
assert(is_matrix(transform),"Invalid transformation matrix")
|
assert(is_matrix(transform),"Invalid transformation matrix")
|
||||||
assert(is_matrix(points),"Invalid points list")
|
assert(is_matrix(points),"Invalid points list")
|
||||||
let(
|
let(
|
||||||
tdim = len(transform[0])-1,
|
tdim = len(transform[0])-1,
|
||||||
datadim = len(points[0]),
|
datadim = len(points[0])
|
||||||
outdim = min(datadim,len(transform)),
|
|
||||||
matrix = [for(i=[0:1:tdim]) [for(j=[0:1:outdim-1]) transform[j][i]]]
|
|
||||||
)
|
)
|
||||||
tdim==datadim && (datadim==3 || datadim==2)
|
assert(len(transform)==tdim || len(transform)-1==tdim, "transform matrix height not compatible with width")
|
||||||
? [for(p=points) concat(p,1)] * matrix
|
assert(datadim==2 || datadim==3,"Data must be 2D or 3D")
|
||||||
: tdim == 3 && datadim == 2 ?
|
let(
|
||||||
assert(is_2d_transform(transform), str("Transforms is 3d but points are 2d"))
|
scale = len(transform)==tdim ? 1 : transform[tdim][tdim],
|
||||||
|
matrix = [for(i=[0:1:tdim]) [for(j=[0:1:datadim-1]) transform[j][i]]] / scale
|
||||||
|
)
|
||||||
|
tdim==datadim ? [for(p=points) concat(p,1)] * matrix
|
||||||
|
: tdim == 3 && datadim == 2 ?
|
||||||
|
assert(is_2d_transform(transform), str("Transforms is 3D and acts on Z, but points are 2D"))
|
||||||
[for(p=points) concat(p,[0,1])]*matrix
|
[for(p=points) concat(p,[0,1])]*matrix
|
||||||
: tdim == 2 && datadim == 3 ?
|
: assert(false, str("Unsupported combination: ",len(transform),"x",len(transform[0])," transform (dimension ",tdim,
|
||||||
let(
|
"), data of dimension ",datadim));
|
||||||
matrix3d =[[ matrix[0][0], matrix[0][1], 0],
|
|
||||||
[ matrix[1][0], matrix[1][1], 0],
|
|
||||||
[ 0, 0, 1],
|
|
||||||
[ matrix[2][0], matrix[2][1], 0]]
|
|
||||||
)
|
|
||||||
[for(p=points) concat(p,1)] * matrix3d
|
|
||||||
: assert(false, str("Unsupported combination: transform with dimension ",tdim,", data of dimension ",datadim));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
|
Loading…
Reference in a new issue