wfmath  1.0.3
A math library for the Worldforge system.
rotmatrix_funcs.h
1 // rotmatrix_funcs.h (RotMatrix<> template functions)
2 //
3 // The WorldForge Project
4 // Copyright (C) 2001 The WorldForge Project
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // For information about WorldForge and its authors, please contact
21 // the Worldforge Web Site at http://www.worldforge.org.
22 
23 // Author: Ron Steinke
24 // Created: 2001-12-7
25 
26 #ifndef WFMATH_ROTMATRIX_FUNCS_H
27 #define WFMATH_ROTMATRIX_FUNCS_H
28 
29 #include <wfmath/rotmatrix.h>
30 
31 #include <wfmath/vector.h>
32 #include <wfmath/error.h>
33 #include <wfmath/const.h>
34 
35 #include <cmath>
36 
37 #include <cassert>
38 
39 namespace WFMath {
40 
41 template<int dim>
42 inline RotMatrix<dim>::RotMatrix(const RotMatrix<dim>& m)
43  : m_flip(m.m_flip), m_valid(m.m_valid), m_age(1)
44 {
45  for(int i = 0; i < dim; ++i)
46  for(int j = 0; j < dim; ++j)
47  m_elem[i][j] = m.m_elem[i][j];
48 }
49 
50 template<int dim>
51 inline RotMatrix<dim>& RotMatrix<dim>::operator=(const RotMatrix<dim>& m)
52 {
53  for(int i = 0; i < dim; ++i)
54  for(int j = 0; j < dim; ++j)
55  m_elem[i][j] = m.m_elem[i][j];
56 
57  m_flip = m.m_flip;
58  m_valid = m.m_valid;
59  m_age = m.m_age;
60 
61  return *this;
62 }
63 
64 template<int dim>
65 inline bool RotMatrix<dim>::isEqualTo(const RotMatrix<dim>& m, CoordType epsilon) const
66 {
67  // Since the sum of the squares of the elements in any row or column add
68  // up to 1, all the elements lie between -1 and 1, and each row has
69  // at least one element whose magnitude is at least 1/sqrt(dim).
70  // Therefore, we don't need to scale epsilon, as we did for
71  // Vector<> and Point<>.
72 
73  assert(epsilon > 0);
74 
75  //If anyone is invalid they are never equal
76  if (!m.m_valid || !m_valid) {
77  return false;
78  }
79 
80 
81  for(int i = 0; i < dim; ++i)
82  for(int j = 0; j < dim; ++j)
83  if(std::fabs(m_elem[i][j] - m.m_elem[i][j]) > epsilon)
84  return false;
85 
86  // Don't need to test m_flip, it's determined by the values of m_elem.
87 
88  assert("Generated values, must be equal if all components are equal" &&
89  m_flip == m.m_flip);
90 
91  return true;
92 }
93 
94 template<int dim> // m1 * m2
95 inline RotMatrix<dim> Prod(const RotMatrix<dim>& m1, const RotMatrix<dim>& m2)
96 {
97  RotMatrix<dim> out;
98 
99  for(int i = 0; i < dim; ++i) {
100  for(int j = 0; j < dim; ++j) {
101  out.m_elem[i][j] = 0;
102  for(int k = 0; k < dim; ++k) {
103  out.m_elem[i][j] += m1.m_elem[i][k] * m2.m_elem[k][j];
104  }
105  }
106  }
107 
108  out.m_flip = (m1.m_flip != m2.m_flip); // XOR
109  out.m_valid = m1.m_valid && m2.m_valid;
110  out.m_age = m1.m_age + m2.m_age;
111  out.checkNormalization();
112 
113  return out;
114 }
115 
116 template<int dim> // m1 * m2^-1
118 {
119  RotMatrix<dim> out;
120 
121  for(int i = 0; i < dim; ++i) {
122  for(int j = 0; j < dim; ++j) {
123  out.m_elem[i][j] = 0;
124  for(int k = 0; k < dim; ++k) {
125  out.m_elem[i][j] += m1.m_elem[i][k] * m2.m_elem[j][k];
126  }
127  }
128  }
129 
130  out.m_flip = (m1.m_flip != m2.m_flip); // XOR
131  out.m_valid = m1.m_valid && m2.m_valid;
132  out.m_age = m1.m_age + m2.m_age;
133  out.checkNormalization();
134 
135  return out;
136 }
137 
138 template<int dim> // m1^-1 * m2
140 {
141  RotMatrix<dim> out;
142 
143  for(int i = 0; i < dim; ++i) {
144  for(int j = 0; j < dim; ++j) {
145  out.m_elem[i][j] = 0;
146  for(int k = 0; k < dim; ++k) {
147  out.m_elem[i][j] += m1.m_elem[k][i] * m2.m_elem[k][j];
148  }
149  }
150  }
151 
152  out.m_flip = (m1.m_flip != m2.m_flip); // XOR
153  out.m_valid = m1.m_valid && m2.m_valid;
154  out.m_age = m1.m_age + m2.m_age;
155  out.checkNormalization();
156 
157  return out;
158 }
159 
160 template<int dim> // m1^-1 * m2^-1
162 {
163  RotMatrix<dim> out;
164 
165  for(int i = 0; i < dim; ++i) {
166  for(int j = 0; j < dim; ++j) {
167  out.m_elem[i][j] = 0;
168  for(int k = 0; k < dim; ++k) {
169  out.m_elem[i][j] += m1.m_elem[k][i] * m2.m_elem[j][k];
170  }
171  }
172  }
173 
174  out.m_flip = (m1.m_flip != m2.m_flip); // XOR
175  out.m_valid = m1.m_valid && m2.m_valid;
176  out.m_age = m1.m_age + m2.m_age;
177  out.checkNormalization();
178 
179  return out;
180 }
181 
182 template<int dim> // m * v
183 inline Vector<dim> Prod(const RotMatrix<dim>& m, const Vector<dim>& v)
184 {
185  Vector<dim> out;
186 
187  for(int i = 0; i < dim; ++i) {
188  out.m_elem[i] = 0;
189  for(int j = 0; j < dim; ++j) {
190  out.m_elem[i] += m.m_elem[i][j] * v.m_elem[j];
191  }
192  }
193 
194  out.m_valid = m.m_valid && v.m_valid;
195 
196  return out;
197 }
198 
199 template<int dim> // m^-1 * v
200 inline Vector<dim> InvProd(const RotMatrix<dim>& m, const Vector<dim>& v)
201 {
202  Vector<dim> out;
203 
204  for(int i = 0; i < dim; ++i) {
205  out.m_elem[i] = 0;
206  for(int j = 0; j < dim; ++j) {
207  out.m_elem[i] += m.m_elem[j][i] * v.m_elem[j];
208  }
209  }
210 
211  out.m_valid = m.m_valid && v.m_valid;
212 
213  return out;
214 }
215 
216 template<int dim> // v * m
217 inline Vector<dim> Prod(const Vector<dim>& v, const RotMatrix<dim>& m)
218 {
219  return InvProd(m, v); // Since transpose() and inverse() are the same
220 }
221 
222 template<int dim> // v * m^-1
223 inline Vector<dim> ProdInv(const Vector<dim>& v, const RotMatrix<dim>& m)
224 {
225  return Prod(m, v); // Since transpose() and inverse() are the same
226 }
227 
228 template<int dim>
230 {
231  return Prod(m1, m2);
232 }
233 
234 template<int dim>
235 inline Vector<dim> operator*(const RotMatrix<dim>& m, const Vector<dim>& v)
236 {
237  return Prod(m, v);
238 }
239 
240 template<int dim>
241 inline Vector<dim> operator*(const Vector<dim>& v, const RotMatrix<dim>& m)
242 {
243  return InvProd(m, v); // Since transpose() and inverse() are the same
244 }
245 
246 template<int dim>
247 inline bool RotMatrix<dim>::setVals(const CoordType vals[dim][dim], CoordType precision)
248 {
249  // Scratch space for the backend
250  CoordType scratch_vals[dim*dim];
251 
252  for(int i = 0; i < dim; ++i)
253  for(int j = 0; j < dim; ++j)
254  scratch_vals[i*dim+j] = vals[i][j];
255 
256  return _setVals(scratch_vals, precision);
257 }
258 
259 template<int dim>
260 inline bool RotMatrix<dim>::setVals(const CoordType vals[dim*dim], CoordType precision)
261 {
262  // Scratch space for the backend
263  CoordType scratch_vals[dim*dim];
264 
265  for(int i = 0; i < dim*dim; ++i)
266  scratch_vals[i] = vals[i];
267 
268  return _setVals(scratch_vals, precision);
269 }
270 
271 bool _MatrixSetValsImpl(int size, CoordType* vals, bool& flip,
272  CoordType* buf1, CoordType* buf2, CoordType precision);
273 
274 template<int dim>
275 inline bool RotMatrix<dim>::_setVals(CoordType *vals, CoordType precision)
276 {
277  // Cheaper to allocate space on the stack here than with
278  // new in _MatrixSetValsImpl()
279  CoordType buf1[dim*dim], buf2[dim*dim];
280  bool flip;
281 
282  if(!_MatrixSetValsImpl(dim, vals, flip, buf1, buf2, precision))
283  return false;
284 
285  // Do the assignment
286 
287  for(int i = 0; i < dim; ++i)
288  for(int j = 0; j < dim; ++j)
289  m_elem[i][j] = vals[i*dim+j];
290 
291  m_flip = flip;
292  m_valid = true;
293  m_age = 1;
294 
295  return true;
296 }
297 
298 template<int dim>
299 inline Vector<dim> RotMatrix<dim>::row(const int i) const
300 {
301  Vector<dim> out;
302 
303  for(int j = 0; j < dim; ++j)
304  out[j] = m_elem[i][j];
305 
306  out.setValid(m_valid);
307 
308  return out;
309 }
310 
311 template<int dim>
312 inline Vector<dim> RotMatrix<dim>::column(const int i) const
313 {
314  Vector<dim> out;
315 
316  for(int j = 0; j < dim; ++j)
317  out[j] = m_elem[j][i];
318 
319  out.setValid(m_valid);
320 
321  return out;
322 }
323 
324 template<int dim>
326 {
327  RotMatrix<dim> m;
328 
329  for(int i = 0; i < dim; ++i)
330  for(int j = 0; j < dim; ++j)
331  m.m_elem[j][i] = m_elem[i][j];
332 
333  m.m_flip = m_flip;
334  m.m_valid = m_valid;
335  m.m_age = m_age + 1;
336 
337  return m;
338 }
339 
340 template<int dim>
342 {
343  for(int i = 0; i < dim; ++i)
344  for(int j = 0; j < dim; ++j)
345  m_elem[i][j] = ((i == j) ? 1.0f : 0.0f);
346 
347  m_flip = false;
348  m_valid = true;
349  m_age = 0; // 1 and 0 are exact, no roundoff error
350 
351  return *this;
352 }
353 
354 template<int dim>
356 {
357  CoordType out = 0;
358 
359  for(int i = 0; i < dim; ++i)
360  out += m_elem[i][i];
361 
362  return out;
363 }
364 
365 template<int dim>
366 RotMatrix<dim>& RotMatrix<dim>::rotation (const int i, const int j,
367  CoordType theta)
368 {
369  assert(i >= 0 && i < dim && j >= 0 && j < dim && i != j);
370 
371  CoordType ctheta = std::cos(theta), stheta = std::sin(theta);
372 
373  for(int k = 0; k < dim; ++k) {
374  for(int l = 0; l < dim; ++l) {
375  if(k == l) {
376  if(k == i || k == j)
377  m_elem[k][l] = ctheta;
378  else
379  m_elem[k][l] = 1;
380  }
381  else {
382  if(k == i && l == j)
383  m_elem[k][l] = stheta;
384  else if(k == j && l == i)
385  m_elem[k][l] = -stheta;
386  else
387  m_elem[k][l] = 0;
388  }
389  }
390  }
391 
392  m_flip = false;
393  m_valid = true;
394  m_age = 1;
395 
396  return *this;
397 }
398 
399 template<int dim>
401  const Vector<dim>& v2,
402  CoordType theta)
403 {
404  CoordType v1_sqr_mag = v1.sqrMag();
405 
406  assert("need nonzero length vector" && v1_sqr_mag > 0);
407 
408  // Get an in-plane vector which is perpendicular to v1
409 
410  Vector<dim> vperp = v2 - v1 * Dot(v1, v2) / v1_sqr_mag;
411  CoordType vperp_sqr_mag = vperp.sqrMag();
412 
413  if((vperp_sqr_mag / v1_sqr_mag) < (dim * numeric_constants<CoordType>::epsilon() * numeric_constants<CoordType>::epsilon())) {
414  assert("need nonzero length vector" && v2.sqrMag() > 0);
415  // The original vectors were parallel
416  throw ColinearVectors<dim>(v1, v2);
417  }
418 
419  // If we were rotating a vector vin, the answer would be
420  // vin + Dot(v1, vin) * (v1 (cos(theta) - 1)/ v1_sqr_mag
421  // + vperp * sin(theta) / sqrt(v1_sqr_mag * vperp_sqr_mag))
422  // + Dot(vperp, vin) * (a similar term). From this, we find
423  // the matrix components.
424 
425  CoordType mag_prod = std::sqrt(v1_sqr_mag * vperp_sqr_mag);
426  CoordType ctheta = std::cos(theta),
427  stheta = std::sin(theta);
428 
429  identity(); // Initialize to identity matrix
430 
431  for(int i = 0; i < dim; ++i)
432  for(int j = 0; j < dim; ++j)
433  m_elem[i][j] += ((ctheta - 1) * (v1[i] * v1[j] / v1_sqr_mag
434  + vperp[i] * vperp[j] / vperp_sqr_mag)
435  - stheta * (vperp[i] * v1[j] - v1[i] * vperp[j]) / mag_prod);
436 
437  m_flip = false;
438  m_valid = true;
439  m_age = 1;
440 
441  return *this;
442 }
443 
444 template<int dim>
446  const Vector<dim>& to)
447 {
448  // This is sort of similar to the above, with the rotation angle determined
449  // by the angle between the vectors
450 
451  CoordType fromSqrMag = from.sqrMag();
452  assert("need nonzero length vector" && fromSqrMag > 0);
453  CoordType toSqrMag = to.sqrMag();
454  assert("need nonzero length vector" && toSqrMag > 0);
455  CoordType dot = Dot(from, to);
456  CoordType sqrmagprod = fromSqrMag * toSqrMag;
457  CoordType magprod = std::sqrt(sqrmagprod);
458  CoordType ctheta_plus_1 = dot / magprod + 1;
459 
460  if(ctheta_plus_1 < numeric_constants<CoordType>::epsilon()) {
461  // 180 degree rotation, rotation plane indeterminate
462  if(dim == 2) { // special case, only one rotation plane possible
463  m_elem[0][0] = m_elem[1][1] = ctheta_plus_1 - 1;
464  CoordType sin_theta = std::sqrt(2 * ctheta_plus_1); // to leading order
465  bool direction = ((to[0] * from[1] - to[1] * from[0]) >= 0);
466  m_elem[0][1] = direction ? sin_theta : -sin_theta;
467  m_elem[1][0] = -m_elem[0][1];
468  m_flip = false;
469  m_valid = true;
470  m_age = 1;
471  return *this;
472  }
473  throw ColinearVectors<dim>(from, to);
474  }
475 
476  for(int i = 0; i < dim; ++i) {
477  for(int j = i; j < dim; ++j) {
478  CoordType projfrom = from[i] * from[j] / fromSqrMag;
479  CoordType projto = to[i] * to[j] / toSqrMag;
480 
481  CoordType ijprod = from[i] * to[j], jiprod = to[i] * from[j];
482 
483  CoordType termthree = (ijprod + jiprod) * dot / sqrmagprod;
484 
485  CoordType combined = (projfrom + projto - termthree) / ctheta_plus_1;
486 
487  if(i == j) {
488  m_elem[i][i] = 1 - combined;
489  }
490  else {
491  CoordType diffterm = (jiprod - ijprod) / magprod;
492 
493  m_elem[i][j] = -diffterm - combined;
494  m_elem[j][i] = diffterm - combined;
495  }
496  }
497  }
498 
499  m_flip = false;
500  m_valid = true;
501  m_age = 1;
502 
503  return *this;
504 }
505 
506 template<> RotMatrix<3>& RotMatrix<3>::rotation (const Vector<3>& axis,
507  CoordType theta);
508 template<> RotMatrix<3>& RotMatrix<3>::rotation (const Vector<3>& axis);
510  const bool not_flip);
511 
512 template<> RotMatrix<3>& RotMatrix<3>::rotate(const Quaternion&);
513 
514 template<int dim>
516 {
517  // Get a flip by subtracting twice the projection operator in the
518  // direction of the vector. A projection operator is idempotent (P*P == P),
519  // and symmetric, so I - 2P is an orthogonal matrix.
520  //
521  // (I - 2P) * (I - 2P)^T == (I - 2P)^2 == I - 4P + 4P^2 == I
522 
523  CoordType sqr_mag = v.sqrMag();
524 
525  assert("need nonzero length vector" && sqr_mag > 0);
526 
527  // off diagonal
528  for(int i = 0; i < dim - 1; ++i)
529  for(int j = i + 1; j < dim; ++j)
530  m_elem[i][j] = m_elem[j][i] = -2 * v[i] * v[j] / sqr_mag;
531 
532  // diagonal
533  for(int i = 0; i < dim; ++i)
534  m_elem[i][i] = 1 - 2 * v[i] * v[i] / sqr_mag;
535 
536  m_flip = true;
537  m_valid = true;
538  m_age = 1;
539 
540  return *this;
541 }
542 
543 template<int dim>
545 {
546  for(int i = 0; i < dim; ++i)
547  for(int j = 0; j < dim; ++j)
548  m_elem[i][j] = (i == j) ? -1 : 0;
549 
550  m_flip = dim%2;
551  m_valid = true;
552  m_age = 0; // -1 and 0 are exact, no roundoff error
553 
554 
555  return *this;
556 }
557 
558 bool _MatrixInverseImpl(int size, CoordType* in, CoordType* out);
559 
560 template<int dim>
562 {
563  // average the matrix with it's inverse transpose,
564  // that will clean up the error to linear order
565 
566  CoordType buf1[dim*dim], buf2[dim*dim];
567 
568  for(int i = 0; i < dim; ++i) {
569  for(int j = 0; j < dim; ++j) {
570  buf1[j*dim + i] = m_elem[i][j];
571  buf2[j*dim + i] = ((i == j) ? 1.f : 0.f);
572  }
573  }
574 
575  bool success = _MatrixInverseImpl(dim, buf1, buf2);
576  assert(success); // matrix can't be degenerate
577  if (!success) {
578  return;
579  }
580 
581  for(int i = 0; i < dim; ++i) {
582  for(int j = 0; j < dim; ++j) {
583  CoordType& elem = m_elem[i][j];
584  elem += buf2[i*dim + j];
585  elem /= 2;
586  }
587  }
588 
589  m_age = 1;
590 }
591 
592 } // namespace WFMath
593 
594 #endif // WFMATH_ROTMATRIX_FUNCS_H
WFMath::Vector::setValid
void setValid(bool valid=true)
make isValid() return true if you've initialized the vector by hand
Definition: vector.h:154
WFMath::RotMatrix::rotation
RotMatrix & rotation(int i, int j, CoordType theta)
set the matrix to a rotation by the angle theta in the (i, j) plane
Definition: rotmatrix_funcs.h:366
WFMath::RotMatrix::row
Vector< dim > row(int i) const
Get a copy of the i'th row as a Vector.
Definition: rotmatrix_funcs.h:299
WFMath::RotMatrix::mirror
RotMatrix & mirror()
set the matrix to mirror all axes
Definition: rotmatrix_funcs.h:544
WFMath::RotMatrix
A dim dimensional rotation matrix. Technically, a member of the group O(dim).
Definition: const.h:53
WFMath::operator*
RotMatrix< dim > operator*(const RotMatrix< dim > &m1, const RotMatrix< dim > &m2)
returns m1 * m2
Definition: rotmatrix_funcs.h:229
WFMath::ProdInv
RotMatrix< dim > ProdInv(const RotMatrix< dim > &m1, const RotMatrix< dim > &m2)
returns m1 * m2^-1
Definition: rotmatrix_funcs.h:117
WFMath::ColinearVectors
An error thrown by certain functions when passed parallel vectors.
Definition: error.h:37
WFMath::Vector::sqrMag
CoordType sqrMag() const
The squared magnitude of a vector.
Definition: vector_funcs.h:241
WFMath::Quaternion
A normalized quaternion.
Definition: quaternion.h:35
WFMath::Prod
RotMatrix< dim > Prod(const RotMatrix< dim > &m1, const RotMatrix< dim > &m2)
returns m1 * m2
Definition: rotmatrix_funcs.h:95
WFMath
Generic library namespace.
Definition: shape.h:41
WFMath::RotMatrix::column
Vector< dim > column(int i) const
Get a copy of the i'th column as a Vector.
Definition: rotmatrix_funcs.h:312
WFMath::Vector
A dim dimensional vector.
Definition: const.h:55
WFMath::RotMatrix::rotate
RotMatrix & rotate(const RotMatrix &m)
rotate the matrix using another matrix
Definition: rotmatrix.h:196
WFMath::RotMatrix::normalize
void normalize()
normalize to remove accumulated round-off error
Definition: rotmatrix_funcs.h:561
WFMath::RotMatrix::setVals
bool setVals(const CoordType vals[dim][dim], CoordType precision=numeric_constants< CoordType >::epsilon())
Set the values of the elements of the matrix.
Definition: rotmatrix_funcs.h:247
WFMath::CoordType
double CoordType
Basic floating point type.
Definition: const.h:140
WFMath::numeric_constants
Definition: const.h:64
WFMath::InvProdInv
RotMatrix< dim > InvProdInv(const RotMatrix< dim > &m1, const RotMatrix< dim > &m2)
returns m1^-1 * m2^-1
Definition: rotmatrix_funcs.h:161
WFMath::RotMatrix::identity
RotMatrix & identity()
set the matrix to the identity matrix
Definition: rotmatrix_funcs.h:341
WFMath::RotMatrix::trace
CoordType trace() const
Get the trace of the matrix.
Definition: rotmatrix_funcs.h:355
WFMath::InvProd
RotMatrix< dim > InvProd(const RotMatrix< dim > &m1, const RotMatrix< dim > &m2)
returns m1^-1 * m2
Definition: rotmatrix_funcs.h:139
WFMath::RotMatrix::fromQuaternion
RotMatrix & fromQuaternion(const Quaternion &q, bool not_flip=true)
3D only: set a RotMatrix from a Quaternion
WFMath::RotMatrix::inverse
RotMatrix inverse() const
Get the inverse of the matrix.
Definition: rotmatrix_funcs.h:325