1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
AffineTransform(float m00,float m01,float m02,float m10,float m11,float m12)29 AffineTransform::AffineTransform (float m00, float m01, float m02,
30                                   float m10, float m11, float m12) noexcept
31  :  mat00 (m00), mat01 (m01), mat02 (m02),
32     mat10 (m10), mat11 (m11), mat12 (m12)
33 {
34 }
35 
operator ==(const AffineTransform & other) const36 bool AffineTransform::operator== (const AffineTransform& other) const noexcept
37 {
38     return mat00 == other.mat00
39         && mat01 == other.mat01
40         && mat02 == other.mat02
41         && mat10 == other.mat10
42         && mat11 == other.mat11
43         && mat12 == other.mat12;
44 }
45 
operator !=(const AffineTransform & other) const46 bool AffineTransform::operator!= (const AffineTransform& other) const noexcept
47 {
48     return ! operator== (other);
49 }
50 
51 //==============================================================================
isIdentity() const52 bool AffineTransform::isIdentity() const noexcept
53 {
54     return mat01 == 0.0f
55         && mat02 == 0.0f
56         && mat10 == 0.0f
57         && mat12 == 0.0f
58         && mat00 == 1.0f
59         && mat11 == 1.0f;
60 }
61 
62 JUCE_DECLARE_DEPRECATED_STATIC (const AffineTransform AffineTransform::identity (1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);)
63 
64 //==============================================================================
followedBy(const AffineTransform & other) const65 AffineTransform AffineTransform::followedBy (const AffineTransform& other) const noexcept
66 {
67     return { other.mat00 * mat00 + other.mat01 * mat10,
68              other.mat00 * mat01 + other.mat01 * mat11,
69              other.mat00 * mat02 + other.mat01 * mat12 + other.mat02,
70              other.mat10 * mat00 + other.mat11 * mat10,
71              other.mat10 * mat01 + other.mat11 * mat11,
72              other.mat10 * mat02 + other.mat11 * mat12 + other.mat12 };
73 }
74 
translated(float dx,float dy) const75 AffineTransform AffineTransform::translated (float dx, float dy) const noexcept
76 {
77     return { mat00, mat01, mat02 + dx,
78              mat10, mat11, mat12 + dy };
79 }
80 
translation(float dx,float dy)81 AffineTransform AffineTransform::translation (float dx, float dy) noexcept
82 {
83     return { 1.0f, 0.0f, dx,
84              0.0f, 1.0f, dy };
85 }
86 
withAbsoluteTranslation(float tx,float ty) const87 AffineTransform AffineTransform::withAbsoluteTranslation (float tx, float ty) const noexcept
88 {
89     return { mat00, mat01, tx,
90              mat10, mat11, ty };
91 }
92 
rotated(float rad) const93 AffineTransform AffineTransform::rotated (float rad) const noexcept
94 {
95     auto cosRad = std::cos (rad);
96     auto sinRad = std::sin (rad);
97 
98     return { cosRad * mat00 - sinRad * mat10,
99              cosRad * mat01 - sinRad * mat11,
100              cosRad * mat02 - sinRad * mat12,
101              sinRad * mat00 + cosRad * mat10,
102              sinRad * mat01 + cosRad * mat11,
103              sinRad * mat02 + cosRad * mat12 };
104 }
105 
rotation(float rad)106 AffineTransform AffineTransform::rotation (float rad) noexcept
107 {
108     auto cosRad = std::cos (rad);
109     auto sinRad = std::sin (rad);
110 
111     return { cosRad, -sinRad, 0,
112              sinRad,  cosRad, 0 };
113 }
114 
rotation(float rad,float pivotX,float pivotY)115 AffineTransform AffineTransform::rotation (float rad, float pivotX, float pivotY) noexcept
116 {
117     auto cosRad = std::cos (rad);
118     auto sinRad = std::sin (rad);
119 
120     return { cosRad, -sinRad, -cosRad * pivotX +  sinRad * pivotY + pivotX,
121              sinRad,  cosRad, -sinRad * pivotX + -cosRad * pivotY + pivotY };
122 }
123 
rotated(float angle,float pivotX,float pivotY) const124 AffineTransform AffineTransform::rotated (float angle, float pivotX, float pivotY) const noexcept
125 {
126     return followedBy (rotation (angle, pivotX, pivotY));
127 }
128 
scaled(float factorX,float factorY) const129 AffineTransform AffineTransform::scaled (float factorX, float factorY) const noexcept
130 {
131     return { factorX * mat00, factorX * mat01, factorX * mat02,
132              factorY * mat10, factorY * mat11, factorY * mat12 };
133 }
134 
scaled(float factor) const135 AffineTransform AffineTransform::scaled (float factor) const noexcept
136 {
137     return { factor * mat00, factor * mat01, factor * mat02,
138              factor * mat10, factor * mat11, factor * mat12 };
139 }
140 
scale(float factorX,float factorY)141 AffineTransform AffineTransform::scale (float factorX, float factorY) noexcept
142 {
143     return { factorX, 0, 0, 0, factorY, 0 };
144 }
145 
scale(float factor)146 AffineTransform AffineTransform::scale (float factor) noexcept
147 {
148     return { factor, 0, 0, 0, factor, 0 };
149 }
150 
scaled(float factorX,float factorY,float pivotX,float pivotY) const151 AffineTransform AffineTransform::scaled (float factorX, float factorY,
152                                          float pivotX, float pivotY) const noexcept
153 {
154     return { factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX),
155              factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY) };
156 }
157 
scale(float factorX,float factorY,float pivotX,float pivotY)158 AffineTransform AffineTransform::scale (float factorX, float factorY,
159                                         float pivotX, float pivotY) noexcept
160 {
161     return { factorX, 0, pivotX * (1.0f - factorX),
162              0, factorY, pivotY * (1.0f - factorY) };
163 }
164 
shear(float shearX,float shearY)165 AffineTransform AffineTransform::shear (float shearX, float shearY) noexcept
166 {
167     return { 1.0f,   shearX, 0,
168              shearY, 1.0f,   0 };
169 }
170 
sheared(float shearX,float shearY) const171 AffineTransform AffineTransform::sheared (float shearX, float shearY) const noexcept
172 {
173     return { mat00 + shearX * mat10,
174              mat01 + shearX * mat11,
175              mat02 + shearX * mat12,
176              mat10 + shearY * mat00,
177              mat11 + shearY * mat01,
178              mat12 + shearY * mat02 };
179 }
180 
verticalFlip(float height)181 AffineTransform AffineTransform::verticalFlip (float height) noexcept
182 {
183     return { 1.0f,  0.0f, 0.0f,
184              0.0f, -1.0f, height };
185 }
186 
inverted() const187 AffineTransform AffineTransform::inverted() const noexcept
188 {
189     double determinant = getDeterminant();
190 
191     if (! approximatelyEqual (determinant, 0.0))
192     {
193         determinant = 1.0 / determinant;
194 
195         auto dst00 = (float) ( mat11 * determinant);
196         auto dst10 = (float) (-mat10 * determinant);
197         auto dst01 = (float) (-mat01 * determinant);
198         auto dst11 = (float) ( mat00 * determinant);
199 
200         return { dst00, dst01, -mat02 * dst00 - mat12 * dst01,
201                  dst10, dst11, -mat02 * dst10 - mat12 * dst11 };
202     }
203 
204     // singularity..
205     return *this;
206 }
207 
isSingularity() const208 bool AffineTransform::isSingularity() const noexcept
209 {
210     return (mat00 * mat11 - mat10 * mat01) == 0.0f;
211 }
212 
fromTargetPoints(float x00,float y00,float x10,float y10,float x01,float y01)213 AffineTransform AffineTransform::fromTargetPoints (float x00, float y00,
214                                                    float x10, float y10,
215                                                    float x01, float y01) noexcept
216 {
217     return { x10 - x00, x01 - x00, x00,
218              y10 - y00, y01 - y00, y00 };
219 }
220 
fromTargetPoints(float sx1,float sy1,float tx1,float ty1,float sx2,float sy2,float tx2,float ty2,float sx3,float sy3,float tx3,float ty3)221 AffineTransform AffineTransform::fromTargetPoints (float sx1, float sy1, float tx1, float ty1,
222                                                    float sx2, float sy2, float tx2, float ty2,
223                                                    float sx3, float sy3, float tx3, float ty3) noexcept
224 {
225     return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3)
226             .inverted()
227             .followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3));
228 }
229 
isOnlyTranslation() const230 bool AffineTransform::isOnlyTranslation() const noexcept
231 {
232     return mat01 == 0.0f
233         && mat10 == 0.0f
234         && mat00 == 1.0f
235         && mat11 == 1.0f;
236 }
237 
getDeterminant() const238 float AffineTransform::getDeterminant() const noexcept
239 {
240     return (mat00 * mat11) - (mat01 * mat10);
241 }
242 
getScaleFactor() const243 float AffineTransform::getScaleFactor() const noexcept
244 {
245     return (std::abs (mat00) + std::abs (mat11)) / 2.0f;
246 }
247 
248 
249 //==============================================================================
250 //==============================================================================
251 #if JUCE_UNIT_TESTS
252 
253 class AffineTransformTests  : public UnitTest
254 {
255 public:
AffineTransformTests()256     AffineTransformTests()
257         : UnitTest ("AffineTransform", UnitTestCategories::maths)
258     {}
259 
runTest()260     void runTest() override
261     {
262         beginTest ("Determinant");
263         {
264             constexpr float scale1 = 1.5f, scale2 = 1.3f;
265 
266             auto transform = AffineTransform::scale (scale1)
267                                              .followedBy (AffineTransform::rotation (degreesToRadians (72.0f)))
268                                              .followedBy (AffineTransform::translation (100.0f, 20.0f))
269                                              .followedBy (AffineTransform::scale (scale2));
270 
271             expect (approximatelyEqual (std::sqrt (std::abs (transform.getDeterminant())), scale1 * scale2));
272         }
273     }
274 };
275 
276 static AffineTransformTests timeTests;
277 
278 #endif
279 
280 } // namespace juce
281