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 
29 //==============================================================================
30 /**
31     Represents a 2D affine-transformation matrix.
32 
33     An affine transformation is a transformation such as a rotation, scale, shear,
34     resize or translation.
35 
36     These are used for various 2D transformation tasks, e.g. with Path objects.
37 
38     @see Path, Point, Line
39 
40     @tags{Graphics}
41 */
42 class JUCE_API  AffineTransform  final
43 {
44 public:
45     //==============================================================================
46     /** Creates an identity transform. */
47     AffineTransform() = default;
48 
49     /** Creates a copy of another transform. */
50     AffineTransform (const AffineTransform&) = default;
51 
52     /** Creates a transform from a set of raw matrix values.
53 
54         The resulting matrix is:
55 
56             (mat00 mat01 mat02)
57             (mat10 mat11 mat12)
58             (  0     0     1  )
59     */
60     AffineTransform (float mat00, float mat01, float mat02,
61                      float mat10, float mat11, float mat12) noexcept;
62 
63     /** Copies from another AffineTransform object */
64     AffineTransform& operator= (const AffineTransform&) = default;
65 
66     /** Compares two transforms. */
67     bool operator== (const AffineTransform& other) const noexcept;
68 
69     /** Compares two transforms. */
70     bool operator!= (const AffineTransform& other) const noexcept;
71 
72     //==============================================================================
73     /** Transforms a 2D coordinate using this matrix. */
74     template <typename ValueType>
transformPoint(ValueType & x,ValueType & y)75     void transformPoint (ValueType& x, ValueType& y) const noexcept
76     {
77         auto oldX = x;
78         x = static_cast<ValueType> (mat00 * oldX + mat01 * y + mat02);
79         y = static_cast<ValueType> (mat10 * oldX + mat11 * y + mat12);
80     }
81 
82     /** Transforms two 2D coordinates using this matrix.
83         This is just a shortcut for calling transformPoint() on each of these pairs of
84         coordinates in turn. (And putting all the calculations into one function hopefully
85         also gives the compiler a bit more scope for pipelining it).
86     */
87     template <typename ValueType>
transformPoints(ValueType & x1,ValueType & y1,ValueType & x2,ValueType & y2)88     void transformPoints (ValueType& x1, ValueType& y1,
89                           ValueType& x2, ValueType& y2) const noexcept
90     {
91         auto oldX1 = x1, oldX2 = x2;
92         x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
93         y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
94         x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
95         y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
96     }
97 
98     /** Transforms three 2D coordinates using this matrix.
99         This is just a shortcut for calling transformPoint() on each of these pairs of
100         coordinates in turn. (And putting all the calculations into one function hopefully
101         also gives the compiler a bit more scope for pipelining it).
102     */
103     template <typename ValueType>
transformPoints(ValueType & x1,ValueType & y1,ValueType & x2,ValueType & y2,ValueType & x3,ValueType & y3)104     void transformPoints (ValueType& x1, ValueType& y1,
105                           ValueType& x2, ValueType& y2,
106                           ValueType& x3, ValueType& y3) const noexcept
107     {
108         auto oldX1 = x1, oldX2 = x2, oldX3 = x3;
109         x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
110         y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
111         x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
112         y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
113         x3 = static_cast<ValueType> (mat00 * oldX3 + mat01 * y3 + mat02);
114         y3 = static_cast<ValueType> (mat10 * oldX3 + mat11 * y3 + mat12);
115     }
116 
117     //==============================================================================
118     /** Returns a new transform which is the same as this one followed by a translation. */
119     AffineTransform translated (float deltaX,
120                                 float deltaY) const noexcept;
121 
122     /** Returns a new transform which is the same as this one followed by a translation. */
123     template <typename PointType>
translated(PointType delta)124     AffineTransform translated (PointType delta) const noexcept
125     {
126         return translated ((float) delta.x, (float) delta.y);
127     }
128 
129     /** Returns a new transform which is a translation. */
130     static AffineTransform translation (float deltaX,
131                                         float deltaY) noexcept;
132 
133     /** Returns a new transform which is a translation. */
134     template <typename PointType>
translation(PointType delta)135     static AffineTransform translation (PointType delta) noexcept
136     {
137         return translation ((float) delta.x, (float) delta.y);
138     }
139 
140     /** Returns a copy of this transform with the specified translation matrix values. */
141     AffineTransform withAbsoluteTranslation (float translationX,
142                                              float translationY) const noexcept;
143 
144     /** Returns a transform which is the same as this one followed by a rotation.
145 
146         The rotation is specified by a number of radians to rotate clockwise, centred around
147         the origin (0, 0).
148     */
149     AffineTransform rotated (float angleInRadians) const noexcept;
150 
151     /** Returns a transform which is the same as this one followed by a rotation about a given point.
152 
153         The rotation is specified by a number of radians to rotate clockwise, centred around
154         the coordinates passed in.
155     */
156     AffineTransform rotated (float angleInRadians,
157                              float pivotX,
158                              float pivotY) const noexcept;
159 
160     /** Returns a new transform which is a rotation about (0, 0). */
161     static AffineTransform rotation (float angleInRadians) noexcept;
162 
163     /** Returns a new transform which is a rotation about a given point. */
164     static AffineTransform rotation (float angleInRadians,
165                                      float pivotX,
166                                      float pivotY) noexcept;
167 
168     /** Returns a transform which is the same as this one followed by a re-scaling.
169         The scaling is centred around the origin (0, 0).
170     */
171     AffineTransform scaled (float factorX,
172                             float factorY) const noexcept;
173 
174     /** Returns a transform which is the same as this one followed by a re-scaling.
175         The scaling is centred around the origin (0, 0).
176     */
177     AffineTransform scaled (float factor) const noexcept;
178 
179     /** Returns a transform which is the same as this one followed by a re-scaling.
180         The scaling is centred around the origin provided.
181     */
182     AffineTransform scaled (float factorX, float factorY,
183                             float pivotX, float pivotY) const noexcept;
184 
185     /** Returns a new transform which is a re-scale about the origin. */
186     static AffineTransform scale (float factorX,
187                                   float factorY) noexcept;
188 
189     /** Returns a new transform which is a re-scale about the origin. */
190     static AffineTransform scale (float factor) noexcept;
191 
192     /** Returns a new transform which is a re-scale centred around the point provided. */
193     static AffineTransform scale (float factorX, float factorY,
194                                   float pivotX, float pivotY) noexcept;
195 
196     /** Returns a transform which is the same as this one followed by a shear.
197         The shear is centred around the origin (0, 0).
198     */
199     AffineTransform sheared (float shearX, float shearY) const noexcept;
200 
201     /** Returns a shear transform, centred around the origin (0, 0). */
202     static AffineTransform shear (float shearX, float shearY) noexcept;
203 
204     /** Returns a transform that will flip coordinates vertically within a window of the given height.
205         This is handy for converting between upside-down coordinate systems such as OpenGL or CoreGraphics.
206     */
207     static AffineTransform verticalFlip (float height) noexcept;
208 
209     /** Returns a matrix which is the inverse operation of this one.
210 
211         Some matrices don't have an inverse - in this case, the method will just return
212         an identity transform.
213     */
214     AffineTransform inverted() const noexcept;
215 
216     /** Returns the transform that will map three known points onto three coordinates
217         that are supplied.
218 
219         This returns the transform that will transform (0, 0) into (x00, y00),
220         (1, 0) to (x10, y10), and (0, 1) to (x01, y01).
221     */
222     static AffineTransform fromTargetPoints (float x00, float y00,
223                                              float x10, float y10,
224                                              float x01, float y01) noexcept;
225 
226     /** Returns the transform that will map three specified points onto three target points. */
227     static AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1,
228                                              float sourceX2, float sourceY2, float targetX2, float targetY2,
229                                              float sourceX3, float sourceY3, float targetX3, float targetY3) noexcept;
230 
231     /** Returns the transform that will map three specified points onto three target points. */
232     template <typename PointType>
fromTargetPoints(PointType source1,PointType target1,PointType source2,PointType target2,PointType source3,PointType target3)233     static AffineTransform fromTargetPoints (PointType source1, PointType target1,
234                                              PointType source2, PointType target2,
235                                              PointType source3, PointType target3) noexcept
236     {
237         return fromTargetPoints (source1.x, source1.y, target1.x, target1.y,
238                                  source2.x, source2.y, target2.x, target2.y,
239                                  source3.x, source3.y, target3.x, target3.y);
240     }
241 
242     //==============================================================================
243     /** Returns the result of concatenating another transformation after this one. */
244     AffineTransform followedBy (const AffineTransform& other) const noexcept;
245 
246     /** Returns true if this transform has no effect on points. */
247     bool isIdentity() const noexcept;
248 
249     /** Returns true if this transform maps to a singularity - i.e. if it has no inverse. */
250     bool isSingularity() const noexcept;
251 
252     /** Returns true if the transform only translates, and doesn't scale or rotate the
253         points. */
254     bool isOnlyTranslation() const noexcept;
255 
256     /** If this transform is only a translation, this returns the X offset.
257         @see isOnlyTranslation
258     */
getTranslationX()259     float getTranslationX() const noexcept                  { return mat02; }
260 
261     /** If this transform is only a translation, this returns the X offset.
262         @see isOnlyTranslation
263     */
getTranslationY()264     float getTranslationY() const noexcept                  { return mat12; }
265 
266     /** Returns the determinant of the transform. */
267     float getDeterminant() const noexcept;
268 
269     /** This method has been deprecated.
270 
271         You can calculate the scale factor using:
272         @code
273         std::sqrt (std::abs (AffineTransform::getDeterminant()))
274         @endcode
275 
276         This method produces incorrect values for transforms containing rotations.
277 
278         Returns the approximate scale factor by which lengths will be transformed.
279         Obviously a length may be scaled by entirely different amounts depending on its
280         direction, so this is only appropriate as a rough guide.
281     */
282     JUCE_DEPRECATED (float getScaleFactor() const noexcept);
283 
284     /* A ready-to-use identity transform - now deprecated.
285        @deprecated If you need an identity transform, just use AffineTransform() or {}.
286     */
JUCE_DEPRECATED_STATIC(static const AffineTransform identity;)287     JUCE_DEPRECATED_STATIC (static const AffineTransform identity;)
288 
289     //==============================================================================
290     /* The transform matrix is:
291 
292         (mat00 mat01 mat02)
293         (mat10 mat11 mat12)
294         (  0     0     1  )
295     */
296     float mat00 { 1.0f }, mat01 { 0.0f }, mat02 { 0.0f };
297     float mat10 { 0.0f }, mat11 { 1.0f }, mat12 { 0.0f };
298 };
299 
300 } // namespace juce
301