1//////////////////////////////////////////////////////////////////////
  2// LibFile: affine.scad
  3//   Matrix math and affine transformation matrices.
  4// Includes:
  5//   include <BOSL2/std.scad>
  6//////////////////////////////////////////////////////////////////////
  7
  8
  9// Section: Affine2d 3x3 Transformation Matrices
 10
 11
 12// Function: affine2d_identity()
 13// Usage:
 14//   mat = affine2d_identify();
 15// Topics: Affine, Matrices, Transforms
 16// Description:
 17//   Create a 3x3 affine2d identity matrix.
 18// Example:
 19//   mat = affine2d_identity();
 20//   // Returns:
 21//   //   [
 22//   //     [1, 0, 0],
 23//   //     [0, 1, 0],
 24//   //     [0, 0, 1]
 25//   //   ]
 26function affine2d_identity() = ident(3);
 27
 28
 29// Function: affine2d_translate()
 30// Usage:
 31//   mat = affine2d_translate(v);
 32// Topics: Affine, Matrices, Transforms, Translation
 33// See Also: move(), affine3d_translate()
 34// Description:
 35//   Returns the 3x3 affine2d matrix to perform a 2D translation.
 36// Arguments:
 37//   v = 2D Offset to translate by.  [X,Y]
 38// Example:
 39//   mat = affine2d_translate([30,40]);
 40//   // Returns:
 41//   //   [
 42//   //     [1, 0, 30],
 43//   //     [0, 1, 40],
 44//   //     [0, 0,  1]
 45//   //   ]
 46function affine2d_translate(v=[0,0]) =
 47    assert(is_vector(v),2)
 48    [
 49        [1, 0, v.x],
 50        [0, 1, v.y],
 51        [0 ,0,   1]
 52    ];
 53
 54
 55// Function: affine2d_scale()
 56// Usage:
 57//   mat = affine2d_scale(v);
 58// Topics: Affine, Matrices, Transforms, Scaling
 59// See Also: scale(), xscale(), yscale(), zscale(), affine3d_scale()
 60// Description:
 61//   Returns the 3x3 affine2d matrix to perform a 2D scaling transformation.
 62// Arguments:
 63//   v = 2D vector of scaling factors.  [X,Y]
 64// Example:
 65//   mat = affine2d_scale([3,4]);
 66//   // Returns:
 67//   //   [
 68//   //     [3, 0, 0],
 69//   //     [0, 4, 0],
 70//   //     [0, 0, 1]
 71//   //   ]
 72function affine2d_scale(v=[1,1]) =
 73    assert(is_vector(v,2))
 74    [
 75        [v.x,   0, 0],
 76        [  0, v.y, 0],
 77        [  0,   0, 1]
 78    ];
 79
 80
 81// Function: affine2d_zrot()
 82// Usage:
 83//   mat = affine2d_zrot(ang);
 84// Topics: Affine, Matrices, Transforms, Rotation
 85// See Also: rot(), xrot(), yrot(), zrot(), affine3d_zrot()
 86// Description:
 87//   Returns the 3x3 affine2d matrix to perform a rotation of a 2D vector around the Z axis.
 88// Arguments:
 89//   ang = Number of degrees to rotate.
 90// Example:
 91//   mat = affine2d_zrot(90);
 92//   // Returns:
 93//   //   [
 94//   //     [0,-1, 0],
 95//   //     [1, 0, 0],
 96//   //     [0, 0, 1]
 97//   //   ]
 98function affine2d_zrot(ang=0) =
 99    assert(is_finite(ang))
100    [
101        [cos(ang), -sin(ang), 0],
102        [sin(ang),  cos(ang), 0],
103        [       0,         0, 1]
104    ];
105
106
107// Function: affine2d_mirror()
108// Usage:
109//   mat = affine2d_mirror(v);
110// Topics: Affine, Matrices, Transforms, Reflection, Mirroring
111// See Also: mirror(), xflip(), yflip(), zflip(), affine3d_mirror()
112// Description:
113//   Returns the 3x3 affine2d matrix to perform a reflection of a 2D vector across the line given by its normal vector.
114// Arguments:
115//   v = The normal vector of the line to reflect across.
116// Example:
117//   mat = affine2d_mirror([0,1]);
118//   // Returns:
119//   //   [
120//   //     [ 1, 0, 0],
121//   //     [ 0,-1, 0],
122//   //     [ 0, 0, 1]
123//   //   ]
124// Example:
125//   mat = affine2d_mirror([1,0]);
126//   // Returns:
127//   //   [
128//   //     [-1, 0, 0],
129//   //     [ 0, 1, 0],
130//   //     [ 0, 0, 1]
131//   //   ]
132// Example:
133//   mat = affine2d_mirror([1,1]);
134//   // Returns approximately:
135//   //   [
136//   //     [ 0,-1, 0],
137//   //     [-1, 0, 0],
138//   //     [ 0, 0, 1]
139//   //   ]
140function affine2d_mirror(v) =
141    assert(is_vector(v,2))
142    let(v=unit(point2d(v)), a=v.x, b=v.y)
143    [
144        [1-2*a*a, 0-2*a*b, 0],
145        [0-2*a*b, 1-2*b*b, 0],
146        [      0,       0, 1]
147    ];
148
149
150// Function: affine2d_skew()
151// Usage:
152//   mat = affine2d_skew(xa);
153//   mat = affine2d_skew(ya=);
154//   mat = affine2d_skew(xa, ya);
155// Topics: Affine, Matrices, Transforms, Skewing
156// See Also: skew(), affine3d_skew()
157// Description:
158//   Returns the 3x3 affine2d matrix to skew a 2D vector along the XY plane.
159// Arguments:
160//   xa = Skew angle, in degrees, in the direction of the X axis. Default: 0
161//   ya = Skew angle, in degrees, in the direction of the Y axis. Default: 0
162// Example:
163//   mat = affine2d_skew(xa=45,ya=-45);
164//   // Returns approximately:
165//   //   [
166//   //     [ 1, 1, 0],
167//   //     [-1, 1, 0],
168//   //     [ 0, 0, 1]
169//   //   ]
170function affine2d_skew(xa=0, ya=0) =
171    assert(is_finite(xa))
172    assert(is_finite(ya))
173    [
174        [1,       tan(xa), 0],
175        [tan(ya), 1,       0],
176        [0,       0,       1]
177    ];
178
179
180
181// Section: Affine3d 4x4 Transformation Matrices
182
183
184// Function: affine3d_identity()
185// Usage:
186//   mat = affine3d_identity();
187// Topics: Affine, Matrices, Transforms
188// Description:
189//   Create a 4x4 affine3d identity matrix.
190// Example:
191//   mat = affine2d_identity();
192//   // Returns:
193//   //   [
194//   //     [1, 0, 0, 0],
195//   //     [0, 1, 0, 0],
196//   //     [0, 0, 1, 0],
197//   //     [0, 0, 0, 1]
198//   //   ]
199function affine3d_identity() = ident(4);
200
201
202// Function: affine3d_translate()
203// Usage:
204//   mat = affine3d_translate(v);
205// Topics: Affine, Matrices, Transforms, Translation
206// See Also: move(), affine2d_translate()
207// Description:
208//   Returns the 4x4 affine3d matrix to perform a 3D translation.
209// Arguments:
210//   v = 3D offset to translate by.  [X,Y,Z]
211// Example:
212//   mat = affine2d_translate([30,40,50]);
213//   // Returns:
214//   //   [
215//   //     [1, 0, 0, 30],
216//   //     [0, 1, 0, 40],
217//   //     [0, 0, 1, 50]
218//   //     [0, 0, 0,  1]
219//   //   ]
220function affine3d_translate(v=[0,0,0]) =
221    assert(is_list(v))
222    let( v = [for (i=[0:2]) default(v[i],0)] )
223    [
224        [1, 0, 0, v.x],
225        [0, 1, 0, v.y],
226        [0, 0, 1, v.z],
227        [0 ,0, 0,   1]
228    ];
229
230
231// Function: affine3d_scale()
232// Usage:
233//   mat = affine3d_scale(v);
234// Topics: Affine, Matrices, Transforms, Scaling
235// See Also: scale(), affine2d_scale()
236// Description:
237//   Returns the 4x4 affine3d matrix to perform a 3D scaling transformation.
238// Arguments:
239//   v = 3D vector of scaling factors.  [X,Y,Z]
240// Example:
241//   mat = affine3d_scale([3,4,5]);
242//   // Returns:
243//   //   [
244//   //     [3, 0, 0, 0],
245//   //     [0, 4, 0, 0],
246//   //     [0, 0, 5, 0],
247//   //     [0, 0, 0, 1]
248//   //   ]
249function affine3d_scale(v=[1,1,1]) =
250    assert(is_list(v))
251    let( v = [for (i=[0:2]) default(v[i],1)] )
252    [
253        [v.x,   0,   0, 0],
254        [  0, v.y,   0, 0],
255        [  0,   0, v.z, 0],
256        [  0,   0,   0, 1]
257    ];
258
259
260// Function: affine3d_xrot()
261// Usage:
262//   mat = affine3d_xrot(ang);
263// Topics: Affine, Matrices, Transforms, Rotation
264// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot()
265// Description:
266//   Returns the 4x4 affine3d matrix to perform a rotation of a 3D vector around the X axis.
267// Arguments:
268//   ang = number of degrees to rotate.
269// Example:
270//   mat = affine3d_xrot(90);
271//   // Returns:
272//   //   [
273//   //     [1, 0, 0, 0],
274//   //     [0, 0,-1, 0],
275//   //     [0, 1, 0, 0],
276//   //     [0, 0, 0, 1]
277//   //   ]
278function affine3d_xrot(ang=0) =
279    assert(is_finite(ang))
280    [
281        [1,        0,         0,   0],
282        [0, cos(ang), -sin(ang),   0],
283        [0, sin(ang),  cos(ang),   0],
284        [0,        0,         0,   1]
285    ];
286
287
288// Function: affine3d_yrot()
289// Usage:
290//   mat = affine3d_yrot(ang);
291// Topics: Affine, Matrices, Transforms, Rotation
292// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot()
293// Description:
294//   Returns the 4x4 affine3d matrix to perform a rotation of a 3D vector around the Y axis.
295// Arguments:
296//   ang = Number of degrees to rotate.
297// Example:
298//   mat = affine3d_yrot(90);
299//   // Returns:
300//   //   [
301//   //     [ 0, 0, 1, 0],
302//   //     [ 0, 1, 0, 0],
303//   //     [-1, 0, 0, 0],
304//   //     [ 0, 0, 0, 1]
305//   //   ]
306function affine3d_yrot(ang=0) =
307    assert(is_finite(ang))
308    [
309        [ cos(ang), 0, sin(ang),   0],
310        [        0, 1,        0,   0],
311        [-sin(ang), 0, cos(ang),   0],
312        [        0, 0,        0,   1]
313    ];
314
315
316// Function: affine3d_zrot()
317// Usage:
318//   mat = affine3d_zrot(ang);
319// Topics: Affine, Matrices, Transforms, Rotation
320// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot()
321// Description:
322//   Returns the 4x4 affine3d matrix to perform a rotation of a 3D vector around the Z axis.
323// Arguments:
324//   ang = number of degrees to rotate.
325// Example:
326//   mat = affine3d_zrot(90);
327//   // Returns:
328//   //   [
329//   //     [ 0,-1, 0, 0],
330//   //     [ 1, 0, 0, 0],
331//   //     [ 0, 0, 1, 0],
332//   //     [ 0, 0, 0, 1]
333//   //   ]
334function affine3d_zrot(ang=0) =
335    assert(is_finite(ang))
336    [
337        [cos(ang), -sin(ang), 0, 0],
338        [sin(ang),  cos(ang), 0, 0],
339        [       0,         0, 1, 0],
340        [       0,         0, 0, 1]
341    ];
342
343
344// Function: affine3d_rot_by_axis()
345// Usage:
346//   mat = affine3d_rot_by_axis(u, ang);
347// Topics: Affine, Matrices, Transforms, Rotation
348// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot()
349// Description:
350//   Returns the 4x4 affine3d matrix to perform a rotation of a 3D vector around an axis.
351// Arguments:
352//   u = 3D axis vector to rotate around.
353//   ang = number of degrees to rotate.
354// Example:
355//   mat = affine3d_rot_by_axis([1,1,1], 120);
356//   // Returns approx:
357//   //   [
358//   //     [ 0, 0, 1, 0],
359//   //     [ 1, 0, 0, 0],
360//   //     [ 0, 1, 0, 0],
361//   //     [ 0, 0, 0, 1]
362//   //   ]
363function affine3d_rot_by_axis(u=UP, ang=0) =
364    assert(is_finite(ang))
365    assert(is_vector(u,3))
366    approx(ang,0)? affine3d_identity() :
367    let(
368        u = unit(u),
369        c = cos(ang),
370        c2 = 1-c,
371        s = sin(ang)
372    ) [
373        [u.x*u.x*c2+c    , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0],
374        [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c    , u.y*u.z*c2-u.x*s, 0],
375        [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c    , 0],
376        [               0,                0,                0, 1]
377    ];
378
379
380// Function: affine3d_rot_from_to()
381// Usage:
382//   mat = affine3d_rot_from_to(from, to);
383// Topics: Affine, Matrices, Transforms, Rotation
384// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot()
385// Description:
386//   Returns the 4x4 affine3d matrix to perform a rotation of a 3D vector from one vector direction to another.
387// Arguments:
388//   from = 3D axis vector to rotate from.
389//   to = 3D axis vector to rotate to.
390// Example:
391//   mat = affine3d_rot_from_to(UP, RIGHT);
392//   // Returns:
393//   //   [
394//   //     [ 0, 0, 1, 0],
395//   //     [ 0, 1, 0, 0],
396//   //     [-1, 0, 0, 0],
397//   //     [ 0, 0, 0, 1]
398//   //   ]
399function affine3d_rot_from_to(from, to) =
400    assert(is_vector(from))
401    assert(is_vector(to))
402    assert(len(from)==len(to))
403    let(
404        from = unit(point3d(from)),
405        to = unit(point3d(to))
406    ) approx(from,to)? affine3d_identity() :
407    from.z==0 && to.z==0 ?  affine3d_zrot(v_theta(point2d(to)) - v_theta(point2d(from)))
408    :
409    let(
410        u = vector_axis(from,to),
411        ang = vector_angle(from,to),
412        c = cos(ang),
413        c2 = 1-c,
414        s = sin(ang)
415    ) [
416        [u.x*u.x*c2+c    , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0],
417        [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c    , u.y*u.z*c2-u.x*s, 0],
418        [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c    , 0],
419        [               0,                0,                0, 1]
420    ];
421
422
423
424
425
426// Function: affine3d_mirror()
427// Usage:
428//   mat = affine3d_mirror(v);
429// Topics: Affine, Matrices, Transforms, Reflection, Mirroring
430// See Also: mirror(), xflip(), yflip(), zflip(), affine2d_mirror()
431// Description:
432//   Returns the 4x4 affine3d matrix to perform a reflection of a 3D vector across the plane given by its normal vector.
433// Arguments:
434//   v = The normal vector of the plane to reflect across.
435// Example:
436//   mat = affine3d_mirror([1,0,0]);
437//   // Returns:
438//   //   [
439//   //     [-1, 0, 0, 0],
440//   //     [ 0, 1, 0, 0],
441//   //     [ 0, 0, 1, 0],
442//   //     [ 0, 0, 0, 1]
443//   //   ]
444// Example:
445//   mat = affine3d_mirror([0,1,0]);
446//   // Returns:
447//   //   [
448//   //     [ 1, 0, 0, 0],
449//   //     [ 0,-1, 0, 0],
450//   //     [ 0, 0, 1, 0],
451//   //     [ 0, 0, 0, 1]
452//   //   ]
453function affine3d_mirror(v) =
454    assert(is_vector(v))
455    let(
456        v=unit(point3d(v)),
457        a=v.x, b=v.y, c=v.z
458    ) [
459        [1-2*a*a,  -2*a*b,  -2*a*c, 0],
460        [ -2*b*a, 1-2*b*b,  -2*b*c, 0],
461        [ -2*c*a,  -2*c*b, 1-2*c*c, 0],
462        [      0,       0,       0, 1]
463    ];
464
465
466// Function: affine3d_skew()
467// Usage:
468//   mat = affine3d_skew([sxy=], [sxz=], [syx=], [syz=], [szx=], [szy=]);
469// Topics: Affine, Matrices, Transforms, Skewing
470// See Also: skew(), affine3d_skew_xy(), affine3d_skew_xz(), affine3d_skew_yz(), affine2d_skew()
471// Description:
472//   Returns the 4x4 affine3d matrix to perform a skew transformation.
473// Arguments:
474//   sxy = Skew factor multiplier for skewing along the X axis as you get farther from the Y axis.  Default: 0
475//   sxz = Skew factor multiplier for skewing along the X axis as you get farther from the Z axis.  Default: 0
476//   syx = Skew factor multiplier for skewing along the Y axis as you get farther from the X axis.  Default: 0
477//   syz = Skew factor multiplier for skewing along the Y axis as you get farther from the Z axis.  Default: 0
478//   szx = Skew factor multiplier for skewing along the Z axis as you get farther from the X axis.  Default: 0
479//   szy = Skew factor multiplier for skewing along the Z axis as you get farther from the Y axis.  Default: 0
480// Example:
481//   mat = affine3d_skew(sxy=2,szx=3);
482//   // Returns:
483//   //   [
484//   //     [ 1, 2, 0, 0],
485//   //     [ 0, 1, 0, 0],
486//   //     [ 0, 0, 1, 0],
487//   //     [ 3, 0, 0, 1]
488//   //   ]
489function affine3d_skew(sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0) = [
490    [  1, sxy, sxz, 0],
491    [syx,   1, syz, 0],
492    [szx, szy,   1, 0],
493    [  0,   0,   0, 1]
494];
495
496
497// Function: affine3d_skew_xy()
498// Usage:
499//   mat = affine3d_skew_xy(xa);
500//   mat = affine3d_skew_xy(ya=);
501//   mat = affine3d_skew_xy(xa, ya);
502// Topics: Affine, Matrices, Transforms, Skewing
503// See Also: skew(), affine3d_skew(), affine3d_skew_xz(), affine3d_skew_yz(), affine2d_skew()
504// Description:
505//   Returns the 4x4 affine3d matrix to perform a skew transformation along the XY plane.
506// Arguments:
507//   xa = Skew angle, in degrees, in the direction of the X axis.  Default: 0
508//   ya = Skew angle, in degrees, in the direction of the Y axis.  Default: 0
509// Example:
510//   mat = affine3d_skew_xy(xa=45,ya=-45);
511//   // Returns:
512//   //   [
513//   //     [ 1, 0, 1, 0],
514//   //     [ 0, 1,-1, 0],
515//   //     [ 0, 0, 1, 0],
516//   //     [ 0, 0, 0, 1]
517//   //   ]
518function affine3d_skew_xy(xa=0, ya=0) =
519    assert(is_finite(xa))
520    assert(is_finite(ya))
521    [
522        [      1, tan(xa), 0, 0],
523        [tan(ya),       1, 0, 0],
524        [      0,       0, 1, 0],
525        [      0,       0, 0, 1]
526    ];
527
528
529// Function: affine3d_skew_xz()
530// Usage:
531//   mat = affine3d_skew_xz(xa);
532//   mat = affine3d_skew_xz(za=);
533//   mat = affine3d_skew_xz(xa, za);
534// Topics: Affine, Matrices, Transforms, Skewing
535// See Also: skew(), affine3d_skew(), affine3d_skew_xy(), affine3d_skew_yz(), affine2d_skew()
536// Description:
537//   Returns the 4x4 affine3d matrix to perform a skew transformation along the XZ plane.
538// Arguments:
539//   xa = Skew angle, in degrees, in the direction of the X axis.  Default: 0
540//   za = Skew angle, in degrees, in the direction of the Z axis.  Default: 0
541// Example:
542//   mat = affine3d_skew_xz(xa=45,za=-45);
543//   // Returns:
544//   //   [
545//   //     [ 1, 1, 0, 0],
546//   //     [ 0, 1, 0, 0],
547//   //     [ 0,-1, 1, 0],
548//   //     [ 0, 0, 0, 1]
549//   //   ]
550function affine3d_skew_xz(xa=0, za=0) =
551    assert(is_finite(xa))
552    assert(is_finite(za))
553    [
554        [      1, 0, tan(xa), 0],
555        [      0, 1,       0, 0],
556        [tan(za), 0,       1, 0],
557        [      0, 0,       0, 1]
558    ];
559
560
561// Function: affine3d_skew_yz()
562// Usage:
563//   mat = affine3d_skew_yz(ya);
564//   mat = affine3d_skew_yz(za=);
565//   mat = affine3d_skew_yz(ya, za);
566// Topics: Affine, Matrices, Transforms, Skewing
567// See Also: skew(), affine3d_skew(), affine3d_skew_xy(), affine3d_skew_xz(), affine2d_skew()
568// Description:
569//   Returns the 4x4 affine3d matrix to perform a skew transformation along the YZ plane.
570// Arguments:
571//   ya = Skew angle, in degrees, in the direction of the Y axis.  Default: 0
572//   za = Skew angle, in degrees, in the direction of the Z axis.  Default: 0
573// Example:
574//   mat = affine3d_skew_yz(ya=45,za=-45);
575//   // Returns:
576//   //   [
577//   //     [ 1, 0, 0, 0],
578//   //     [ 1, 1, 0, 0],
579//   //     [-1, 0, 1, 0],
580//   //     [ 0, 0, 0, 1]
581//   //   ]
582function affine3d_skew_yz(ya=0, za=0) =
583    assert(is_finite(ya))
584    assert(is_finite(za))
585    [
586        [1,       0,       0, 0],
587        [0,       1, tan(ya), 0],
588        [0, tan(za),       1, 0],
589        [0,       0,       0, 1]
590    ];
591
592
593
594// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap