1*2f083884Ss.makeev_local /* -----------------------------------------------------------------------------
2*2f083884Ss.makeev_local
3*2f083884Ss.makeev_local Copyright (c) 2006 Simon Brown [email protected]
4*2f083884Ss.makeev_local
5*2f083884Ss.makeev_local Permission is hereby granted, free of charge, to any person obtaining
6*2f083884Ss.makeev_local a copy of this software and associated documentation files (the
7*2f083884Ss.makeev_local "Software"), to deal in the Software without restriction, including
8*2f083884Ss.makeev_local without limitation the rights to use, copy, modify, merge, publish,
9*2f083884Ss.makeev_local distribute, sublicense, and/or sell copies of the Software, and to
10*2f083884Ss.makeev_local permit persons to whom the Software is furnished to do so, subject to
11*2f083884Ss.makeev_local the following conditions:
12*2f083884Ss.makeev_local
13*2f083884Ss.makeev_local The above copyright notice and this permission notice shall be included
14*2f083884Ss.makeev_local in all copies or substantial portions of the Software.
15*2f083884Ss.makeev_local
16*2f083884Ss.makeev_local THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17*2f083884Ss.makeev_local OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*2f083884Ss.makeev_local MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19*2f083884Ss.makeev_local IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20*2f083884Ss.makeev_local CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21*2f083884Ss.makeev_local TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22*2f083884Ss.makeev_local SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23*2f083884Ss.makeev_local
24*2f083884Ss.makeev_local -------------------------------------------------------------------------- */
25*2f083884Ss.makeev_local
26*2f083884Ss.makeev_local /*! @file
27*2f083884Ss.makeev_local
28*2f083884Ss.makeev_local The symmetric eigensystem solver algorithm is from
29*2f083884Ss.makeev_local http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf
30*2f083884Ss.makeev_local */
31*2f083884Ss.makeev_local
32*2f083884Ss.makeev_local #include "maths.h"
33*2f083884Ss.makeev_local #include <cfloat>
34*2f083884Ss.makeev_local
35*2f083884Ss.makeev_local namespace squish {
36*2f083884Ss.makeev_local
ComputeWeightedCovariance(int n,Vec3 const * points,float const * weights)37*2f083884Ss.makeev_local Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights )
38*2f083884Ss.makeev_local {
39*2f083884Ss.makeev_local // compute the centroid
40*2f083884Ss.makeev_local float total = 0.0f;
41*2f083884Ss.makeev_local Vec3 centroid( 0.0f );
42*2f083884Ss.makeev_local for( int i = 0; i < n; ++i )
43*2f083884Ss.makeev_local {
44*2f083884Ss.makeev_local total += weights[i];
45*2f083884Ss.makeev_local centroid += weights[i]*points[i];
46*2f083884Ss.makeev_local }
47*2f083884Ss.makeev_local centroid /= total;
48*2f083884Ss.makeev_local
49*2f083884Ss.makeev_local // accumulate the covariance matrix
50*2f083884Ss.makeev_local Sym3x3 covariance( 0.0f );
51*2f083884Ss.makeev_local for( int i = 0; i < n; ++i )
52*2f083884Ss.makeev_local {
53*2f083884Ss.makeev_local Vec3 a = points[i] - centroid;
54*2f083884Ss.makeev_local Vec3 b = weights[i]*a;
55*2f083884Ss.makeev_local
56*2f083884Ss.makeev_local covariance[0] += a.X()*b.X();
57*2f083884Ss.makeev_local covariance[1] += a.X()*b.Y();
58*2f083884Ss.makeev_local covariance[2] += a.X()*b.Z();
59*2f083884Ss.makeev_local covariance[3] += a.Y()*b.Y();
60*2f083884Ss.makeev_local covariance[4] += a.Y()*b.Z();
61*2f083884Ss.makeev_local covariance[5] += a.Z()*b.Z();
62*2f083884Ss.makeev_local }
63*2f083884Ss.makeev_local
64*2f083884Ss.makeev_local // return it
65*2f083884Ss.makeev_local return covariance;
66*2f083884Ss.makeev_local }
67*2f083884Ss.makeev_local
GetMultiplicity1Evector(Sym3x3 const & matrix,float evalue)68*2f083884Ss.makeev_local static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue )
69*2f083884Ss.makeev_local {
70*2f083884Ss.makeev_local // compute M
71*2f083884Ss.makeev_local Sym3x3 m;
72*2f083884Ss.makeev_local m[0] = matrix[0] - evalue;
73*2f083884Ss.makeev_local m[1] = matrix[1];
74*2f083884Ss.makeev_local m[2] = matrix[2];
75*2f083884Ss.makeev_local m[3] = matrix[3] - evalue;
76*2f083884Ss.makeev_local m[4] = matrix[4];
77*2f083884Ss.makeev_local m[5] = matrix[5] - evalue;
78*2f083884Ss.makeev_local
79*2f083884Ss.makeev_local // compute U
80*2f083884Ss.makeev_local Sym3x3 u;
81*2f083884Ss.makeev_local u[0] = m[3]*m[5] - m[4]*m[4];
82*2f083884Ss.makeev_local u[1] = m[2]*m[4] - m[1]*m[5];
83*2f083884Ss.makeev_local u[2] = m[1]*m[4] - m[2]*m[3];
84*2f083884Ss.makeev_local u[3] = m[0]*m[5] - m[2]*m[2];
85*2f083884Ss.makeev_local u[4] = m[1]*m[2] - m[4]*m[0];
86*2f083884Ss.makeev_local u[5] = m[0]*m[3] - m[1]*m[1];
87*2f083884Ss.makeev_local
88*2f083884Ss.makeev_local // find the largest component
89*2f083884Ss.makeev_local float mc = std::fabs( u[0] );
90*2f083884Ss.makeev_local int mi = 0;
91*2f083884Ss.makeev_local for( int i = 1; i < 6; ++i )
92*2f083884Ss.makeev_local {
93*2f083884Ss.makeev_local float c = std::fabs( u[i] );
94*2f083884Ss.makeev_local if( c > mc )
95*2f083884Ss.makeev_local {
96*2f083884Ss.makeev_local mc = c;
97*2f083884Ss.makeev_local mi = i;
98*2f083884Ss.makeev_local }
99*2f083884Ss.makeev_local }
100*2f083884Ss.makeev_local
101*2f083884Ss.makeev_local // pick the column with this component
102*2f083884Ss.makeev_local switch( mi )
103*2f083884Ss.makeev_local {
104*2f083884Ss.makeev_local case 0:
105*2f083884Ss.makeev_local return Vec3( u[0], u[1], u[2] );
106*2f083884Ss.makeev_local
107*2f083884Ss.makeev_local case 1:
108*2f083884Ss.makeev_local case 3:
109*2f083884Ss.makeev_local return Vec3( u[1], u[3], u[4] );
110*2f083884Ss.makeev_local
111*2f083884Ss.makeev_local default:
112*2f083884Ss.makeev_local return Vec3( u[2], u[4], u[5] );
113*2f083884Ss.makeev_local }
114*2f083884Ss.makeev_local }
115*2f083884Ss.makeev_local
GetMultiplicity2Evector(Sym3x3 const & matrix,float evalue)116*2f083884Ss.makeev_local static Vec3 GetMultiplicity2Evector( Sym3x3 const& matrix, float evalue )
117*2f083884Ss.makeev_local {
118*2f083884Ss.makeev_local // compute M
119*2f083884Ss.makeev_local Sym3x3 m;
120*2f083884Ss.makeev_local m[0] = matrix[0] - evalue;
121*2f083884Ss.makeev_local m[1] = matrix[1];
122*2f083884Ss.makeev_local m[2] = matrix[2];
123*2f083884Ss.makeev_local m[3] = matrix[3] - evalue;
124*2f083884Ss.makeev_local m[4] = matrix[4];
125*2f083884Ss.makeev_local m[5] = matrix[5] - evalue;
126*2f083884Ss.makeev_local
127*2f083884Ss.makeev_local // find the largest component
128*2f083884Ss.makeev_local float mc = std::fabs( m[0] );
129*2f083884Ss.makeev_local int mi = 0;
130*2f083884Ss.makeev_local for( int i = 1; i < 6; ++i )
131*2f083884Ss.makeev_local {
132*2f083884Ss.makeev_local float c = std::fabs( m[i] );
133*2f083884Ss.makeev_local if( c > mc )
134*2f083884Ss.makeev_local {
135*2f083884Ss.makeev_local mc = c;
136*2f083884Ss.makeev_local mi = i;
137*2f083884Ss.makeev_local }
138*2f083884Ss.makeev_local }
139*2f083884Ss.makeev_local
140*2f083884Ss.makeev_local // pick the first eigenvector based on this index
141*2f083884Ss.makeev_local switch( mi )
142*2f083884Ss.makeev_local {
143*2f083884Ss.makeev_local case 0:
144*2f083884Ss.makeev_local case 1:
145*2f083884Ss.makeev_local return Vec3( -m[1], m[0], 0.0f );
146*2f083884Ss.makeev_local
147*2f083884Ss.makeev_local case 2:
148*2f083884Ss.makeev_local return Vec3( m[2], 0.0f, -m[0] );
149*2f083884Ss.makeev_local
150*2f083884Ss.makeev_local case 3:
151*2f083884Ss.makeev_local case 4:
152*2f083884Ss.makeev_local return Vec3( 0.0f, -m[4], m[3] );
153*2f083884Ss.makeev_local
154*2f083884Ss.makeev_local default:
155*2f083884Ss.makeev_local return Vec3( 0.0f, -m[5], m[4] );
156*2f083884Ss.makeev_local }
157*2f083884Ss.makeev_local }
158*2f083884Ss.makeev_local
ComputePrincipleComponent(Sym3x3 const & matrix)159*2f083884Ss.makeev_local Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
160*2f083884Ss.makeev_local {
161*2f083884Ss.makeev_local // compute the cubic coefficients
162*2f083884Ss.makeev_local float c0 = matrix[0]*matrix[3]*matrix[5]
163*2f083884Ss.makeev_local + 2.0f*matrix[1]*matrix[2]*matrix[4]
164*2f083884Ss.makeev_local - matrix[0]*matrix[4]*matrix[4]
165*2f083884Ss.makeev_local - matrix[3]*matrix[2]*matrix[2]
166*2f083884Ss.makeev_local - matrix[5]*matrix[1]*matrix[1];
167*2f083884Ss.makeev_local float c1 = matrix[0]*matrix[3] + matrix[0]*matrix[5] + matrix[3]*matrix[5]
168*2f083884Ss.makeev_local - matrix[1]*matrix[1] - matrix[2]*matrix[2] - matrix[4]*matrix[4];
169*2f083884Ss.makeev_local float c2 = matrix[0] + matrix[3] + matrix[5];
170*2f083884Ss.makeev_local
171*2f083884Ss.makeev_local // compute the quadratic coefficients
172*2f083884Ss.makeev_local float a = c1 - ( 1.0f/3.0f )*c2*c2;
173*2f083884Ss.makeev_local float b = ( -2.0f/27.0f )*c2*c2*c2 + ( 1.0f/3.0f )*c1*c2 - c0;
174*2f083884Ss.makeev_local
175*2f083884Ss.makeev_local // compute the root count check
176*2f083884Ss.makeev_local float Q = 0.25f*b*b + ( 1.0f/27.0f )*a*a*a;
177*2f083884Ss.makeev_local
178*2f083884Ss.makeev_local // test the multiplicity
179*2f083884Ss.makeev_local if( FLT_EPSILON < Q )
180*2f083884Ss.makeev_local {
181*2f083884Ss.makeev_local // only one root, which implies we have a multiple of the identity
182*2f083884Ss.makeev_local return Vec3( 1.0f );
183*2f083884Ss.makeev_local }
184*2f083884Ss.makeev_local else if( Q < -FLT_EPSILON )
185*2f083884Ss.makeev_local {
186*2f083884Ss.makeev_local // three distinct roots
187*2f083884Ss.makeev_local float theta = std::atan2( std::sqrt( -Q ), -0.5f*b );
188*2f083884Ss.makeev_local float rho = std::sqrt( 0.25f*b*b - Q );
189*2f083884Ss.makeev_local
190*2f083884Ss.makeev_local float rt = std::pow( rho, 1.0f/3.0f );
191*2f083884Ss.makeev_local float ct = std::cos( theta/3.0f );
192*2f083884Ss.makeev_local float st = std::sin( theta/3.0f );
193*2f083884Ss.makeev_local
194*2f083884Ss.makeev_local float l1 = ( 1.0f/3.0f )*c2 + 2.0f*rt*ct;
195*2f083884Ss.makeev_local float l2 = ( 1.0f/3.0f )*c2 - rt*( ct + ( float )sqrt( 3.0f )*st );
196*2f083884Ss.makeev_local float l3 = ( 1.0f/3.0f )*c2 - rt*( ct - ( float )sqrt( 3.0f )*st );
197*2f083884Ss.makeev_local
198*2f083884Ss.makeev_local // pick the larger
199*2f083884Ss.makeev_local if( std::fabs( l2 ) > std::fabs( l1 ) )
200*2f083884Ss.makeev_local l1 = l2;
201*2f083884Ss.makeev_local if( std::fabs( l3 ) > std::fabs( l1 ) )
202*2f083884Ss.makeev_local l1 = l3;
203*2f083884Ss.makeev_local
204*2f083884Ss.makeev_local // get the eigenvector
205*2f083884Ss.makeev_local return GetMultiplicity1Evector( matrix, l1 );
206*2f083884Ss.makeev_local }
207*2f083884Ss.makeev_local else // if( -FLT_EPSILON <= Q && Q <= FLT_EPSILON )
208*2f083884Ss.makeev_local {
209*2f083884Ss.makeev_local // two roots
210*2f083884Ss.makeev_local float rt;
211*2f083884Ss.makeev_local if( b < 0.0f )
212*2f083884Ss.makeev_local rt = -std::pow( -0.5f*b, 1.0f/3.0f );
213*2f083884Ss.makeev_local else
214*2f083884Ss.makeev_local rt = std::pow( 0.5f*b, 1.0f/3.0f );
215*2f083884Ss.makeev_local
216*2f083884Ss.makeev_local float l1 = ( 1.0f/3.0f )*c2 + rt; // repeated
217*2f083884Ss.makeev_local float l2 = ( 1.0f/3.0f )*c2 - 2.0f*rt;
218*2f083884Ss.makeev_local
219*2f083884Ss.makeev_local // get the eigenvector
220*2f083884Ss.makeev_local if( std::fabs( l1 ) > std::fabs( l2 ) )
221*2f083884Ss.makeev_local return GetMultiplicity2Evector( matrix, l1 );
222*2f083884Ss.makeev_local else
223*2f083884Ss.makeev_local return GetMultiplicity1Evector( matrix, l2 );
224*2f083884Ss.makeev_local }
225*2f083884Ss.makeev_local }
226*2f083884Ss.makeev_local
227*2f083884Ss.makeev_local } // namespace squish
228