1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.ink.geometry
18 
19 import androidx.annotation.RestrictTo
20 import androidx.annotation.Size
21 import kotlin.math.cos
22 import kotlin.math.sin
23 
24 /**
25  * An affine transformation in the plane. The transformation can be thought of as a 3x3 matrix:
26  * ```
27  *   ⎡m00  m10  m20⎤
28  *   ⎢m01  m11  m21⎥
29  *   ⎣ 0    0    1 ⎦
30  * ```
31  *
32  * Applying the transformation can be thought of as a matrix multiplication, with the
33  * to-be-transformed point represented as a column vector with an extra 1:
34  * ```
35  *   ⎡m00  m10  m20⎤   ⎡x⎤   ⎡m00*x + m10*y + m20⎤
36  *   ⎢m01  m11  m21⎥ * ⎢y⎥ = ⎢m01*x + m11*y + m21⎥
37  *   ⎣ 0    0    1 ⎦   ⎣1⎦   ⎣         1         ⎦
38  * ```
39  *
40  * Transformations are composed via multiplication. Multiplication is not commutative (i.e. A*B !=
41  * B*A), and the left-hand transformation is composed "after" the right hand transformation. E.g.,
42  * if you have:
43  * ```
44  * val rotate = ImmutableAffineTransform.rotate(Angle.degreesToRadians(45))
45  * val translate = ImmutableAffineTransform.translate(Vec(10, 0))
46  * ```
47  *
48  * then `rotate * translate` first translates 10 units in the positive x-direction, then rotates 45°
49  * about the origin.
50  *
51  * See [ImmutableAffineTransform] for an immutable alternative to this class.
52  *
53  * @constructor Constructs this transform with 6 float values, starting with the top left corner of
54  *   the matrix and proceeding in row-major order. Prefer to create this object with functions that
55  *   apply specific transform operations, such as [populateFromScale] or [populateFromRotate],
56  *   rather than directly passing in the actual numeric values of this transform. This constructor
57  *   is useful for when the values are needed to be provided all at once, for example for
58  *   serialization. To access these values in the same order as they are passed in here, use
59  *   [AffineTransform.getValues]. To construct this object using an array as input, there is another
60  *   public constructor for that.
61  */
62 public class MutableAffineTransform
63 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
64 public constructor(
65     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
66     @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
67     override var m00: Float,
68     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
69     @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
70     override var m10: Float,
71     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
72     @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
73     override var m20: Float,
74     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
75     @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
76     override var m01: Float,
77     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
78     @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
79     override var m11: Float,
80     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
81     @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
82     override var m21: Float,
83 ) : AffineTransform() {
84 
85     /**
86      * Constructs an identity [MutableAffineTransform]:
87      * ```
88      *   ⎡1  0  0⎤
89      *   ⎢0  1  0⎥
90      *   ⎣0  0  1⎦
91      * ```
92      *
93      * This is useful when pre-allocating a scratch instance to be filled later.
94      */
95     public constructor() : this(1f, 0f, 0f, 0f, 1f, 0f)
96 
97     /**
98      * Populates this transform with the given values, starting with the top left corner of the
99      * matrix and proceeding in row-major order.
100      *
101      * Prefer to modify this object with functions that apply specific transform operations, such as
102      * [populateFromScale] or [populateFromRotate], rather than directly setting the actual numeric
103      * values of this transform. This function is useful for when the values are needed to be
104      * provided in bulk, for example for serialization.
105      *
106      * To access these values in the same order as they are set here, use
107      * [AffineTransform.getValues].
108      */
setValuesnull109     public fun setValues(m00: Float, m10: Float, m20: Float, m01: Float, m11: Float, m21: Float) {
110         this.m00 = m00
111         this.m10 = m10
112         this.m20 = m20
113         this.m01 = m01
114         this.m11 = m11
115         this.m21 = m21
116     }
117 
118     /** Like [setValues], but accepts a [FloatArray] instead of individual float values. */
setValuesnull119     public fun setValues(@Size(min = 6) values: FloatArray) {
120         m00 = values[0]
121         m10 = values[1]
122         m20 = values[2]
123         m01 = values[3]
124         m11 = values[4]
125         m21 = values[5]
126     }
127 
128     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
asImmutablenull129     override fun asImmutable(): ImmutableAffineTransform =
130         ImmutableAffineTransform(m00, m10, m20, m01, m11, m21)
131 
132     /**
133      * Fills this [MutableAffineTransform] with the same values contained in [input]. Returns
134      * [this].
135      */
136     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
137     public fun populateFrom(input: AffineTransform): MutableAffineTransform {
138         m00 = input.m00
139         m10 = input.m10
140         m20 = input.m20
141         m01 = input.m01
142         m11 = input.m11
143         m21 = input.m21
144         return this
145     }
146 
147     /**
148      * Fills this [MutableAffineTransform] with an identity transformation, which maps a point to
149      * itself, i.e. it leaves it unchanged. Returns [this].
150      */
151     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromIdentitynull152     public fun populateFromIdentity(): MutableAffineTransform {
153         m00 = 1f
154         m10 = 0f
155         m20 = 0f
156         m01 = 0f
157         m11 = 1f
158         m21 = 0f
159         return this
160     }
161 
162     /**
163      * Fills this [MutableAffineTransform] with a transformation that translates by the given
164      * [offset] vector. Returns [this].
165      */
166     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromTranslatenull167     public fun populateFromTranslate(offset: Vec): MutableAffineTransform {
168         m00 = 1f
169         m10 = 0f
170         m20 = offset.x
171         m01 = 0f
172         m11 = 1f
173         m21 = offset.y
174         return this
175     }
176 
177     /**
178      * Fills this [MutableAffineTransform] with a transformation that scales in both the x- and
179      * y-direction by the given [scaleFactor], centered about the origin. Returns [this].
180      */
181     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromScalenull182     public fun populateFromScale(scaleFactor: Float): MutableAffineTransform {
183         m00 = scaleFactor
184         m10 = 0f
185         m20 = 0f
186         m01 = 0f
187         m11 = scaleFactor
188         m21 = 0f
189         return this
190     }
191 
192     /**
193      * Fills this [MutableAffineTransform] with a transformation that scales in both the x- and
194      * y-direction by the given pair of factors; [xScaleFactor] and [yScaleFactor] respectively,
195      * centered about the origin. Returns [this].
196      */
197     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromScalenull198     public fun populateFromScale(xScaleFactor: Float, yScaleFactor: Float): MutableAffineTransform {
199         m00 = xScaleFactor
200         m10 = 0f
201         m20 = 0f
202         m01 = 0f
203         m11 = yScaleFactor
204         m21 = 0f
205         return this
206     }
207 
208     /**
209      * Fills this [MutableAffineTransform] with a transformation that scales in the x-direction by
210      * the given factor, centered about the origin. Returns [this].
211      */
212     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromScaleXnull213     public fun populateFromScaleX(scaleFactor: Float): MutableAffineTransform {
214         m00 = scaleFactor
215         m10 = 0f
216         m20 = 0f
217         m01 = 0f
218         m11 = 1f
219         m21 = 0f
220         return this
221     }
222 
223     /**
224      * Fills this [MutableAffineTransform] with a transformation that scales in the y-direction by
225      * the given factor, centered about the origin. Returns [this].
226      */
227     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromScaleYnull228     public fun populateFromScaleY(scaleFactor: Float): MutableAffineTransform {
229         m00 = 1f
230         m10 = 0f
231         m20 = 0f
232         m01 = 0f
233         m11 = scaleFactor
234         m21 = 0f
235         return this
236     }
237 
238     /**
239      * Fills this [MutableAffineTransform] with a transformation that shears in the x-direction by
240      * the given factor. Returns [this].
241      */
242     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromShearXnull243     public fun populateFromShearX(shearFactor: Float): MutableAffineTransform {
244         m00 = 1f
245         m10 = shearFactor
246         m20 = 0f
247         m01 = 0f
248         m11 = 1f
249         m21 = 0f
250         return this
251     }
252 
253     /**
254      * Fills this [MutableAffineTransform] with a transformation that shears in the y-direction by
255      * the given factor. Returns [this].
256      */
257     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromShearYnull258     public fun populateFromShearY(shearFactor: Float): MutableAffineTransform {
259         m00 = 1f
260         m10 = 0f
261         m20 = 0f
262         m01 = shearFactor
263         m11 = 1f
264         m21 = 0f
265         return this
266     }
267 
268     /**
269      * Fills this [MutableAffineTransform] with a transformation that rotates by the given angle,
270      * centered about the origin. Returns [this].
271      */
272     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // PublicApiNotReadyForJetpackReview
populateFromRotatenull273     public fun populateFromRotate(
274         @AngleRadiansFloat angleOfRotation: Float
275     ): MutableAffineTransform {
276         val sin = sin(angleOfRotation)
277         val cos = cos(angleOfRotation)
278         m00 = cos
279         m10 = -sin
280         m20 = 0f
281         m01 = sin
282         m11 = cos
283         m21 = 0f
284         return this
285     }
286 
287     /**
288      * Component-wise equality operator for [MutableAffineTransform].
289      *
290      * Due to the propagation floating point precision errors, operations that may be equivalent
291      * over the real numbers are not always equivalent for floats, and might return false for
292      * [equals] in some cases.
293      */
equalsnull294     override fun equals(other: Any?): Boolean =
295         other === this || (other is AffineTransform && areEquivalent(this, other))
296 
297     // NOMUTANTS -- not testing exact hashCode values, just that equality implies same hashCode
298     override fun hashCode(): Int = hash(this)
299 
300     override fun toString(): String = "Mutable${string(this)}"
301 }
302