Library of Assembled Shared Sources
transformation_2d.inl
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2011 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43#ifndef LASS_GUARDIAN_OF_INCLUSION_PRIM_TRANSFORMATION_2D_INL
44#define LASS_GUARDIAN_OF_INCLUSION_PRIM_TRANSFORMATION_2D_INL
45
46#include "transformation_2d.h"
47
48#if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
49# pragma warning(push)
50# pragma warning(disable:4996) // std::copy may be unsafe
51#endif
52
53namespace lass
54{
55
56namespace prim
57{
58
59// --- public --------------------------------------------------------------------------------------
60
61/** construct an identity transformation.
62 * An identity transformation transforms every point to itself.
63 */
64template <typename T>
66 matrix_(impl::allocateArray<T>(matrixSize_)),
67 inverseMatrix_(0)
68{
69 matrix_[ 0] = TNumTraits::one;
70 matrix_[ 1] = TNumTraits::zero;
71 matrix_[ 2] = TNumTraits::zero;
72 matrix_[ 3] = TNumTraits::zero;
73 matrix_[ 4] = TNumTraits::one;
74 matrix_[ 5] = TNumTraits::zero;
75 matrix_[ 6] = TNumTraits::zero;
76 matrix_[ 7] = TNumTraits::zero;
77 matrix_[ 8] = TNumTraits::one;
78}
79
80
81
82template <typename T>
83Transformation2D<T>::Transformation2D(const TPoint& origin, const TVector& baseX, const TVector& baseY):
84 matrix_(impl::allocateArray<T>(matrixSize_)),
85 inverseMatrix_(0)
86{
87 matrix_[ 0] = baseX.x;
88 matrix_[ 1] = baseY.x;
89 matrix_[ 2] = origin.x;
90 matrix_[ 3] = baseX.y;
91 matrix_[ 4] = baseY.y;
92 matrix_[ 5] = origin.y;
93 matrix_[ 6] = TNumTraits::one;
94 matrix_[ 7] = TNumTraits::zero;
95 matrix_[ 8] = TNumTraits::zero;
96}
97
98
99
100/** construct a transformation from a 3x3 tranformation matrix.
101 * The elements of the 3x3 matrix will represented in a row major way by an iterator
102 * range [first, last) of 9 elements.
103 */
104template <typename T>
105template <typename InputIterator>
106Transformation2D<T>::Transformation2D(InputIterator first, InputIterator last):
107 matrix_(impl::allocateArray<T>(matrixSize_)),
108 inverseMatrix_(0)
110 LASS_ENFORCE(std::distance(first, last) == matrixSize_);
111 std::copy(first, last, matrix_.get());
112}
113
114
115
116/** return the inverse transformation.
117 * The inverse is calculated on the first call, and then cached for later use.
118 * For the transformation, we've use the C version of Cramer's rule as described in
119 * the Intel (R) article "Streaming SIMD Extensions -Inverse of 4x4 Matrix" which
120 * can be found here: http://www.intel.com/design/pentiumiii/sml/245043.htm
121 */
122template <typename T>
125{
126 if (inverseMatrix_.isEmpty())
127 {
128 TMatrix inverseMatrix(impl::allocateArray<T>(matrixSize_));
129 const TValue* const mat = matrix_.get();
130 TValue* const inv = inverseMatrix.get();
131
132 inv[0] = mat[4] * mat[8] - mat[5] * mat[7];
133 inv[1] = mat[7] * mat[2] - mat[8] * mat[1];
134 inv[2] = mat[1] * mat[5] - mat[2] * mat[4];
135
136 inv[3] = mat[6] * mat[5] - mat[8] * mat[3];
137 inv[4] = mat[0] * mat[8] - mat[2] * mat[6];
138 inv[5] = mat[3] * mat[2] - mat[5] * mat[0];
139
140 inv[6] = mat[3] * mat[7] - mat[4] * mat[6];
141 inv[7] = mat[6] * mat[1] - mat[7] * mat[0];
142 inv[8] = mat[0] * mat[4] - mat[1] * mat[3];
143
144 const TValue det = mat[0] * inv[0] + mat[1] * inv[3] + mat[2] * inv[6];
145 if (det == TNumTraits::zero)
146 {
147 inverseMatrix.reset();
148 LASS_THROW_EX(util::SingularityError, "transformation not invertible");
149 }
150 const TValue invDet = num::inv(det);
151 for (int i = 0; i < 9; ++i)
152 {
153 inv[i] *= invDet;
154 }
155
156 sync_.lock();
157 inverseMatrix_.swap(inverseMatrix);
158 sync_.unlock();
159 }
160
161 LASS_ASSERT(inverseMatrix_ && matrix_);
162 return TSelf(inverseMatrix_, matrix_, false);
163}
164
165
166
167/** Return pointer to row major matrix representation of transformation.
168 * This is for immediate use only, like @c std::basic_string::data().
169 */
170template <typename T> inline
171const typename Transformation2D<T>::TValue*
173{
174 return matrix_.get();
175}
176
177
178
179template <typename T>
180bool Transformation2D<T>::isIdentity() const
181{
182 const TValue* const forward = matrix();
183 for (size_t i = 0; i < 3; ++i)
184 {
185 for (size_t j = 0; j < 3; ++j)
186 {
187 if (forward[i * 3 + j] != (i == j ? TNumTraits::one : TNumTraits::zero))
188 {
189 return false;
190 }
191 }
192 }
193 return true;
194}
195
196
197
198template <typename T>
199bool Transformation2D<T>::isTranslation() const
200{
201 const TValue* const forward = matrix();
202 for (size_t i = 0; i < 3; ++i)
203 {
204 for (size_t j = 0; j < 3; ++j)
205 {
206 if (j < 2 && forward[i * 3 + j] != (i == j ? TNumTraits::one : TNumTraits::zero))
207 {
208 return false;
209 }
210 }
211 }
212 return true;
213}
214
215
216
217template <typename T>
218void Transformation2D<T>::swap(TSelf& other)
219{
220 matrix_.swap(other.matrix_);
221 inverseMatrix_.swap(other.inverseMatrix_);
222}
223
224
225
226/** make a 2D identity transformation
227 */
228template <typename T>
230{
231 return TSelf();
232}
233
234
235
236/** make a 2D transformation representing a translation
237 */
238template <typename T>
240{
241 Transformation2D<T> result;
242 result.matrix_[2] = offset.x;
243 result.matrix_[5] = offset.y;
244 return result;
245}
246
247
248
249/** make a 3D transformation representing a uniform scaling
250 */
251template <typename T>
253{
254 Transformation2D<T> result;
255 result.matrix_[0] = scale;
256 result.matrix_[4] = scale;
257 return result;
258}
259
260
261
262/** make a 3D transformation representing a scaling with different factors per axis
263 */
264template <typename T>
266{
267 Transformation2D<T> result;
268 result.matrix_[0] = scale.x;
269 result.matrix_[4] = scale.y;
270 return result;
271}
272
273
274
275/** make a 3D transformation representing a counterclockwise rotation
276 */
277template <typename T>
279{
280 const T c = num::cos(radians);
281 const T s = num::sin(radians);
282
283 Transformation2D<T> result;
284 result.matrix_[0] = c;
285 result.matrix_[4] = c;
286 result.matrix_[1] = -s;
287 result.matrix_[3] = s;
288 return result;
289}
290
291
292
293// --- protected -----------------------------------------------------------------------------------
294
295
296
297// --- private -------------------------------------------------------------------------------------
298
299template <typename T> inline
300Transformation2D<T>::Transformation2D(const TMatrix& matrix, const TMatrix& inverseMatrix, bool):
301 matrix_(matrix),
302 inverseMatrix_(inverseMatrix)
303{
304}
305
306
307
308// --- free ----------------------------------------------------------------------------------------
309
310/** concatenate two transformations @a first and @a second in one.
311 * @relates Transformation2D
312 * The result is one transformation that performs the same actions as first performing
313 * @a first and then @a second. Hence, the following lines of code are equivalent (ignoring
314 * numerical imprecions):
315 *
316 * @code
317 * y = transform(x, concatenate(first, second));
318 * y = transform(transform(x, first), second);
319 * @endcode
320 */
321template <typename T>
323{
324 // right-handed vector product, so it's @a second * @a first instead of @a first * @a second
325 const T* const a = second.matrix();
326 const T* const b = first.matrix();
327 T result[9];
328 for (size_t i = 0; i < 9; i += 3)
329 {
330 for (size_t j = 0; j < 3; ++j)
331 {
332 result[i + j] =
333 a[i ] * b[ j] +
334 a[i + 1] * b[3 + j] +
335 a[i + 2] * b[6 + j];
336 }
337 }
338 return Transformation2D<T>(result, result + 9);
339}
340
341
342
343/** apply transformation to a vector
344 * @relates Transformation2D
345 */
346template <typename T>
347Vector2D<T> transform(const Vector2D<T>& subject, const Transformation2D<T>& transformation)
348{
349 const T* const mat = transformation.matrix();
350 return Vector2D<T>(
351 mat[0] * subject.x + mat[1] * subject.y,
352 mat[3] * subject.x + mat[4] * subject.y);
353}
354
355
356
357/** apply transformation to a point
358 * @relates Transformation2D
359 */
360template <typename T>
361Point2D<T> transform(const Point2D<T>& subject, const Transformation2D<T>& transformation)
362{
363 const T* const mat = transformation.matrix();
364 const T weight = num::inv(mat[6] * subject.x + mat[7] * subject.y + mat[8]);
365 return Point2D<T>(
366 weight * (mat[0] * subject.x + mat[1] * subject.y + mat[2]),
367 weight * (mat[3] * subject.x + mat[4] * subject.y + mat[5]));
368
369}
370
371
372
373/** apply transformation to a normal vector.
374 * @relates Transformation2D
375 * Vectors that represent a normal vector should transform differentely than ordinary
376 * vectors. Use this transformation function for normals.
377 */
378template <typename T>
379Vector2D<T> normalTransform(const Vector2D<T>& subject, const Transformation2D<T>& transformation)
380{
381 const T* const invMat = transformation.inverse().matrix();
382 return Vector2D<T>(
383 invMat[0] * subject.x + invMat[3] * subject.y,
384 invMat[1] * subject.x + invMat[4] * subject.y);
385}
386
387
388
389/** apply transformation to a 3D normal vector.
390 * @relates Transformation2D
391 * Vectors that represent a normal vector should transform differentely than ordinary
392 * vectors. Use this transformation function for normals.
393 *
394 * Cartesian lines have a 3D normal vector that must be transformed in 2D. Use this
395 * function to do it:
396 *
397 * @code
398 * // ax + by + c == 0
399 * normalTransform(std::make_pair(Vector2D<float>(a, b), c), transformation);
400 * @endcode
401 */
402template <typename T>
403std::pair<Vector2D<T>, T> normalTransform(const std::pair<Vector2D<T>, T>& subject,
404 const Transformation2D<T>& transformation)
405{
406 const T* const invMat = transformation.inverse().matrix();
407 const Vector2D<T>& n = subject.first;
408 const T c = subject.second;
409 return std::make_pair(
411 invMat[0] * n.x + invMat[3] * n.y + invMat[6] * c,
412 invMat[1] * n.x + invMat[4] * n.y + invMat[7] * c),
413 invMat[2] * n.x + invMat[5] * n.y + invMat[8] * c);
414}
415
416
417
418/** @relates Transformation2D
419 */
420template<typename T, typename Char, typename Traits>
421std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream,
422 const Transformation2D<T>& transformation)
423{
424 const T* const mat = transformation.matrix();
425 LASS_ENFORCE_STREAM(stream) << "(("
426 << mat[0] << ", " << mat[1] << ", " << mat[2] << "), ("
427 << mat[3] << ", " << mat[4] << ", " << mat[5] << "), ("
428 << mat[6] << ", " << mat[7] << ", " << mat[9] << "))";
429 return stream;
430}
431
432
433
434/** @relates Transformation2D
435 */
436template<typename T>
437io::XmlOStream& operator<<(io::XmlOStream& stream, const Transformation2D<T>& transformation)
438{
439 const T* const mat = transformation.matrix();
440 LASS_ENFORCE_STREAM(stream) << "<Transformation2D>"
441 << mat[0] << " " << mat[1] << " " << mat[2] << " "
442 << mat[3] << " " << mat[4] << " " << mat[5] << " "
443 << mat[6] << " " << mat[7] << " " << mat[9]
444 << "</Transformation2D>\n";
445 return stream;
446}
447
448
449
450}
451
452}
453
454#if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
455# pragma warning(pop)
456#endif
457
458#endif
Output stream for writing a selection of geometric primitives to XML files.
a linear 2D transformation
Point2D< T > transform(const Point2D< T > &subject, const Transformation2D< T > &transformation)
apply transformation to a point
static const TSelf translation(const Vector2D< T > &offset)
make a 2D transformation representing a translation
static const TSelf rotation(TParam radians)
make a 3D transformation representing a counterclockwise rotation
const TValue * matrix() const
Return pointer to row major matrix representation of transformation.
Transformation2D()
construct an identity transformation.
const Transformation2D< T > inverse() const
return the inverse transformation.
Vector2D< T > normalTransform(const Vector2D< T > &subject, const Transformation2D< T > &transformation)
apply transformation to a normal vector.
static const TSelf scaler(const T &scale)
make a 3D transformation representing a uniform scaling
Transformation2D< T > concatenate(const Transformation2D< T > &first, const Transformation2D< T > &second)
concatenate two transformations first and second in one.
std::pair< Vector2D< T >, T > normalTransform(const std::pair< Vector2D< T >, T > &subject, const Transformation2D< T > &transformation)
apply transformation to a 3D normal vector.
static const TSelf identity()
make a 2D identity transformation
Vector2D< T > transform(const Vector2D< T > &subject, const Transformation2D< T > &transformation)
apply transformation to a vector
T inv(const T &x)
return x ^ -1
Definition basic_ops.h:178
T inv(const T &x)
return x ^ -1
implementation details of lass::prim
set of geometrical primitives
Definition aabb_2d.h:81
Library for Assembled Shared Sources.
Definition config.h:53