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
454
455
456// Function: affine3d_mirror()
457// Synopsis: Returns a 3D (4x4) reflection transformation matrix.
458// SynTags: Mat
459// Topics: Affine, Matrices, Transforms, Reflection, Mirroring
460// See Also: mirror(), xflip(), yflip(), zflip(), affine2d_mirror()
461// Usage:
462//   mat = affine3d_mirror(v);
463// Description:
464//   Returns the 4x4 affine3d matrix to perform a reflection of a 3D vector across the plane given by its normal vector.
465// Arguments:
466//   v = The normal vector of the plane to reflect across.
467// Example:
468//   mat = affine3d_mirror([1,0,0]);
469//   // Returns:
470//   //   [
471//   //     [-1, 0, 0, 0],
472//   //     [ 0, 1, 0, 0],
473//   //     [ 0, 0, 1, 0],
474//   //     [ 0, 0, 0, 1]
475//   //   ]
476// Example:
477//   mat = affine3d_mirror([0,1,0]);
478//   // Returns:
479//   //   [
480//   //     [ 1, 0, 0, 0],
481//   //     [ 0,-1, 0, 0],
482//   //     [ 0, 0, 1, 0],
483//   //     [ 0, 0, 0, 1]
484//   //   ]
485function affine3d_mirror(v) =
486    assert(is_vector(v))
487    let(
488        v=unit(point3d(v)),
489        a=v.x, b=v.y, c=v.z
490    ) [
491        [1-2*a*a,  -2*a*b,  -2*a*c, 0],
492        [ -2*b*a, 1-2*b*b,  -2*b*c, 0],
493        [ -2*c*a,  -2*c*b, 1-2*c*c, 0],
494        [      0,       0,       0, 1]
495    ];
496
497
498// Function: affine3d_skew()
499// Synopsis: Returns a 3D (4x4) skewing transformation matrix.
500// SynTags: Mat
501// Topics: Affine, Matrices, Transforms, Skewing
502// See Also: skew(), affine3d_skew_xy(), affine3d_skew_xz(), affine3d_skew_yz(), affine2d_skew()
503// Usage:
504//   mat = affine3d_skew([sxy=], [sxz=], [syx=], [syz=], [szx=], [szy=]);
505// Description:
506//   Returns the 4x4 affine3d matrix to perform a skew transformation.
507// Arguments:
508//   sxy = Skew factor multiplier for skewing along the X axis as you get farther from the Y axis.  Default: 0
509//   sxz = Skew factor multiplier for skewing along the X axis as you get farther from the Z axis.  Default: 0
510//   syx = Skew factor multiplier for skewing along the Y axis as you get farther from the X axis.  Default: 0
511//   syz = Skew factor multiplier for skewing along the Y axis as you get farther from the Z axis.  Default: 0
512//   szx = Skew factor multiplier for skewing along the Z axis as you get farther from the X axis.  Default: 0
513//   szy = Skew factor multiplier for skewing along the Z axis as you get farther from the Y axis.  Default: 0
514// Example:
515//   mat = affine3d_skew(sxy=2,szx=3);
516//   // Returns:
517//   //   [
518//   //     [ 1, 2, 0, 0],
519//   //     [ 0, 1, 0, 0],
520//   //     [ 0, 0, 1, 0],
521//   //     [ 3, 0, 0, 1]
522//   //   ]
523function affine3d_skew(sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0) = [
524    [  1, sxy, sxz, 0],
525    [syx,   1, syz, 0],
526    [szx, szy,   1, 0],
527    [  0,   0,   0, 1]
528];
529
530
531// Function: affine3d_skew_xy()
532// Synopsis: Returns a 3D (4x4) XY-plane skewing transformation matrix.
533// SynTags: Mat
534// Topics: Affine, Matrices, Transforms, Skewing
535// See Also: skew(), affine3d_skew(), affine3d_skew_xz(), affine3d_skew_yz(), affine2d_skew()
536// Usage:
537//   mat = affine3d_skew_xy(xa);
538//   mat = affine3d_skew_xy(ya=);
539//   mat = affine3d_skew_xy(xa, ya);
540// Description:
541//   Returns the 4x4 affine3d matrix to perform a skew transformation along the XY plane.
542// Arguments:
543//   xa = Skew angle, in degrees, in the direction of the X axis.  Default: 0
544//   ya = Skew angle, in degrees, in the direction of the Y axis.  Default: 0
545// Example:
546//   mat = affine3d_skew_xy(xa=45,ya=-45);
547//   // Returns:
548//   //   [
549//   //     [ 1, 0, 1, 0],
550//   //     [ 0, 1,-1, 0],
551//   //     [ 0, 0, 1, 0],
552//   //     [ 0, 0, 0, 1]
553//   //   ]
554function affine3d_skew_xy(xa=0, ya=0) =
555    assert(is_finite(xa))
556    assert(is_finite(ya))
557    [
558        [      1, tan(xa), 0, 0],
559        [tan(ya),       1, 0, 0],
560        [      0,       0, 1, 0],
561        [      0,       0, 0, 1]
562    ];
563
564
565// Function: affine3d_skew_xz()
566// Synopsis: Returns a 3D (4x4) XZ-plane skewing transformation matrix.
567// SynTags: Mat
568// Topics: Affine, Matrices, Transforms, Skewing
569// See Also: skew(), affine3d_skew(), affine3d_skew_xy(), affine3d_skew_yz(), affine2d_skew()
570// Usage:
571//   mat = affine3d_skew_xz(xa);
572//   mat = affine3d_skew_xz(za=);
573//   mat = affine3d_skew_xz(xa, za);
574// Description:
575//   Returns the 4x4 affine3d matrix to perform a skew transformation along the XZ plane.
576// Arguments:
577//   xa = Skew angle, in degrees, in the direction of the X axis.  Default: 0
578//   za = Skew angle, in degrees, in the direction of the Z axis.  Default: 0
579// Example:
580//   mat = affine3d_skew_xz(xa=45,za=-45);
581//   // Returns:
582//   //   [
583//   //     [ 1, 1, 0, 0],
584//   //     [ 0, 1, 0, 0],
585//   //     [ 0,-1, 1, 0],
586//   //     [ 0, 0, 0, 1]
587//   //   ]
588function affine3d_skew_xz(xa=0, za=0) =
589    assert(is_finite(xa))
590    assert(is_finite(za))
591    [
592        [      1, 0, tan(xa), 0],
593        [      0, 1,       0, 0],
594        [tan(za), 0,       1, 0],
595        [      0, 0,       0, 1]
596    ];
597
598
599// Function: affine3d_skew_yz()
600// Synopsis: Returns a 3D (4x4) YZ-plane skewing transformation matrix.
601// SynTags: Mat
602// Topics: Affine, Matrices, Transforms, Skewing
603// See Also: skew(), affine3d_skew(), affine3d_skew_xy(), affine3d_skew_xz(), affine2d_skew()
604// Usage:
605//   mat = affine3d_skew_yz(ya);
606//   mat = affine3d_skew_yz(za=);
607//   mat = affine3d_skew_yz(ya, za);
608// Description:
609//   Returns the 4x4 affine3d matrix to perform a skew transformation along the YZ plane.
610// Arguments:
611//   ya = Skew angle, in degrees, in the direction of the Y axis.  Default: 0
612//   za = Skew angle, in degrees, in the direction of the Z axis.  Default: 0
613// Example:
614//   mat = affine3d_skew_yz(ya=45,za=-45);
615//   // Returns:
616//   //   [
617//   //     [ 1, 0, 0, 0],
618//   //     [ 1, 1, 0, 0],
619//   //     [-1, 0, 1, 0],
620//   //     [ 0, 0, 0, 1]
621//   //   ]
622function affine3d_skew_yz(ya=0, za=0) =
623    assert(is_finite(ya))
624    assert(is_finite(za))
625    [
626        [1,       0,       0, 0],
627        [0,       1, tan(ya), 0],
628        [0, tan(za),       1, 0],
629        [0,       0,       0, 1]
630    ];
631
632
633
634// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap