]> git.tdb.fi Git - libs/math.git/blob - source/geometry/affinetransformation.h
Put ray and bounding box transformations in AffineTransformation
[libs/math.git] / source / geometry / affinetransformation.h
1 #ifndef MSP_GEOMETRY_AFFINETRANSFORMATION_H_
2 #define MSP_GEOMETRY_AFFINETRANSFORMATION_H_
3
4 #include <msp/linal/squarematrix.h>
5 #include "angle.h"
6 #include "boundingbox.h"
7 #include "ray.h"
8
9 namespace Msp {
10 namespace Geometry {
11
12 template<typename T, unsigned D>
13 class AffineTransformation;
14
15
16 /**
17 Helper class to provide specialized operations for AffineTransformation.
18 */
19 template<typename T, unsigned D>
20 class AffineTransformationOps
21 {
22 protected:
23         AffineTransformationOps() { }
24 };
25
26 template<typename T>
27 class AffineTransformationOps<T, 2>
28 {
29 protected:
30         AffineTransformationOps() { }
31
32 public:
33         static AffineTransformation<T, 2> rotation(const Angle<T> &);
34 };
35
36 template<typename T>
37 class AffineTransformationOps<T, 3>
38 {
39 protected:
40         AffineTransformationOps() { }
41
42 public:
43         static AffineTransformation<T, 3> rotation(const Angle<T> &, const LinAl::Vector<T, 3> &);
44 };
45
46
47 /**
48 An affine transformation in D dimensions.  Affine transformations preserve
49 straightness of lines and ratios of distances.  Angles and distances themselves
50 may change.  Internally this is represented by a square matrix of size D+1.
51 */
52 template<typename T, unsigned D>
53 class AffineTransformation: public AffineTransformationOps<T, D>
54 {
55         friend class AffineTransformationOps<T, D>;
56
57 private:
58         LinAl::SquareMatrix<T, D+1> matrix;
59
60 public:
61         AffineTransformation();
62
63         static AffineTransformation<T, D> translation(const LinAl::Vector<T, D> &);
64         static AffineTransformation<T, D> scaling(const LinAl::Vector<T, D> &);
65         static AffineTransformation<T, D> shear(const LinAl::Vector<T, D> &, const LinAl::Vector<T, D> &);
66
67         AffineTransformation &operator*=(const AffineTransformation &);
68         AffineTransformation &invert();
69
70         const LinAl::SquareMatrix<T, D+1> &get_matrix() const { return matrix; }
71         operator const LinAl::SquareMatrix<T, D+1> &() const { return matrix; }
72
73         LinAl::Vector<T, D> transform(const LinAl::Vector<T, D> &) const;
74         LinAl::Vector<T, D> transform_linear(const LinAl::Vector<T, D> &) const;
75         Ray<T, D> transform(const Ray<T, D> &) const;
76         BoundingBox<T, D> transform(const BoundingBox<T, D> &) const;
77 };
78
79 template<typename T, unsigned D>
80 inline AffineTransformation<T, D>::AffineTransformation()
81 {
82         this->matrix = LinAl::SquareMatrix<T, D+1>::identity();
83 }
84
85
86 template<typename T, unsigned D>
87 AffineTransformation<T, D> AffineTransformation<T, D>::translation(const LinAl::Vector<T, D> &v)
88 {
89         AffineTransformation<T, D> r;
90         for(unsigned i=0; i<D; ++i)
91                 r.matrix(i, D) = v[i];
92         return r;
93 }
94
95 template<typename T, unsigned D>
96 AffineTransformation<T, D> AffineTransformation<T, D>::scaling(const LinAl::Vector<T, D> &factors)
97 {
98         AffineTransformation<T, D> r;
99         for(unsigned i=0; i<D; ++i)
100                 r.matrix(i, i) = factors[i];
101         return r;
102 }
103
104 template<typename T, unsigned D>
105 AffineTransformation<T, D> AffineTransformation<T, D>::shear(const LinAl::Vector<T, D> &normal, const LinAl::Vector<T, D> &shift)
106 {
107         AffineTransformation<T, D> r;
108         for(unsigned i=0; i<D; ++i)
109                 for(unsigned j=0; j<D; ++j)
110                         r.matrix(i, j) += normal[j]*shift[i];
111         return r;
112 }
113
114 template<typename T>
115 AffineTransformation<T, 2> AffineTransformationOps<T, 2>::rotation(const Angle<T> &angle)
116 {
117         AffineTransformation<T, 2> r;
118         T c = cos(angle);
119         T s = sin(angle);
120         r.matrix(0, 0) = c;
121         r.matrix(0, 1) = -s;
122         r.matrix(1, 0) = s;
123         r.matrix(1, 1) = c;
124         return r;
125 }
126
127 template<typename T>
128 AffineTransformation<T, 3> AffineTransformationOps<T, 3>::rotation(const Angle<T> &angle, const LinAl::Vector<T, 3> &axis)
129 {
130         AffineTransformation<T, 3> r;
131         LinAl::Vector<T, 3> axn = normalize(axis);
132         T c = cos(angle);
133         T s = sin(angle);
134         // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
135         r.matrix(0, 0) = c+axn.x*axn.x*(1-c);
136         r.matrix(0, 1) = axn.x*axn.y*(1-c)-axn.z*s;
137         r.matrix(0, 2) = axn.x*axn.z*(1-c)+axn.y*s;
138         r.matrix(1, 0) = axn.y*axn.x*(1-c)+axn.z*s;
139         r.matrix(1, 1) = c+axn.y*axn.y*(1-c);
140         r.matrix(1, 2) = axn.y*axn.z*(1-c)-axn.x*s;
141         r.matrix(2, 0) = axn.z*axn.x*(1-c)-axn.y*s;
142         r.matrix(2, 1) = axn.z*axn.y*(1-c)+axn.x*s;
143         r.matrix(2, 2) = c+axn.z*axn.z*(1-c);
144         return r;
145 }
146
147 template<typename T, unsigned D>
148 inline AffineTransformation<T, D> &AffineTransformation<T, D>::operator*=(const AffineTransformation<T, D> &other)
149 {
150         matrix *= other.get_matrix();
151         return *this;
152 }
153
154 template<typename T, unsigned D>
155 inline AffineTransformation<T, D> operator*(const AffineTransformation<T, D> &at1, const AffineTransformation<T, D> &at2)
156 {
157         AffineTransformation<T, D> r = at1;
158         return r *= at2;
159 }
160
161 template<typename T, unsigned D>
162 inline AffineTransformation<T, D> &AffineTransformation<T, D>::invert()
163 {
164         matrix.invert();
165         return *this;
166 }
167
168 template<typename T, unsigned D>
169 inline AffineTransformation<T, D> invert(const AffineTransformation<T, D> &at)
170 {
171         AffineTransformation<T, D> r = at;
172         return r.invert();
173 }
174
175 template<typename T, unsigned D>
176 inline LinAl::Vector<T, D> AffineTransformation<T, D>::transform(const LinAl::Vector<T, D> &v) const
177 {
178         return LinAl::Vector<T, D>(matrix*LinAl::Vector<T, D+1>(v, T(1)));
179 }
180
181 template<typename T, unsigned D>
182 inline LinAl::Vector<T, D> AffineTransformation<T, D>::transform_linear(const LinAl::Vector<T, D> &v) const
183 {
184         return LinAl::Vector<T, D>(matrix*LinAl::Vector<T, D+1>(v, T(0)));
185 }
186
187 template<typename T, unsigned D>
188 inline Ray<T, D> AffineTransformation<T, D>::transform(const Ray<T, D> &ray) const
189 {
190         LinAl::Vector<T, D> dir = transform_linear(ray.get_direction());
191         return Ray<T, D>(transform(ray.get_start()), dir, ray.get_limit()*dir.norm());
192 }
193
194 template<typename T, unsigned D>
195 inline BoundingBox<T, D> AffineTransformation<T, D>::transform(const BoundingBox<T, D> &bbox) const
196 {
197         LinAl::Vector<T, D> min_pt;
198         LinAl::Vector<T, D> max_pt;
199         for(unsigned i=0; i<(1<<D); ++i)
200         {
201                 LinAl::Vector<T, D> point;
202                 for(unsigned j=0; j<D; ++j)
203                         point[j] = ((i>>j)&1 ? bbox.get_maximum_coordinate(j) : bbox.get_minimum_coordinate(j));
204
205                 point = transform(point);
206
207                 if(i==0)
208                 {
209                         min_pt = point;
210                         max_pt = point;
211                 }
212                 else
213                 {
214                         for(unsigned j=0; j<D; ++j)
215                         {
216                                 min_pt[j] = std::min(min_pt[j], point[j]);
217                                 max_pt[j] = std::max(max_pt[j], point[j]);
218                         }
219                 }
220         }
221
222         return BoundingBox<T, D>(min_pt, max_pt);
223 }
224
225 } // namespace Geometry
226 } // namespace Msp
227
228 #endif