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