Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F123456
Matrix3.h
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Size
45 KB
Referenced Files
None
Subscribers
None
Matrix3.h
View Options
#ifndef _ECHO_MATRIX3_H_
#define _ECHO_MATRIX3_H_
#include
<echo/Maths/Vector3.h>
#include
<echo/Util/StringUtils.h>
#include
<algorithm>
namespace
Echo
{
// NB All code adapted from Wild Magic 0.2 Matrix math (free source code)
// http://www.geometrictools.com/
// NOTE. The (x,y,z) coordinate system is assumed to be right-handed.
// Coordinate axis rotation matrices are of the form
// RX = 1 0 0
// 0 cos(t) -sin(t)
// 0 sin(t) cos(t)
// where t > 0 indicates a counterclockwise rotation in the yz-plane
// RY = cos(t) 0 sin(t)
// 0 1 0
// -sin(t) 0 cos(t)
// where t > 0 indicates a counterclockwise rotation in the zx-plane
// RZ = cos(t) -sin(t) 0
// sin(t) cos(t) 0
// 0 0 1
// where t > 0 indicates a counterclockwise rotation in the xy-plane.
/** \addtogroup Core
* @{
*/
/** \addtogroup Maths
* @{
*/
/** A 3x3 matrix which can represent rotations around axes.
@note
<b>All the code is adapted from the Wild Magic 0.2 Matrix
library (http://www.geometrictools.com/).</b>
@par
The coordinate system is assumed to be <b>right-handed</b>.
*/
template
<
typename
T
>
class
Matrix3Generic
{
public
:
/** Default constructor.
@note
It does <b>NOT</b> initialize the matrix for efficiency.
*/
inline
Matrix3Generic
()
{
}
inline
explicit
Matrix3Generic
(
const
T
arr
[
3
][
3
])
{
m
[
0
][
0
]
=
arr
[
0
][
0
];
m
[
0
][
1
]
=
arr
[
0
][
1
];
m
[
0
][
2
]
=
arr
[
0
][
2
];
m
[
1
][
0
]
=
arr
[
1
][
0
];
m
[
1
][
1
]
=
arr
[
1
][
1
];
m
[
1
][
2
]
=
arr
[
1
][
2
];
m
[
2
][
0
]
=
arr
[
2
][
0
];
m
[
2
][
1
]
=
arr
[
2
][
1
];
m
[
2
][
2
]
=
arr
[
2
][
2
];
}
inline
Matrix3Generic
(
const
Matrix3Generic
<
T
>&
rhs
)
{
m
[
0
][
0
]
=
rhs
[
0
][
0
];
m
[
0
][
1
]
=
rhs
[
0
][
1
];
m
[
0
][
2
]
=
rhs
[
0
][
2
];
m
[
1
][
0
]
=
rhs
[
1
][
0
];
m
[
1
][
1
]
=
rhs
[
1
][
1
];
m
[
1
][
2
]
=
rhs
[
1
][
2
];
m
[
2
][
0
]
=
rhs
[
2
][
0
];
m
[
2
][
1
]
=
rhs
[
2
][
1
];
m
[
2
][
2
]
=
rhs
[
2
][
2
];
}
Matrix3Generic
(
T
fEntry00
,
T
fEntry01
,
T
fEntry02
,
T
fEntry10
,
T
fEntry11
,
T
fEntry12
,
T
fEntry20
,
T
fEntry21
,
T
fEntry22
)
{
m
[
0
][
0
]
=
fEntry00
;
m
[
0
][
1
]
=
fEntry01
;
m
[
0
][
2
]
=
fEntry02
;
m
[
1
][
0
]
=
fEntry10
;
m
[
1
][
1
]
=
fEntry11
;
m
[
1
][
2
]
=
fEntry12
;
m
[
2
][
0
]
=
fEntry20
;
m
[
2
][
1
]
=
fEntry21
;
m
[
2
][
2
]
=
fEntry22
;
}
/**
* Construct from a buffer of 9 entries in row major form.
* @note No checking is performed on the buffer, it is required that the buffer
* contains enough data.
*/
inline
Matrix3Generic
(
T
*
buffer
)
{
m
[
0
][
0
]
=
buffer
[
0
];
m
[
0
][
1
]
=
buffer
[
1
];
m
[
0
][
2
]
=
buffer
[
2
];
m
[
1
][
0
]
=
buffer
[
3
];
m
[
1
][
1
]
=
buffer
[
4
];
m
[
1
][
2
]
=
buffer
[
5
];
m
[
2
][
0
]
=
buffer
[
6
];
m
[
2
][
1
]
=
buffer
[
7
];
m
[
2
][
2
]
=
buffer
[
8
];
}
/** Exchange the contents of this matrix with another.
*/
inline
void
Swap
(
Matrix3Generic
<
T
>&
other
)
{
std
::
swap
(
m
[
0
][
0
],
other
.
m
[
0
][
0
]);
std
::
swap
(
m
[
0
][
1
],
other
.
m
[
0
][
1
]);
std
::
swap
(
m
[
0
][
2
],
other
.
m
[
0
][
2
]);
std
::
swap
(
m
[
1
][
0
],
other
.
m
[
1
][
0
]);
std
::
swap
(
m
[
1
][
1
],
other
.
m
[
1
][
1
]);
std
::
swap
(
m
[
1
][
2
],
other
.
m
[
1
][
2
]);
std
::
swap
(
m
[
2
][
0
],
other
.
m
[
2
][
0
]);
std
::
swap
(
m
[
2
][
1
],
other
.
m
[
2
][
1
]);
std
::
swap
(
m
[
2
][
2
],
other
.
m
[
2
][
2
]);
}
// member access, allows use of construct mat[r][c]
inline
T
*
operator
[]
(
size_t
iRow
)
const
{
return
(
T
*
)
m
[
iRow
];
}
Vector3Generic
<
T
>
GetColumn
(
size_t
col
)
const
{
assert
(
0
<=
col
&&
col
<
3
);
return
Vector3Generic
<
T
>
(
m
[
0
][
col
],
m
[
1
][
col
],
m
[
2
][
col
]);
}
void
SetColumn
(
size_t
col
,
const
Vector3Generic
<
T
>&
vec
)
{
assert
(
0
<=
col
&&
col
<
3
);
m
[
0
][
col
]
=
vec
.
x
;
m
[
1
][
col
]
=
vec
.
y
;
m
[
2
][
col
]
=
vec
.
z
;
}
void
FromAxes
(
const
Vector3Generic
<
T
>&
xAxis
,
const
Vector3Generic
<
T
>&
yAxis
,
const
Vector3Generic
<
T
>&
zAxis
)
{
SetColumn
(
0
,
xAxis
);
SetColumn
(
1
,
yAxis
);
SetColumn
(
2
,
zAxis
);
}
// assignment and comparison
inline
Matrix3Generic
<
T
>&
operator
=
(
const
Matrix3Generic
<
T
>&
rhs
)
{
m
[
0
][
0
]
=
rhs
[
0
][
0
];
m
[
0
][
1
]
=
rhs
[
0
][
1
];
m
[
0
][
2
]
=
rhs
[
0
][
2
];
m
[
1
][
0
]
=
rhs
[
1
][
0
];
m
[
1
][
1
]
=
rhs
[
1
][
1
];
m
[
1
][
2
]
=
rhs
[
1
][
2
];
m
[
2
][
0
]
=
rhs
[
2
][
0
];
m
[
2
][
1
]
=
rhs
[
2
][
1
];
m
[
2
][
2
]
=
rhs
[
2
][
2
];
return
*
this
;
}
bool
operator
==
(
const
Matrix3Generic
<
T
>&
rhs
)
const
{
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
if
(
m
[
row
][
col
]
!=
rhs
.
m
[
row
][
col
]
)
return
false
;
}
}
return
true
;
}
inline
bool
operator
!=
(
const
Matrix3Generic
<
T
>&
rkMatrix
)
const
{
return
!
operator
==
(
rkMatrix
);
}
// arithmetic operations
Matrix3Generic
<
T
>
operator
+
(
const
Matrix3Generic
<
T
>&
rhs
)
const
{
Matrix3Generic
<
T
>
sum
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
sum
.
m
[
row
][
col
]
=
m
[
row
][
col
]
+
rhs
.
m
[
row
][
col
];
}
}
return
sum
;
}
Matrix3Generic
<
T
>
operator
-
(
const
Matrix3Generic
<
T
>&
rhs
)
const
{
Matrix3Generic
<
T
>
diff
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
diff
.
m
[
row
][
col
]
=
m
[
row
][
col
]
-
rhs
.
m
[
row
][
col
];
}
}
return
diff
;
}
Matrix3Generic
<
T
>
operator
*
(
const
Matrix3Generic
<
T
>&
rhs
)
const
{
Matrix3Generic
<
T
>
prod
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
prod
.
m
[
row
][
col
]
=
m
[
row
][
0
]
*
rhs
.
m
[
0
][
col
]
+
m
[
row
][
1
]
*
rhs
.
m
[
1
][
col
]
+
m
[
row
][
2
]
*
rhs
.
m
[
2
][
col
];
}
}
return
prod
;
}
Matrix3Generic
<
T
>
operator
-
()
const
{
Matrix3Generic
<
T
>
neg
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
neg
[
row
][
col
]
=
-
m
[
row
][
col
];
}
}
return
neg
;
}
// matrix * vector [3x3 * 3x1 = 3x1]
Vector3
operator
*
(
const
Vector3
&
rhs
)
const
{
Vector3
product
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
product
[
row
]
=
m
[
row
][
0
]
*
rhs
[
0
]
+
m
[
row
][
1
]
*
rhs
[
1
]
+
m
[
row
][
2
]
*
rhs
[
2
];
}
return
product
;
}
// vector * matrix [1x3 * 3x3 = 1x3]
friend
Vector3Generic
<
T
>
operator
*
(
const
Vector3Generic
<
T
>&
vector
,
const
Matrix3Generic
<
T
>&
matrix
)
{
Vector3
product
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
product
[
row
]
=
vector
[
0
]
*
matrix
.
m
[
0
][
row
]
+
vector
[
1
]
*
matrix
.
m
[
1
][
row
]
+
vector
[
2
]
*
matrix
.
m
[
2
][
row
];
}
return
product
;
}
// matrix * scalar
Matrix3Generic
<
T
>
operator
*
(
T
scalar
)
const
{
Matrix3Generic
<
T
>
product
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
product
[
row
][
col
]
=
scalar
*
m
[
row
][
col
];
}
}
return
product
;
}
// scalar * matrix
friend
Matrix3Generic
<
T
>
operator
*
(
T
scalar
,
const
Matrix3Generic
<
T
>&
matrix
)
{
Matrix3Generic
<
T
>
product
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
product
[
row
][
col
]
=
scalar
*
matrix
.
m
[
row
][
col
];
}
}
return
product
;
}
// utilities
Matrix3Generic
<
T
>
Transpose
()
const
{
Matrix3Generic
<
T
>
transpose
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
transpose
[
row
][
col
]
=
m
[
col
][
row
];
}
}
return
transpose
;
}
bool
Inverse
(
Matrix3Generic
<
T
>&
inverse
,
T
tolerance
=
1e-06
)
const
{
// Invert a 3x3 using cofactors. This is about 8 times faster than
// the Numerical Recipes code which uses Gaussian elimination.
inverse
[
0
][
0
]
=
m
[
1
][
1
]
*
m
[
2
][
2
]
-
m
[
1
][
2
]
*
m
[
2
][
1
];
inverse
[
0
][
1
]
=
m
[
0
][
2
]
*
m
[
2
][
1
]
-
m
[
0
][
1
]
*
m
[
2
][
2
];
inverse
[
0
][
2
]
=
m
[
0
][
1
]
*
m
[
1
][
2
]
-
m
[
0
][
2
]
*
m
[
1
][
1
];
inverse
[
1
][
0
]
=
m
[
1
][
2
]
*
m
[
2
][
0
]
-
m
[
1
][
0
]
*
m
[
2
][
2
];
inverse
[
1
][
1
]
=
m
[
0
][
0
]
*
m
[
2
][
2
]
-
m
[
0
][
2
]
*
m
[
2
][
0
];
inverse
[
1
][
2
]
=
m
[
0
][
2
]
*
m
[
1
][
0
]
-
m
[
0
][
0
]
*
m
[
1
][
2
];
inverse
[
2
][
0
]
=
m
[
1
][
0
]
*
m
[
2
][
1
]
-
m
[
1
][
1
]
*
m
[
2
][
0
];
inverse
[
2
][
1
]
=
m
[
0
][
1
]
*
m
[
2
][
0
]
-
m
[
0
][
0
]
*
m
[
2
][
1
];
inverse
[
2
][
2
]
=
m
[
0
][
0
]
*
m
[
1
][
1
]
-
m
[
0
][
1
]
*
m
[
1
][
0
];
T
det
=
m
[
0
][
0
]
*
inverse
[
0
][
0
]
+
m
[
0
][
1
]
*
inverse
[
1
][
0
]
+
m
[
0
][
2
]
*
inverse
[
2
][
0
];
if
(
Maths
::
Abs
(
det
)
<=
tolerance
)
{
return
false
;
}
T
invDet
=
T
(
1
)
/
det
;
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
inverse
[
row
][
col
]
*=
invDet
;
}
}
return
true
;
}
Matrix3Generic
<
T
>
Inverse
(
T
tolerance
=
1e-06
)
const
{
Matrix3Generic
<
T
>
inverse
=
Matrix3Generic
<
T
>::
ZERO
;
Inverse
(
inverse
,
tolerance
);
return
inverse
;
}
T
Determinant
()
const
{
T
cofactor00
=
m
[
1
][
1
]
*
m
[
2
][
2
]
-
m
[
1
][
2
]
*
m
[
2
][
1
];
T
cofactor10
=
m
[
1
][
2
]
*
m
[
2
][
0
]
-
m
[
1
][
0
]
*
m
[
2
][
2
];
T
cofactor20
=
m
[
1
][
0
]
*
m
[
2
][
1
]
-
m
[
1
][
1
]
*
m
[
2
][
0
];
T
det
=
m
[
0
][
0
]
*
cofactor00
+
m
[
0
][
1
]
*
cofactor10
+
m
[
0
][
2
]
*
cofactor20
;
return
det
;
}
// singular value decomposition
void
SingularValueDecomposition
(
Matrix3Generic
<
T
>&
kL
,
Vector3
&
kS
,
Matrix3Generic
<
T
>&
kR
)
const
{
// temas: currently unused
//const int iMax = 16;
Size
row
,
col
;
Matrix3Generic
<
T
>
kA
=
*
this
;
Bidiagonalize
(
kA
,
kL
,
kR
);
for
(
unsigned
int
i
=
0
;
i
<
ms_iSvdMaxIterations
;
i
++
)
{
T
fTmp
,
fTmp0
,
fTmp1
;
T
fSin0
,
fCos0
,
fTan0
;
T
fSin1
,
fCos1
,
fTan1
;
bool
bTest1
=
(
Maths
::
Abs
(
kA
[
0
][
1
])
<=
ms_fSvdEpsilon
*
(
Maths
::
Abs
(
kA
[
0
][
0
])
+
Maths
::
Abs
(
kA
[
1
][
1
])));
bool
bTest2
=
(
Maths
::
Abs
(
kA
[
1
][
2
])
<=
ms_fSvdEpsilon
*
(
Maths
::
Abs
(
kA
[
1
][
1
])
+
Maths
::
Abs
(
kA
[
2
][
2
])));
if
(
bTest1
)
{
if
(
bTest2
)
{
kS
[
0
]
=
kA
[
0
][
0
];
kS
[
1
]
=
kA
[
1
][
1
];
kS
[
2
]
=
kA
[
2
][
2
];
break
;
}
else
{
// 2x2 closed form factorization
fTmp
=
(
kA
[
1
][
1
]
*
kA
[
1
][
1
]
-
kA
[
2
][
2
]
*
kA
[
2
][
2
]
+
kA
[
1
][
2
]
*
kA
[
1
][
2
])
/
(
kA
[
1
][
2
]
*
kA
[
2
][
2
]);
fTan0
=
0.5f
*
(
fTmp
+
Maths
::
Sqrt
(
fTmp
*
fTmp
+
4.0f
));
fCos0
=
Maths
::
InvSqrt
(
T
(
1
)
+
fTan0
*
fTan0
);
fSin0
=
fTan0
*
fCos0
;
for
(
col
=
0
;
col
<
3
;
col
++
)
{
fTmp0
=
kL
[
col
][
1
];
fTmp1
=
kL
[
col
][
2
];
kL
[
col
][
1
]
=
fCos0
*
fTmp0
-
fSin0
*
fTmp1
;
kL
[
col
][
2
]
=
fSin0
*
fTmp0
+
fCos0
*
fTmp1
;
}
fTan1
=
(
kA
[
1
][
2
]
-
kA
[
2
][
2
]
*
fTan0
)
/
kA
[
1
][
1
];
fCos1
=
Maths
::
InvSqrt
(
T
(
1
)
+
fTan1
*
fTan1
);
fSin1
=
-
fTan1
*
fCos1
;
for
(
row
=
0
;
row
<
3
;
row
++
)
{
fTmp0
=
kR
[
1
][
row
];
fTmp1
=
kR
[
2
][
row
];
kR
[
1
][
row
]
=
fCos1
*
fTmp0
-
fSin1
*
fTmp1
;
kR
[
2
][
row
]
=
fSin1
*
fTmp0
+
fCos1
*
fTmp1
;
}
kS
[
0
]
=
kA
[
0
][
0
];
kS
[
1
]
=
fCos0
*
fCos1
*
kA
[
1
][
1
]
-
fSin1
*
(
fCos0
*
kA
[
1
][
2
]
-
fSin0
*
kA
[
2
][
2
]);
kS
[
2
]
=
fSin0
*
fSin1
*
kA
[
1
][
1
]
+
fCos1
*
(
fSin0
*
kA
[
1
][
2
]
+
fCos0
*
kA
[
2
][
2
]);
break
;
}
}
else
{
if
(
bTest2
)
{
// 2x2 closed form factorization
fTmp
=
(
kA
[
0
][
0
]
*
kA
[
0
][
0
]
+
kA
[
1
][
1
]
*
kA
[
1
][
1
]
-
kA
[
0
][
1
]
*
kA
[
0
][
1
])
/
(
kA
[
0
][
1
]
*
kA
[
1
][
1
]);
fTan0
=
0.5f
*
(
-
fTmp
+
Maths
::
Sqrt
(
fTmp
*
fTmp
+
4.0f
));
fCos0
=
Maths
::
InvSqrt
(
T
(
1
)
+
fTan0
*
fTan0
);
fSin0
=
fTan0
*
fCos0
;
for
(
col
=
0
;
col
<
3
;
col
++
)
{
fTmp0
=
kL
[
col
][
0
];
fTmp1
=
kL
[
col
][
1
];
kL
[
col
][
0
]
=
fCos0
*
fTmp0
-
fSin0
*
fTmp1
;
kL
[
col
][
1
]
=
fSin0
*
fTmp0
+
fCos0
*
fTmp1
;
}
fTan1
=
(
kA
[
0
][
1
]
-
kA
[
1
][
1
]
*
fTan0
)
/
kA
[
0
][
0
];
fCos1
=
Maths
::
InvSqrt
(
T
(
1
)
+
fTan1
*
fTan1
);
fSin1
=
-
fTan1
*
fCos1
;
for
(
row
=
0
;
row
<
3
;
row
++
)
{
fTmp0
=
kR
[
0
][
row
];
fTmp1
=
kR
[
1
][
row
];
kR
[
0
][
row
]
=
fCos1
*
fTmp0
-
fSin1
*
fTmp1
;
kR
[
1
][
row
]
=
fSin1
*
fTmp0
+
fCos1
*
fTmp1
;
}
kS
[
0
]
=
fCos0
*
fCos1
*
kA
[
0
][
0
]
-
fSin1
*
(
fCos0
*
kA
[
0
][
1
]
-
fSin0
*
kA
[
1
][
1
]);
kS
[
1
]
=
fSin0
*
fSin1
*
kA
[
0
][
0
]
+
fCos1
*
(
fSin0
*
kA
[
0
][
1
]
+
fCos0
*
kA
[
1
][
1
]);
kS
[
2
]
=
kA
[
2
][
2
];
break
;
}
else
{
GolubKahanStep
(
kA
,
kL
,
kR
);
}
}
}
// positize diagonal
for
(
row
=
0
;
row
<
3
;
row
++
)
{
if
(
kS
[
row
]
<
T
(
0
)
)
{
kS
[
row
]
=
-
kS
[
row
];
for
(
col
=
0
;
col
<
3
;
col
++
)
kR
[
row
][
col
]
=
-
kR
[
row
][
col
];
}
}
}
void
SingularValueComposition
(
const
Matrix3Generic
<
T
>&
kL
,
const
Vector3
&
kS
,
const
Matrix3Generic
<
T
>&
kR
)
{
Size
row
,
col
;
Matrix3Generic
<
T
>
kTmp
;
// product S*R
for
(
row
=
0
;
row
<
3
;
row
++
)
{
for
(
col
=
0
;
col
<
3
;
col
++
)
{
kTmp
[
row
][
col
]
=
kS
[
row
]
*
kR
[
row
][
col
];
}
}
// product L*S*R
for
(
row
=
0
;
row
<
3
;
row
++
)
{
for
(
col
=
0
;
col
<
3
;
col
++
)
{
m
[
row
][
col
]
=
T
(
0
);
for
(
int
iMid
=
0
;
iMid
<
3
;
iMid
++
)
{
m
[
row
][
col
]
+=
kL
[
row
][
iMid
]
*
kTmp
[
iMid
][
col
];
}
}
}
}
// Gram-Schmidt orthonormalisation (applied to columns of rotation matrix)
void
Orthonormalise
()
{
// Algorithm uses Gram-Schmidt orthogonalisation. If 'this' matrix is
// M = [m0|m1|m2], then orthonormal output matrix is Q = [q0|q1|q2],
//
// q0 = m0/|m0|
// q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0|
// q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1|
//
// where |V| indicates length of vector V and A*B indicates dot
// product of vectors A and B.
// compute q0
T
fInvLength
=
Maths
::
InvSqrt
(
m
[
0
][
0
]
*
m
[
0
][
0
]
+
m
[
1
][
0
]
*
m
[
1
][
0
]
+
m
[
2
][
0
]
*
m
[
2
][
0
]);
m
[
0
][
0
]
*=
fInvLength
;
m
[
1
][
0
]
*=
fInvLength
;
m
[
2
][
0
]
*=
fInvLength
;
// compute q1
T
fDot0
=
m
[
0
][
0
]
*
m
[
0
][
1
]
+
m
[
1
][
0
]
*
m
[
1
][
1
]
+
m
[
2
][
0
]
*
m
[
2
][
1
];
m
[
0
][
1
]
-=
fDot0
*
m
[
0
][
0
];
m
[
1
][
1
]
-=
fDot0
*
m
[
1
][
0
];
m
[
2
][
1
]
-=
fDot0
*
m
[
2
][
0
];
fInvLength
=
Maths
::
InvSqrt
(
m
[
0
][
1
]
*
m
[
0
][
1
]
+
m
[
1
][
1
]
*
m
[
1
][
1
]
+
m
[
2
][
1
]
*
m
[
2
][
1
]);
m
[
0
][
1
]
*=
fInvLength
;
m
[
1
][
1
]
*=
fInvLength
;
m
[
2
][
1
]
*=
fInvLength
;
// compute q2
T
fDot1
=
m
[
0
][
1
]
*
m
[
0
][
2
]
+
m
[
1
][
1
]
*
m
[
1
][
2
]
+
m
[
2
][
1
]
*
m
[
2
][
2
];
fDot0
=
m
[
0
][
0
]
*
m
[
0
][
2
]
+
m
[
1
][
0
]
*
m
[
1
][
2
]
+
m
[
2
][
0
]
*
m
[
2
][
2
];
m
[
0
][
2
]
-=
fDot0
*
m
[
0
][
0
]
+
fDot1
*
m
[
0
][
1
];
m
[
1
][
2
]
-=
fDot0
*
m
[
1
][
0
]
+
fDot1
*
m
[
1
][
1
];
m
[
2
][
2
]
-=
fDot0
*
m
[
2
][
0
]
+
fDot1
*
m
[
2
][
1
];
fInvLength
=
Maths
::
InvSqrt
(
m
[
0
][
2
]
*
m
[
0
][
2
]
+
m
[
1
][
2
]
*
m
[
1
][
2
]
+
m
[
2
][
2
]
*
m
[
2
][
2
]);
m
[
0
][
2
]
*=
fInvLength
;
m
[
1
][
2
]
*=
fInvLength
;
m
[
2
][
2
]
*=
fInvLength
;
}
// orthogonal Q, diagonal D, upper triangular U stored as (u01,u02,u12)
void
QDUDecomposition
(
Matrix3Generic
<
T
>&
kQ
,
Vector3Generic
<
T
>&
kD
,
Vector3Generic
<
T
>&
kU
)
const
{
// Factor M = QR = QDU where Q is orthogonal, D is diagonal,
// and U is upper triangular with ones on its diagonal. Algorithm uses
// Gram-Schmidt orthogonalization (the QR algorithm).
//
// If M = [ m0 | m1 | m2 ] and Q = [ q0 | q1 | q2 ], then
//
// q0 = m0/|m0|
// q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0|
// q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1|
//
// where |V| indicates length of vector V and A*B indicates dot
// product of vectors A and B. The matrix R has entries
//
// r00 = q0*m0 r01 = q0*m1 r02 = q0*m2
// r10 = 0 r11 = q1*m1 r12 = q1*m2
// r20 = 0 r21 = 0 r22 = q2*m2
//
// so D = diag(r00,r11,r22) and U has entries u01 = r01/r00,
// u02 = r02/r00, and u12 = r12/r11.
// Q = rotation
// D = scaling
// U = shear
// D stores the three diagonal entries r00, r11, r22
// U stores the entries U[0] = u01, U[1] = u02, U[2] = u12
// build orthogonal matrix Q
T
fInvLength
=
Maths
::
InvSqrt
(
m
[
0
][
0
]
*
m
[
0
][
0
]
+
m
[
1
][
0
]
*
m
[
1
][
0
]
+
m
[
2
][
0
]
*
m
[
2
][
0
]);
kQ
[
0
][
0
]
=
m
[
0
][
0
]
*
fInvLength
;
kQ
[
1
][
0
]
=
m
[
1
][
0
]
*
fInvLength
;
kQ
[
2
][
0
]
=
m
[
2
][
0
]
*
fInvLength
;
T
fDot
=
kQ
[
0
][
0
]
*
m
[
0
][
1
]
+
kQ
[
1
][
0
]
*
m
[
1
][
1
]
+
kQ
[
2
][
0
]
*
m
[
2
][
1
];
kQ
[
0
][
1
]
=
m
[
0
][
1
]
-
fDot
*
kQ
[
0
][
0
];
kQ
[
1
][
1
]
=
m
[
1
][
1
]
-
fDot
*
kQ
[
1
][
0
];
kQ
[
2
][
1
]
=
m
[
2
][
1
]
-
fDot
*
kQ
[
2
][
0
];
fInvLength
=
Maths
::
InvSqrt
(
kQ
[
0
][
1
]
*
kQ
[
0
][
1
]
+
kQ
[
1
][
1
]
*
kQ
[
1
][
1
]
+
kQ
[
2
][
1
]
*
kQ
[
2
][
1
]);
kQ
[
0
][
1
]
*=
fInvLength
;
kQ
[
1
][
1
]
*=
fInvLength
;
kQ
[
2
][
1
]
*=
fInvLength
;
fDot
=
kQ
[
0
][
0
]
*
m
[
0
][
2
]
+
kQ
[
1
][
0
]
*
m
[
1
][
2
]
+
kQ
[
2
][
0
]
*
m
[
2
][
2
];
kQ
[
0
][
2
]
=
m
[
0
][
2
]
-
fDot
*
kQ
[
0
][
0
];
kQ
[
1
][
2
]
=
m
[
1
][
2
]
-
fDot
*
kQ
[
1
][
0
];
kQ
[
2
][
2
]
=
m
[
2
][
2
]
-
fDot
*
kQ
[
2
][
0
];
fDot
=
kQ
[
0
][
1
]
*
m
[
0
][
2
]
+
kQ
[
1
][
1
]
*
m
[
1
][
2
]
+
kQ
[
2
][
1
]
*
m
[
2
][
2
];
kQ
[
0
][
2
]
-=
fDot
*
kQ
[
0
][
1
];
kQ
[
1
][
2
]
-=
fDot
*
kQ
[
1
][
1
];
kQ
[
2
][
2
]
-=
fDot
*
kQ
[
2
][
1
];
fInvLength
=
Maths
::
InvSqrt
(
kQ
[
0
][
2
]
*
kQ
[
0
][
2
]
+
kQ
[
1
][
2
]
*
kQ
[
1
][
2
]
+
kQ
[
2
][
2
]
*
kQ
[
2
][
2
]);
kQ
[
0
][
2
]
*=
fInvLength
;
kQ
[
1
][
2
]
*=
fInvLength
;
kQ
[
2
][
2
]
*=
fInvLength
;
// guarantee that orthogonal matrix has determinant 1 (no reflections)
T
fDet
=
kQ
[
0
][
0
]
*
kQ
[
1
][
1
]
*
kQ
[
2
][
2
]
+
kQ
[
0
][
1
]
*
kQ
[
1
][
2
]
*
kQ
[
2
][
0
]
+
kQ
[
0
][
2
]
*
kQ
[
1
][
0
]
*
kQ
[
2
][
1
]
-
kQ
[
0
][
2
]
*
kQ
[
1
][
1
]
*
kQ
[
2
][
0
]
-
kQ
[
0
][
1
]
*
kQ
[
1
][
0
]
*
kQ
[
2
][
2
]
-
kQ
[
0
][
0
]
*
kQ
[
1
][
2
]
*
kQ
[
2
][
1
];
if
(
fDet
<
T
(
0
)
)
{
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
kQ
[
row
][
col
]
=
-
kQ
[
row
][
col
];
}
}
}
// build "right" matrix R
Matrix3Generic
<
T
>
kR
;
kR
[
0
][
0
]
=
kQ
[
0
][
0
]
*
m
[
0
][
0
]
+
kQ
[
1
][
0
]
*
m
[
1
][
0
]
+
kQ
[
2
][
0
]
*
m
[
2
][
0
];
kR
[
0
][
1
]
=
kQ
[
0
][
0
]
*
m
[
0
][
1
]
+
kQ
[
1
][
0
]
*
m
[
1
][
1
]
+
kQ
[
2
][
0
]
*
m
[
2
][
1
];
kR
[
1
][
1
]
=
kQ
[
0
][
1
]
*
m
[
0
][
1
]
+
kQ
[
1
][
1
]
*
m
[
1
][
1
]
+
kQ
[
2
][
1
]
*
m
[
2
][
1
];
kR
[
0
][
2
]
=
kQ
[
0
][
0
]
*
m
[
0
][
2
]
+
kQ
[
1
][
0
]
*
m
[
1
][
2
]
+
kQ
[
2
][
0
]
*
m
[
2
][
2
];
kR
[
1
][
2
]
=
kQ
[
0
][
1
]
*
m
[
0
][
2
]
+
kQ
[
1
][
1
]
*
m
[
1
][
2
]
+
kQ
[
2
][
1
]
*
m
[
2
][
2
];
kR
[
2
][
2
]
=
kQ
[
0
][
2
]
*
m
[
0
][
2
]
+
kQ
[
1
][
2
]
*
m
[
1
][
2
]
+
kQ
[
2
][
2
]
*
m
[
2
][
2
];
// the scaling component
kD
[
0
]
=
kR
[
0
][
0
];
kD
[
1
]
=
kR
[
1
][
1
];
kD
[
2
]
=
kR
[
2
][
2
];
// the shear component
T
fInvD0
=
T
(
1
)
/
kD
[
0
];
kU
[
0
]
=
kR
[
0
][
1
]
*
fInvD0
;
kU
[
1
]
=
kR
[
0
][
2
]
*
fInvD0
;
kU
[
2
]
=
kR
[
1
][
2
]
/
kD
[
1
];
}
T
SpectralNorm
()
const
{
Matrix3Generic
<
T
>
kP
;
Size
row
,
col
;
T
fPmax
=
T
(
0
);
for
(
row
=
0
;
row
<
3
;
row
++
)
{
for
(
col
=
0
;
col
<
3
;
col
++
)
{
kP
[
row
][
col
]
=
T
(
0
);
for
(
int
iMid
=
0
;
iMid
<
3
;
iMid
++
)
{
kP
[
row
][
col
]
+=
m
[
iMid
][
row
]
*
m
[
iMid
][
col
];
}
if
(
kP
[
row
][
col
]
>
fPmax
)
{
fPmax
=
kP
[
row
][
col
];
}
}
}
T
fInvPmax
=
T
(
1
)
/
fPmax
;
for
(
row
=
0
;
row
<
3
;
row
++
)
{
for
(
col
=
0
;
col
<
3
;
col
++
)
{
kP
[
row
][
col
]
*=
fInvPmax
;
}
}
T
afCoeff
[
3
];
afCoeff
[
0
]
=
-
(
kP
[
0
][
0
]
*
(
kP
[
1
][
1
]
*
kP
[
2
][
2
]
-
kP
[
1
][
2
]
*
kP
[
2
][
1
])
+
kP
[
0
][
1
]
*
(
kP
[
2
][
0
]
*
kP
[
1
][
2
]
-
kP
[
1
][
0
]
*
kP
[
2
][
2
])
+
kP
[
0
][
2
]
*
(
kP
[
1
][
0
]
*
kP
[
2
][
1
]
-
kP
[
2
][
0
]
*
kP
[
1
][
1
]));
afCoeff
[
1
]
=
kP
[
0
][
0
]
*
kP
[
1
][
1
]
-
kP
[
0
][
1
]
*
kP
[
1
][
0
]
+
kP
[
0
][
0
]
*
kP
[
2
][
2
]
-
kP
[
0
][
2
]
*
kP
[
2
][
0
]
+
kP
[
1
][
1
]
*
kP
[
2
][
2
]
-
kP
[
1
][
2
]
*
kP
[
2
][
1
];
afCoeff
[
2
]
=
-
(
kP
[
0
][
0
]
+
kP
[
1
][
1
]
+
kP
[
2
][
2
]);
T
fRoot
=
MaxCubicRoot
(
afCoeff
);
T
fNorm
=
Maths
::
Sqrt
(
fPmax
*
fRoot
);
return
fNorm
;
}
// matrix must be orthonormal
void
ToAxisAngle
(
Vector3
&
rkAxis
,
Radian
&
rfRadians
)
const
{
// Let (x,y,z) be the unit-length axis and let A be an angle of rotation.
// The rotation matrix is R = I + sin(A)*P + (1-cos(A))*P^2 where
// I is the identity and
//
// +- -+
// P = | 0 -z +y |
// | +z 0 -x |
// | -y +x 0 |
// +- -+
//
// If A > 0, R represents a counterclockwise rotation about the axis in
// the sense of looking from the tip of the axis vector towards the
// origin. Some algebra will show that
//
// cos(A) = (trace(R)-1)/2 and R - R^t = 2*sin(A)*P
//
// In the event that A = pi, R-R^t = 0 which prevents us from extracting
// the axis through P. Instead note that R = I+2*P^2 when A = pi, so
// P^2 = (R-I)/2. The diagonal entries of P^2 are x^2-1, y^2-1, and
// z^2-1. We can solve these for axis (x,y,z). Because the angle is pi,
// it does not matter which sign you choose on the square roots.
T
fTrace
=
m
[
0
][
0
]
+
m
[
1
][
1
]
+
m
[
2
][
2
];
T
fCos
=
0.5f
*
(
fTrace
-
T
(
1
));
rfRadians
=
Maths
::
ACos
(
fCos
);
// in [0,PI]
if
(
rfRadians
>
Radian
(
0
)
)
{
if
(
rfRadians
<
Radian
(
Maths
::
PI
))
{
rkAxis
.
x
=
m
[
2
][
1
]
-
m
[
1
][
2
];
rkAxis
.
y
=
m
[
0
][
2
]
-
m
[
2
][
0
];
rkAxis
.
z
=
m
[
1
][
0
]
-
m
[
0
][
1
];
rkAxis
.
Normalise
();
}
else
{
// angle is PI
T
fHalfInverse
;
if
(
m
[
0
][
0
]
>=
m
[
1
][
1
]
)
{
// r00 >= r11
if
(
m
[
0
][
0
]
>=
m
[
2
][
2
]
)
{
// r00 is maximum diagonal term
rkAxis
.
x
=
0.5f
*
Maths
::
Sqrt
(
m
[
0
][
0
]
-
m
[
1
][
1
]
-
m
[
2
][
2
]
+
T
(
1
));
fHalfInverse
=
0.5f
/
rkAxis
.
x
;
rkAxis
.
y
=
fHalfInverse
*
m
[
0
][
1
];
rkAxis
.
z
=
fHalfInverse
*
m
[
0
][
2
];
}
else
{
// r22 is maximum diagonal term
rkAxis
.
z
=
0.5f
*
Maths
::
Sqrt
(
m
[
2
][
2
]
-
m
[
0
][
0
]
-
m
[
1
][
1
]
+
T
(
1
));
fHalfInverse
=
0.5f
/
rkAxis
.
z
;
rkAxis
.
x
=
fHalfInverse
*
m
[
0
][
2
];
rkAxis
.
y
=
fHalfInverse
*
m
[
1
][
2
];
}
}
else
{
// r11 > r00
if
(
m
[
1
][
1
]
>=
m
[
2
][
2
]
)
{
// r11 is maximum diagonal term
rkAxis
.
y
=
0.5f
*
Maths
::
Sqrt
(
m
[
1
][
1
]
-
m
[
0
][
0
]
-
m
[
2
][
2
]
+
T
(
1
));
fHalfInverse
=
0.5f
/
rkAxis
.
y
;
rkAxis
.
x
=
fHalfInverse
*
m
[
0
][
1
];
rkAxis
.
z
=
fHalfInverse
*
m
[
1
][
2
];
}
else
{
// r22 is maximum diagonal term
rkAxis
.
z
=
0.5f
*
Maths
::
Sqrt
(
m
[
2
][
2
]
-
m
[
0
][
0
]
-
m
[
1
][
1
]
+
T
(
1
));
fHalfInverse
=
0.5f
/
rkAxis
.
z
;
rkAxis
.
x
=
fHalfInverse
*
m
[
0
][
2
];
rkAxis
.
y
=
fHalfInverse
*
m
[
1
][
2
];
}
}
}
}
else
{
// The angle is 0 and the matrix is the identity. Any axis will
// work, so just use the x-axis.
rkAxis
.
x
=
T
(
1
);
rkAxis
.
y
=
T
(
0
);
rkAxis
.
z
=
T
(
0
);
}
}
inline
void
ToAxisAngle
(
Vector3
&
rkAxis
,
Degree
&
rfAngle
)
const
{
Radian
r
;
ToAxisAngle
(
rkAxis
,
r
);
rfAngle
=
r
;
}
void
FromAxisAngle
(
const
Vector3
&
rkAxis
,
const
Radian
&
fRadians
)
{
T
fCos
=
Maths
::
Cos
(
fRadians
);
T
fSin
=
Maths
::
Sin
(
fRadians
);
T
fOneMinusCos
=
T
(
1
)
-
fCos
;
T
fX2
=
rkAxis
.
x
*
rkAxis
.
x
;
T
fY2
=
rkAxis
.
y
*
rkAxis
.
y
;
T
fZ2
=
rkAxis
.
z
*
rkAxis
.
z
;
T
fXYM
=
rkAxis
.
x
*
rkAxis
.
y
*
fOneMinusCos
;
T
fXZM
=
rkAxis
.
x
*
rkAxis
.
z
*
fOneMinusCos
;
T
fYZM
=
rkAxis
.
y
*
rkAxis
.
z
*
fOneMinusCos
;
T
fXSin
=
rkAxis
.
x
*
fSin
;
T
fYSin
=
rkAxis
.
y
*
fSin
;
T
fZSin
=
rkAxis
.
z
*
fSin
;
m
[
0
][
0
]
=
fX2
*
fOneMinusCos
+
fCos
;
m
[
0
][
1
]
=
fXYM
-
fZSin
;
m
[
0
][
2
]
=
fXZM
+
fYSin
;
m
[
1
][
0
]
=
fXYM
+
fZSin
;
m
[
1
][
1
]
=
fY2
*
fOneMinusCos
+
fCos
;
m
[
1
][
2
]
=
fYZM
-
fXSin
;
m
[
2
][
0
]
=
fXZM
-
fYSin
;
m
[
2
][
1
]
=
fYZM
+
fXSin
;
m
[
2
][
2
]
=
fZ2
*
fOneMinusCos
+
fCos
;
}
// The matrix must be orthonormal. The decomposition is yaw*pitch*roll
// where yaw is rotation about the Up vector, pitch is rotation about the
// Right axis, and roll is rotation about the Direction axis.
bool
ToEulerAnglesXYZ
(
Radian
&
rfYAngle
,
Radian
&
rfPAngle
,
Radian
&
rfRAngle
)
const
{
// rot = cy*cz -cy*sz sy
// cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
// -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
rfPAngle
=
Radian
(
Maths
::
ASin
(
m
[
0
][
2
]));
if
(
rfPAngle
<
Radian
(
Maths
::
HALF_PI
))
{
if
(
rfPAngle
>
Radian
(
-
Maths
::
HALF_PI
))
{
rfYAngle
=
Maths
::
ATan2
(
-
m
[
1
][
2
],
m
[
2
][
2
]);
rfRAngle
=
Maths
::
ATan2
(
-
m
[
0
][
1
],
m
[
0
][
0
]);
return
true
;
}
else
{
// WARNING. Not a unique solution.
Radian
fRmY
=
Maths
::
ATan2
(
m
[
1
][
0
],
m
[
1
][
1
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
rfRAngle
-
fRmY
;
return
false
;
}
}
else
{
// WARNING. Not a unique solution.
Radian
fRpY
=
Maths
::
ATan2
(
m
[
1
][
0
],
m
[
1
][
1
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
fRpY
-
rfRAngle
;
return
false
;
}
}
bool
ToEulerAnglesXZY
(
Radian
&
rfYAngle
,
Radian
&
rfPAngle
,
Radian
&
rfRAngle
)
const
{
// rot = cy*cz -sz cz*sy
// sx*sy+cx*cy*sz cx*cz -cy*sx+cx*sy*sz
// -cx*sy+cy*sx*sz cz*sx cx*cy+sx*sy*sz
rfPAngle
=
Maths
::
ASin
(
-
m
[
0
][
1
]);
if
(
rfPAngle
<
Radian
(
Maths
::
HALF_PI
))
{
if
(
rfPAngle
>
Radian
(
-
Maths
::
HALF_PI
))
{
rfYAngle
=
Maths
::
ATan2
(
m
[
2
][
1
],
m
[
1
][
1
]);
rfRAngle
=
Maths
::
ATan2
(
m
[
0
][
2
],
m
[
0
][
0
]);
return
true
;
}
else
{
// WARNING. Not a unique solution.
Radian
fRmY
=
Maths
::
ATan2
(
-
m
[
2
][
0
],
m
[
2
][
2
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
rfRAngle
-
fRmY
;
return
false
;
}
}
else
{
// WARNING. Not a unique solution.
Radian
fRpY
=
Maths
::
ATan2
(
-
m
[
2
][
0
],
m
[
2
][
2
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
fRpY
-
rfRAngle
;
return
false
;
}
}
bool
ToEulerAnglesYXZ
(
Radian
&
rfYAngle
,
Radian
&
rfPAngle
,
Radian
&
rfRAngle
)
const
{
// rot = cy*cz+sx*sy*sz cz*sx*sy-cy*sz cx*sy
// cx*sz cx*cz -sx
// -cz*sy+cy*sx*sz cy*cz*sx+sy*sz cx*cy
rfPAngle
=
Maths
::
ASin
(
-
m
[
1
][
2
]);
if
(
rfPAngle
<
Radian
(
Maths
::
HALF_PI
))
{
if
(
rfPAngle
>
Radian
(
-
Maths
::
HALF_PI
))
{
rfYAngle
=
Maths
::
ATan2
(
m
[
0
][
2
],
m
[
2
][
2
]);
rfRAngle
=
Maths
::
ATan2
(
m
[
1
][
0
],
m
[
1
][
1
]);
return
true
;
}
else
{
// WARNING. Not a unique solution.
Radian
fRmY
=
Maths
::
ATan2
(
-
m
[
0
][
1
],
m
[
0
][
0
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
rfRAngle
-
fRmY
;
return
false
;
}
}
else
{
// WARNING. Not a unique solution.
Radian
fRpY
=
Maths
::
ATan2
(
-
m
[
0
][
1
],
m
[
0
][
0
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
fRpY
-
rfRAngle
;
return
false
;
}
}
bool
ToEulerAnglesYZX
(
Radian
&
rfYAngle
,
Radian
&
rfPAngle
,
Radian
&
rfRAngle
)
const
{
// rot = cy*cz sx*sy-cx*cy*sz cx*sy+cy*sx*sz
// sz cx*cz -cz*sx
// -cz*sy cy*sx+cx*sy*sz cx*cy-sx*sy*sz
rfPAngle
=
Maths
::
ASin
(
m
[
1
][
0
]);
if
(
rfPAngle
<
Radian
(
Maths
::
HALF_PI
))
{
if
(
rfPAngle
>
Radian
(
-
Maths
::
HALF_PI
))
{
rfYAngle
=
Maths
::
ATan2
(
-
m
[
2
][
0
],
m
[
0
][
0
]);
rfRAngle
=
Maths
::
ATan2
(
-
m
[
1
][
2
],
m
[
1
][
1
]);
return
true
;
}
else
{
// WARNING. Not a unique solution.
Radian
fRmY
=
Maths
::
ATan2
(
m
[
2
][
1
],
m
[
2
][
2
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
rfRAngle
-
fRmY
;
return
false
;
}
}
else
{
// WARNING. Not a unique solution.
Radian
fRpY
=
Maths
::
ATan2
(
m
[
2
][
1
],
m
[
2
][
2
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
fRpY
-
rfRAngle
;
return
false
;
}
}
bool
ToEulerAnglesZXY
(
Radian
&
rfYAngle
,
Radian
&
rfPAngle
,
Radian
&
rfRAngle
)
const
{
// rot = cy*cz-sx*sy*sz -cx*sz cz*sy+cy*sx*sz
// cz*sx*sy+cy*sz cx*cz -cy*cz*sx+sy*sz
// -cx*sy sx cx*cy
rfPAngle
=
Maths
::
ASin
(
m
[
2
][
1
]);
if
(
rfPAngle
<
Radian
(
Maths
::
HALF_PI
))
{
if
(
rfPAngle
>
Radian
(
-
Maths
::
HALF_PI
))
{
rfYAngle
=
Maths
::
ATan2
(
-
m
[
0
][
1
],
m
[
1
][
1
]);
rfRAngle
=
Maths
::
ATan2
(
-
m
[
2
][
0
],
m
[
2
][
2
]);
return
true
;
}
else
{
// WARNING. Not a unique solution.
Radian
fRmY
=
Maths
::
ATan2
(
m
[
0
][
2
],
m
[
0
][
0
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
rfRAngle
-
fRmY
;
return
false
;
}
}
else
{
// WARNING. Not a unique solution.
Radian
fRpY
=
Maths
::
ATan2
(
m
[
0
][
2
],
m
[
0
][
0
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
fRpY
-
rfRAngle
;
return
false
;
}
}
bool
ToEulerAnglesZYX
(
Radian
&
rfYAngle
,
Radian
&
rfPAngle
,
Radian
&
rfRAngle
)
const
{
// rot = cy*cz cz*sx*sy-cx*sz cx*cz*sy+sx*sz
// cy*sz cx*cz+sx*sy*sz -cz*sx+cx*sy*sz
// -sy cy*sx cx*cy
rfPAngle
=
Maths
::
ASin
(
-
m
[
2
][
0
]);
if
(
rfPAngle
<
Radian
(
Maths
::
HALF_PI
))
{
if
(
rfPAngle
>
Radian
(
-
Maths
::
HALF_PI
))
{
rfYAngle
=
Maths
::
ATan2
(
m
[
1
][
0
],
m
[
0
][
0
]);
rfRAngle
=
Maths
::
ATan2
(
m
[
2
][
1
],
m
[
2
][
2
]);
return
true
;
}
else
{
// WARNING. Not a unique solution.
Radian
fRmY
=
Maths
::
ATan2
(
-
m
[
0
][
1
],
m
[
0
][
2
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
rfRAngle
-
fRmY
;
return
false
;
}
}
else
{
// WARNING. Not a unique solution.
Radian
fRpY
=
Maths
::
ATan2
(
-
m
[
0
][
1
],
m
[
0
][
2
]);
rfRAngle
=
Radian
(
0
);
// any angle works
rfYAngle
=
fRpY
-
rfRAngle
;
return
false
;
}
}
void
FromEulerAnglesXYZ
(
const
Radian
&
fYAngle
,
const
Radian
&
fPAngle
,
const
Radian
&
fRAngle
)
{
T
fCos
,
fSin
;
fCos
=
Maths
::
Cos
(
fYAngle
);
fSin
=
Maths
::
Sin
(
fYAngle
);
Matrix3Generic
<
T
>
kXMat
(
T
(
1
),
T
(
0
),
T
(
0
),
T
(
0
),
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
);
fCos
=
Maths
::
Cos
(
fPAngle
);
fSin
=
Maths
::
Sin
(
fPAngle
);
Matrix3Generic
<
T
>
kYMat
(
fCos
,
T
(
0
),
fSin
,
T
(
0
),
T
(
1
),
T
(
0
),
-
fSin
,
T
(
0
),
fCos
);
fCos
=
Maths
::
Cos
(
fRAngle
);
fSin
=
Maths
::
Sin
(
fRAngle
);
Matrix3Generic
<
T
>
kZMat
(
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
,
T
(
0
),
T
(
0
),
T
(
0
),
T
(
1
));
*
this
=
kXMat
*
(
kYMat
*
kZMat
);
}
void
FromEulerAnglesXZY
(
const
Radian
&
fYAngle
,
const
Radian
&
fPAngle
,
const
Radian
&
fRAngle
)
{
T
fCos
,
fSin
;
fCos
=
Maths
::
Cos
(
fYAngle
);
fSin
=
Maths
::
Sin
(
fYAngle
);
Matrix3Generic
<
T
>
kXMat
(
T
(
1
),
T
(
0
),
T
(
0
),
T
(
0
),
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
);
fCos
=
Maths
::
Cos
(
fPAngle
);
fSin
=
Maths
::
Sin
(
fPAngle
);
Matrix3Generic
<
T
>
kZMat
(
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
,
T
(
0
),
T
(
0
),
T
(
0
),
T
(
1
));
fCos
=
Maths
::
Cos
(
fRAngle
);
fSin
=
Maths
::
Sin
(
fRAngle
);
Matrix3Generic
<
T
>
kYMat
(
fCos
,
T
(
0
),
fSin
,
T
(
0
),
T
(
1
),
T
(
0
),
-
fSin
,
T
(
0
),
fCos
);
*
this
=
kXMat
*
(
kZMat
*
kYMat
);
}
void
FromEulerAnglesYXZ
(
const
Radian
&
fYAngle
,
const
Radian
&
fPAngle
,
const
Radian
&
fRAngle
)
{
T
fCos
,
fSin
;
fCos
=
Maths
::
Cos
(
fYAngle
);
fSin
=
Maths
::
Sin
(
fYAngle
);
Matrix3Generic
<
T
>
kYMat
(
fCos
,
T
(
0
),
fSin
,
T
(
0
),
T
(
1
),
T
(
0
),
-
fSin
,
T
(
0
),
fCos
);
fCos
=
Maths
::
Cos
(
fPAngle
);
fSin
=
Maths
::
Sin
(
fPAngle
);
Matrix3Generic
<
T
>
kXMat
(
T
(
1
),
T
(
0
),
T
(
0
),
T
(
0
),
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
);
fCos
=
Maths
::
Cos
(
fRAngle
);
fSin
=
Maths
::
Sin
(
fRAngle
);
Matrix3Generic
<
T
>
kZMat
(
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
,
T
(
0
),
T
(
0
),
T
(
0
),
T
(
1
));
*
this
=
kYMat
*
(
kXMat
*
kZMat
);
}
void
FromEulerAnglesYZX
(
const
Radian
&
fYAngle
,
const
Radian
&
fPAngle
,
const
Radian
&
fRAngle
)
{
T
fCos
,
fSin
;
fCos
=
Maths
::
Cos
(
fYAngle
);
fSin
=
Maths
::
Sin
(
fYAngle
);
Matrix3Generic
<
T
>
kYMat
(
fCos
,
T
(
0
),
fSin
,
T
(
0
),
T
(
1
),
T
(
0
),
-
fSin
,
T
(
0
),
fCos
);
fCos
=
Maths
::
Cos
(
fPAngle
);
fSin
=
Maths
::
Sin
(
fPAngle
);
Matrix3Generic
<
T
>
kZMat
(
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
,
T
(
0
),
T
(
0
),
T
(
0
),
T
(
1
));
fCos
=
Maths
::
Cos
(
fRAngle
);
fSin
=
Maths
::
Sin
(
fRAngle
);
Matrix3Generic
<
T
>
kXMat
(
T
(
1
),
T
(
0
),
T
(
0
),
T
(
0
),
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
);
*
this
=
kYMat
*
(
kZMat
*
kXMat
);
}
void
FromEulerAnglesZXY
(
const
Radian
&
fYAngle
,
const
Radian
&
fPAngle
,
const
Radian
&
fRAngle
)
{
T
fCos
,
fSin
;
fCos
=
Maths
::
Cos
(
fYAngle
);
fSin
=
Maths
::
Sin
(
fYAngle
);
Matrix3Generic
<
T
>
kZMat
(
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
,
T
(
0
),
T
(
0
),
T
(
0
),
T
(
1
));
fCos
=
Maths
::
Cos
(
fPAngle
);
fSin
=
Maths
::
Sin
(
fPAngle
);
Matrix3Generic
<
T
>
kXMat
(
T
(
1
),
T
(
0
),
T
(
0
),
T
(
0
),
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
);
fCos
=
Maths
::
Cos
(
fRAngle
);
fSin
=
Maths
::
Sin
(
fRAngle
);
Matrix3Generic
<
T
>
kYMat
(
fCos
,
T
(
0
),
fSin
,
T
(
0
),
T
(
1
),
T
(
0
),
-
fSin
,
T
(
0
),
fCos
);
*
this
=
kZMat
*
(
kXMat
*
kYMat
);
}
void
FromEulerAnglesZYX
(
const
Radian
&
fYAngle
,
const
Radian
&
fPAngle
,
const
Radian
&
fRAngle
)
{
T
fCos
,
fSin
;
fCos
=
Maths
::
Cos
(
fYAngle
);
fSin
=
Maths
::
Sin
(
fYAngle
);
Matrix3Generic
<
T
>
kZMat
(
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
,
T
(
0
),
T
(
0
),
T
(
0
),
T
(
1
));
fCos
=
Maths
::
Cos
(
fPAngle
);
fSin
=
Maths
::
Sin
(
fPAngle
);
Matrix3Generic
<
T
>
kYMat
(
fCos
,
T
(
0
),
fSin
,
T
(
0
),
T
(
1
),
T
(
0
),
-
fSin
,
T
(
0
),
fCos
);
fCos
=
Maths
::
Cos
(
fRAngle
);
fSin
=
Maths
::
Sin
(
fRAngle
);
Matrix3Generic
<
T
>
kXMat
(
T
(
1
),
T
(
0
),
T
(
0
),
T
(
0
),
fCos
,
-
fSin
,
T
(
0
),
fSin
,
fCos
);
*
this
=
kZMat
*
(
kYMat
*
kXMat
);
}
// eigensolver, matrix must be symmetric
void
EigenSolveSymmetric
(
T
afEigenvalue
[
3
],
Vector3
akEigenvector
[
3
])
const
{
Matrix3Generic
<
T
>
kMatrix
=
*
this
;
T
afSubDiag
[
3
];
kMatrix
.
Tridiagonal
(
afEigenvalue
,
afSubDiag
);
kMatrix
.
QLAlgorithm
(
afEigenvalue
,
afSubDiag
);
for
(
Size
i
=
0
;
i
<
3
;
i
++
)
{
akEigenvector
[
i
][
0
]
=
kMatrix
[
0
][
i
];
akEigenvector
[
i
][
1
]
=
kMatrix
[
1
][
i
];
akEigenvector
[
i
][
2
]
=
kMatrix
[
2
][
i
];
}
// make eigenvectors form a right--handed system
Vector3
kCross
=
akEigenvector
[
1
].
Cross
(
akEigenvector
[
2
]);
T
fDet
=
akEigenvector
[
0
].
Dot
(
kCross
);
if
(
fDet
<
T
(
0
)
)
{
akEigenvector
[
2
][
0
]
=
-
akEigenvector
[
2
][
0
];
akEigenvector
[
2
][
1
]
=
-
akEigenvector
[
2
][
1
];
akEigenvector
[
2
][
2
]
=
-
akEigenvector
[
2
][
2
];
}
}
static
void
TensorProduct
(
const
Vector3
&
rkU
,
const
Vector3
&
rkV
,
Matrix3Generic
<
T
>&
rkProduct
)
{
for
(
Size
row
=
0
;
row
<
3
;
row
++
)
{
for
(
Size
col
=
0
;
col
<
3
;
col
++
)
{
rkProduct
[
row
][
col
]
=
rkU
[
row
]
*
rkV
[
col
];
}
}
}
/** Determines if this matrix involves a scaling. */
inline
bool
HasScale
()
const
{
// check magnitude of column vectors (==local axes)
T
t
=
m
[
0
][
0
]
*
m
[
0
][
0
]
+
m
[
1
][
0
]
*
m
[
1
][
0
]
+
m
[
2
][
0
]
*
m
[
2
][
0
];
if
(
!
Maths
::
RealEqual
(
t
,
T
(
1
),
T
(
1e-04
)))
return
true
;
t
=
m
[
0
][
1
]
*
m
[
0
][
1
]
+
m
[
1
][
1
]
*
m
[
1
][
1
]
+
m
[
2
][
1
]
*
m
[
2
][
1
];
if
(
!
Maths
::
RealEqual
(
t
,
T
(
1
),
T
(
1e-04
)
))
return
true
;
t
=
m
[
0
][
2
]
*
m
[
0
][
2
]
+
m
[
1
][
2
]
*
m
[
1
][
2
]
+
m
[
2
][
2
]
*
m
[
2
][
2
];
if
(
!
Maths
::
RealEqual
(
t
,
T
(
1
),
T
(
1e-04
)))
return
true
;
return
false
;
}
/** Function for writing to a stream.
*/
inline
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
o
,
const
Matrix3Generic
<
T
>&
m
)
{
o
<<
"Matrix3("
<<
m
[
0
][
0
]
<<
","
<<
m
[
0
][
1
]
<<
","
<<
m
[
0
][
2
]
<<
","
<<
m
[
1
][
0
]
<<
","
<<
m
[
1
][
1
]
<<
","
<<
m
[
1
][
2
]
<<
","
<<
m
[
2
][
0
]
<<
","
<<
m
[
2
][
1
]
<<
","
<<
m
[
2
][
2
]
<<
")"
;
return
o
;
}
inline
friend
std
::
istream
&
operator
>>
(
std
::
istream
&
i
,
Matrix3Generic
<
T
>&
v
)
{
std
::
string
temp
;
i
>>
temp
;
//Read the input chunk to the first whitespace.
//temp should now be either one of the following formats:
//1. "Matrix3(00,01,02,10,11,12,20,21,22)" if it was converted using <<
//2. "00,01,02,10,11,12,20,21,22" if it was from some other input
if
(
!
Utils
::
String
::
VerifyConstructorAndExtractParameters
(
temp
,
"Matrix3"
))
{
v
=
Matrix3Generic
<
T
>::
IDENTITY
;
return
i
;
}
if
(
!
Utils
::
String
::
ConvertAndAssign
(
temp
,
&
(
v
.
m
[
0
][
0
]),
9
))
{
v
=
Matrix3Generic
<
T
>::
IDENTITY
;
i
.
setstate
(
std
::
ios_base
::
failbit
);
}
return
i
;
}
static
const
T
EPSILON
;
static
const
Matrix3Generic
<
T
>
ZERO
;
static
const
Matrix3Generic
<
T
>
IDENTITY
;
protected
:
// support for eigensolver
void
Tridiagonal
(
T
afDiag
[
3
],
T
afSubDiag
[
3
])
{
// Householder reduction T = Q^t M Q
// Input:
// mat, symmetric 3x3 matrix M
// Output:
// mat, orthogonal matrix Q
// diag, diagonal entries of T
// subd, subdiagonal entries of T (T is symmetric)
T
fA
=
m
[
0
][
0
];
T
fB
=
m
[
0
][
1
];
T
fC
=
m
[
0
][
2
];
T
fD
=
m
[
1
][
1
];
T
fE
=
m
[
1
][
2
];
T
fF
=
m
[
2
][
2
];
afDiag
[
0
]
=
fA
;
afSubDiag
[
2
]
=
T
(
0
);
if
(
Maths
::
Abs
(
fC
)
>=
EPSILON
)
{
T
fLength
=
Maths
::
Sqrt
(
fB
*
fB
+
fC
*
fC
);
T
fInvLength
=
T
(
1
)
/
fLength
;
fB
*=
fInvLength
;
fC
*=
fInvLength
;
T
fQ
=
T
(
2
)
*
fB
*
fE
+
fC
*
(
fF
-
fD
);
afDiag
[
1
]
=
fD
+
fC
*
fQ
;
afDiag
[
2
]
=
fF
-
fC
*
fQ
;
afSubDiag
[
0
]
=
fLength
;
afSubDiag
[
1
]
=
fE
-
fB
*
fQ
;
m
[
0
][
0
]
=
T
(
1
);
m
[
0
][
1
]
=
T
(
0
);
m
[
0
][
2
]
=
T
(
0
);
m
[
1
][
0
]
=
T
(
0
);
m
[
1
][
1
]
=
fB
;
m
[
1
][
2
]
=
fC
;
m
[
2
][
0
]
=
T
(
0
);
m
[
2
][
1
]
=
fC
;
m
[
2
][
2
]
=
-
fB
;
}
else
{
afDiag
[
1
]
=
fD
;
afDiag
[
2
]
=
fF
;
afSubDiag
[
0
]
=
fB
;
afSubDiag
[
1
]
=
fE
;
m
[
0
][
0
]
=
T
(
1
);
m
[
0
][
1
]
=
T
(
0
);
m
[
0
][
2
]
=
T
(
0
);
m
[
1
][
0
]
=
T
(
0
);
m
[
1
][
1
]
=
T
(
1
);
m
[
1
][
2
]
=
T
(
0
);
m
[
2
][
0
]
=
T
(
0
);
m
[
2
][
1
]
=
T
(
0
);
m
[
2
][
2
]
=
T
(
1
);
}
}
bool
QLAlgorithm
(
T
afDiag
[
3
],
T
afSubDiag
[
3
])
{
// QL iteration with implicit shifting to reduce matrix from tridiagonal
// to diagonal
for
(
int
i0
=
0
;
i0
<
3
;
i0
++
)
{
const
unsigned
int
iMaxIter
=
32
;
unsigned
int
iIter
;
for
(
iIter
=
0
;
iIter
<
iMaxIter
;
iIter
++
)
{
int
i1
;
for
(
i1
=
i0
;
i1
<=
1
;
i1
++
)
{
T
fSum
=
Maths
::
Abs
(
afDiag
[
i1
])
+
Maths
::
Abs
(
afDiag
[
i1
+
1
]);
if
(
Maths
::
Abs
(
afSubDiag
[
i1
])
+
fSum
==
fSum
)
break
;
}
if
(
i1
==
i0
)
break
;
T
fTmp0
=
(
afDiag
[
i0
+
1
]
-
afDiag
[
i0
])
/
(
T
(
2
)
*
afSubDiag
[
i0
]);
T
fTmp1
=
Maths
::
Sqrt
(
fTmp0
*
fTmp0
+
T
(
1
));
if
(
fTmp0
<
T
(
0
)
)
fTmp0
=
afDiag
[
i1
]
-
afDiag
[
i0
]
+
afSubDiag
[
i0
]
/
(
fTmp0
-
fTmp1
);
else
fTmp0
=
afDiag
[
i1
]
-
afDiag
[
i0
]
+
afSubDiag
[
i0
]
/
(
fTmp0
+
fTmp1
);
T
fSin
=
T
(
1
);
T
fCos
=
T
(
1
);
T
fTmp2
=
T
(
0
);
for
(
int
i2
=
i1
-
1
;
i2
>=
i0
;
i2
--
)
{
T
fTmp3
=
fSin
*
afSubDiag
[
i2
];
T
fTmp4
=
fCos
*
afSubDiag
[
i2
];
if
(
Maths
::
Abs
(
fTmp3
)
>=
Maths
::
Abs
(
fTmp0
))
{
fCos
=
fTmp0
/
fTmp3
;
fTmp1
=
Maths
::
Sqrt
(
fCos
*
fCos
+
T
(
1
));
afSubDiag
[
i2
+
1
]
=
fTmp3
*
fTmp1
;
fSin
=
T
(
1
)
/
fTmp1
;
fCos
*=
fSin
;
}
else
{
fSin
=
fTmp3
/
fTmp0
;
fTmp1
=
Maths
::
Sqrt
(
fSin
*
fSin
+
T
(
1
));
afSubDiag
[
i2
+
1
]
=
fTmp0
*
fTmp1
;
fCos
=
T
(
1
)
/
fTmp1
;
fSin
*=
fCos
;
}
fTmp0
=
afDiag
[
i2
+
1
]
-
fTmp2
;
fTmp1
=
(
afDiag
[
i2
]
-
fTmp0
)
*
fSin
+
T
(
2
)
*
fTmp4
*
fCos
;
fTmp2
=
fSin
*
fTmp1
;
afDiag
[
i2
+
1
]
=
fTmp0
+
fTmp2
;
fTmp0
=
fCos
*
fTmp1
-
fTmp4
;
for
(
int
row
=
0
;
row
<
3
;
row
++
)
{
fTmp3
=
m
[
row
][
i2
+
1
];
m
[
row
][
i2
+
1
]
=
fSin
*
m
[
row
][
i2
]
+
fCos
*
fTmp3
;
m
[
row
][
i2
]
=
fCos
*
m
[
row
][
i2
]
-
fSin
*
fTmp3
;
}
}
afDiag
[
i0
]
-=
fTmp2
;
afSubDiag
[
i0
]
=
fTmp0
;
afSubDiag
[
i1
]
=
T
(
0
);
}
if
(
iIter
==
iMaxIter
)
{
// should not get here under normal circumstances
return
false
;
}
}
return
true
;
}
// support for singular value decomposition
static
const
T
ms_fSvdEpsilon
;
static
const
unsigned
int
ms_iSvdMaxIterations
;
static
void
Bidiagonalize
(
Matrix3Generic
<
T
>&
kA
,
Matrix3Generic
<
T
>&
kL
,
Matrix3Generic
<
T
>&
kR
)
{
T
afV
[
3
],
afW
[
3
];
T
fLength
,
fSign
,
fT1
,
fInvT1
,
fT2
;
bool
bIdentity
;
// map first column to (*,0,0)
fLength
=
Maths
::
Sqrt
(
kA
[
0
][
0
]
*
kA
[
0
][
0
]
+
kA
[
1
][
0
]
*
kA
[
1
][
0
]
+
kA
[
2
][
0
]
*
kA
[
2
][
0
]);
if
(
fLength
>
T
(
0
)
)
{
fSign
=
(
kA
[
0
][
0
]
>
T
(
0
)
?
T
(
1
)
:
T
(
-
1
));
fT1
=
kA
[
0
][
0
]
+
fSign
*
fLength
;
fInvT1
=
T
(
1
)
/
fT1
;
afV
[
1
]
=
kA
[
1
][
0
]
*
fInvT1
;
afV
[
2
]
=
kA
[
2
][
0
]
*
fInvT1
;
fT2
=
T
(
-
2
)
/
(
T
(
1
)
+
afV
[
1
]
*
afV
[
1
]
+
afV
[
2
]
*
afV
[
2
]);
afW
[
0
]
=
fT2
*
(
kA
[
0
][
0
]
+
kA
[
1
][
0
]
*
afV
[
1
]
+
kA
[
2
][
0
]
*
afV
[
2
]);
afW
[
1
]
=
fT2
*
(
kA
[
0
][
1
]
+
kA
[
1
][
1
]
*
afV
[
1
]
+
kA
[
2
][
1
]
*
afV
[
2
]);
afW
[
2
]
=
fT2
*
(
kA
[
0
][
2
]
+
kA
[
1
][
2
]
*
afV
[
1
]
+
kA
[
2
][
2
]
*
afV
[
2
]);
kA
[
0
][
0
]
+=
afW
[
0
];
kA
[
0
][
1
]
+=
afW
[
1
];
kA
[
0
][
2
]
+=
afW
[
2
];
kA
[
1
][
1
]
+=
afV
[
1
]
*
afW
[
1
];
kA
[
1
][
2
]
+=
afV
[
1
]
*
afW
[
2
];
kA
[
2
][
1
]
+=
afV
[
2
]
*
afW
[
1
];
kA
[
2
][
2
]
+=
afV
[
2
]
*
afW
[
2
];
kL
[
0
][
0
]
=
T
(
1
)
+
fT2
;
kL
[
0
][
1
]
=
kL
[
1
][
0
]
=
fT2
*
afV
[
1
];
kL
[
0
][
2
]
=
kL
[
2
][
0
]
=
fT2
*
afV
[
2
];
kL
[
1
][
1
]
=
T
(
1
)
+
fT2
*
afV
[
1
]
*
afV
[
1
];
kL
[
1
][
2
]
=
kL
[
2
][
1
]
=
fT2
*
afV
[
1
]
*
afV
[
2
];
kL
[
2
][
2
]
=
T
(
1
)
+
fT2
*
afV
[
2
]
*
afV
[
2
];
bIdentity
=
false
;
}
else
{
kL
=
Matrix3Generic
<
T
>::
IDENTITY
;
bIdentity
=
true
;
}
// map first row to (*,*,0)
fLength
=
Maths
::
Sqrt
(
kA
[
0
][
1
]
*
kA
[
0
][
1
]
+
kA
[
0
][
2
]
*
kA
[
0
][
2
]);
if
(
fLength
>
T
(
0
)
)
{
fSign
=
(
kA
[
0
][
1
]
>
T
(
0
)
?
T
(
1
)
:
T
(
-
1
));
fT1
=
kA
[
0
][
1
]
+
fSign
*
fLength
;
afV
[
2
]
=
kA
[
0
][
2
]
/
fT1
;
fT2
=
T
(
-
2
)
/
(
T
(
1
)
+
afV
[
2
]
*
afV
[
2
]);
afW
[
0
]
=
fT2
*
(
kA
[
0
][
1
]
+
kA
[
0
][
2
]
*
afV
[
2
]);
afW
[
1
]
=
fT2
*
(
kA
[
1
][
1
]
+
kA
[
1
][
2
]
*
afV
[
2
]);
afW
[
2
]
=
fT2
*
(
kA
[
2
][
1
]
+
kA
[
2
][
2
]
*
afV
[
2
]);
kA
[
0
][
1
]
+=
afW
[
0
];
kA
[
1
][
1
]
+=
afW
[
1
];
kA
[
1
][
2
]
+=
afW
[
1
]
*
afV
[
2
];
kA
[
2
][
1
]
+=
afW
[
2
];
kA
[
2
][
2
]
+=
afW
[
2
]
*
afV
[
2
];
kR
[
0
][
0
]
=
T
(
1
);
kR
[
0
][
1
]
=
kR
[
1
][
0
]
=
T
(
0
);
kR
[
0
][
2
]
=
kR
[
2
][
0
]
=
T
(
0
);
kR
[
1
][
1
]
=
T
(
1
)
+
fT2
;
kR
[
1
][
2
]
=
kR
[
2
][
1
]
=
fT2
*
afV
[
2
];
kR
[
2
][
2
]
=
T
(
1
)
+
fT2
*
afV
[
2
]
*
afV
[
2
];
}
else
{
kR
=
Matrix3Generic
<
T
>::
IDENTITY
;
}
// map second column to (*,*,0)
fLength
=
Maths
::
Sqrt
(
kA
[
1
][
1
]
*
kA
[
1
][
1
]
+
kA
[
2
][
1
]
*
kA
[
2
][
1
]);
if
(
fLength
>
T
(
0
)
)
{
fSign
=
(
kA
[
1
][
1
]
>
T
(
0
)
?
T
(
1
)
:
T
(
-
1
));
fT1
=
kA
[
1
][
1
]
+
fSign
*
fLength
;
afV
[
2
]
=
kA
[
2
][
1
]
/
fT1
;
fT2
=
T
(
-
2
)
/
(
T
(
1
)
+
afV
[
2
]
*
afV
[
2
]);
afW
[
1
]
=
fT2
*
(
kA
[
1
][
1
]
+
kA
[
2
][
1
]
*
afV
[
2
]);
afW
[
2
]
=
fT2
*
(
kA
[
1
][
2
]
+
kA
[
2
][
2
]
*
afV
[
2
]);
kA
[
1
][
1
]
+=
afW
[
1
];
kA
[
1
][
2
]
+=
afW
[
2
];
kA
[
2
][
2
]
+=
afV
[
2
]
*
afW
[
2
];
T
fA
=
T
(
1
)
+
fT2
;
T
fB
=
fT2
*
afV
[
2
];
T
fC
=
T
(
1
)
+
fB
*
afV
[
2
];
if
(
bIdentity
)
{
kL
[
0
][
0
]
=
T
(
1
);
kL
[
0
][
1
]
=
kL
[
1
][
0
]
=
T
(
0
);
kL
[
0
][
2
]
=
kL
[
2
][
0
]
=
T
(
0
);
kL
[
1
][
1
]
=
fA
;
kL
[
1
][
2
]
=
kL
[
2
][
1
]
=
fB
;
kL
[
2
][
2
]
=
fC
;
}
else
{
for
(
int
row
=
0
;
row
<
3
;
row
++
)
{
T
fTmp0
=
kL
[
row
][
1
];
T
fTmp1
=
kL
[
row
][
2
];
kL
[
row
][
1
]
=
fA
*
fTmp0
+
fB
*
fTmp1
;
kL
[
row
][
2
]
=
fB
*
fTmp0
+
fC
*
fTmp1
;
}
}
}
}
static
void
GolubKahanStep
(
Matrix3Generic
<
T
>&
kA
,
Matrix3Generic
<
T
>&
kL
,
Matrix3Generic
<
T
>&
kR
)
{
T
fT11
=
kA
[
0
][
1
]
*
kA
[
0
][
1
]
+
kA
[
1
][
1
]
*
kA
[
1
][
1
];
T
fT22
=
kA
[
1
][
2
]
*
kA
[
1
][
2
]
+
kA
[
2
][
2
]
*
kA
[
2
][
2
];
T
fT12
=
kA
[
1
][
1
]
*
kA
[
1
][
2
];
T
fTrace
=
fT11
+
fT22
;
T
fDiff
=
fT11
-
fT22
;
T
fDiscr
=
Maths
::
Sqrt
(
fDiff
*
fDiff
+
4.0f
*
fT12
*
fT12
);
T
fRoot1
=
0.5f
*
(
fTrace
+
fDiscr
);
T
fRoot2
=
0.5f
*
(
fTrace
-
fDiscr
);
// adjust right
T
fY
=
kA
[
0
][
0
]
-
(
Maths
::
Abs
(
fRoot1
-
fT22
)
<=
Maths
::
Abs
(
fRoot2
-
fT22
)
?
fRoot1
:
fRoot2
);
T
fZ
=
kA
[
0
][
1
];
T
fInvLength
=
Maths
::
InvSqrt
(
fY
*
fY
+
fZ
*
fZ
);
T
fSin
=
fZ
*
fInvLength
;
T
fCos
=
-
fY
*
fInvLength
;
T
fTmp0
=
kA
[
0
][
0
];
T
fTmp1
=
kA
[
0
][
1
];
kA
[
0
][
0
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kA
[
0
][
1
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
kA
[
1
][
0
]
=
-
fSin
*
kA
[
1
][
1
];
kA
[
1
][
1
]
*=
fCos
;
Size
row
;
for
(
row
=
0
;
row
<
3
;
row
++
)
{
fTmp0
=
kR
[
0
][
row
];
fTmp1
=
kR
[
1
][
row
];
kR
[
0
][
row
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kR
[
1
][
row
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
}
// adjust left
fY
=
kA
[
0
][
0
];
fZ
=
kA
[
1
][
0
];
fInvLength
=
Maths
::
InvSqrt
(
fY
*
fY
+
fZ
*
fZ
);
fSin
=
fZ
*
fInvLength
;
fCos
=
-
fY
*
fInvLength
;
kA
[
0
][
0
]
=
fCos
*
kA
[
0
][
0
]
-
fSin
*
kA
[
1
][
0
];
fTmp0
=
kA
[
0
][
1
];
fTmp1
=
kA
[
1
][
1
];
kA
[
0
][
1
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kA
[
1
][
1
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
kA
[
0
][
2
]
=
-
fSin
*
kA
[
1
][
2
];
kA
[
1
][
2
]
*=
fCos
;
Size
col
;
for
(
col
=
0
;
col
<
3
;
col
++
)
{
fTmp0
=
kL
[
col
][
0
];
fTmp1
=
kL
[
col
][
1
];
kL
[
col
][
0
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kL
[
col
][
1
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
}
// adjust right
fY
=
kA
[
0
][
1
];
fZ
=
kA
[
0
][
2
];
fInvLength
=
Maths
::
InvSqrt
(
fY
*
fY
+
fZ
*
fZ
);
fSin
=
fZ
*
fInvLength
;
fCos
=
-
fY
*
fInvLength
;
kA
[
0
][
1
]
=
fCos
*
kA
[
0
][
1
]
-
fSin
*
kA
[
0
][
2
];
fTmp0
=
kA
[
1
][
1
];
fTmp1
=
kA
[
1
][
2
];
kA
[
1
][
1
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kA
[
1
][
2
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
kA
[
2
][
1
]
=
-
fSin
*
kA
[
2
][
2
];
kA
[
2
][
2
]
*=
fCos
;
for
(
row
=
0
;
row
<
3
;
row
++
)
{
fTmp0
=
kR
[
1
][
row
];
fTmp1
=
kR
[
2
][
row
];
kR
[
1
][
row
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kR
[
2
][
row
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
}
// adjust left
fY
=
kA
[
1
][
1
];
fZ
=
kA
[
2
][
1
];
fInvLength
=
Maths
::
InvSqrt
(
fY
*
fY
+
fZ
*
fZ
);
fSin
=
fZ
*
fInvLength
;
fCos
=
-
fY
*
fInvLength
;
kA
[
1
][
1
]
=
fCos
*
kA
[
1
][
1
]
-
fSin
*
kA
[
2
][
1
];
fTmp0
=
kA
[
1
][
2
];
fTmp1
=
kA
[
2
][
2
];
kA
[
1
][
2
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kA
[
2
][
2
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
for
(
col
=
0
;
col
<
3
;
col
++
)
{
fTmp0
=
kL
[
col
][
1
];
fTmp1
=
kL
[
col
][
2
];
kL
[
col
][
1
]
=
fCos
*
fTmp0
-
fSin
*
fTmp1
;
kL
[
col
][
2
]
=
fSin
*
fTmp0
+
fCos
*
fTmp1
;
}
}
// support for spectral norm
static
T
MaxCubicRoot
(
T
afCoeff
[
3
])
{
// Spectral norm is for A^T*A, so characteristic polynomial
// P(x) = c[0]+c[1]*x+c[2]*x^2+x^3 has three positive real roots.
// This yields the assertions c[0] < 0 and c[2]*c[2] >= 3*c[1].
// quick out for uniform scale (triple root)
const
T
fOneThird
=
T
(
1
)
/
T
(
3
);
const
T
fEpsilon
=
1e-06
;
T
fDiscr
=
afCoeff
[
2
]
*
afCoeff
[
2
]
-
T
(
3
)
*
afCoeff
[
1
];
if
(
fDiscr
<=
fEpsilon
)
return
-
fOneThird
*
afCoeff
[
2
];
// Compute an upper bound on roots of P(x). This assumes that A^T*A
// has been scaled by its largest entry.
T
fX
=
T
(
1
);
T
fPoly
=
afCoeff
[
0
]
+
fX
*
(
afCoeff
[
1
]
+
fX
*
(
afCoeff
[
2
]
+
fX
));
if
(
fPoly
<
T
(
0
)
)
{
// uses a matrix norm to find an upper bound on maximum root
fX
=
Maths
::
Abs
(
afCoeff
[
0
]);
T
fTmp
=
T
(
1
)
+
Maths
::
Abs
(
afCoeff
[
1
]);
if
(
fTmp
>
fX
)
fX
=
fTmp
;
fTmp
=
T
(
1
)
+
Maths
::
Abs
(
afCoeff
[
2
]);
if
(
fTmp
>
fX
)
fX
=
fTmp
;
}
// Newton's method to find root
T
fTwoC2
=
T
(
2
)
*
afCoeff
[
2
];
for
(
int
i
=
0
;
i
<
16
;
i
++
)
{
fPoly
=
afCoeff
[
0
]
+
fX
*
(
afCoeff
[
1
]
+
fX
*
(
afCoeff
[
2
]
+
fX
));
if
(
Maths
::
Abs
(
fPoly
)
<=
fEpsilon
)
{
return
fX
;
}
T
fDeriv
=
afCoeff
[
1
]
+
fX
*
(
fTwoC2
+
T
(
3
)
*
fX
);
fX
-=
fPoly
/
fDeriv
;
}
return
fX
;
}
T
m
[
3
][
3
];
// for faster access
friend
class
Matrix4
;
};
template
<
typename
T
>
const
T
Matrix3Generic
<
T
>::
EPSILON
=
T
(
1e-06
);
template
<
typename
T
>
const
Matrix3Generic
<
T
>
Matrix3Generic
<
T
>::
ZERO
(
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
);
template
<
typename
T
>
const
Matrix3Generic
<
T
>
Matrix3Generic
<
T
>::
IDENTITY
(
1
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
1
);
template
<
typename
T
>
const
T
Matrix3Generic
<
T
>::
ms_fSvdEpsilon
=
T
(
1e-04
);
template
<
typename
T
>
const
unsigned
int
Matrix3Generic
<
T
>::
ms_iSvdMaxIterations
=
32
;
typedef
Matrix3Generic
<
f32
>
Matrix3
;
/** @} */
/** @} */
}
#endif
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Wed, Jan 15, 11:48 PM (11 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
72097
Default Alt Text
Matrix3.h (45 KB)
Attached To
Mode
rEE Echo 3
Attached
Detach File
Event Timeline
Log In to Comment