GLC_lib  2.5.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
glc_matrix4x4.h
Go to the documentation of this file.
1 /****************************************************************************
2 
3  This file is part of the GLC-lib library.
4  Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
5  http://glc-lib.sourceforge.net
6 
7  GLC-lib is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version.
11 
12  GLC-lib is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public License
18  along with GLC-lib; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21 *****************************************************************************/
22 
24 
25 #ifndef GLC_MATRIX4X4_H_
26 #define GLC_MATRIX4X4_H_
27 
28 #include <QVector>
29 #include <QQuaternion>
30 #include <QPair>
31 
32 #include "glc_vector3d.h"
33 
34 #include "../glc_config.h"
35 
38 
43 
45 {
46  friend class GLC_Vector3d;
47 public:
49  enum
50  {
51  General= 0x0000,
52  Direct= 0x0001,
53  Indirect= 0x0002,
54  Identity= 0x0003
55  };
56 
58 // Constructor
60 public:
62 
63 
64  inline GLC_Matrix4x4();
65 
67  inline GLC_Matrix4x4(const GLC_Matrix4x4 &matrix)
68  :m_Type(matrix.m_Type)
69  {
70  memcpy(m_Matrix, matrix.m_Matrix, sizeof(double) * 16);
71  }
72 
74  inline GLC_Matrix4x4(const double *pArray)
75  : m_Type(General)
76  {
77  memcpy(m_Matrix, pArray, sizeof(double) * 16);
78  }
79 
81  inline GLC_Matrix4x4(const float *);
82 
84  inline GLC_Matrix4x4(const GLC_Vector3d &Vect, const double &dAngleRad);
85 
87  inline GLC_Matrix4x4(const GLC_Vector3d &Vect1, const GLC_Vector3d &Vect2);
88 
90  inline GLC_Matrix4x4(const GLC_Vector3d &Vect)
91  {setMatTranslate(Vect);}
92 
94  inline GLC_Matrix4x4(const double Tx, const double Ty, const double Tz)
95  {setMatTranslate(Tx, Ty, Tz);}
97 
99 
101 
102 public:
104  inline GLC_Matrix4x4& operator = (const GLC_Matrix4x4 &matrix);
105 
107  inline GLC_Matrix4x4 operator * (const GLC_Matrix4x4 &Mat) const;
108 
110  inline GLC_Vector3d operator * (const GLC_Vector3d &Vect) const;
111 
113  inline bool operator==(const GLC_Matrix4x4& mat) const;
114 
116  inline bool operator!=(const GLC_Matrix4x4& mat) const
117  {return !operator==(mat);}
118 
120 
122 
124 
125 public:
127  inline double determinant(void) const;
128 
130 
131  inline const double* getData(void)
132  {return m_Matrix;}
133 
135  inline const double* getData(void) const
136  {return m_Matrix;}
137 
139  inline double* setData(void)
140  {
141  m_Type= General;
142  return m_Matrix;
143  }
144 
146  QVector<double> toEuler(void) const;
147 
149  QString toString() const;
150 
152  inline GLC_Matrix4x4 rotationMatrix() const;
153 
155  inline GLC_Matrix4x4 isometricMatrix() const;
156 
158  inline double scalingX() const
159  {return GLC_Vector3d(m_Matrix[0], m_Matrix[1], m_Matrix[2]).length();}
160 
162  inline double scalingY() const
163  {return GLC_Vector3d(m_Matrix[4], m_Matrix[5], m_Matrix[6]).length();}
164 
166  inline double scalingZ() const
167  {return GLC_Vector3d(m_Matrix[8], m_Matrix[9], m_Matrix[10]).length();}
168 
170  inline GLC_Matrix4x4 inverted() const
171  {return GLC_Matrix4x4(*this).invert();}
172 
174  inline int type() const
175  {
176  return m_Type;
177  }
178 
180  inline bool isDirect() const
181  {return (m_Type & Direct);}
182 
184  inline double trace() const
185  {return (m_Matrix[0] + m_Matrix[5] + m_Matrix[10] + m_Matrix[15]);}
186 
188  QQuaternion quaternion() const;
189 
191  QPair<GLC_Vector3d, double> rotationVectorAndAngle() const;
192 
194 
196 
198 
199 public:
201  inline GLC_Matrix4x4& setMatRot(const GLC_Vector3d &, const double &);
202 
204  inline GLC_Matrix4x4& setMatRot(const GLC_Vector3d &, const GLC_Vector3d &);
205 
207  inline GLC_Matrix4x4& setMatTranslate(const GLC_Vector3d &);
208 
210  inline GLC_Matrix4x4& setMatTranslate(const double, const double, const double);
211 
213  inline GLC_Matrix4x4& setMatScaling(const double, const double, const double);
214 
216  inline GLC_Matrix4x4& invert(void);
217 
219  inline GLC_Matrix4x4& setToIdentity();
220 
222  inline GLC_Matrix4x4& transpose(void);
223 
225  GLC_Matrix4x4& fromEuler(const double, const double, const double);
226 
228  GLC_Matrix4x4& setColumn(int index, const GLC_Vector3d& vector);
229 
231  inline GLC_Matrix4x4& optimise(bool force= false);
232 
234 
238 private:
239 
241  inline bool isInDiagonal(const int index) const
242  {
243  if ((index == 0) || (index == 5) || (index == 10) || (index == 15))
244  return true;
245  else
246  return false;
247  }
248 
250  inline double getDeterminantLC(const int, const int) const;
251 
253  inline void getSubMat(const int, const int, double *) const;
254 
256  inline GLC_Matrix4x4 getTranspose(void) const;
257 
259  inline GLC_Matrix4x4 getCoMat4x4(void) const;
260 
261 
262 
264 // Private members
266 private:
267 
269  enum {TAILLEMAT4X4 = 16};
270 
272  enum {DIMMAT4X4 = 4};
273 
275  double m_Matrix[TAILLEMAT4X4];
276 
278  int m_Type;
279 
280 /*
281 the matrix :
282  a[00] a[04] a[08] a[12]
283 
284  a[01] a[05] a[09] a[13]
285 
286  a[02] a[06] a[10] a[14]
287 
288  a[03] a[07] a[11] a[15]
289  */
290 // Tx = 12, Ty = 13, Tz = 14
291 
292 };
293 
294 
296 // Constructor/Destructor
298 
300 : m_Type(Identity)
301 {
302  setToIdentity();
303 }
304 
305 GLC_Matrix4x4::GLC_Matrix4x4(const float *Tableau)
306 : m_Type(General)
307 {
308 
309  for (int i=0; i < TAILLEMAT4X4; i++)
310  {
311  m_Matrix[i]= static_cast<double>(Tableau[i]);
312  }
313 }
314 GLC_Matrix4x4::GLC_Matrix4x4(const GLC_Vector3d &Vect, const double &dAngleRad)
315 : m_Type(Direct)
316 {
317  setToIdentity();
318  setMatRot(Vect, dAngleRad);
319 }
320 
322 : m_Type(Direct)
323 {
324  setToIdentity();
325  setMatRot(Vect1, Vect2);
326 }
327 
329 {
330  if (m_Type == Identity)
331  {
332  return Mat;
333  }
334  else if (Mat.m_Type == Identity)
335  {
336  return *this;
337  }
338 
339  int Colonne;
340  int Ligne;
341  int i;
342  double ValInt;
343 
344  int IndexInt;
345 
346  GLC_Matrix4x4 MatResult;
347  for (Ligne= 0; Ligne < DIMMAT4X4; Ligne++)
348  {
349  for (Colonne=0; Colonne < DIMMAT4X4; Colonne++)
350  {
351  ValInt= 0.0;
352  IndexInt= Colonne * DIMMAT4X4;
353 
354  for (i= 0; i < DIMMAT4X4; i++)
355  {
356  ValInt+= m_Matrix[ (i * DIMMAT4X4) + Ligne] * Mat.m_Matrix[ IndexInt + i];
357  }
358  MatResult.m_Matrix[ IndexInt + Ligne]= ValInt;
359  }
360  }
361  if ((m_Type == Indirect) || (Mat.m_Type == Indirect))
362  {
363  MatResult.m_Type= Indirect;
364  }
365  else
366  {
367  MatResult.m_Type= m_Type & Mat.m_Type;
368  }
369 
370  return MatResult;
371 }
372 
374 {
375  m_Type= matrix.m_Type;
376  memcpy(m_Matrix, matrix.m_Matrix, sizeof(double) * 16);
377 
378  return *this;
379 }
380 
382 {
383  double ValInt;
384  int i;
385  GLC_Vector3d VectResult;
386  double mat[4];
387 
388  for (int Index= 0; Index < DIMMAT4X4; Index++)
389  {
390  ValInt= 0.0;
391  for (i= 0; i < DIMMAT4X4 - 1; i++)
392  {
393  ValInt+= m_Matrix[(i * DIMMAT4X4) + Index] * Vect.m_Vector[i];
394  }
395  ValInt+= m_Matrix[(3 * DIMMAT4X4) + Index];
396  mat[Index]= ValInt;
397  }
398 
399  double invW= 1.0;
400  if (fabs(mat[3]) > 0.00001)
401  {
402  invW/= mat[3];
403  }
404  VectResult.m_Vector[0]= mat[0] * invW;
405  VectResult.m_Vector[1]= mat[1] * invW;
406  VectResult.m_Vector[2]= mat[2] * invW;
407 
408 
409  return VectResult;
410 }
411 
413 {
414  bool result= true;
415  int i= 0;
416  while (result && (i < TAILLEMAT4X4))
417  {
418  result= (qAbs(m_Matrix[i] - mat.m_Matrix[i]) <= glc::EPSILON);
419  ++i;
420  }
421  return result;
422 }
423 
425 {
426  GLC_Matrix4x4 result(*this);
427  const double invScaleX= 1.0 / scalingX();
428  const double invScaleY= 1.0 / scalingY();
429  const double invScaleZ= 1.0 / scalingZ();
430  result.m_Matrix[0]= result.m_Matrix[0] * invScaleX;
431  result.m_Matrix[1]= result.m_Matrix[1] * invScaleX;
432  result.m_Matrix[2]= result.m_Matrix[2] * invScaleX;
433 
434  result.m_Matrix[4]= result.m_Matrix[4] * invScaleY;
435  result.m_Matrix[5]= result.m_Matrix[5] * invScaleY;
436  result.m_Matrix[6]= result.m_Matrix[6] * invScaleY;
437 
438  result.m_Matrix[8]= result.m_Matrix[8] * invScaleZ;
439  result.m_Matrix[9]= result.m_Matrix[9] * invScaleZ;
440  result.m_Matrix[10]= result.m_Matrix[10] * invScaleZ;
441 
442  result.m_Matrix[12]= 0.0; result.m_Matrix[13]= 0.0; result.m_Matrix[14]= 0.0;
443  result.m_Matrix[3]= 0.0; result.m_Matrix[7]= 0.0; result.m_Matrix[11]= 0.0;
444  result.m_Matrix[15]= 1.0;
445 
446  result.m_Type= General;
447 
448  return result;
449 }
450 
452 {
453  GLC_Matrix4x4 result(*this);
454  const double invScaleX= 1.0 / scalingX();
455  const double invScaleY= 1.0 / scalingY();
456  const double invScaleZ= 1.0 / scalingZ();
457  result.m_Matrix[0]= result.m_Matrix[0] * invScaleX;
458  result.m_Matrix[1]= result.m_Matrix[1] * invScaleX;
459  result.m_Matrix[2]= result.m_Matrix[2] * invScaleX;
460 
461  result.m_Matrix[4]= result.m_Matrix[4] * invScaleY;
462  result.m_Matrix[5]= result.m_Matrix[5] * invScaleY;
463  result.m_Matrix[6]= result.m_Matrix[6] * invScaleY;
464 
465  result.m_Matrix[8]= result.m_Matrix[8] * invScaleZ;
466  result.m_Matrix[9]= result.m_Matrix[9] * invScaleZ;
467  result.m_Matrix[10]= result.m_Matrix[10] * invScaleZ;
468 
469  result.m_Type= General;
470 
471  return result;
472 }
473 
474 GLC_Matrix4x4& GLC_Matrix4x4::setMatRot(const GLC_Vector3d &Vect, const double &dAngleRad)
475 {
476  // Normalize the vector
477  GLC_Vector3d VectRot(Vect);
478  VectRot.normalize();
479 
480  // Code optimisation
481  const double SinAngleSur2= sin(dAngleRad / 2.0);
482 
483  // Quaternion computation
484  const double q0= cos(dAngleRad / 2);
485  const double q1= VectRot.m_Vector[0] * SinAngleSur2;
486  const double q2= VectRot.m_Vector[1] * SinAngleSur2;
487  const double q3= VectRot.m_Vector[2] * SinAngleSur2;
488 
489  // Code optimisation
490  const double q0Carre= (q0 * q0);
491  const double q1Carre= (q1 * q1);
492  const double q2Carre= (q2 * q2);
493  const double q3Carre= (q3 * q3);
494 
495  m_Matrix[0]= q0Carre + q1Carre - q2Carre - q3Carre;
496  m_Matrix[1]= 2.0 * (q1 *q2 + q0 * q3);
497  m_Matrix[2]= 2.0 * (q1 * q3 - q0 * q2);
498  m_Matrix[3]= 0.0;
499  m_Matrix[4]= 2.0 * (q1 * q2 - q0 * q3);
500  m_Matrix[5]= q0Carre + q2Carre - q3Carre - q1Carre;
501  m_Matrix[6]= 2.0 * (q2 * q3 + q0 * q1);
502  m_Matrix[7]= 0.0;
503  m_Matrix[8]= 2.0 * (q1 * q3 + q0 * q2);
504  m_Matrix[9]= 2.0 * (q2 * q3 - q0 * q1);
505  m_Matrix[10]= q0Carre + q3Carre - q1Carre - q2Carre;
506  m_Matrix[11]= 0.0;
507 
508  m_Matrix[12]= 0.0; //TX
509  m_Matrix[13]= 0.0; //TY
510  m_Matrix[14]= 0.0; //TZ
511  m_Matrix[15]= 1.0;
512 
513  m_Type= Direct;
514 
515  return *this;
516 }
517 
519 {
520 
521  if ((v1 != v2) && !v1.isNull() && !v2.isNull())
522  {
523  if (v1 != -v2)
524  {
525  const GLC_Vector3d rotationAxis(v1 ^ v2);
526  if (!rotationAxis.isNull())
527  {
528  const double Angle= acos(v1 * v2);
529  setMatRot(rotationAxis, Angle);
530  }
531  }
532  else
533  {
534  // v1 == -v2
535  GLC_Vector3d otherVector(glc::Z_AXIS);
536  if ((otherVector == v1) || (otherVector == -v2))
537  {
538  otherVector= glc::Y_AXIS;
539  }
540  const GLC_Vector3d rotationVector((v1 ^ otherVector).normalize());
541  setMatRot(rotationVector, glc::PI);
542  }
543  }
544 
545  return *this;
546 }
547 
549 {
550  m_Matrix[0]= 1.0; m_Matrix[4]= 0.0; m_Matrix[8]= 0.0; m_Matrix[12]= Vect.m_Vector[0];
551  m_Matrix[1]= 0.0; m_Matrix[5]= 1.0; m_Matrix[9]= 0.0; m_Matrix[13]= Vect.m_Vector[1];
552  m_Matrix[2]= 0.0; m_Matrix[6]= 0.0; m_Matrix[10]= 1.0; m_Matrix[14]= Vect.m_Vector[2];
553  m_Matrix[3]= 0.0; m_Matrix[7]= 0.0; m_Matrix[11]= 0.0; m_Matrix[15]= 1.0;
554 
555  m_Type= Direct;
556 
557  return *this;
558 }
559 
560 GLC_Matrix4x4& GLC_Matrix4x4::setMatTranslate(const double Tx, const double Ty, const double Tz)
561 {
562  m_Matrix[0]= 1.0; m_Matrix[4]= 0.0; m_Matrix[8]= 0.0; m_Matrix[12]= Tx;
563  m_Matrix[1]= 0.0; m_Matrix[5]= 1.0; m_Matrix[9]= 0.0; m_Matrix[13]= Ty;
564  m_Matrix[2]= 0.0; m_Matrix[6]= 0.0; m_Matrix[10]= 1.0; m_Matrix[14]= Tz;
565  m_Matrix[3]= 0.0; m_Matrix[7]= 0.0; m_Matrix[11]= 0.0; m_Matrix[15]= 1.0;
566 
567  m_Type= Direct;
568 
569  return *this;
570 }
571 
572 GLC_Matrix4x4& GLC_Matrix4x4::setMatScaling(const double sX, const double sY, const double sZ)
573 {
574  m_Matrix[0]= sX; m_Matrix[4]= 0.0; m_Matrix[8]= 0.0; m_Matrix[12]= 0.0;
575  m_Matrix[1]= 0.0; m_Matrix[5]= sY; m_Matrix[9]= 0.0; m_Matrix[13]= 0.0;
576  m_Matrix[2]= 0.0; m_Matrix[6]= 0.0; m_Matrix[10]= sZ; m_Matrix[14]= 0.0;
577  m_Matrix[3]= 0.0; m_Matrix[7]= 0.0; m_Matrix[11]= 0.0; m_Matrix[15]= 1.0;
578 
579  m_Type= General;
580 
581  return *this;
582 }
583 
584 
586 {
587  const double det= determinant();
588 
589  // Test if the inverion is possible
590  if (det == 0.0f) return *this;
591 
592  const double invDet = 1.0 / det;
594 
595  for (int i= 0; i < TAILLEMAT4X4; i++)
596  {
597  m_Matrix[i]= TCoMat.m_Matrix[i] * invDet;
598  }
599 
600  return *this;
601 }
602 
604 {
605  m_Matrix[0]= 1.0; m_Matrix[4]= 0.0; m_Matrix[8]= 0.0; m_Matrix[12]= 0.0;
606  m_Matrix[1]= 0.0; m_Matrix[5]= 1.0; m_Matrix[9]= 0.0; m_Matrix[13]= 0.0;
607  m_Matrix[2]= 0.0; m_Matrix[6]= 0.0; m_Matrix[10]= 1.0; m_Matrix[14]= 0.0;
608  m_Matrix[3]= 0.0; m_Matrix[7]= 0.0; m_Matrix[11]= 0.0; m_Matrix[15]= 1.0;
609 
610  m_Type= Identity;
611 
612  return *this;
613 }
614 
616 {
617  GLC_Matrix4x4 MatT(m_Matrix);
618  int IndexOrigine;
619  int IndexTrans;
620  for (int Colonne= 0; Colonne < DIMMAT4X4; Colonne++)
621  {
622  for (int Ligne=0 ; Ligne < DIMMAT4X4; Ligne++)
623  {
624  IndexOrigine= (Colonne * DIMMAT4X4) + Ligne;
625  IndexTrans= (Ligne * DIMMAT4X4) + Colonne;
626 
627  MatT.m_Matrix[IndexTrans]= m_Matrix[IndexOrigine];
628  }
629  }
630 
631  // Load the transposed matrix in this matrix
632  memcpy(m_Matrix, MatT.m_Matrix, sizeof(double) * 16);
633 
634  return *this;
635 }
636 
638 {
639  if (force || (m_Type == General))
640  {
641  bool identityVal= (m_Matrix[0] == 1.0f) && (m_Matrix[4] == 0.0f) && (m_Matrix[8] == 0.0f) && (m_Matrix[12] == 0.0f);
642  identityVal= identityVal && (m_Matrix[1] == 0.0f) && (m_Matrix[5] == 1.0f) && (m_Matrix[9] == 0.0f) && (m_Matrix[13] == 0.0);
643  identityVal= identityVal && (m_Matrix[2] == 0.0f) && (m_Matrix[6] == 0.0f) && (m_Matrix[10] == 1.0f) && (m_Matrix[14] == 0.0);
644  identityVal= identityVal && (m_Matrix[3] == 0.0f) && (m_Matrix[7] == 0.0f) && (m_Matrix[11] == 0.0f) && (m_Matrix[15] == 1.0f);
645  if (identityVal)
646  {
647  m_Type= Identity;
648  }
649  else
650  {
651  if (determinant() > 0)
652  {
653  m_Type= Direct;
654  }
655  else
656  {
657  m_Type= Indirect;
658  }
659  }
660  }
661  return *this;
662 }
663 
664 double GLC_Matrix4x4::determinant(void) const
665 {
666  double Determinant= 0.0;
667  double SubMat3x3[9];
668  int Signe= 1;
669 
670  for (int Colonne= 0; Colonne < DIMMAT4X4; Colonne++, Signe*= -1)
671  {
672  getSubMat(0, Colonne, SubMat3x3);
673  Determinant+= Signe * m_Matrix[Colonne * DIMMAT4X4] * getDeterminant3x3(SubMat3x3);
674  }
675 
676  return Determinant;
677 
678 }
679 
680 double GLC_Matrix4x4::getDeterminantLC(const int Ligne, const int Colonne) const
681 {
682  double Mat3x3[9];
683  double Determinant;
684 
685  getSubMat(Ligne, Colonne, Mat3x3);
686 
687  if ( 0 == ((Ligne + Colonne) % 2)) // Even number
688  Determinant= m_Matrix[(Colonne + DIMMAT4X4) + Ligne] * getDeterminant3x3(Mat3x3);
689  else
690  Determinant= - m_Matrix[(Colonne + DIMMAT4X4) + Ligne] * getDeterminant3x3(Mat3x3);
691 
692  return Determinant;
693 }
694 
695 void GLC_Matrix4x4::getSubMat(const int Ligne, const int Colonne, double *ResultMat) const
696 {
697 
698  int LigneResult;
699  int ColonneResult;
700  int IndexOrigine;
701  int IndexResult;
702 
703  for (int ColonneOrigine= 0; ColonneOrigine < DIMMAT4X4; ColonneOrigine++)
704  {
705  if (ColonneOrigine != Colonne)
706  {
707  if (ColonneOrigine < Colonne)
708  ColonneResult= ColonneOrigine;
709  else
710  ColonneResult= ColonneOrigine - 1;
711 
712  for (int LigneOrigine= 0; LigneOrigine < DIMMAT4X4; LigneOrigine++)
713  {
714  if (LigneOrigine != Ligne)
715  {
716  if (LigneOrigine < Ligne)
717  LigneResult= LigneOrigine;
718  else
719  LigneResult= LigneOrigine - 1;
720  IndexOrigine= (ColonneOrigine * DIMMAT4X4) + LigneOrigine;
721  IndexResult= (ColonneResult * (DIMMAT4X4 - 1)) + LigneResult;
722 
723  ResultMat[IndexResult]= m_Matrix[IndexOrigine];
724  }
725  }
726  }
727  }
728 }
729 
731 {
732  GLC_Matrix4x4 MatT(m_Matrix);
733  int IndexOrigine;
734  int IndexTrans;
735  for (int Colonne= 0; Colonne < DIMMAT4X4; Colonne++)
736  {
737  for (int Ligne=0 ; Ligne < DIMMAT4X4; Ligne++)
738  {
739  IndexOrigine= (Colonne * DIMMAT4X4) + Ligne;
740  IndexTrans= (Ligne * DIMMAT4X4) + Colonne;
741 
742  MatT.m_Matrix[IndexTrans]= m_Matrix[IndexOrigine];
743  }
744  }
745 
746  MatT.m_Type= m_Type;
747  return MatT;
748 }
749 
751 {
752  GLC_Matrix4x4 CoMat(m_Matrix);
753  double SubMat3x3[9];
754  int Index;
755 
756  for (int Colonne= 0; Colonne < DIMMAT4X4; Colonne++)
757  {
758  for (int Ligne=0 ; Ligne < DIMMAT4X4; Ligne++)
759  {
760  getSubMat(Ligne, Colonne, SubMat3x3);
761  Index= (Colonne * DIMMAT4X4) + Ligne;
762  if (((Colonne + Ligne + 2) % 2) == 0) // Even Number
763  CoMat.m_Matrix[Index]= getDeterminant3x3(SubMat3x3);
764  else
765  CoMat.m_Matrix[Index]= -getDeterminant3x3(SubMat3x3);
766  }
767  }
768 
769 
770  CoMat.m_Type= General;
771  return CoMat;
772 }
773 
774 
775 #endif /*GLC_MATRIX4X4_H_*/

©2005-2013 Laurent Ribon