library of assembled shared sources

http://lass.cocamware.com

transformation_3d.inl

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
00041  */
00042 
00043 #ifndef LASS_GUARDIAN_OF_INCLUSION_PRIM_TRANSFORMATION_3D_INL
00044 #define LASS_GUARDIAN_OF_INCLUSION_PRIM_TRANSFORMATION_3D_INL
00045 
00046 #include "transformation_3d.h"
00047 
00048 #if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
00049 #   pragma warning(push)
00050 #   pragma warning(disable: 4996) // std::copy: function call with parameters that may be unsafe
00051 #endif
00052 
00053 namespace lass
00054 {
00055 
00056 namespace prim
00057 {
00058 
00059 // --- public --------------------------------------------------------------------------------------
00060 
00061 /** construct an identity transformation.
00062  *  An identity transformation transforms every point to itself.
00063  */
00064 template <typename T>
00065 Transformation3D<T>::Transformation3D():
00066     matrix_(impl::allocateArray<T>(matrixSize_)),
00067     inverseMatrix_(0)
00068 {
00069     matrix_[ 0] = TNumTraits::one;
00070     matrix_[ 1] = TNumTraits::zero;
00071     matrix_[ 2] = TNumTraits::zero;
00072     matrix_[ 3] = TNumTraits::zero;
00073     matrix_[ 4] = TNumTraits::zero;
00074     matrix_[ 5] = TNumTraits::one;
00075     matrix_[ 6] = TNumTraits::zero;
00076     matrix_[ 7] = TNumTraits::zero;
00077     matrix_[ 8] = TNumTraits::zero;
00078     matrix_[ 9] = TNumTraits::zero;
00079     matrix_[10] = TNumTraits::one;
00080     matrix_[11] = TNumTraits::zero;
00081     matrix_[12] = TNumTraits::zero;
00082     matrix_[13] = TNumTraits::zero;
00083     matrix_[14] = TNumTraits::zero;
00084     matrix_[15] = TNumTraits::one;
00085 }
00086 
00087 
00088 
00089 /** construct an matrix from an origin and three base vectors
00090  *  
00091  *  @code
00092  *  TVector3D<T> i, j, k;
00093  *  TPoint3D<T> o, p;
00094  *  // ...
00095  *
00096  *  Transformation3D<T> transformation(o, i, j, k);
00097  *  LASS_ASSERT(transform(p, transformation) == (o + i * p.x + j * p.y + k * p.z));
00098  *  @endcode
00099  */
00100 template <typename T>
00101 Transformation3D<T>::Transformation3D(
00102         const TPoint& origin, const TVector& baseX, const TVector& baseY, const TVector& baseZ):
00103     matrix_(impl::allocateArray<T>(matrixSize_)),
00104     inverseMatrix_(0)
00105 {
00106     matrix_[ 0] = baseX.x;
00107     matrix_[ 1] = baseY.x;
00108     matrix_[ 2] = baseZ.x;
00109     matrix_[ 3] = origin.x;
00110     matrix_[ 4] = baseX.y;
00111     matrix_[ 5] = baseY.y;
00112     matrix_[ 6] = baseZ.y;
00113     matrix_[ 7] = origin.y;
00114     matrix_[ 8] = baseX.z;
00115     matrix_[ 9] = baseY.z;
00116     matrix_[10] = baseZ.z;
00117     matrix_[11] = origin.z;
00118     matrix_[12] = TNumTraits::zero;
00119     matrix_[13] = TNumTraits::zero;
00120     matrix_[14] = TNumTraits::zero;
00121     matrix_[15] = TNumTraits::one;
00122 }
00123 
00124 
00125 
00126 /** construct a transformation from a 4x4 tranformation matrix.
00127  *  The elements of the 4x4 matrix will represented in a row major way by an iterator
00128  *  range [first, last) of 16 elements.
00129  */
00130 template <typename T>
00131 template <typename InputIterator>
00132 Transformation3D<T>::Transformation3D(InputIterator first, InputIterator last):
00133     matrix_(impl::allocateArray<T>(matrixSize_)),
00134     inverseMatrix_(0)
00135 {
00136     LASS_ENFORCE(std::distance(first, last) == 16);
00137     std::copy(first, last, matrix_.get());
00138 }
00139 
00140 
00141 
00142 /** return the inverse transformation.
00143  *  The inverse is calculated on the first call, and then cached for later use.
00144  *  For the transformation, we've use the C version of Cramer's rule as described in
00145  *  the Intel (R) article "Streaming SIMD Extensions -Inverse of 4x4 Matrix" which
00146  *  can be found here: http://www.intel.com/design/pentiumiii/sml/245043.htm
00147  */
00148 template <typename T>
00149 const Transformation3D<T>
00150 Transformation3D<T>::inverse() const
00151 {
00152     if (inverseMatrix_.isEmpty())
00153     {
00154         TMatrix inverseMatrix(impl::allocateArray<T>(matrixSize_));
00155         const TValue* const mat = matrix_.get();
00156         TValue* const inv = inverseMatrix.get();
00157 
00158         const TValue v1015 = mat[10] * mat[15];
00159         const TValue v1411 = mat[14] * mat[11];
00160         const TValue v0615 = mat[ 6] * mat[15];
00161         const TValue v1407 = mat[14] * mat[ 7];
00162         const TValue v0611 = mat[ 6] * mat[11];
00163         const TValue v1007 = mat[10] * mat[ 7];
00164         const TValue v0215 = mat[ 2] * mat[15];
00165         const TValue v1403 = mat[14] * mat[ 3];
00166         const TValue v0211 = mat[ 2] * mat[11];
00167         const TValue v1003 = mat[10] * mat[ 3];
00168         const TValue v0207 = mat[ 2] * mat[ 7];
00169         const TValue v0603 = mat[ 6] * mat[ 3];
00170 
00171         inv[0] = v1015 * mat[ 5] + v1407 * mat[ 9] + v0611 * mat[13]
00172                - v1411 * mat[ 5] - v0615 * mat[ 9] - v1007 * mat[13];
00173         inv[1] = v1411 * mat[ 1] + v0215 * mat[ 9] + v1003 * mat[13]
00174                - v1015 * mat[ 1] - v1403 * mat[ 9] - v0211 * mat[13];
00175         inv[2] = v0615 * mat[ 1] + v1403 * mat[ 5] + v0207 * mat[13]
00176                - v1407 * mat[ 1] - v0215 * mat[ 5] - v0603 * mat[13];
00177         inv[3] = v1007 * mat[ 1] + v0211 * mat[ 5] + v0603 * mat[ 9]
00178                - v0611 * mat[ 1] - v1003 * mat[ 5] - v0207 * mat[ 9];
00179         inv[4] = v1411 * mat[ 4] + v0615 * mat[ 8] + v1007 * mat[12]
00180                - v1015 * mat[ 4] - v1407 * mat[ 8] - v0611 * mat[12];
00181         inv[5] = v1015 * mat[ 0] + v1403 * mat[ 8] + v0211 * mat[12]
00182                - v1411 * mat[ 0] - v0215 * mat[ 8] - v1003 * mat[12];
00183         inv[6] = v1407 * mat[ 0] + v0215 * mat[ 4] + v0603 * mat[12]
00184                - v0615 * mat[ 0] - v1403 * mat[ 4] - v0207 * mat[12];
00185         inv[7] = v0611 * mat[ 0] + v1003 * mat[ 4] + v0207 * mat[ 8]
00186                - v1007 * mat[ 0] - v0211 * mat[ 4] - v0603 * mat[ 8];
00187 
00188         const TValue v0813 = mat[ 8] * mat[13];
00189         const TValue v1209 = mat[12] * mat[ 9];
00190         const TValue v0413 = mat[ 4] * mat[13];
00191         const TValue v1205 = mat[12] * mat[ 5];
00192         const TValue v0409 = mat[ 4] * mat[ 9];
00193         const TValue v0805 = mat[ 8] * mat[ 5];
00194         const TValue v0013 = mat[ 0] * mat[13];
00195         const TValue v1201 = mat[12] * mat[ 1];
00196         const TValue v0009 = mat[ 0] * mat[ 9];
00197         const TValue v0801 = mat[ 8] * mat[ 1];
00198         const TValue v0005 = mat[ 0] * mat[ 5];
00199         const TValue v0401 = mat[ 4] * mat[ 1];
00200 
00201         inv[ 8] = v0813 * mat[ 7] + v1205 * mat[11] + v0409 * mat[15]
00202                 - v1209 * mat[ 7] - v0413 * mat[11] - v0805 * mat[15];
00203         inv[ 9] = v1209 * mat[ 3] + v0013 * mat[11] + v0801 * mat[15]
00204                 - v0813 * mat[ 3] - v1201 * mat[11] - v0009 * mat[15];
00205         inv[10] = v0413 * mat[ 3] + v1201 * mat[ 7] + v0005 * mat[15]
00206                 - v1205 * mat[ 3] - v0013 * mat[ 7] - v0401 * mat[15];
00207         inv[11] = v0805 * mat[ 3] + v0009 * mat[ 7] + v0401 * mat[11]
00208                 - v0409 * mat[ 3] - v0801 * mat[ 7] - v0005 * mat[11];
00209         inv[12] = v0413 * mat[10] + v0805 * mat[14] + v1209 * mat[ 6]
00210                 - v0409 * mat[14] - v0813 * mat[ 6] - v1205 * mat[10];
00211         inv[13] = v0009 * mat[14] + v0813 * mat[ 2] + v1201 * mat[10]
00212                 - v0013 * mat[10] - v0801 * mat[14] - v1209 * mat[ 2];
00213         inv[14] = v0013 * mat[ 6] + v0401 * mat[14] + v1205 * mat[ 2]
00214                 - v0005 * mat[14] - v0413 * mat[ 2] - v1201 * mat[ 6];
00215         inv[15] = v0005 * mat[10] + v0409 * mat[ 2] + v0801 * mat[ 6]
00216                 - v0009 * mat[ 6] - v0401 * mat[10] - v0805 * mat[ 2];
00217 
00218         const TValue det = mat[0] * inv[0] + mat[4] * inv[1] + mat[8] * inv[2] + mat[12] * inv[3];
00219         if (det == TNumTraits::zero)
00220         {
00221             LASS_THROW_EX(SingularityError, "transformation not invertible");
00222         }
00223         const TValue invDet = num::inv(det);
00224         for (unsigned i = 0; i < 16; ++i)
00225         {
00226             inv[i] *= invDet;
00227         }
00228         sync_.lock();
00229         inverseMatrix_.swap(inverseMatrix);
00230         sync_.unlock();
00231     }
00232 
00233     LASS_ASSERT(inverseMatrix_ && matrix_);
00234     return TSelf(inverseMatrix_, matrix_, false);
00235 }
00236 
00237 
00238 
00239 /** Return pointer to row major matrix representation of transformation.
00240  *  This is for immediate use only, like @c std::basic_string::data().
00241  */
00242 template <typename T> inline
00243 const typename Transformation3D<T>::TValue*
00244 Transformation3D<T>::matrix() const
00245 {
00246     return matrix_.get();
00247 }
00248 
00249 
00250 
00251 template <typename T>
00252 void Transformation3D<T>::swap(TSelf& ioOther)
00253 {
00254     matrix_.swap(ioOther.matrix_);
00255     inverseMatrix_.swap(ioOther.inverseMatrix_);
00256 }
00257 
00258 
00259 
00260 /** make a 3D identity transformation
00261  */
00262 template <typename T>
00263 const Transformation3D<T> Transformation3D<T>::identity()
00264 {
00265     return TSelf();
00266 }
00267 
00268 
00269 
00270 /** make a 3D transformation representing a translation
00271  */
00272 template <typename T>
00273 const Transformation3D<T> Transformation3D<T>::translation(const Vector3D<T>& offset)
00274 {
00275     TSelf result;
00276     result.matrix_[3] = offset.x;
00277     result.matrix_[7] = offset.y;
00278     result.matrix_[11] = offset.z;
00279     return result;
00280 }
00281 
00282 
00283 
00284 /** make a 3D transformation representing a uniform scaling
00285  */
00286 template <typename T>
00287 const Transformation3D<T> Transformation3D<T>::scaler(const T& scale)
00288 {
00289     TSelf result;
00290     result.matrix_[0] = scale;
00291     result.matrix_[5] = scale;
00292     result.matrix_[10] = scale;
00293     return result;
00294 }
00295 
00296 
00297 
00298 /** make a 3D transformation representing a scaling with different factors per axis
00299  */
00300 template <typename T>
00301 const Transformation3D<T> Transformation3D<T>::scaler(const Vector3D<T>& scale)
00302 {
00303     TSelf result;
00304     result.matrix_[0] = scale.x;
00305     result.matrix_[5] = scale.y;
00306     result.matrix_[10] = scale.z;
00307     return result;
00308 }
00309 
00310 
00311 
00312 /** make a 3D transformation representing a rotation around a primary axis
00313  */
00314 template <typename T>
00315 const Transformation3D<T> Transformation3D<T>::rotation(XYZ axis, TParam radians)
00316 {
00317     const T c = num::cos(radians);
00318     const T s = num::sin(radians);
00319     const size_t a = (axis + 1);
00320     const size_t b = (axis + 2);
00321     LASS_ASSERT(a < 3 && b < 3);
00322 
00323     Transformation3D<T> result;
00324     result.matrix_[5 * a] = c;
00325     result.matrix_[5 * b] = c;
00326     result.matrix_[4 * a + b] = -s;
00327     result.matrix_[4 * b + a] = s;
00328     return result;
00329 }
00330 
00331 
00332 
00333 /** make a 3D transformation representing a rotation around an arbitrary axis
00334  */
00335 template <typename T>
00336 const Transformation3D<T> Transformation3D<T>::rotation(const Vector3D<T>& axis, TParam radians)
00337 {
00338     Vector3D<T> a = axis.normal();
00339     const T c = num::cos(radians);
00340     const T s = num::sin(radians);
00341     const TValue oneMinusC = TNumTraits::one - c;
00342 
00343     Transformation3D<T> result;
00344     result.matrix_[ 0] = a.x * a.x * oneMinusC + c;
00345     result.matrix_[ 1] = a.x * a.y * oneMinusC - a.z * s;
00346     result.matrix_[ 2] = a.x * a.z * oneMinusC + a.y * s;
00347     result.matrix_[ 4] = a.y * a.x * oneMinusC + a.z * s;
00348     result.matrix_[ 5] = a.y * a.y + oneMinusC + c;
00349     result.matrix_[ 6] = a.y * a.z * oneMinusC - a.x * s;
00350     result.matrix_[ 8] = a.z * a.x * oneMinusC - a.y * s;
00351     result.matrix_[ 9] = a.z * a.y * oneMinusC + a.x * s;
00352     result.matrix_[10] = a.z * a.z + oneMinusC + c;
00353     return result;
00354 }
00355 
00356 
00357 
00358 // --- protected -----------------------------------------------------------------------------------
00359 
00360 
00361 
00362 // --- private -------------------------------------------------------------------------------------
00363 
00364 template <typename T> inline
00365 Transformation3D<T>::Transformation3D(const TMatrix& matrix, const TMatrix& inverseMatrix, bool):
00366     matrix_(matrix),
00367     inverseMatrix_(inverseMatrix)
00368 {
00369 }
00370 
00371 
00372 
00373 // --- free ----------------------------------------------------------------------------------------
00374 
00375 /** concatenate two transformations @a first and @a second in one.
00376  *  @relates Transformation3D
00377  *  The result is one transformation that performs the same actions as first performing
00378  *  @a first and then @a second.  Hence, the following lines of code are equivalent (ignoring
00379  *  numerical imprecions):
00380  *
00381  *  @code
00382  *  y = transform(x, concatenate(first, second));
00383  *  y = transform(transform(x, first), second);
00384  *  @endcode
00385  */
00386 template <typename T>
00387 Transformation3D<T> concatenate(const Transformation3D<T>& first, const Transformation3D<T>& second)
00388 {
00389     // right-handed vector product, so it's @a second * @a first instead of @a first * @a second
00390     const T* const a = second.matrix();
00391     const T* const b = first.matrix();
00392     T result[16];
00393     for (size_t i = 0; i < 16; i += 4)
00394     {
00395         for (size_t j = 0; j < 4; ++j)
00396         {
00397             result[i + j] = 
00398                 a[i    ] * b[     j] + 
00399                 a[i + 1] * b[ 4 + j] + 
00400                 a[i + 2] * b[ 8 + j] + 
00401                 a[i + 3] * b[12 + j];
00402         }
00403     }
00404     return Transformation3D<T>(result, result + 16);
00405 }
00406 
00407 
00408 
00409 /** apply transformation to a vector
00410  *  @relates Transformation3D
00411  */
00412 template <typename T>
00413 Vector3D<T> transform(const Vector3D<T>& subject, const Transformation3D<T>& transformation)
00414 {
00415     const T* const mat = transformation.matrix();
00416     return Vector3D<T>(
00417         mat[ 0] * subject.x + mat[ 1] * subject.y + mat[ 2] * subject.z,
00418         mat[ 4] * subject.x + mat[ 5] * subject.y + mat[ 6] * subject.z,
00419         mat[ 8] * subject.x + mat[ 9] * subject.y + mat[10] * subject.z);
00420 }
00421 
00422 
00423 
00424 /** apply transformation to a point
00425  *  @relates Transformation3D
00426  */
00427 template <typename T>
00428 Point3D<T> transform(const Point3D<T>& subject, const Transformation3D<T>& transformation)
00429 {
00430     const T* const mat = transformation.matrix();
00431     const T weight = num::inv(mat[12] * subject.x + mat[13] * subject.y + mat[14] * subject.z + mat[15]);
00432     return Point3D<T>(
00433         weight * (mat[ 0] * subject.x + mat[ 1] * subject.y + mat[ 2] * subject.z + mat[ 3]),
00434         weight * (mat[ 4] * subject.x + mat[ 5] * subject.y + mat[ 6] * subject.z + mat[ 7]),
00435         weight * (mat[ 8] * subject.x + mat[ 9] * subject.y + mat[10] * subject.z + mat[11]));
00436 
00437 }
00438 
00439 
00440 
00441 /** apply transformation to a normal vector.
00442  *  @relates Transformation3D
00443  *  Vectors that represent a normal vector should transform differentely than ordinary
00444  *  vectors.  Use this transformation function for normals.
00445  */
00446 template <typename T>
00447 Vector3D<T> normalTransform(const Vector3D<T>& subject, const Transformation3D<T>& transformation)
00448 {
00449     const T* const invMat = transformation.inverse().matrix();
00450     return Vector3D<T>(
00451         invMat[ 0] * subject.x + invMat[ 4] * subject.y + invMat[ 8] * subject.z,
00452         invMat[ 1] * subject.x + invMat[ 5] * subject.y + invMat[ 9] * subject.z,
00453         invMat[ 2] * subject.x + invMat[ 6] * subject.y + invMat[10] * subject.z);
00454 }
00455 
00456 
00457 
00458 /** apply transformation to a 4D normal vector.
00459  *  @relates Transformation3D
00460  *  Vectors that represent a normal vector should transform differentely than ordinary
00461  *  vectors.  Use this transformation function for normals.
00462  *
00463  *  Cartesian planes have a 4D normal vector that must be transformed in 3D.  Use this
00464  *  function to do it:
00465  *
00466  *  @code
00467  *  // ax + by + cz + d == 0
00468  *  normalTransform(std::make_pair(Vector3D<float>(a, b, c), d), transformation);
00469  *  @endcode
00470  */
00471 template <typename T>
00472 std::pair<Vector3D<T>, T> normalTransform(const std::pair<Vector3D<T>, T>& subject, 
00473                                           const Transformation3D<T>& transformation)
00474 {
00475     const T* const invMat = transformation.inverse().matrix();
00476     const Vector3D<T>& n = subject.first;
00477     const T d = subject.second;
00478     const Vector3D<T> transformedN(
00479         invMat[ 0] * n.x + invMat[ 4] * n.y + invMat[ 8] * n.z + invMat[12] * d,
00480         invMat[ 1] * n.x + invMat[ 5] * n.y + invMat[ 9] * n.z + invMat[13] * d,
00481         invMat[ 2] * n.x + invMat[ 6] * n.y + invMat[10] * n.z + invMat[14] * d);
00482     const T transformedD = 
00483         invMat[ 3] * n.x + invMat[ 7] * n.y + invMat[11] * n.z + invMat[15] * d;
00484     return std::make_pair(transformedN, transformedD);
00485 }
00486 
00487 
00488 
00489 /** @relates Transformation3D
00490  */
00491 template<typename T, typename Char, typename Traits>
00492 std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream,
00493                                              const Transformation3D<T>& transformation)
00494 {
00495     const T* const mat = transformation.matrix();
00496     LASS_ENFORCE_STREAM(stream) << "(("
00497         << mat[ 0] << ", " << mat[ 1] << ", " << mat[ 2] << ", " << mat[ 3] << "), ("
00498         << mat[ 4] << ", " << mat[ 5] << ", " << mat[ 6] << ", " << mat[ 7] << "), ("
00499         << mat[ 8] << ", " << mat[ 9] << ", " << mat[10] << ", " << mat[11] << "), ("
00500         << mat[12] << ", " << mat[13] << ", " << mat[14] << ", " << mat[15] << "))";
00501     return stream;
00502 }
00503 
00504 
00505 
00506 /** @relates Transformation3D
00507  */
00508 template<typename T>
00509 io::XmlOStream& operator<<(io::XmlOStream& stream, const Transformation3D<T>& transformation)
00510 {
00511     const T* const mat = transformation.matrix();
00512     LASS_ENFORCE_STREAM(stream) << "<Transformation3D>"
00513         << mat[ 0] << " " << mat[ 1] << " " << mat[ 2] << " " << mat[ 3] << " "
00514         << mat[ 4] << " " << mat[ 5] << " " << mat[ 6] << " " << mat[ 7] << " "
00515         << mat[ 8] << " " << mat[ 9] << " " << mat[10] << " " << mat[11] << " "
00516         << mat[12] << " " << mat[13] << " " << mat[14] << " " << mat[15]
00517         << "</Transformation3D>\n";
00518     return stream;
00519 }
00520 
00521 
00522 
00523 }
00524 
00525 }
00526 
00527 #if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
00528 #   pragma warning(pop)
00529 #endif
00530 
00531 #endif

Generated on Mon Nov 10 14:21:40 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo