source: projectionDesigner/tag/ProjectionDesigner_1.1.5/projdesigner/include/gmtl/Generate.h

Last change on this file was 2, checked in by Torben Dannhauer, 10 years ago
File size: 58.9 KB
Line 
1/************************************************************** ggt-head beg
2 *
3 * GGT: Generic Graphics Toolkit
4 *
5 * Original Authors:
6 *   Allen Bierbaum
7 *
8 * -----------------------------------------------------------------
9 * File:          Generate.h,v
10 * Date modified: 2006/04/24 14:33:04
11 * Version:       1.91
12 * -----------------------------------------------------------------
13 *
14 *********************************************************** ggt-head end */
15/*************************************************************** ggt-cpr beg
16*
17* GGT: The Generic Graphics Toolkit
18* Copyright (C) 2001,2002 Allen Bierbaum
19*
20* This library is free software; you can redistribute it and/or
21* modify it under the terms of the GNU Lesser General Public
22* License as published by the Free Software Foundation; either
23* version 2.1 of the License, or (at your option) any later version.
24*
25* This library is distributed in the hope that it will be useful,
26* but WITHOUT ANY WARRANTY; without even the implied warranty of
27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28* Lesser General Public License for more details.
29*
30* You should have received a copy of the GNU Lesser General Public
31* License along with this library; if not, write to the Free Software
32* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33*
34 ************************************************************ ggt-cpr end */
35#ifndef _GMTL_GENERATE_H_
36#define _GMTL_GENERATE_H_
37
38#include <gmtl/Defines.h>
39#include <gmtl/Util/Assert.h>
40#include <gmtl/Util/StaticAssert.h>
41
42#include <gmtl/Vec.h>    // for Vec
43#include <gmtl/VecOps.h> // for lengthSquared
44#include <gmtl/Quat.h>
45#include <gmtl/QuatOps.h>
46#include <gmtl/Coord.h>
47#include <gmtl/Matrix.h>
48#include <gmtl/Util/Meta.h>
49#include <gmtl/Math.h>
50#include <gmtl/Xforms.h>
51
52#include <gmtl/EulerAngle.h>
53#include <gmtl/AxisAngle.h>
54
55namespace gmtl
56{
57   /** @ingroup Generate
58    *  @name Vec Generators
59    *  @{
60    */
61
62   /** create a vector from the vector component of a quaternion
63    * @returns a vector of the quaternion's axis. quat = [v,0] = [v0,v1,v2,0]
64    */
65   template <typename DATA_TYPE>
66   inline Vec<DATA_TYPE, 3> makeVec( const Quat<DATA_TYPE>& quat )
67   {
68      return Vec<DATA_TYPE, 3>( quat[Xelt], quat[Yelt], quat[Zelt] );
69   }
70
71   /** create a normalized vector from the given vector.
72    */
73   template <typename DATA_TYPE, unsigned SIZE>
74   inline Vec<DATA_TYPE, SIZE> makeNormal( Vec<DATA_TYPE, SIZE> vec )
75   {
76      normalize( vec );
77      return vec;
78   }
79
80   /**
81    * Computes the cross product between v1 and v2 and returns the result. Note
82    * that this only applies to 3-dimensional vectors.
83    *
84    * @pre  v1 and v2 must be 3-D vectors
85    * @post result = v1 x v2
86    *
87    * @param v1   the first vector
88    * @param v2   the second vector
89    *
90    * @return  the result of the cross product between v1 and v2
91    */
92   template<class DATA_TYPE>
93   Vec<DATA_TYPE,3> makeCross(const Vec<DATA_TYPE, 3>& v1,
94                              const Vec<DATA_TYPE, 3>& v2)
95   {
96      return Vec<DATA_TYPE,3>( ((v1[Yelt]*v2[Zelt]) - (v1[Zelt]*v2[Yelt])),
97                               ((v1[Zelt]*v2[Xelt]) - (v1[Xelt]*v2[Zelt])),
98                               ((v1[Xelt]*v2[Yelt]) - (v1[Yelt]*v2[Xelt])) );
99   }
100
101   /** Set vector using translation portion of the matrix.
102    * @pre if making an n x n matrix, then for
103    *    - <b>vector is homogeneous:</b> SIZE of vector needs to equal number of Matrix ROWS - 1
104    *    - <b>vector has scale component:</b> SIZE of vector needs to equal number of Matrix ROWS
105    * if making an n x n+1 matrix, then for
106    *    - <b>vector is homogeneous:</b> SIZE of vector needs to equal number of Matrix ROWS
107    *    - <b>vector has scale component:</b> SIZE of vector needs to equal number of Matrix ROWS + 1
108    * @post if preconditions are not met, then function is undefined (will not compile)
109    */
110   template<typename VEC_TYPE, typename DATA_TYPE, unsigned ROWS, unsigned COLS >
111   inline VEC_TYPE& setTrans( VEC_TYPE& result, const Matrix<DATA_TYPE, ROWS, COLS>& arg )
112   {
113      // ASSERT: There are as many
114
115      // if n x n   then (homogeneous case) vecsize == rows-1 or (scale component case) vecsize == rows
116      // if n x n+1 then (homogeneous case) vecsize == rows   or (scale component case) vecsize == rows+1
117      gmtlASSERT( ((ROWS == COLS && ( VEC_TYPE::Size == (ROWS-1) ||  VEC_TYPE::Size == ROWS)) ||
118               (COLS == (ROWS+1) && ( VEC_TYPE::Size == ROWS ||  VEC_TYPE::Size == (ROWS+1)))) &&
119              "preconditions not met for vector size in call to makeTrans.  Read your documentation." );
120
121      // homogeneous case...
122      if ((ROWS == COLS &&  VEC_TYPE::Size == ROWS)              // Square matrix and vec so assume homogeneous vector. ex. 4x4 with vec 4
123          || (COLS == (ROWS+1) &&  VEC_TYPE::Size == (ROWS+1)))  // ex: 3x4 with vec4
124      {
125         result[VEC_TYPE::Size-1] = 1.0f;
126      }
127
128      // non-homogeneous case... (SIZE == ROWS),
129      //else
130      //{}
131
132      for (unsigned x = 0; x < COLS - 1; ++x)
133      {
134         result[x] = arg( x, COLS - 1 );
135      }
136
137      return result;
138   }
139
140   /** @} */
141
142   /** @ingroup Generate
143    *  @name Quat Generators
144    *  @{
145    */
146
147   /** Set pure quaternion
148   * @pre vec should be normalized
149   * @param quat     any quaternion object
150   * @param vec      a normalized vector representing an axis
151   * @post quat will have vec as its axis, and no rotation
152   */
153   template <typename DATA_TYPE>
154   inline Quat<DATA_TYPE>& setPure( Quat<DATA_TYPE>& quat, const Vec<DATA_TYPE, 3>& vec )
155   {
156      quat.set( vec[0], vec[1], vec[2], 0 );
157      return quat;
158   }
159
160   /** create a pure quaternion
161   * @pre vec should be normalized
162   * @param vec      a normalized vector representing an axis
163   * @return a quaternion with vec as its axis, and no rotation
164   * @post quat = [v,0] = [v0,v1,v2,0]
165   */
166   template <typename DATA_TYPE>
167   inline Quat<DATA_TYPE> makePure( const Vec<DATA_TYPE, 3>& vec )
168   {
169      return Quat<DATA_TYPE>( vec[0], vec[1], vec[2], 0 );
170   }
171
172   /** Normalize a quaternion
173    * @param quat       a quaternion
174    * @post quat is normalized
175    */
176   template <typename DATA_TYPE>
177   inline Quat<DATA_TYPE> makeNormal( const Quat<DATA_TYPE>& quat )
178   {
179      Quat<DATA_TYPE> temporary( quat );
180      return normalize( temporary );
181   }
182
183   /** quaternion complex conjugate.
184    *  @param quat      any quaternion object
185    *  @post set result to the complex conjugate of result.
186    *  @post result'[x,y,z,w] == result[-x,-y,-z,w]
187    *  @see Quat
188    */
189   template <typename DATA_TYPE>
190   inline Quat<DATA_TYPE> makeConj( const Quat<DATA_TYPE>& quat )
191   {
192      Quat<DATA_TYPE> temporary( quat );
193      return conj( temporary );
194   }
195
196   /** create quaternion from the inverse of another quaternion.
197    *  @param quat      any quaternion object
198    *  @return    a quaternion that is the multiplicative inverse of quat
199    *  @see Quat
200    */
201   template <typename DATA_TYPE>
202   inline Quat<DATA_TYPE> makeInvert( const Quat<DATA_TYPE>& quat )
203   {
204      Quat<DATA_TYPE> temporary( quat );
205      return invert( temporary );
206   }
207
208   /** Convert an AxisAngle to a Quat.  sets a rotation quaternion from an angle and an axis.
209    * @pre AxisAngle::axis must be normalized to length == 1 prior to calling this.
210    * @post q = [ cos(rad/2), sin(rad/2) * [x,y,z] ]
211    */
212   template <typename DATA_TYPE>
213   inline Quat<DATA_TYPE>& set( Quat<DATA_TYPE>& result, const AxisAngle<DATA_TYPE>& axisAngle )
214   {
215      gmtlASSERT( (Math::isEqual( lengthSquared( axisAngle.getAxis() ), (DATA_TYPE)1.0, (DATA_TYPE)0.0001 )) &&
216                   "you must pass in a normalized vector to setRot( quat, rad, vec )" );
217
218      DATA_TYPE half_angle = axisAngle.getAngle() * (DATA_TYPE)0.5;
219      DATA_TYPE sin_half_angle = Math::sin( half_angle );
220
221      result[Welt] = Math::cos( half_angle );
222      result[Xelt] = sin_half_angle * axisAngle.getAxis()[0];
223      result[Yelt] = sin_half_angle * axisAngle.getAxis()[1];
224      result[Zelt] = sin_half_angle * axisAngle.getAxis()[2];
225
226      // should automagically be normalized (unit magnitude) now...
227      return result;
228   }
229
230   /** Redundant duplication of the set(quat,axisangle) function, this is provided only for template compatibility.
231    *  unless you're writing template functions, you should use set(quat,axisangle).
232    */
233   template <typename DATA_TYPE>
234   inline Quat<DATA_TYPE>& setRot( Quat<DATA_TYPE>& result, const AxisAngle<DATA_TYPE>& axisAngle )
235   {
236      return gmtl::set( result, axisAngle );
237   }
238
239   /** Convert an EulerAngle rotation to a Quaternion rotation.
240    * Sets a rotation quaternion using euler angles (each angle in radians).
241    * @pre pass in your angles in the same order as the RotationOrder you specify
242    */
243   template <typename DATA_TYPE, typename ROT_ORDER>
244   inline Quat<DATA_TYPE>& set( Quat<DATA_TYPE>& result, const EulerAngle<DATA_TYPE, ROT_ORDER>& euler )
245   {
246      // this might be faster if put into the switch statement... (testme)
247      const int& order = ROT_ORDER::ID;
248      const DATA_TYPE xRot = (order == XYZ::ID) ? euler[0] : ((order == ZXY::ID) ? euler[1] : euler[2]);
249      const DATA_TYPE yRot = (order == XYZ::ID) ? euler[1] : ((order == ZXY::ID) ? euler[2] : euler[1]);
250      const DATA_TYPE zRot = (order == XYZ::ID) ? euler[2] : ((order == ZXY::ID) ? euler[0] : euler[0]);
251
252      // this could be written better for each rotation order, but this is really general...
253      Quat<DATA_TYPE> qx, qy, qz;
254
255      // precompute half angles
256      DATA_TYPE xOver2 = xRot * (DATA_TYPE)0.5;
257      DATA_TYPE yOver2 = yRot * (DATA_TYPE)0.5;
258      DATA_TYPE zOver2 = zRot * (DATA_TYPE)0.5;
259
260      // set the pitch quat
261      qx[Xelt] = Math::sin( xOver2 );
262      qx[Yelt] = (DATA_TYPE)0.0;
263      qx[Zelt] = (DATA_TYPE)0.0;
264      qx[Welt] = Math::cos( xOver2 );
265
266      // set the yaw quat
267      qy[Xelt] = (DATA_TYPE)0.0;
268      qy[Yelt] = Math::sin( yOver2 );
269      qy[Zelt] = (DATA_TYPE)0.0;
270      qy[Welt] = Math::cos( yOver2 );
271
272      // set the roll quat
273      qz[Xelt] = (DATA_TYPE)0.0;
274      qz[Yelt] = (DATA_TYPE)0.0;
275      qz[Zelt] = Math::sin( zOver2 );
276      qz[Welt] = Math::cos( zOver2 );
277
278      // compose the three in pyr order...
279      switch (order)
280      {
281      case XYZ::ID: result = qx * qy * qz; break;
282      case ZYX::ID: result = qz * qy * qx; break;
283      case ZXY::ID: result = qz * qx * qy; break;
284      default:
285         gmtlASSERT( false && "unknown rotation order passed to setRot" );
286         break;
287      }
288
289      // ensure the quaternion is normalized
290      normalize( result );
291      return result;
292   }
293
294   /** Redundant duplication of the set(quat,eulerangle) function, this is provided only for template compatibility.
295    *  unless you're writing template functions, you should use set(quat,eulerangle).
296    */
297   template <typename DATA_TYPE, typename ROT_ORDER>
298   inline Quat<DATA_TYPE>& setRot( Quat<DATA_TYPE>& result, const EulerAngle<DATA_TYPE, ROT_ORDER>& euler )
299   {
300      return gmtl::set( result, euler );
301   }
302
303   /** Convert a Matrix to a Quat.
304    *  Sets the rotation quaternion using the given matrix
305    *  (3x3, 3x4, 4x3, or 4x4 are all valid sizes).
306    */
307   template <typename DATA_TYPE, unsigned ROWS, unsigned COLS>
308   inline Quat<DATA_TYPE>& set( Quat<DATA_TYPE>& quat, const Matrix<DATA_TYPE, ROWS, COLS>& mat  )
309   {
310      gmtlASSERT( ((ROWS == 3 && COLS == 3) ||
311               (ROWS == 3 && COLS == 4) ||
312               (ROWS == 4 && COLS == 3) ||
313               (ROWS == 4 && COLS == 4)) &&
314               "pre conditions not met on set( quat, pos, mat ) which only sets a quaternion to the rotation part of a 3x3, 3x4, 4x3, or 4x4 matrix." );
315
316      DATA_TYPE tr( mat( 0, 0 ) + mat( 1, 1 ) + mat( 2, 2 ) ), s( 0.0f );
317
318      // If diagonal is positive
319      if (tr > (DATA_TYPE)0.0)
320      {
321         s = Math::sqrt( tr + (DATA_TYPE)1.0 );
322         quat[Welt] = s * (DATA_TYPE)0.5;
323         s = DATA_TYPE(0.5) / s;
324
325         quat[Xelt] = (mat( 2, 1 ) - mat( 1, 2 )) * s;
326         quat[Yelt] = (mat( 0, 2 ) - mat( 2, 0 )) * s;
327         quat[Zelt] = (mat( 1, 0 ) - mat( 0, 1 )) * s;
328      }
329
330      // when Diagonal is negative
331      else
332      {
333         static const unsigned int nxt[3] = { 1, 2, 0 };
334         unsigned int i( Xelt ), j, k;
335
336         if (mat( 1, 1 ) > mat( 0, 0 ))
337            i = 1;
338
339         if (mat( 2, 2 ) > mat( i, i ))
340            i = 2;
341
342         j = nxt[i];
343         k = nxt[j];
344
345         s = Math::sqrt( (mat( i, i )-(mat( j, j )+mat( k, k ))) + (DATA_TYPE)1.0 );
346
347         DATA_TYPE q[4];
348         q[i] = s * (DATA_TYPE)0.5;
349
350         if (s != (DATA_TYPE)0.0)
351            s = DATA_TYPE(0.5) / s;
352
353         q[3] = (mat( k, j ) - mat( j, k )) * s;
354         q[j] = (mat( j, i ) + mat( i, j )) * s;
355         q[k] = (mat( k, i ) + mat( i, k )) * s;
356
357         quat[Xelt] = q[0];
358         quat[Yelt] = q[1];
359         quat[Zelt] = q[2];
360         quat[Welt] = q[3];
361      }
362
363      return quat;
364   }
365
366   /** Redundant duplication of the set(quat,mat) function, this is provided only for template compatibility.
367    *  unless you're writing template functions, you should use set(quat,mat).
368    */
369   template <typename DATA_TYPE, unsigned ROWS, unsigned COLS>
370   inline Quat<DATA_TYPE>& setRot( Quat<DATA_TYPE>& result, const Matrix<DATA_TYPE, ROWS, COLS>& mat )
371   {
372      return gmtl::set( result, mat );
373   }
374   /** @} */
375
376   /** @ingroup Generate
377    *  @name AxisAngle Generators
378    *  @{
379    */
380
381   /** Convert a rotation quaternion to an AxisAngle.
382    *
383    * @post returns an angle in radians, and a normalized axis equivilent to the quaternion's rotation.
384    * @post returns rad and xyz such that
385    * <ul>
386    * <li>  rad = acos( w ) * 2.0
387    * <li>  vec = v / (asin( w ) * 2.0)   [where v is the xyz or vector component of the quat]
388    * </ul>
389    * @post axisAngle = quat;
390    */
391   template <typename DATA_TYPE>
392   inline AxisAngle<DATA_TYPE>& set( AxisAngle<DATA_TYPE>& axisAngle, Quat<DATA_TYPE> quat )
393   {
394      // set sure we don't get a NaN result from acos...
395      if (Math::abs( quat[Welt] ) > (DATA_TYPE)1.0)
396      {
397         gmtl::normalize( quat );
398      }
399      gmtlASSERT( Math::abs( quat[Welt] ) <= (DATA_TYPE)1.0 && "acos returns NaN when quat[Welt] > 1, try normalizing your quat." );
400
401      // [acos( w ) * 2.0, v / (asin( w ) * 2.0)]
402
403      // set the angle - aCos is mathematically defined to be between 0 and PI
404      DATA_TYPE rad = Math::aCos( quat[Welt] ) * (DATA_TYPE)2.0;
405      axisAngle.setAngle( rad );
406
407      // set the axis: (use sin(rad) instead of asin(w))
408      DATA_TYPE sin_half_angle = Math::sin( rad * (DATA_TYPE)0.5 );
409      if (sin_half_angle >= (DATA_TYPE)0.0001) // because (PI >= rad >= 0)
410      {
411         DATA_TYPE sin_half_angle_inv = DATA_TYPE(1.0) / sin_half_angle;
412         Vec<DATA_TYPE, 3> axis( quat[Xelt] * sin_half_angle_inv,
413                                 quat[Yelt] * sin_half_angle_inv,
414                                 quat[Zelt] * sin_half_angle_inv );
415         normalize( axis );
416         axisAngle.setAxis( axis );
417      }
418
419      // avoid NAN
420      else
421      {
422         // one of the terms should be a 1,
423         // so we can maintain unit-ness
424         // in case w is 0 (which here w is 0)
425         axisAngle.setAxis( gmtl::Vec<DATA_TYPE, 3>(
426                             DATA_TYPE( 1.0 ) /*- gmtl::Math::abs( quat[Welt] )*/,
427                            (DATA_TYPE)0.0,
428                            (DATA_TYPE)0.0 ) );
429      }
430      return axisAngle;
431   }
432
433   /** Redundant duplication of the set(axisangle,quat) function, this is provided only for template compatibility.
434    *  unless you're writing template functions, you should use set(axisangle,quat) for clarity.
435    */
436   template <typename DATA_TYPE>
437   inline AxisAngle<DATA_TYPE>& setRot( AxisAngle<DATA_TYPE>& result, Quat<DATA_TYPE> quat )
438   {
439      return gmtl::set( result, quat );
440   }
441
442   /** make the axis of an AxisAngle normalized */
443   template <typename DATA_TYPE>
444   AxisAngle<DATA_TYPE> makeNormal( const AxisAngle<DATA_TYPE>& a )
445   {
446      return AxisAngle<DATA_TYPE>( a.getAngle(), makeNormal( a.getAxis() ) );
447   }
448   /** @} */
449
450   /** @ingroup Generate
451    *  @name EulerAngle Generators
452    *  @{
453    */
454
455   /** Convert Matrix to EulerAngle.
456    *  Set the Euler Angle from the given rotation portion (3x3) of the matrix.
457    *
458    * @param input  order, mat
459    * @param output param0, param1, param2
460    *
461    * @pre pass in your args in the same order as the RotationOrder you specify
462    * @post this function only reads 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
463    *       NOTE: Angles are returned in radians (this is always true in GMTL).
464    */
465   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, typename ROT_ORDER >
466   inline EulerAngle<DATA_TYPE, ROT_ORDER>& set( EulerAngle<DATA_TYPE, ROT_ORDER>& euler,
467                    const Matrix<DATA_TYPE, ROWS, COLS>& mat )
468   {
469      // @todo set this a compile time assert...
470      gmtlASSERT( ROWS >= 3 && COLS >= 3 && ROWS <= 4 && COLS <= 4 &&
471            "this is undefined for Matrix smaller than 3x3 or bigger than 4x4" );
472
473      // @todo metaprogram this!
474      const int& order = ROT_ORDER::ID;
475      switch (order)
476      {
477      case XYZ::ID:
478         {
479#if 0
480            euler[2] = Math::aTan2( -mat(0,1), mat(0,0) );       // -(-cy*sz)/(cy*cz) - cy falls out
481            euler[0] = Math::aTan2( -mat(1,2), mat(2,2) );       // -(sx*cy)/(cx*cy) - cy falls out
482            DATA_TYPE cz = Math::cos( euler[2] );
483            euler[1] = Math::aTan2( mat(0,2), mat(0,0) / cz );   // (sy)/((cy*cz)/cz)
484#else
485            DATA_TYPE x(0), y(0), z(0);
486            y = Math::aSin( mat(0,2));
487            if (y < gmtl::Math::PI_OVER_2)
488            {
489               if(y > -gmtl::Math::PI_OVER_2)
490               {
491                  x = Math::aTan2(-mat(1,2),mat(2,2));
492                  z = Math::aTan2(-mat(0,1),mat(0,0));
493               }
494               else  // Non-unique (x - z constant)
495               {
496                  x = -Math::aTan2(mat(1,0), mat(1,1));
497                  z = 0;
498               }
499            }
500            else
501            {
502               // not unique (x + z constant)
503               x = Math::aTan2(mat(1,0), mat(1,1));
504               z = 0;
505            }
506            euler[0] = x;
507            euler[1] = y;
508            euler[2] = z;
509
510#endif
511         }
512         break;
513      case ZYX::ID:
514         {
515#if 0
516            euler[0] = Math::aTan2( mat(1,0), mat(0,0) );        // (cy*sz)/(cy*cz) - cy falls out
517            euler[2] = Math::aTan2( mat(2,1), mat(2,2) );        // (sx*cy)/(cx*cy) - cy falls out
518            DATA_TYPE sx = Math::sin( euler[2] );
519            euler[1] = Math::aTan2( -mat(2,0), mat(2,1) / sx );  // -(-sy)/((sx*cy)/sx)
520#else
521            DATA_TYPE x(0), y(0), z(0);
522
523            y = Math::aSin(-mat(2,0));
524            if(y < Math::PI_OVER_2)
525            {
526               if(y>-Math::PI_OVER_2)
527               {
528                  z = Math::aTan2(mat(1,0), mat(0,0));
529                  x = Math::aTan2(mat(2,1), mat(2,2));
530               }
531               else  // not unique (x + z constant)
532               {
533                  z = Math::aTan2(-mat(0,1),-mat(0,2));
534                  x = 0;
535               }
536            }
537            else  // not unique (x - z constant)
538            {
539               z = -Math::aTan2(mat(0,1), mat(0,2));
540               x = 0;
541            }
542            euler[0] = z;
543            euler[1] = y;
544            euler[2] = x;
545#endif
546         }
547         break;
548      case ZXY::ID:
549         {
550#if 0
551         // Extract the rotation directly from the matrix
552            DATA_TYPE x_angle;
553            DATA_TYPE y_angle;
554            DATA_TYPE z_angle;
555            DATA_TYPE cos_y, sin_y;
556            DATA_TYPE cos_x, sin_x;
557            DATA_TYPE cos_z, sin_z;
558
559            sin_x = mat(2,1);
560            x_angle = Math::aSin( sin_x );     // Get x angle
561            cos_x = Math::cos( x_angle );
562
563            // Check if cos_x = Zero
564            if (cos_x != 0.0f)     // ASSERT: cos_x != 0
565            {
566                  // Get y Angle
567               cos_y = mat(2,2) / cos_x;
568               sin_y = -mat(2,0) / cos_x;
569               y_angle = Math::aTan2( cos_y, sin_y );
570
571                  // Get z Angle
572               cos_z = mat(1,1) / cos_x;
573               sin_z = -mat(0,1) / cos_x;
574               z_angle = Math::aTan2( cos_z, sin_z );
575            }
576            else
577            {
578               // Arbitrarily set z_angle = 0
579               z_angle = 0;
580
581                  // Get y Angle
582               cos_y = mat(0,0);
583               sin_y = mat(1,0);
584               y_angle = Math::aTan2( cos_y, sin_y );
585            }
586
587            euler[1] = x_angle;
588            euler[2] = y_angle;
589            euler[0] = z_angle;
590#else
591            DATA_TYPE x,y,z;
592
593            x = Math::aSin(mat(2,1));
594            if(x < Math::PI_OVER_2)
595            {
596               if(x > -Math::PI_OVER_2)
597               {
598                  z = Math::aTan2(-mat(0,1), mat(1,1));
599                  y = Math::aTan2(-mat(2,0), mat(2,2));
600               }
601               else  // not unique (y - z constant)
602               {
603                  z = -Math::aTan2(mat(0,2), mat(0,0));
604                  y = 0;
605               }
606            }
607            else  // not unique (y + z constant)
608            {
609               z = Math::aTan2(mat(0,2), mat(0,0));
610               y = 0;
611            }
612            euler[0] = z;
613            euler[1] = x;
614            euler[2] = y;
615#endif
616         }
617         break;
618      default:
619         gmtlASSERT( false && "unknown rotation order passed to setRot" );
620         break;
621      }
622      return euler;
623   }
624
625   /** Redundant duplication of the set(eulerangle,quat) function, this is provided only for template compatibility.
626    *  unless you're writing template functions, you should use set(eulerangle,quat) for clarity.
627    */
628   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, typename ROT_ORDER >
629   inline EulerAngle<DATA_TYPE, ROT_ORDER>& setRot( EulerAngle<DATA_TYPE, ROT_ORDER>& result, const Matrix<DATA_TYPE, ROWS, COLS>& mat )
630   {
631      return gmtl::set( result, mat );
632   }
633   /** @} */
634
635   /** @ingroup Generate
636    *  @name Matrix Generators
637    *  @{
638    */
639
640   /** Set an arbitrary projection matrix
641    *  @result: set a projection matrix (similar to glFrustum).
642    */
643   template <typename T >
644   inline Matrix<T, 4,4>& setFrustum( Matrix<T, 4, 4>& result,
645                                                   T left, T top, T right,
646                                                   T bottom, T nr, T fr )
647   {
648      result.mData[0] = ( T( 2.0 ) * nr ) / ( right - left );
649      result.mData[1] = T( 0.0 );
650      result.mData[2] = T( 0.0 );
651      result.mData[3] = T( 0.0 );
652
653      result.mData[4] = T( 0.0 );
654      result.mData[5] = ( T( 2.0 ) * nr ) / ( top - bottom );
655      result.mData[6] = T( 0.0 );
656      result.mData[7] = T( 0.0 );
657
658      result.mData[8] = ( right + left ) / ( right - left );
659      result.mData[9] = ( top + bottom ) / ( top - bottom );
660      result.mData[10] = -( fr + nr ) / ( fr - nr );
661      result.mData[11] = T( -1.0 );
662
663      result.mData[12] = T( 0.0 );
664      result.mData[13] = T( 0.0 );
665      result.mData[14] = -( T( 2.0 ) * fr * nr ) / ( fr - nr );
666      result.mData[15] = T( 0.0 );
667
668      result.mState = Matrix<T, 4, 4>::FULL; // track state
669
670      return result;
671   }
672
673   /** Set an orthographic projection matrix
674    *  creates a transformation that produces a parallel projection matrix.
675    *  @nr = -nr is the value of the near clipping plane (positive value for near is negative in the z direction)
676    *  @fr = -fr is the value of the far clipping plane (positive value for far is negative in the z direction)
677    *  @result: set a orthographic matrix (similar to glOrtho).
678    */
679   template <typename T >
680   inline Matrix<T, 4,4>& setOrtho( Matrix<T,4,4>& result,
681                                          T left, T top, T right,
682                                          T bottom, T nr, T fr )
683   {
684      result.mData[1] = result.mData[2] = result.mData[3] =
685      result.mData[4] = result.mData[6] = result.mData[7] =
686      result.mData[8] = result.mData[9] = result.mData[11] = T(0);
687
688      T rl_inv = T(1) / (right - left);
689      T tb_inv = T(1) / (top - bottom);
690      T nf_inv = T(1) / (fr - nr);
691
692      result.mData[0] =  T(2) * rl_inv;
693      result.mData[5] =  T(2) * tb_inv;
694      result.mData[10] = -T(2) * nf_inv;
695
696      result.mData[12] = -(right + left) * rl_inv;
697      result.mData[13] = -(top + bottom) * tb_inv;
698      result.mData[14] = -(fr + nr) * nf_inv;
699      result.mData[15] = T(1);
700
701      return result;
702   }
703
704   /** Set a symmetric perspective projection matrix
705    * @param fovy
706    *   The field of view angle, in degrees, about the y-axis.
707    * @param aspect
708    *   The aspect ratio that determines the field of view about the x-axis.
709    *   The aspect ratio is the ratio of x (width) to y (height).
710    * @param zNear
711    *   The distance from the viewer to the near clipping plane (always positive).
712    * @param zFar
713    *   The distance from the viewer to the far clipping plane (always positive).
714    * @result Set matrix to perspective transform
715    */
716   template <typename T>
717   inline Matrix<T, 4,4>& setPerspective( Matrix<T, 4, 4>& result,
718                                                 T fovy, T aspect, T nr, T fr )
719   {
720      gmtlASSERT( nr > 0 && fr > nr && "invalid near and far values" );
721      T theta = Math::deg2Rad( fovy * T( 0.5 ) );
722      T tangentTheta = Math::tan( theta );
723
724      // tan(theta) = right / nr
725      // top = tan(theta) * nr
726      // right = tan(theta) * nr * aspect
727
728      T top = tangentTheta * nr;
729      T right = top * aspect; // aspect determines the fieald of view in the x-axis
730
731      // TODO: args need to match...
732      return setFrustum( result, -right, top, right, -top, nr, fr );
733   }
734
735
736   /*
737   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, unsigned SIZE, typename REP >
738   inline Matrix<DATA_TYPE, ROWS, COLS>& setTrans( Matrix<DATA_TYPE, ROWS, COLS>& result,
739                                                   const VecBase<DATA_TYPE, SIZE, REP>& trans )
740   {
741      const Vec<DATA_TYPE,SIZE,meta::DefaultVecTag> temp_vec(trans);
742      return setTrans(result,trans);
743   }
744   */
745
746   /** Set matrix translation from vec.
747    * @pre if making an n x n matrix, then for
748    *    - <b>vector is homogeneous:</b> SIZE of vector needs to equal number of Matrix ROWS - 1
749    *    - <b>vector has scale component:</b> SIZE of vector needs to equal number of Matrix ROWS
750    * if making an n x n+1 matrix, then for
751    *    - <b>vector is homogeneous:</b> SIZE of vector needs to equal number of Matrix ROWS
752    *    - <b>vector has scale component:</b> SIZE of vector needs to equal number of Matrix ROWS + 1
753    * @post if preconditions are not met, then function is undefined (will not compile)
754    */
755#ifdef GMTL_NO_METAPROG
756   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, unsigned SIZE >
757   inline Matrix<DATA_TYPE, ROWS, COLS>& setTrans( Matrix<DATA_TYPE, ROWS, COLS>& result,
758                                                   const VecBase<DATA_TYPE, SIZE>& trans )
759#else
760   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, unsigned SIZE, typename REP >
761   inline Matrix<DATA_TYPE, ROWS, COLS>& setTrans( Matrix<DATA_TYPE, ROWS, COLS>& result,
762                                                   const VecBase<DATA_TYPE, SIZE, REP>& trans )
763#endif
764   {
765      /* @todo make this a compile time assert... */
766      // if n x n   then (homogeneous case) vecsize == rows-1 or (scale component case) vecsize == rows
767      // if n x n+1 then (homogeneous case) vecsize == rows   or (scale component case) vecsize == rows+1
768      gmtlASSERT( ((ROWS == COLS && (SIZE == (ROWS-1) || SIZE == ROWS)) ||
769               (COLS == (ROWS+1) && (SIZE == ROWS || SIZE == (ROWS+1)))) &&
770              "preconditions not met for vector size in call to makeTrans.  Read your documentation." );
771
772      // homogeneous case...
773      if ((ROWS == COLS && SIZE == ROWS) /* Square matrix and vec so assume homogeneous vector. ex. 4x4 with vec 4 */
774          || (COLS == (ROWS+1) && SIZE == (ROWS+1)))  /* ex: 3x4 with vec4 */
775      {
776         DATA_TYPE homog_val = trans[SIZE-1];
777         for (unsigned x = 0; x < COLS - 1; ++x)
778            result( x, COLS - 1 ) = trans[x] / homog_val;
779      }
780
781      // non-homogeneous case...
782      else
783      {
784         for (unsigned x = 0; x < COLS - 1; ++x)
785            result( x, COLS - 1 ) = trans[x];
786      }
787      // track state, only override identity
788      switch (result.mState)
789      {
790      case Matrix<DATA_TYPE, ROWS, COLS>::ORTHOGONAL: result.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE; break;
791      case Matrix<DATA_TYPE, ROWS, COLS>::IDENTITY:   result.mState = Matrix<DATA_TYPE, ROWS, COLS>::TRANS; break;
792      }
793      return result;
794   }
795
796   /** Set the scale part of a matrix.
797    */
798   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, unsigned SIZE >
799   inline Matrix<DATA_TYPE, ROWS, COLS>& setScale( Matrix<DATA_TYPE, ROWS, COLS>& result, const Vec<DATA_TYPE, SIZE>& scale )
800   {
801      gmtlASSERT( ((SIZE == (ROWS-1) && SIZE == (COLS-1)) || (SIZE == (ROWS-1) && SIZE == COLS) || (SIZE == (COLS-1) && SIZE == ROWS)) && "the scale params must fit within the matrix, check your sizes." );
802      for (unsigned x = 0; x < SIZE; ++x)
803      {
804         result( x, x ) = scale[x];
805      }
806      // track state: affine matrix with non-uniform scale now.
807      result.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE;
808      result.mState |= Matrix<DATA_TYPE, ROWS, COLS>::NON_UNISCALE;
809      return result;
810   }
811
812   /** Sets the scale part of a matrix.
813    */
814   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
815   inline Matrix<DATA_TYPE, ROWS, COLS>& setScale( Matrix<DATA_TYPE, ROWS, COLS>& result, const DATA_TYPE scale )
816   {
817      for (unsigned x = 0; x < Math::Min( ROWS, COLS, Math::Max( ROWS, COLS ) - 1 ); ++x) // account for 2x4 or other weird sizes...
818      {
819         result( x, x ) = scale;
820      }
821      // track state: affine matrix with non-uniform scale now.
822      result.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE;
823      result.mState |= Matrix<DATA_TYPE, ROWS, COLS>::NON_UNISCALE;
824      return result;
825   }
826
827   /** Create a scale matrix.
828    *  @param scale  You'll typically pass in a Vec or a float here.
829    *  @seealso      setScale() for all possible argument types for this function.
830    */
831   template <typename MATRIX_TYPE, typename INPUT_TYPE>
832   inline MATRIX_TYPE makeScale( const INPUT_TYPE& scale,
833                               Type2Type< MATRIX_TYPE > t = Type2Type< MATRIX_TYPE >() )
834   {
835      gmtl::ignore_unused_variable_warning(t);
836      MATRIX_TYPE temporary;
837      return setScale( temporary, scale );
838   }
839
840   /** Set the rotation portion of a rotation matrix using an axis and an angle (in radians).
841    *  Only writes to the rotation matrix (3x3) defined by the rotation part of M
842    * @post this function only writes to 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
843    * @pre you must pass a normalized vector in for the axis, results are undefined if not.
844    */
845   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
846   inline Matrix<DATA_TYPE, ROWS, COLS>& setRot( Matrix<DATA_TYPE, ROWS, COLS>& result, const AxisAngle<DATA_TYPE>& axisAngle )
847   {
848      /* @todo set this a compile time assert... */
849      gmtlASSERT( ROWS >= 3 && COLS >= 3 && ROWS <= 4 && COLS <= 4 &&
850                     "this func is undefined for Matrix smaller than 3x3 or bigger than 4x4" );
851      gmtlASSERT( Math::isEqual( lengthSquared( axisAngle.getAxis() ), (DATA_TYPE)1.0, (DATA_TYPE)0.001 ) /* &&
852                     "you must pass in a normalized vector to setRot( mat, rad, vec )" */ );
853
854      // GGI: pg 466
855      DATA_TYPE s = Math::sin( axisAngle.getAngle() );
856      DATA_TYPE c = Math::cos( axisAngle.getAngle() );
857      DATA_TYPE t = DATA_TYPE( 1.0 ) - c;
858      DATA_TYPE x = axisAngle.getAxis()[0];
859      DATA_TYPE y = axisAngle.getAxis()[1];
860      DATA_TYPE z = axisAngle.getAxis()[2];
861
862      /* From: Introduction to robotic.  Craig.  Pg. 52 */
863      result( 0, 0 ) = (t*x*x)+c;     result( 0, 1 ) = (t*x*y)-(s*z); result( 0, 2 ) = (t*x*z)+(s*y);
864      result( 1, 0 ) = (t*x*y)+(s*z); result( 1, 1 ) = (t*y*y)+c;     result( 1, 2 ) = (t*y*z)-(s*x);
865      result( 2, 0 ) = (t*x*z)-(s*y); result( 2, 1 ) = (t*y*z)+(s*x); result( 2, 2 ) = (t*z*z)+c;
866
867      // track state
868      switch (result.mState)
869      {
870      case Matrix<DATA_TYPE, ROWS, COLS>::TRANS:    result.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE; break;
871      case Matrix<DATA_TYPE, ROWS, COLS>::IDENTITY: result.mState = Matrix<DATA_TYPE, ROWS, COLS>::ORTHOGONAL; break;
872      }
873      return result;
874   }
875
876   /** Set (only) the rotation part of a matrix using an EulerAngle (angles are in radians).
877    * @post this function only produces 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
878    * @see EulerAngle for angle ordering (usually ordered based on RotationOrder)
879    */
880   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, typename ROT_ORDER >
881   inline Matrix<DATA_TYPE, ROWS, COLS>& setRot( Matrix<DATA_TYPE, ROWS, COLS>& result, const EulerAngle<DATA_TYPE, ROT_ORDER>& euler )
882   {
883      // @todo set this a compile time assert...
884      gmtlASSERT( ROWS >= 3 && COLS >= 3 && ROWS <= 4 && COLS <= 4 && "this is undefined for Matrix smaller than 3x3 or bigger than 4x4" );
885
886      // this might be faster if put into the switch statement... (testme)
887      const int& order = ROT_ORDER::ID;
888      const DATA_TYPE xRot = (order == XYZ::ID) ? euler[0] : ((order == ZXY::ID) ? euler[1] : euler[2]);
889      const DATA_TYPE yRot = (order == XYZ::ID) ? euler[1] : ((order == ZXY::ID) ? euler[2] : euler[1]);
890      const DATA_TYPE zRot = (order == XYZ::ID) ? euler[2] : ((order == ZXY::ID) ? euler[0] : euler[0]);
891
892      DATA_TYPE sx = Math::sin( xRot );  DATA_TYPE cx = Math::cos( xRot );
893      DATA_TYPE sy = Math::sin( yRot );  DATA_TYPE cy = Math::cos( yRot );
894      DATA_TYPE sz = Math::sin( zRot );  DATA_TYPE cz = Math::cos( zRot );
895
896      // @todo metaprogram this!
897      switch (order)
898      {
899      case XYZ::ID:
900         // Derived by simply multiplying out the matrices by hand X * Y * Z
901         result( 0, 0 ) = cy*cz;             result( 0, 1 ) = -cy*sz;            result( 0, 2 ) = sy;
902         result( 1, 0 ) = sx*sy*cz + cx*sz;  result( 1, 1 ) = -sx*sy*sz + cx*cz; result( 1, 2 ) = -sx*cy;
903         result( 2, 0 ) = -cx*sy*cz + sx*sz; result( 2, 1 ) = cx*sy*sz + sx*cz;  result( 2, 2 ) = cx*cy;
904         break;
905      case ZYX::ID:
906         // Derived by simply multiplying out the matrices by hand Z * Y * Z
907         result( 0, 0 ) = cy*cz; result( 0, 1 ) = -cx*sz + sx*sy*cz; result( 0, 2 ) = sx*sz + cx*sy*cz;
908         result( 1, 0 ) = cy*sz; result( 1, 1 ) = cx*cz + sx*sy*sz;  result( 1, 2 ) = -sx*cz + cx*sy*sz;
909         result( 2, 0 ) = -sy;   result( 2, 1 ) = sx*cy;             result( 2, 2 ) = cx*cy;
910         break;
911      case ZXY::ID:
912         // Derived by simply multiplying out the matrices by hand Z * X * Y
913         result( 0, 0 ) = cy*cz - sx*sy*sz; result( 0, 1 ) = -cx*sz; result( 0, 2 ) = sy*cz + sx*cy*sz;
914         result( 1, 0 ) = cy*sz + sx*sy*cz; result( 1, 1 ) = cx*cz;  result( 1, 2 ) = sy*sz - sx*cy*cz;
915         result( 2, 0 ) = -cx*sy;           result( 2, 1 ) = sx;     result( 2, 2 ) = cx*cy;
916         break;
917      default:
918         gmtlASSERT( false && "unknown rotation order passed to setRot" );
919         break;
920      }
921
922      // track state
923      switch (result.mState)
924      {
925      case Matrix<DATA_TYPE, ROWS, COLS>::TRANS:    result.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE; break;
926      case Matrix<DATA_TYPE, ROWS, COLS>::IDENTITY: result.mState = Matrix<DATA_TYPE, ROWS, COLS>::ORTHOGONAL; break;
927      }
928      return result;
929   }
930
931   /** Convert an AxisAngle to a rotation matrix.
932    * @post this function only writes to 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
933    * @pre AxisAngle must be normalized (the axis part), results are undefined if not.
934    */
935   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
936   inline Matrix<DATA_TYPE, ROWS, COLS>& set( Matrix<DATA_TYPE, ROWS, COLS>& result, const AxisAngle<DATA_TYPE>& axisAngle )
937   {
938      gmtl::identity( result );
939      return setRot( result, axisAngle );
940   }
941
942   /** Convert an EulerAngle to a rotation matrix.
943    * @post this function only writes to 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
944    */
945   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS, typename ROT_ORDER >
946   inline Matrix<DATA_TYPE, ROWS, COLS>& set( Matrix<DATA_TYPE, ROWS, COLS>& result, const EulerAngle<DATA_TYPE, ROT_ORDER>& euler )
947   {
948      gmtl::identity( result );
949      return setRot( result, euler );
950   }
951
952   /**
953    * Extracts the Y axis rotation information from the matrix.
954    * @post Returned value is from -PI to PI, where 0 is none.
955    */
956   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
957   inline DATA_TYPE makeYRot( const Matrix<DATA_TYPE, ROWS, COLS>& mat )
958   {
959      const gmtl::Vec<DATA_TYPE, 3> forward_point(0.0f, 0.0f, -1.0f);   // -Z
960      const gmtl::Vec<DATA_TYPE, 3> origin_point(0.0f, 0.0f, 0.0f);
961      gmtl::Vec<DATA_TYPE, 3> end_point, start_point;
962
963      gmtl::xform(end_point, mat, forward_point);
964      gmtl::xform(start_point, mat, origin_point);
965      gmtl::Vec<DATA_TYPE, 3> direction_vector = end_point - start_point;
966
967      // Constrain the direction to XZ-plane only.
968      direction_vector[1] = 0.0f;                  // Eliminate Y value
969      gmtl::normalize(direction_vector);
970      DATA_TYPE y_rot = gmtl::Math::aCos(gmtl::dot(direction_vector,
971                                                   forward_point));
972
973      gmtl::Vec<DATA_TYPE, 3> which_side = gmtl::makeCross(forward_point,
974                                                           direction_vector);
975
976      // If direction vector to "right" (negative) side of forward
977      if ( which_side[1] < 0.0f )
978      {
979         y_rot = -y_rot;
980      }
981
982      return y_rot;
983   }
984
985   /**
986    * Extracts the X-axis rotation information from the matrix.
987    * @post Returned value is from -PI to PI, where 0 is no rotation.
988    */
989   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
990   inline DATA_TYPE makeXRot( const Matrix<DATA_TYPE, ROWS, COLS>& mat )
991   {
992      const gmtl::Vec<DATA_TYPE, 3> forward_point(0.0f, 0.0f, -1.0f);   // -Z
993      const gmtl::Vec<DATA_TYPE, 3> origin_point(0.0f, 0.0f, 0.0f);
994      gmtl::Vec<DATA_TYPE, 3> end_point, start_point;
995
996      gmtl::xform(end_point, mat, forward_point);
997      gmtl::xform(start_point, mat, origin_point);
998      gmtl::Vec<DATA_TYPE, 3> direction_vector = end_point - start_point;
999
1000      // Constrain the direction to YZ-plane only.
1001      direction_vector[0] = 0.0f;                  // Eliminate X value
1002      gmtl::normalize(direction_vector);
1003      DATA_TYPE x_rot = gmtl::Math::aCos(gmtl::dot(direction_vector,
1004                                                   forward_point));
1005
1006      gmtl::Vec<DATA_TYPE, 3> which_side = gmtl::makeCross(forward_point,
1007                                                           direction_vector);
1008
1009      // If direction vector to "bottom" (negative) side of forward
1010      if ( which_side[0] < 0.0f )
1011      {
1012         x_rot = -x_rot;
1013      }
1014
1015      return x_rot;
1016   }
1017
1018   /**
1019    * Extracts the Z-axis rotation information from the matrix.
1020    * @post Returned value is from -PI to PI, where 0 is no rotation.
1021    */
1022   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1023   inline DATA_TYPE makeZRot( const Matrix<DATA_TYPE, ROWS, COLS>& mat )
1024   {
1025      const gmtl::Vec<DATA_TYPE, 3> forward_point(1.0f, 0.0f, 0.0f);   // +x
1026      const gmtl::Vec<DATA_TYPE, 3> origin_point(0.0f, 0.0f, 0.0f);
1027      gmtl::Vec<DATA_TYPE, 3> end_point, start_point;
1028
1029      gmtl::xform(end_point, mat, forward_point);
1030      gmtl::xform(start_point, mat, origin_point);
1031      gmtl::Vec<DATA_TYPE, 3> direction_vector = end_point - start_point;
1032
1033      // Constrain the direction to XY-plane only.
1034      direction_vector[2] = 0.0f;                  // Eliminate Z value
1035      gmtl::normalize(direction_vector);
1036      DATA_TYPE z_rot = gmtl::Math::aCos(gmtl::dot(direction_vector,
1037                                                   forward_point));
1038
1039      gmtl::Vec<DATA_TYPE, 3> which_side = gmtl::makeCross(forward_point,
1040                                                           direction_vector);
1041
1042      // If direction vector to "right" (negative) side of forward
1043      if ( which_side[2] < 0.0f )
1044      {
1045         z_rot = -z_rot;
1046      }
1047
1048      return z_rot;
1049   }
1050
1051   /** create a rotation matrix that will rotate from SrcAxis to DestAxis.
1052    *  xSrcAxis, ySrcAxis, zSrcAxis is the base rotation to go from and defaults to xSrcAxis(1,0,0), ySrcAxis(0,1,0), zSrcAxis(0,0,1) if you only pass in 3 axes.
1053    *
1054    *  If the two coordinate frames are labeled: SRC and TARGET, the matrix produced is:  src_M_target
1055    *  this means that it will transform a point in TARGET to SRC but moves the base frame from SRC to TARGET.
1056    *
1057    *  @pre pass in 3 axes, and setDirCos will give you the rotation from MATRIX_IDENTITY to DestAxis
1058    *  @pre pass in 6 axes, and setDirCos will give you the rotation from your 3-axis rotation to your second 3-axis rotation
1059    *  @post this function only produces 3x3, 3x4, 4x3, and 4x4 matrices
1060    */
1061   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1062   inline Matrix<DATA_TYPE, ROWS, COLS>& setDirCos( Matrix<DATA_TYPE, ROWS, COLS>& result,
1063                                                     const Vec<DATA_TYPE, 3>& xDestAxis,
1064                                                     const Vec<DATA_TYPE, 3>& yDestAxis, const Vec<DATA_TYPE, 3>& zDestAxis,
1065                                                     const Vec<DATA_TYPE, 3>& xSrcAxis = Vec<DATA_TYPE, 3>(1,0,0),
1066                                                     const Vec<DATA_TYPE, 3>& ySrcAxis = Vec<DATA_TYPE, 3>(0,1,0),
1067                                                     const Vec<DATA_TYPE, 3>& zSrcAxis = Vec<DATA_TYPE, 3>(0,0,1) )
1068   {
1069      // @todo set this a compile time assert...
1070      gmtlASSERT( ROWS >= 3 && COLS >= 3 && ROWS <= 4 && COLS <= 4 && "this is undefined for Matrix smaller than 3x3 or bigger than 4x4" );
1071
1072      DATA_TYPE Xa, Xb, Xc;    // Direction cosines of the secondary x-axis
1073      DATA_TYPE Ya, Yb, Yc;    // Direction cosines of the secondary y-axis
1074      DATA_TYPE Za, Zb, Zc;    // Direction cosines of the secondary z-axis
1075
1076      Xa = dot(xDestAxis, xSrcAxis);  Xb = dot(xDestAxis, ySrcAxis);  Xc = dot(xDestAxis, zSrcAxis);
1077      Ya = dot(yDestAxis, xSrcAxis);  Yb = dot(yDestAxis, ySrcAxis);  Yc = dot(yDestAxis, zSrcAxis);
1078      Za = dot(zDestAxis, xSrcAxis);  Zb = dot(zDestAxis, ySrcAxis);  Zc = dot(zDestAxis, zSrcAxis);
1079
1080      // Set the matrix correctly
1081      result( 0, 0 ) = Xa; result( 0, 1 ) = Ya; result( 0, 2 ) = Za;
1082      result( 1, 0 ) = Xb; result( 1, 1 ) = Yb; result( 1, 2 ) = Zb;
1083      result( 2, 0 ) = Xc; result( 2, 1 ) = Yc; result( 2, 2 ) = Zc;
1084
1085      // track state
1086      switch (result.mState)
1087      {
1088      case Matrix<DATA_TYPE, ROWS, COLS>::TRANS:    result.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE; break;
1089      case Matrix<DATA_TYPE, ROWS, COLS>::IDENTITY: result.mState = Matrix<DATA_TYPE, ROWS, COLS>::ORTHOGONAL; break;
1090      }
1091
1092      return result;
1093   }
1094
1095   /** set the matrix given the raw coordinate axes.
1096    * @post this function only produces 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
1097    * @post these axes are copied direct to the 3x3 in the matrix
1098    */
1099   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1100   inline Matrix<DATA_TYPE, ROWS, COLS>& setAxes( Matrix<DATA_TYPE, ROWS, COLS>& result,
1101                                                  const Vec<DATA_TYPE, 3>& xAxis,
1102                                                  const Vec<DATA_TYPE, 3>& yAxis,
1103                                                  const Vec<DATA_TYPE, 3>& zAxis )
1104   {
1105      // @todo set this a compile time assert...
1106      gmtlASSERT( ROWS >= 3 && COLS >= 3 && ROWS <= 4 && COLS <= 4 && "this is undefined for Matrix smaller than 3x3 or bigger than 4x4" );
1107
1108      result( 0, 0 ) = xAxis[0];
1109      result( 1, 0 ) = xAxis[1];
1110      result( 2, 0 ) = xAxis[2];
1111
1112      result( 0, 1 ) = yAxis[0];
1113      result( 1, 1 ) = yAxis[1];
1114      result( 2, 1 ) = yAxis[2];
1115
1116      result( 0, 2 ) = zAxis[0];
1117      result( 1, 2 ) = zAxis[1];
1118      result( 2, 2 ) = zAxis[2];
1119
1120      // track state
1121      switch (result.mState)
1122      {
1123      case Matrix<DATA_TYPE, ROWS, COLS>::TRANS:    result.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE; break;
1124      case Matrix<DATA_TYPE, ROWS, COLS>::IDENTITY: result.mState = Matrix<DATA_TYPE, ROWS, COLS>::ORTHOGONAL; break;
1125      }
1126      return result;
1127   }
1128
1129   /** set the matrix given the raw coordinate axes.
1130    * @post this function only produces 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
1131    * @post these axes are copied direct to the 3x3 in the matrix
1132    */
1133   template< typename ROTATION_TYPE >
1134   inline ROTATION_TYPE makeAxes( const Vec<typename ROTATION_TYPE::DataType, 3>& xAxis,
1135                                  const Vec<typename ROTATION_TYPE::DataType, 3>& yAxis,
1136                                  const Vec<typename ROTATION_TYPE::DataType, 3>& zAxis,
1137                                  Type2Type< ROTATION_TYPE > t = Type2Type< ROTATION_TYPE >() )
1138   {
1139      gmtl::ignore_unused_variable_warning(t);
1140      ROTATION_TYPE temporary;
1141      return setAxes( temporary, xAxis, yAxis, zAxis );
1142   }
1143
1144   /** create a matrix transposed from the source.
1145    *  @post returns the transpose of m
1146    *  @see Quat
1147    */
1148   template < typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1149   inline Matrix<DATA_TYPE, ROWS, COLS> makeTranspose( const Matrix<DATA_TYPE, ROWS, COLS>& m )
1150   {
1151      Matrix<DATA_TYPE, ROWS, COLS> temporary( m );
1152      return transpose( temporary );
1153   }
1154
1155   /**
1156    * Creates a matrix that is the inverse of the given source matrix.
1157    *
1158    * @param src     the matrix to compute the inverse of
1159    *
1160    * @return  the inverse of source
1161    */
1162   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1163   inline Matrix<DATA_TYPE, ROWS, COLS> makeInvert(const Matrix<DATA_TYPE, ROWS, COLS>& src)
1164   {
1165      Matrix<DATA_TYPE, ROWS, COLS> result;
1166      return invert( result, src );
1167   }
1168
1169   /** Set the rotation portion of a matrix (3x3) from a rotation quaternion.
1170    * @pre only 3x3, 3x4, 4x3, or 4x4 matrices are allowed, function is undefined otherwise.
1171    */
1172   template <typename DATA_TYPE, unsigned ROWS, unsigned COLS>
1173   Matrix<DATA_TYPE, ROWS, COLS>& setRot( Matrix<DATA_TYPE, ROWS, COLS>& mat, const Quat<DATA_TYPE>& q )
1174   {
1175      gmtlASSERT( ((ROWS == 3 && COLS == 3) ||
1176               (ROWS == 3 && COLS == 4) ||
1177               (ROWS == 4 && COLS == 3) ||
1178               (ROWS == 4 && COLS == 4)) &&
1179               "pre conditions not met on set( mat, quat ) which only sets a quaternion to the rotation part of a 3x3, 3x4, 4x3, or 4x4 matrix." );
1180
1181      // From Watt & Watt
1182      DATA_TYPE wx, wy, wz, xx, yy, yz, xy, xz, zz, xs, ys, zs;
1183
1184      xs = q[Xelt] + q[Xelt]; ys = q[Yelt] + q[Yelt]; zs = q[Zelt] + q[Zelt];
1185      xx = q[Xelt] * xs;      xy = q[Xelt] * ys;      xz = q[Xelt] * zs;
1186      yy = q[Yelt] * ys;      yz = q[Yelt] * zs;      zz = q[Zelt] * zs;
1187      wx = q[Welt] * xs;      wy = q[Welt] * ys;      wz = q[Welt] * zs;
1188
1189      mat( 0, 0 ) = DATA_TYPE(1.0) - (yy + zz);
1190      mat( 1, 0 ) = xy + wz;
1191      mat( 2, 0 ) = xz - wy;
1192
1193      mat( 0, 1 ) = xy - wz;
1194      mat( 1, 1 ) = DATA_TYPE(1.0) - (xx + zz);
1195      mat( 2, 1 ) = yz + wx;
1196
1197      mat( 0, 2 ) = xz + wy;
1198      mat( 1, 2 ) = yz - wx;
1199      mat( 2, 2 ) = DATA_TYPE(1.0) - (xx + yy);
1200
1201      // track state
1202      switch (mat.mState)
1203      {
1204      case Matrix<DATA_TYPE, ROWS, COLS>::TRANS:    mat.mState = Matrix<DATA_TYPE, ROWS, COLS>::AFFINE; break;
1205      case Matrix<DATA_TYPE, ROWS, COLS>::IDENTITY: mat.mState = Matrix<DATA_TYPE, ROWS, COLS>::ORTHOGONAL; break;
1206      }
1207      return mat;
1208   }
1209
1210   /** Convert a Coord to a Matrix
1211    * Note: It is set directly, but this is equivalent to T*R where T is the translation matrix and R is the rotation matrix.
1212    * @see Coord
1213    * @see Matrix
1214    */
1215   template <typename DATATYPE, typename POS_TYPE, typename ROT_TYPE, unsigned MATCOLS, unsigned MATROWS >
1216   inline Matrix<DATATYPE, MATROWS, MATCOLS>& set( Matrix<DATATYPE, MATROWS, MATCOLS>& mat,
1217                                                   const Coord<POS_TYPE, ROT_TYPE>& coord )
1218   {
1219      // set to ident first...
1220      gmtl::identity( mat );
1221
1222      // set translation
1223      // @todo metaprogram this out for 3x3 and 2x2 matrices
1224      if (MATCOLS == 4)
1225      {
1226         setTrans( mat, coord.getPos() );// filtered (only sets the trans part).
1227      }
1228      setRot( mat, coord.getRot() ); // filtered (only sets the rot part).
1229      return mat;
1230   }
1231
1232   /** Convert a Quat to a rotation Matrix.
1233    *  @pre only 3x3, 3x4, 4x3, or 4x4 matrices are allowed, function is undefined otherwise.
1234    *  @post Matrix is entirely overwritten.
1235    *  @todo Implement using setRot
1236    */
1237   template <typename DATA_TYPE, unsigned ROWS, unsigned COLS>
1238   Matrix<DATA_TYPE, ROWS, COLS>& set( Matrix<DATA_TYPE, ROWS, COLS>& mat, const Quat<DATA_TYPE>& q )
1239   {
1240      if (ROWS == 4)
1241      {
1242         mat( 3, 0 ) = DATA_TYPE(0.0);
1243         mat( 3, 1 ) = DATA_TYPE(0.0);
1244         mat( 3, 2 ) = DATA_TYPE(0.0);
1245      }
1246
1247      if (COLS == 4)
1248      {
1249         mat( 0, 3 ) = DATA_TYPE(0.0);
1250         mat( 1, 3 ) = DATA_TYPE(0.0);
1251         mat( 2, 3 ) = DATA_TYPE(0.0);
1252      }
1253
1254      if (ROWS == 4 && COLS == 4)
1255         mat( 3, 3 ) = DATA_TYPE(1.0);
1256
1257      // track state
1258      mat.mState = Matrix<DATA_TYPE, ROWS, COLS>::IDENTITY;
1259
1260      return setRot( mat, q );
1261   }
1262
1263   /** @} */
1264
1265   /** @ingroup Generate
1266    *  @name Coord Generators
1267    *  @{
1268    */
1269
1270   /** convert Matrix to Coord */
1271   template <typename DATATYPE, typename POS_TYPE, typename ROT_TYPE, unsigned MATCOLS, unsigned MATROWS >
1272   inline Coord<POS_TYPE, ROT_TYPE>& set( Coord<POS_TYPE, ROT_TYPE>& eulercoord, const Matrix<DATATYPE, MATROWS, MATCOLS>& mat )
1273   {
1274      gmtl::setTrans( eulercoord.pos(), mat );
1275      gmtl::set( eulercoord.rot(), mat );
1276      return eulercoord;
1277   }
1278
1279   /** Redundant duplication of the set(coord,mat) function, this is provided only for template compatibility.
1280    *  unless you're writing template functions, you should use set(coord,mat) for clarity.
1281    */
1282   template <typename DATATYPE, typename POS_TYPE, typename ROT_TYPE, unsigned MATCOLS, unsigned MATROWS >
1283   inline Coord<POS_TYPE, ROT_TYPE>& setRot( Coord<POS_TYPE, ROT_TYPE>& result, const Matrix<DATATYPE, MATROWS, MATCOLS>& mat )
1284   {
1285      return gmtl::set( result, mat );
1286   }
1287
1288   /** @} */
1289
1290   /** @ingroup Generate
1291     *  @name Generic Generators (any type)
1292     *  @{
1293     */
1294
1295   /** Construct an object from another object of a different type.
1296    *  This allows us to automatically convert from any type to any
1297    *  other type.
1298    *  @pre must have a set() function defined that converts between the
1299    *       two types.
1300    *  @param src    the object to use for creation
1301    *  @return a new object with values based on the src variable
1302    *  @see OpenSGGenerate.h for an example
1303    */
1304   template <typename TARGET_TYPE, typename SOURCE_TYPE>
1305   inline TARGET_TYPE make( const SOURCE_TYPE& src,
1306                           Type2Type< TARGET_TYPE > t = Type2Type< TARGET_TYPE >() )
1307   {
1308      gmtl::ignore_unused_variable_warning(t);
1309      TARGET_TYPE target;
1310      return gmtl::set( target, src );
1311   }
1312
1313   /** Create a rotation datatype from another rotation datatype.
1314    * @post converts the source rotation to a to another type (usually Matrix, Quat, Euler, AxisAngle),
1315    * @post returns a temporary object.
1316    */
1317   template <typename ROTATION_TYPE, typename SOURCE_TYPE >
1318   inline ROTATION_TYPE makeRot( const SOURCE_TYPE& coord,
1319                                Type2Type< ROTATION_TYPE > t = Type2Type< ROTATION_TYPE >() )
1320   {
1321      gmtl::ignore_unused_variable_warning(t);
1322      ROTATION_TYPE temporary;
1323      return gmtl::set( temporary, coord );
1324   }
1325
1326   /** Create a rotation matrix or quaternion (or any other rotation data type) using direction cosines.
1327    *
1328    *  If the two coordinate frames are labeled: SRC and TARGET, the matrix produced is:  src_M_target
1329    *  this means that it will transform a point in TARGET to SRC but moves the base frame from SRC to TARGET.
1330    *
1331    * @param DestAxis required to specify
1332    * @param SrcAxis optional to specify
1333    * @pre specify 1 axis (3 vectors), or 2 axes (6 vectors).
1334    * @post Creates a rotation from SrcAxis to DestAxis
1335    * @post this function only produces 3x3, 3x4, 4x3, and 4x4 matrices, and is undefined otherwise
1336    */
1337   template< typename ROTATION_TYPE >
1338   inline ROTATION_TYPE makeDirCos( const Vec<typename ROTATION_TYPE::DataType, 3>& xDestAxis,
1339                                  const Vec<typename ROTATION_TYPE::DataType, 3>& yDestAxis,
1340                                  const Vec<typename ROTATION_TYPE::DataType, 3>& zDestAxis,
1341                                  const Vec<typename ROTATION_TYPE::DataType, 3>& xSrcAxis = Vec<typename ROTATION_TYPE::DataType, 3>(1,0,0),
1342                                  const Vec<typename ROTATION_TYPE::DataType, 3>& ySrcAxis = Vec<typename ROTATION_TYPE::DataType, 3>(0,1,0),
1343                                  const Vec<typename ROTATION_TYPE::DataType, 3>& zSrcAxis = Vec<typename ROTATION_TYPE::DataType, 3>(0,0,1),
1344                               Type2Type< ROTATION_TYPE > t = Type2Type< ROTATION_TYPE >() )
1345   {
1346      gmtl::ignore_unused_variable_warning(t);
1347      ROTATION_TYPE temporary;
1348      return setDirCos( temporary, xDestAxis, yDestAxis, zDestAxis, xSrcAxis, ySrcAxis, zSrcAxis );
1349   }
1350
1351   /**
1352    * Make a translation datatype from another translation datatype.
1353    * Typically this is from Matrix to Vec or Vec to Matrix.
1354    * This function reads only translation information from the src datatype.
1355    *
1356    * @param arg  the matrix to extract the translation from
1357    *
1358    * @pre if making an n x n matrix, then for
1359    *    - <b>vector is homogeneous:</b> SIZE of vector needs to equal number of Matrix ROWS - 1
1360    *    - <b>vector has scale component:</b> SIZE of vector needs to equal number of Matrix ROWS
1361    * <br>if making an n x n+1 matrix, then for
1362    *    - <b>vector is homogeneous:</b> SIZE of vector needs to equal number of Matrix ROWS
1363    *    - <b>vector has scale component:</b> SIZE of vector needs to equal number of Matrix ROWS + 1
1364    * @post if preconditions are not met, then function is undefined (will not compile)
1365    */
1366   template<typename TRANS_TYPE, typename SRC_TYPE >
1367   inline TRANS_TYPE makeTrans( const SRC_TYPE& arg,
1368                             Type2Type< TRANS_TYPE > t = Type2Type< TRANS_TYPE >())
1369   {
1370      gmtl::ignore_unused_variable_warning(t);
1371      TRANS_TYPE temporary;
1372      return setTrans( temporary, arg );
1373   }
1374
1375   /** Create a rotation datatype that will xform first vector to the second.
1376    *  @pre  each vec needs to be normalized.
1377    *  @post This function returns a temporary object.
1378    */
1379   template< typename ROTATION_TYPE >
1380   inline ROTATION_TYPE makeRot( const Vec<typename ROTATION_TYPE::DataType, 3>& from,
1381                                 const Vec<typename ROTATION_TYPE::DataType, 3>& to )
1382   {
1383      ROTATION_TYPE temporary;
1384      return setRot( temporary, from, to );
1385   }
1386
1387   /** set a rotation datatype that will xform first vector to the second.
1388    *  @pre  each vec needs to be normalized.
1389    *  @post generate rotation datatype that is the rotation between the vectors.
1390    *  @note: only sets the rotation component of result, if result is a matrix, only sets the 3x3.
1391    */
1392   template <typename DEST_TYPE, typename DATA_TYPE>
1393   inline DEST_TYPE& setRot( DEST_TYPE& result, const Vec<DATA_TYPE, 3>& from, const Vec<DATA_TYPE, 3>& to )
1394   {
1395      // @todo should assert that DEST_TYPE::DataType == DATA_TYPE
1396      const DATA_TYPE epsilon = (DATA_TYPE)0.00001;
1397
1398      gmtlASSERT( gmtl::Math::isEqual( gmtl::length( from ), (DATA_TYPE)1.0, epsilon ) &&
1399                  gmtl::Math::isEqual( gmtl::length( to ), (DATA_TYPE)1.0, epsilon ) /* &&
1400                  "input params not normalized" */);
1401
1402      DATA_TYPE cosangle = dot( from, to );
1403
1404      // if cosangle is close to 1, so the vectors are close to being coincident
1405      // Need to generate an angle of zero with any vector we like
1406      // We'll choose identity (no rotation)
1407      if ( Math::isEqual( cosangle, (DATA_TYPE)1.0, epsilon ) )
1408      {
1409         return result = DEST_TYPE();
1410      }
1411
1412      // vectors are close to being opposite, so rotate one a little...
1413      else if ( Math::isEqual( cosangle, (DATA_TYPE)-1.0, epsilon ) )
1414      {
1415         Vec<DATA_TYPE, 3> to_rot( to[0] + (DATA_TYPE)0.3, to[1] - (DATA_TYPE)0.15, to[2] - (DATA_TYPE)0.15 ), axis;
1416         normalize( cross( axis, from, to_rot ) ); // setRot requires normalized vec
1417         DATA_TYPE angle = Math::aCos( cosangle );
1418         return setRot( result, gmtl::AxisAngle<DATA_TYPE>( angle, axis ) );
1419      }
1420
1421      // This is the usual situation - take a cross-product of vec1 and vec2
1422      // and that is the axis around which to rotate.
1423      else
1424      {
1425         Vec<DATA_TYPE, 3> axis;
1426         normalize( cross( axis, from, to ) ); // setRot requires normalized vec
1427         DATA_TYPE angle = Math::aCos( cosangle );
1428         return setRot( result, gmtl::AxisAngle<DATA_TYPE>( angle, axis ) );
1429      }
1430   }
1431
1432   /** @} */
1433
1434   /** Accesses a particular row in the matrix by copying the values in the row
1435    * into the given vector.
1436    *
1437    * @param dest       the vector in which the values of the row will be placed
1438    * @param src        the matrix being accessed
1439    * @param row        the row of the matrix to access
1440    */
1441   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1442   void setRow(Vec<DATA_TYPE, COLS>& dest, const Matrix<DATA_TYPE, ROWS, COLS>& src, unsigned row)
1443   {
1444      for (unsigned i=0; i<COLS; ++i)
1445      {
1446         dest[i] = src[row][i];
1447      }
1448   }
1449
1450   /** Accesses a particular row in the matrix by creating a new vector
1451    * containing the values in the given matrix.
1452    *
1453    * @param src        the matrix being accessed
1454    * @param row        the row of the matrix to access
1455    *
1456    * @return  a vector containing the values in the requested row
1457    */
1458   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1459   Vec<DATA_TYPE, COLS> makeRow(const Matrix<DATA_TYPE, ROWS, COLS>& src, unsigned row)
1460   {
1461      Vec<DATA_TYPE, COLS> result;
1462      setRow(result, src, row);
1463      return result;
1464   }
1465
1466   /** Accesses a particular column in the matrix by copying the values in the
1467    * column into the given vector.
1468    *
1469    * @param dest       the vector in which the values of the column will be placed
1470    * @param src        the matrix being accessed
1471    * @param col        the column of the matrix to access
1472    */
1473   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1474   void setColumn(Vec<DATA_TYPE, ROWS>& dest, const Matrix<DATA_TYPE, ROWS, COLS>& src, unsigned col)
1475   {
1476      for (unsigned i=0; i<ROWS; ++i)
1477      {
1478         dest[i] = src[i][col];
1479      }
1480   }
1481
1482   /** Accesses a particular column in the matrix by creating a new vector
1483    * containing the values in the given matrix.
1484    *
1485    * @param src        the matrix being accessed
1486    * @param col        the column of the matrix to access
1487    *
1488    * @return  a vector containing the values in the requested column
1489    */
1490   template< typename DATA_TYPE, unsigned ROWS, unsigned COLS >
1491   Vec<DATA_TYPE, ROWS> makeColumn(const Matrix<DATA_TYPE, ROWS, COLS>& src, unsigned col)
1492   {
1493      Vec<DATA_TYPE, ROWS> result;
1494      setColumn(result, src, col);
1495      return result;
1496   }
1497
1498} // end gmtl namespace.
1499
1500#endif
Note: See TracBrowser for help on using the repository browser.