source: projectionDesigner/trunk/projdesigner/include/gmtl/Matrix.h @ 54

Last change on this file since 54 was 4, checked in by Torben Dannhauer, 15 years ago
File size: 20.6 KB
Line 
1/************************************************************** ggt-head beg
2 *
3 * GGT: Generic Graphics Toolkit
4 *
5 * Original Authors:
6 *   Allen Bierbaum
7 *
8 * -----------------------------------------------------------------
9 * File:          Matrix.h,v
10 * Date modified: 2004/11/22 15:04:05
11 * Version:       1.39
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_MATRIX_H_
36#define _GMTL_MATRIX_H_
37
38#include <gmtl/Defines.h>
39#include <gmtl/Math.h>
40#include <gmtl/Util/Assert.h>
41#include <gmtl/Util/StaticAssert.h>
42
43namespace gmtl
44{
45
46/**
47 * State tracked NxM dimensional Matrix (ordered in memory by Column)
48 *
49 * <b>Memory mapping:</b>
50 *
51 * gmtl::Matrix stores its elements in column major order.
52 * That is, it stores each column end-to-end in memory.
53 *
54 * Typically, for 3D transform matrices, the 3x3 rotation is
55 * in the first three columns, while the translation is in the last column.
56 *
57 * This memory alignment is chosen for compatibility with the OpenGL graphics
58 * API and others, which take matrices in this specific column major ordering
59 * described above.
60 *
61 * See the interfaces for operator[r][c] and operator(r,c) for how to iterate
62 * over columns and rows for a GMTL Matrix.
63 *
64 * <b>NOTES on Matrix memory layout and [][] accessors:</b>
65 * <ul>
66 * <li> gmtl Matrix memory is "column major" ordered, where columns are end
67 *      to end in memory, while a C/C++ Matrix accessed the same way
68 *      (using operator[][]) as a gmtl Matrix is "row major" ordered.
69 *
70 * <li> As a result, a gmtl matrix stores elements in memory transposed from
71 *      the equivelent matrix defined using an array in the C/C++
72 *      language, assuming they are accessed the same way (see example).
73 * <ul>
74 *    <li> Illustrative Example:                                           <br>
75 *         Given two flavors of matrix, C/C++, and gmtl:                   <br>
76 *             float cmat[n][m];   and    gmtl::Matrix<float, n, m> mat;   <br>
77 *         Writing values into each, while accessing them the same:        <br>
78 *             cmat[row][col] = mat[row][col] = some_values[x];            <br>
79 *         Then reading values from the matrix array:                      <br>
80 *             ((float*)cmat)   and    mat.getData()                       <br>
81 *         <i>Will yield pointers to memory containing matrices that are the transpose of each other.</i>
82 * </ul>
83 * <li> In practice, the differences between GMTL and C/C++ defined matrices
84 *      all depends how you iterate over your matrix.                                              <br>
85 *      If gmtl is accessed mat[row][col] and C/C++ is accessed mat[col][row], then
86 *      memory-wise, these two will yield the same memory mapping (column major as described above),
87 *      thus, are equivelent and can both be used interchangably in many popular graphics APIs
88 *      such as OpenGL, DirectX, and others.
89 *
90 * <li> In C/C++ access of a matrix via mat[row][col] yields this memory mapping after using ((float*)mat) to return it:<br>
91 *  <pre>
92 *    (0,0) (0,1) (0,2) (0,3)   <=== Contiguous memory arranged by row
93 *    (1,0) (1,1) (1,2) (1,3)   <=== Contiguous
94 *    (2,0) (2,1) (2,2) (2,3)   <=== Contiguous
95 *    (3,0) (3,1) (3,2) (3,3)   <=== Contiguous
96 *
97 *  or linearly if you prefer:
98 *    (0,0) (0,1) (0,2) (0,3) (1,0) (1,1) (1,2) (1,3) (2,0) (2,1) (2,2) (2,3) (3,0) (3,1) (3,2) (3,3)
99 *  </pre>
100 *
101 * <li> In gmtl, access of a matrix via mat[row][col] yields this memory mapping after using getData() to return it:<br>
102 *  <pre>
103 *    (0,0) (0,1) (0,2) (0,3)
104 *    (1,0) (1,1) (1,2) (1,3)
105 *    (2,0) (2,1) (2,2) (2,3)
106 *    (3,0) (3,1) (3,2) (3,3)
107 *      ^     ^     ^     ^
108 *    --1-----2-----3-----4---- Contiguous memory arranged by column
109 *
110 *  or linearly if you prefer:
111 *    (0,0) (1,0) (2,0) (3,0) (0,1) (1,1) (2,1) (3,1) (0,2) (1,2) (2,2) (3,2) (0,3) (1,3) (2,3) (3,3)
112 *  </pre>
113 * </ul>
114 *
115 * <b>State Tracking:</b>
116 *
117 * The idea of a state-tracked matrix is that if we track the information
118 * as it is stored into the matrix, then other operations could make more optimal
119 * descisions based on the known state.  A good example is in matrix invertion,
120 * a reletively costly operation for matrices.  However, if we know the matrix state
121 * is (i.e.) ORTHOGONAL, then inversion becomes a simple transpose operation.
122 * There are also optimizations with multiplication, as well as other.
123 *
124 * One side effect of this state tracking is that EVERY MATRIC FUNCTION NEEDS TO
125 * TRACK STATE.  This means that anyone writing custom methods, or extentions to
126 * gmtl, will need to pay close attention to matrix state.
127 *
128 * To facilitate state tracking in extensions, we've provided the function
129 * gmtl::combineMatrixStates() to help in determining state based on two
130 * combined matrices.
131 * @see Matrix44f
132 * @see Matrix44d
133 * @ingroup Types
134 */
135template <typename DATA_TYPE, unsigned ROWS, unsigned COLS>
136class Matrix
137{
138public:
139   // This is a hack to work around a bug with GCC 3.3 on Mac OS X where
140   // boost::is_polymorphic returns a false positive.  The details can be
141   // found in the Boost.Python FAQ:
142   //    http://www.boost.org/libs/python/doc/v2/faq.html#macosx
143#if defined(__MACH__) && defined(__APPLE_CC__) && defined(__GNUC__) && \
144    __GNUC__ == 3 && __GNUC_MINOR__ == 3
145   bool dummy_;
146#endif
147
148   /** use this to declare single value types of the same type as this matrix.
149    */
150   typedef DATA_TYPE DataType;
151   enum Params
152   {
153      Rows = ROWS, Cols = COLS
154   };
155
156   /** Helper class for Matrix op[].
157   * This class encapsulates the row that the user is accessing
158   * and implements a new op[] that passes the column to use
159   */
160   class RowAccessor
161   {
162   public:
163      typedef DATA_TYPE DataType;
164
165      RowAccessor(Matrix<DATA_TYPE,ROWS,COLS>* mat, const unsigned row)
166         : mMat(mat), mRow(row)
167      {
168         gmtlASSERT(row < ROWS);
169         gmtlASSERT(NULL != mat);
170      }
171
172      DATA_TYPE& operator[](const unsigned column)
173      {
174         gmtlASSERT(column < COLS);
175         return (*mMat)(mRow,column);
176      }
177
178      Matrix<DATA_TYPE,ROWS,COLS>*  mMat;
179      unsigned                      mRow;    /** The row being accessed */
180   };
181
182   /** Helper class for Matrix op[] const.
183   * This class encapsulates the row that the user is accessing
184   * and implements a new op[] that passes the column to use
185   */
186   class ConstRowAccessor
187   {
188   public:
189      typedef DATA_TYPE DataType;
190
191      ConstRowAccessor( const Matrix<DATA_TYPE,ROWS,COLS>* mat,
192                        const unsigned row )
193         : mMat( mat ), mRow( row )
194      {
195         gmtlASSERT( row < ROWS );
196         gmtlASSERT( NULL != mat );
197      }
198
199      const DATA_TYPE& operator[](const unsigned column) const
200      {
201         gmtlASSERT(column < COLS);
202         return (*mMat)(mRow,column);
203      }
204
205      const Matrix<DATA_TYPE,ROWS,COLS>*  mMat;
206      unsigned                      mRow;    /** The row being accessed */
207   };
208
209   /** describes the xforms that this matrix has been through. */
210   enum XformState
211   {
212      // identity matrix.
213      IDENTITY = 1,
214
215      // only translation, can simply negate that column
216      TRANS = 2,
217
218      // able to tranpose to get the inverse.  only rotation component is set
219      ORTHOGONAL = 4,
220
221      // orthogonal, and normalized axes.
222      //ORTHONORMAL = 8,
223
224      // leaves the homogeneous coordinate unchanged - that is, in which the last column is (0,0,0,s).
225      // can include rotation, uniform scale, and translation, but no shearing or nonuniform scaling
226      // This can optionally be combined with the NON_UNISCALE state to indicate there is also non-uniform scale
227      AFFINE = 16,
228
229      // AFFINE matrix with non-uniform scale, a matrix cannot
230      // have this state without also having AFFINE (must be or'd together).
231      NON_UNISCALE = 32,
232
233      // fully set matrix containing more information than the above, or state is unknown,
234      // or unclassifiable in terms of the above.
235      FULL = 64,
236
237      // error bit
238      XFORM_ERROR = 128
239   };
240
241   /** Default Constructor (Identity constructor) */
242   Matrix()
243   {
244      /** @todo mp */
245      for (unsigned int r = 0; r < ROWS; ++r)
246      {
247         for (unsigned int c = 0; c < COLS; ++c)
248         {   this->operator()( r, c ) = (DATA_TYPE)0.0; }
249      }
250
251      /** @todo mp */
252      for (unsigned int x = 0; x < Math::Min( COLS, ROWS ); ++x)
253      {  this->operator()( x, x ) = (DATA_TYPE)1.0; }
254
255      /** @todo Set initial state to IDENTITY and test other stuff */
256      mState = IDENTITY;
257   }
258
259   /** copy constructor */
260   Matrix( const Matrix<DATA_TYPE, ROWS, COLS>& matrix )
261   {
262      this->set( matrix.getData() );
263      mState = matrix.mState;
264   }
265
266   /** element wise setter for 2x2.
267    * @note variable names specify the row,column number to put the data into
268    *  @todo needs mp!!
269    */
270   void set( DATA_TYPE v00, DATA_TYPE v01,
271             DATA_TYPE v10, DATA_TYPE v11 )
272   {
273      GMTL_STATIC_ASSERT( (ROWS == 2 && COLS == 2), Set_called_when_Matrix_not_of_size_2_2 );
274      mData[0] = v00;
275      mData[1] = v10;
276      mData[2] = v01;
277      mData[3] = v11;
278      mState = FULL;
279   }
280
281   /** element wise setter for 2x3.
282    * @todo needs mp!!
283    */
284   void set( DATA_TYPE v00, DATA_TYPE v01, DATA_TYPE v02,
285             DATA_TYPE v10, DATA_TYPE v11, DATA_TYPE v12  )
286   {
287      GMTL_STATIC_ASSERT( (ROWS == 2 && COLS == 3), Set_called_when_Matrix_not_of_size_2_3 );
288      mData[0] = v00;
289      mData[1] = v10;
290      mData[2] = v01;
291      mData[3] = v11;
292      mData[4] = v02;
293      mData[5] = v12;
294      mState = FULL;
295   }
296
297   /** element wise setter for 3x3.
298    * @todo needs mp!!
299    */
300   void set( DATA_TYPE v00, DATA_TYPE v01, DATA_TYPE v02,
301             DATA_TYPE v10, DATA_TYPE v11, DATA_TYPE v12,
302             DATA_TYPE v20, DATA_TYPE v21, DATA_TYPE v22)
303   {
304      GMTL_STATIC_ASSERT( (ROWS == 3 && COLS == 3), Set_called_when_Matrix_not_of_size_3_3 );
305      mData[0] = v00;
306      mData[1] = v10;
307      mData[2] = v20;
308
309      mData[3] = v01;
310      mData[4] = v11;
311      mData[5] = v21;
312
313      mData[6] = v02;
314      mData[7] = v12;
315      mData[8] = v22;
316      mState = FULL;
317   }
318
319   /** element wise setter for 3x4.
320    * @todo needs mp!!  currently no way for a 4x3, ....
321    */
322   void set( DATA_TYPE v00, DATA_TYPE v01, DATA_TYPE v02, DATA_TYPE v03,
323             DATA_TYPE v10, DATA_TYPE v11, DATA_TYPE v12, DATA_TYPE v13,
324             DATA_TYPE v20, DATA_TYPE v21, DATA_TYPE v22, DATA_TYPE v23)
325   {
326      GMTL_STATIC_ASSERT( (ROWS == 3 && COLS == 4), Set_called_when_Matrix_not_of_size_3_4 );
327      mData[0] = v00;
328      mData[1] = v10;
329      mData[2] = v20;
330      mData[3] = v01;
331      mData[4] = v11;
332      mData[5] = v21;
333      mData[6] = v02;
334      mData[7] = v12;
335      mData[8] = v22;
336
337      // right row
338      mData[9]  = v03;
339      mData[10] = v13;
340      mData[11] = v23;
341      mState = FULL;
342   }
343
344   /** element wise setter for 4x4.
345    * @todo needs mp!!  currently no way for a 4x3, ....
346    */
347   void set( DATA_TYPE v00, DATA_TYPE v01, DATA_TYPE v02, DATA_TYPE v03,
348             DATA_TYPE v10, DATA_TYPE v11, DATA_TYPE v12, DATA_TYPE v13,
349             DATA_TYPE v20, DATA_TYPE v21, DATA_TYPE v22, DATA_TYPE v23,
350             DATA_TYPE v30, DATA_TYPE v31, DATA_TYPE v32, DATA_TYPE v33 )
351   {
352      GMTL_STATIC_ASSERT( (ROWS == 4 && COLS == 4), Set_called_when_Matrix_not_of_size_4_4 );
353      mData[0]  = v00;
354      mData[1]  = v10;
355      mData[2]  = v20;
356      mData[4]  = v01;
357      mData[5]  = v11;
358      mData[6]  = v21;
359      mData[8]  = v02;
360      mData[9]  = v12;
361      mData[10] = v22;
362
363      // right row
364      mData[12] = v03;
365      mData[13] = v13;
366      mData[14] = v23;
367
368      // bottom row
369      mData[3]  = v30;
370      mData[7]  = v31;
371      mData[11] = v32;
372      mData[15] = v33;
373      mState = FULL;
374   }
375
376   /** comma operator
377    *  @todo implement this!
378    */
379   //void operator,()( DATA_TYPE b ) {}
380
381   /** set the matrix to the given data.
382    *  This function is useful to copy matrix data from another math library.
383    *
384    * <h3> "Example (to a matrix using an external math library):" </h3>
385    * \code
386    *    pfMatrix other_matrix;
387    *    other_matrix.setRot( 90, 1, 0, 0 );
388    *
389    *    gmtl::Matrix44f mat;
390    *    mat.set( other_matrix.getFloatPtr() );
391    * \endcode
392    *
393    *  WARNING: this isn't really safe, size and datatype are not enforced by
394    *           the compiler.
395    * @pre data is in the native format of the gmtl::Matrix class, if not,
396    *      then you might be able to use the setTranspose function.
397    * @pre i.e. in a 4x4 data[0-3] is the 1st column, data[4-7] is 2nd, etc...
398    */
399   void set( const DATA_TYPE* data )
400   {
401      /** @todo mp */
402      for (unsigned int x = 0; x < ROWS * COLS; ++x)
403         mData[x] = data[x];
404      mState = FULL;
405   }
406
407   /** set the matrix to the transpose of the given data.
408    * normally set() takes raw matrix data in column by column order,
409    * this function allows you to pass in row by row data.
410    *
411    * Normally you'll use this function if you want to use a float array
412    * to init the matrix (see code example).
413    *
414    * <h3> "Example (to set a [15 -4 20] translation using float array):" </h3>
415    * \code
416    *    float data[] = { 1, 0, 0, 15,
417    *                     0, 1, 0, -4,
418    *                     0, 0, 1, 20,
419    *                     0, 0, 0, 1   };
420    *    gmtl::Matrix44f mat;
421    *    mat.setTranspose( data );
422    * \endcode
423    *
424    *  WARNING: this isn't really safe, size and datatype are not enforced by
425    *           the compiler.
426    * @pre ptr is in the transpose of the native format of the Matrix class
427    * @pre i.e. in a 4x4 data[0-3] is the 1st row, data[4-7] is 2nd, etc...
428    */
429   void setTranspose( const DATA_TYPE* data )
430   {
431      /** @todo metaprog */
432      for (unsigned int r = 0; r < ROWS; ++r)
433      for (unsigned int c = 0; c < COLS; ++c)
434         this->operator()( r, c ) = data[(r * COLS) + c];
435      mState = FULL;
436   }
437
438   /** access [row, col] in the matrix
439    *  WARNING: If you set data in the matrix (using this interface),
440    *  you are required to set mState
441    *  appropriately, failure to do so will result in incorrect
442    *  calculations by other functions in GMTL.  If you are unsure
443    *  about how to set mState, set it to FULL and you will be sure
444    *  to get the correct result at the cost of some performance.
445    */
446   DATA_TYPE& operator()( const unsigned row, const unsigned column )
447   {
448      gmtlASSERT( (row < ROWS) && (column < COLS) );
449      return mData[column*ROWS + row];
450   }
451
452   /** access [row, col] in the matrix (const version) */
453   const DATA_TYPE&  operator()( const unsigned row, const unsigned column ) const
454   {
455      gmtlASSERT( (row < ROWS) && (column < COLS) );
456      return mData[column*ROWS + row];
457   }
458
459   /** bracket operator
460    *  WARNING: If you set data in the matrix (using this interface),
461    *  you are required to set mState
462    *  appropriately, failure to do so will result in incorrect
463    *  calculations by other functions in GMTL.  If you are unsure
464    *  about how to set mState, set it to FULL and you will be sure
465    *  to get the correct result at the cost of some performance.
466    */
467   RowAccessor operator[]( const unsigned row )
468   {
469      return RowAccessor(this, row);
470   }
471
472   /** bracket operator (const version) */
473   ConstRowAccessor operator[]( const unsigned row ) const
474   {
475      return ConstRowAccessor( this, row );
476   }
477
478   /*
479   // bracket operator
480   const DATA_TYPE& operator[]( const unsigned i ) const
481   {
482      gmtlASSERT( i < (ROWS*COLS) );
483      return mData[i];
484   }
485   */
486
487   /** Gets a DATA_TYPE pointer to the matrix data.
488    * @return Returns a pointer to the head of the matrix data.
489    */
490   const DATA_TYPE*  getData() const { return (DATA_TYPE*)mData; }
491
492   bool isError()
493   {
494      return mState & XFORM_ERROR;
495   }
496   void setError()
497   {
498      mState |= XFORM_ERROR;
499   }
500
501   void setState(int state)
502   { mState = state; }
503
504public:
505   /** Column major.  In other words {Column1, Column2, Column3, Column4} in memory
506    * access element mData[column][row]
507    *  WARNING: If you set data in the matrix (using this interface),
508    *  you are required to set mState appropriately,
509    *  failure to do so will result in incorrect
510    *  calculations by other functions in GMTL.  If you are unsure
511    *  about how to set mState, set it to FULL and you will be sure
512    *  to get the correct result at the cost of some performance.
513    */
514   DATA_TYPE mData[COLS*ROWS];
515
516   /** describes what xforms are in this matrix */
517   int mState;
518};
519
520typedef Matrix<float, 2, 2> Matrix22f;
521typedef Matrix<double, 2, 2> Matrix22d;
522typedef Matrix<float, 2, 3> Matrix23f;
523typedef Matrix<double, 2, 3> Matrix23d;
524typedef Matrix<float, 3, 3> Matrix33f;
525typedef Matrix<double, 3, 3> Matrix33d;
526typedef Matrix<float, 3, 4> Matrix34f;
527typedef Matrix<double, 3, 4> Matrix34d;
528typedef Matrix<float, 4, 4> Matrix44f;
529typedef Matrix<double, 4, 4> Matrix44d;
530
531/** 32bit floating point 2x2 identity matrix */
532const Matrix22f MAT_IDENTITY22F = Matrix22f();
533
534/** 64bit floating point 2x2 identity matrix */
535const Matrix22d MAT_IDENTITY22D = Matrix22d();
536
537/** 32bit floating point 2x2 identity matrix */
538const Matrix23f MAT_IDENTITY23F = Matrix23f();
539
540/** 64bit floating point 2x2 identity matrix */
541const Matrix23d MAT_IDENTITY23D = Matrix23d();
542
543/** 32bit floating point 3x3 identity matrix */
544const Matrix33f MAT_IDENTITY33F = Matrix33f();
545
546/** 64bit floating point 3x3 identity matrix */
547const Matrix33d MAT_IDENTITY33D = Matrix33d();
548
549/** 32bit floating point 3x4 identity matrix */
550const Matrix34f MAT_IDENTITY34F = Matrix34f();
551
552/** 64bit floating point 3x4 identity matrix */
553const Matrix34d MAT_IDENTITY34D = Matrix34d();
554
555/** 32bit floating point 4x4 identity matrix */
556const Matrix44f MAT_IDENTITY44F = Matrix44f();
557
558/** 64bit floating point 4x4 identity matrix */
559const Matrix44d MAT_IDENTITY44D = Matrix44d();
560
561/** utility function for use by matrix operations.
562 *  given two matrices, when combined with set(..) or xform(..) types of operations,
563 *  compute what matrixstate will the resulting matrix have?
564 */
565inline int combineMatrixStates( int state1, int state2 )
566{
567   switch (state1)
568   {
569   case Matrix44f::IDENTITY:
570      switch (state2)
571      {
572      case Matrix44f::XFORM_ERROR: return state2;
573      case Matrix44f::NON_UNISCALE: return Matrix44f::XFORM_ERROR;
574      default: return state2;
575      }
576   case Matrix44f::TRANS:
577      switch (state2)
578      {
579      case Matrix44f::IDENTITY: return state1;
580      case Matrix44f::ORTHOGONAL: return Matrix44f::AFFINE;
581      case Matrix44f::NON_UNISCALE: return Matrix44f::XFORM_ERROR;
582      default: return state2;
583      }
584   case Matrix44f::ORTHOGONAL:
585      switch (state2)
586      {
587      case Matrix44f::IDENTITY: return state1;
588      case Matrix44f::TRANS: return Matrix44f::AFFINE;
589      case Matrix44f::NON_UNISCALE: return Matrix44f::XFORM_ERROR;
590      default: return state2;
591      }
592   case Matrix44f::AFFINE:
593      switch (state2)
594      {
595      case Matrix44f::IDENTITY:
596      case Matrix44f::TRANS:
597      case Matrix44f::ORTHOGONAL:  return state1;
598      case Matrix44f::NON_UNISCALE: return Matrix44f::XFORM_ERROR;
599      case Matrix44f::AFFINE | Matrix44f::NON_UNISCALE:
600      default: return state2;
601      }
602   case Matrix44f::AFFINE | Matrix44f::NON_UNISCALE:
603      switch (state2)
604      {
605      case Matrix44f::IDENTITY:
606      case Matrix44f::TRANS:
607      case Matrix44f::ORTHOGONAL:
608      case Matrix44f::AFFINE:  return state1;
609      case Matrix44f::NON_UNISCALE: return Matrix44f::XFORM_ERROR;
610      default: return state2;
611      }
612   case Matrix44f::FULL:
613      switch (state2)
614      {
615      case Matrix44f::XFORM_ERROR: return state2;
616      case Matrix44f::NON_UNISCALE: return Matrix44f::XFORM_ERROR;
617      default: return state1;
618      }
619      break;
620   default:
621      return Matrix44f::XFORM_ERROR;
622   }
623}
624
625} // end namespace gmtl
626
627
628
629#endif
Note: See TracBrowser for help on using the repository browser.