@@ -132,27 +132,28 @@ extension AffineTransform {
132
132
}
133
133
134
134
extension AffineTransform {
135
- /// Creates an affine transformation matrix by combining the receiver with `transformStruct`.
136
- /// That is, it computes `T * M` and returns the result, where `T` is the receiver's and `M` is
137
- /// the `transformStruct`'s affine transformation matrix.
138
- /// The resulting matrix takes the following form:
135
+ /// Creates an affine transformation matrix by combining the two matrices `A×B` and returns the result.
139
136
///
140
- /// ```swift
141
- /// [ m11_T m12_T 0 ] [ m11_M m12_M 0 ]
142
- /// T * M = [ m21_T m22_T 0 ] [ m21_M m22_M 0 ]
143
- /// [ tX_T tY_T 1 ] [ tX_M tY_M 1 ]
144
- /// ```
137
+ /// The resulting matrix takes the following form
145
138
///
146
139
/// ```swift
147
- /// [ (m11_T*m11_M + m12_T*m21_M) (m11_T*m12_M + m12_T*m22_M) 0 ]
148
- /// = [ (m21_T*m11_M + m22_T*m21_M) (m21_T*m12_M + m22_T*m22_M) 0 ]
149
- /// [ (tX_T*m11_M + tY_T*m21_M + tX_M) (tX_T*m12_M + tY_T*m22_M + tY_M) 1 ]
140
+ ///
141
+ /// [ a1, b1, 0 ] [ a2, b2, 0 ]
142
+ /// A×B = [ c1, d1, 0 ] × [ c2, d2, 0 ]
143
+ /// [ x1, y1, 1 ] [ x2, y2, 1 ]
144
+ ///
145
+ /// [ a1*a2+b1*c2+0*x2 a1*b2+b1*d2+0*y2 a1*0+b1*0+0*1 ]
146
+ /// A×B = [ c1*a2+d1*c2+0*x2 c1*b2+d1*d2+0*y2 c1*0+d1*0+0*1 ]
147
+ /// [ x1*a2+y1*c2+1*x2 x1*b2+y1*d2+1*y2 x1*0+y1*0+1*1 ]
148
+ ///
149
+ /// [ a1*a2+b1*c2 a1*b2+b1*d2 0 ]
150
+ /// A×B = [ c1*a2+d1*c2 c1*b2+d1*d2 0 ]
151
+ /// [ x1*a2+y1*c2+x2 x1*b2+y1*d2+y2 1 ]
150
152
/// ```
151
153
@inline ( __always)
152
154
internal func concatenated( _ other: AffineTransform ) -> AffineTransform {
153
155
let ( t, m) = ( self , other)
154
156
155
- // this could be optimized with a vector version
156
157
return AffineTransform (
157
158
m11: ( t. m11 * m. m11) + ( t. m12 * m. m21) , m12: ( t. m11 * m. m12) + ( t. m12 * m. m22) ,
158
159
m21: ( t. m21 * m. m11) + ( t. m22 * m. m21) , m22: ( t. m21 * m. m12) + ( t. m22 * m. m22) ,
@@ -224,8 +225,59 @@ extension AffineTransform {
224
225
extension AffineTransform {
225
226
/// Returns an inverted version of the matrix if possible, or nil if not.
226
227
public func inverted( ) -> AffineTransform ? {
228
+ // We need the matrix of cofactors to calculate the inverse, but first we
229
+ // need to calculate the minors of each element — where the minor of an
230
+ // element Ai,j is the determinant of the matrix derived from deleting
231
+ // the ith row and jth column:
232
+ //
233
+ // [ |d y| |c x| |c x| ]
234
+ // [ |0 1| |0 1| |d y| ]
235
+ // [ ]
236
+ // [ |b y| |a x| |a x| ]
237
+ // M = [ |0 1| |0 1| |b y| ]
238
+ // [ ]
239
+ // [ |b d| |a c| |a c| ]
240
+ // [ |0 0| |0 0| |b d| ]
241
+ //
242
+ // [ d*1-y*0 c*1-x*0 c*y-x*d ]
243
+ // M = [ b*1-y*0 a*1-x*0 a*y-x*b ]
244
+ // [ b*0-d*0 a*0-c*0 a*d-c*b ]
245
+ //
246
+ // [ d c c*y-x*d ]
247
+ // M = [ b a a*y-x*b ]
248
+ // [ 0 0 |A| ]
249
+ //
250
+ // Now we can calculate the matrix of cofactors by negating each element Ai,j
251
+ // where i+j is odd:
252
+ //
253
+ // [ d -c c*y-x*d ]
254
+ // C = [ -b a -(a*y-x*b) ]
255
+ // [ 0 -0 |A| ]
256
+ //
257
+ // Next, we can find the adjugate matrix, which is the transposed matrix of
258
+ // cofactors — a matrix whose ith column is the ith row of the matrix of C:
259
+ //
260
+ // [ d -b 0 ]
261
+ // adj(A) = [ -c a -0 ]
262
+ // [ c*y-x*d -(a*y-x*b) |A| ]
263
+ //
264
+ // Finally, the inverse matrix is the product of the reciprocal of the determinant
265
+ // of A times adj(A), assuming that |A|≠0:
266
+ //
267
+ // A^-1 = (1 / |A|) × adj(A)
268
+ //
269
+ // [ d/|A| -b/|A| 0/|A| ]
270
+ // A^-1 = [ -c/|A| a/|A| -0/|A| ]
271
+ // [ (c*y-x*d)/|A| -(a*y-x*b)/|A| |A|/|A| ]
272
+ //
273
+ // [ d/|A| -b/|A| 0 ]
274
+ // A^-1 = [ -c/|A| a/|A| 0 ]
275
+ // [ (c*y-x*d)/|A| (x*b-a*y)/|A| 1 ]
276
+
227
277
let determinant = ( m11 * m22) - ( m12 * m21)
228
278
279
+ // We compare to ulp of 0 instead of doing determinant != 0,
280
+ // to catch floating-point rounding errors.
229
281
if abs ( determinant) <= CGFloat . zero. ulp {
230
282
return nil
231
283
}
@@ -260,6 +312,15 @@ extension AffineTransform {
260
312
extension AffineTransform {
261
313
/// Applies the transform to the specified point and returns the result.
262
314
public func transform( _ point: CGPoint ) -> CGPoint {
315
+ // Multiply the given point matrix with the matrix:
316
+ //
317
+ // [ m11 m12 0 ]
318
+ // [ x' y' 1 ] = [ x y 1 ] × [ m21 m22 0 ]
319
+ // [ tX tY 1 ]
320
+ //
321
+ // [ x' y' 1 ] = [ x*m11+y*m21+1*tX x*m12+y*m22+1*tY x*0+y*0+1*1 ]
322
+ //
323
+ // [ x' y' 1 ] = [ x*m11+y*m21+tX x*m12+y*m22+tY 1 ]
263
324
CGPoint (
264
325
x: ( m11 * point. x) + ( m21 * point. y) + tX,
265
326
y: ( m12 * point. x) + ( m22 * point. y) + tY
@@ -268,6 +329,12 @@ extension AffineTransform {
268
329
269
330
/// Applies the transform to the specified size and returns the result.
270
331
public func transform( _ size: CGSize ) -> CGSize {
332
+ // Multiply the given size matrix with the scale & rotation matrix:
333
+ //
334
+ // [ w' h' ] = [ w h ] * [ m11 m12 ]
335
+ // [ m21 m22 ]
336
+ //
337
+ // [ w' h' ] = [ w*m11+h*m21 w*m12+h*m22 ]
271
338
CGSize (
272
339
width : ( m11 * size. width) + ( m21 * size. height) ,
273
340
height: ( m12 * size. width) + ( m22 * size. height)
0 commit comments