Skip to content

Commit 5961557

Browse files
committed
Merge branch 'nubbel-pr/NSAffineTransform'
2 parents 742880b + bc74d81 commit 5961557

File tree

3 files changed

+364
-73
lines changed

3 files changed

+364
-73
lines changed

Foundation/NSAffineTransform.swift

Lines changed: 226 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
#if os(OSX) || os(iOS)
11+
import Darwin
12+
#elseif os(Linux)
13+
import Glibc
14+
#endif
1015

1116
public struct NSAffineTransformStruct {
1217
public var m11: CGFloat
@@ -15,7 +20,11 @@ public struct NSAffineTransformStruct {
1520
public var m22: CGFloat
1621
public var tX: CGFloat
1722
public var tY: CGFloat
18-
public init() { NSUnimplemented() }
23+
24+
public init() {
25+
self.init(m11: CGFloat(), m12: CGFloat(), m21: CGFloat(), m22: CGFloat(), tX: CGFloat(), tY: CGFloat())
26+
}
27+
1928
public init(m11: CGFloat, m12: CGFloat, m21: CGFloat, m22: CGFloat, tX: CGFloat, tY: CGFloat) {
2029
(self.m11, self.m12, self.m21, self.m22) = (m11, m12, m21, m22)
2130
(self.tX, self.tY) = (tX, tY)
@@ -28,7 +37,11 @@ public class NSAffineTransform : NSObject, NSCopying, NSSecureCoding {
2837
NSUnimplemented()
2938
}
3039
public func copyWithZone(zone: NSZone) -> AnyObject {
31-
NSUnimplemented()
40+
return NSAffineTransform(transform: self)
41+
}
42+
// Necessary because `NSObject.copy()` returns `self`.
43+
public override func copy() -> AnyObject {
44+
return copyWithZone(nil)
3245
}
3346
public required init?(coder aDecoder: NSCoder) {
3447
NSUnimplemented()
@@ -38,95 +51,246 @@ public class NSAffineTransform : NSObject, NSCopying, NSSecureCoding {
3851
}
3952

4053
// Initialization
41-
public convenience init(transform: NSAffineTransform) { NSUnimplemented() }
54+
public convenience init(transform: NSAffineTransform) {
55+
self.init()
56+
transformStruct = transform.transformStruct
57+
}
58+
4259
public override init() {
43-
transformStruct = NSAffineTransformStruct(m11: CGFloat(1.0), m12: CGFloat(), m21: CGFloat(), m22: CGFloat(1.0), tX: CGFloat(), tY: CGFloat())
60+
transformStruct = NSAffineTransformStruct(
61+
m11: CGFloat(1.0), m12: CGFloat(),
62+
m21: CGFloat(), m22: CGFloat(1.0),
63+
tX: CGFloat(), tY: CGFloat()
64+
)
4465
}
4566

4667
// Translating
4768
public func translateXBy(deltaX: CGFloat, yBy deltaY: CGFloat) {
48-
let matrix = transformStruct.matrix3x3
49-
let translationMatrix = Matrix3x3(CGFloat(1.0), CGFloat(), deltaX,
50-
CGFloat(), CGFloat(1.0), deltaY,
51-
CGFloat(), CGFloat(), CGFloat(1.0))
52-
let product = multiplyMatrix3x3(matrix, byMatrix3x3: translationMatrix)
53-
transformStruct = NSAffineTransformStruct(matrix: product)
69+
let translation = NSAffineTransformStruct.translation(tX: deltaX, tY: deltaY)
70+
71+
transformStruct = transformStruct.concat(translation)
5472
}
5573

5674
// Rotating
57-
public func rotateByDegrees(angle: CGFloat) { NSUnimplemented() }
58-
public func rotateByRadians(angle: CGFloat) { NSUnimplemented() }
75+
public func rotateByDegrees(angle: CGFloat) {
76+
let rotation = NSAffineTransformStruct.rotation(degrees: angle)
77+
78+
transformStruct = transformStruct.concat(rotation)
79+
}
80+
public func rotateByRadians(angle: CGFloat) {
81+
let rotation = NSAffineTransformStruct.rotation(radians: angle)
82+
83+
transformStruct = transformStruct.concat(rotation)
84+
}
5985

6086
// Scaling
61-
public func scaleBy(scale: CGFloat) { NSUnimplemented() }
62-
public func scaleXBy(scaleX: CGFloat, yBy scaleY: CGFloat) { NSUnimplemented() }
87+
public func scaleBy(scale: CGFloat) {
88+
let scale = NSAffineTransformStruct.scale(sX: scale, sY: scale)
89+
90+
transformStruct = transformStruct.concat(scale)
91+
}
92+
public func scaleXBy(scaleX: CGFloat, yBy scaleY: CGFloat) {
93+
let scale = NSAffineTransformStruct.scale(sX: scaleX, sY: scaleY)
94+
95+
transformStruct = transformStruct.concat(scale)
96+
}
6397

6498
// Inverting
65-
public func invert() { NSUnimplemented() }
99+
public func invert() {
100+
if let inverse = transformStruct.inverse {
101+
transformStruct = inverse
102+
}
103+
else {
104+
preconditionFailure("NSAffineTransform: Transform has no inverse")
105+
}
106+
}
66107

67108
// Transforming with transform
68-
public func appendTransform(transform: NSAffineTransform) { NSUnimplemented() }
69-
public func prependTransform(transform: NSAffineTransform) { NSUnimplemented() }
109+
public func appendTransform(transform: NSAffineTransform) {
110+
transformStruct = transformStruct.concat(transform.transformStruct)
111+
}
112+
public func prependTransform(transform: NSAffineTransform) {
113+
transformStruct = transform.transformStruct.concat(transformStruct)
114+
}
70115

71116
// Transforming points and sizes
72117
public func transformPoint(aPoint: NSPoint) -> NSPoint {
73-
let matrix = transformStruct.matrix3x3
74-
let vector = Vector3(aPoint.x, aPoint.y, CGFloat(1.0))
75-
let resultVector = multiplyMatrix3x3(matrix, byVector3: vector)
76-
return NSMakePoint(resultVector.m1, resultVector.m2)
118+
return transformStruct.applied(toPoint: aPoint)
77119
}
78120

79121
public func transformSize(aSize: NSSize) -> NSSize {
80-
let matrix = transformStruct.matrix3x3
81-
let vector = Vector3(aSize.width, aSize.height, CGFloat(1.0))
82-
let resultVector = multiplyMatrix3x3(matrix, byVector3: vector)
83-
return NSMakeSize(resultVector.m1, resultVector.m2)
122+
return transformStruct.applied(toSize: aSize)
84123
}
85124

86125
// Transform Struct
87126
public var transformStruct: NSAffineTransformStruct
88127
}
89128

90-
// Private helper functions and structures for linear algebra operations.
91-
private typealias Vector3 = (m1: CGFloat, m2: CGFloat, m3: CGFloat)
92-
private typealias Matrix3x3 =
93-
(m11: CGFloat, m12: CGFloat, m13: CGFloat,
94-
m21: CGFloat, m22: CGFloat, m23: CGFloat,
95-
m31: CGFloat, m32: CGFloat, m33: CGFloat)
96-
97-
private func multiplyMatrix3x3(matrix: Matrix3x3, byVector3 vector: Vector3) -> Vector3 {
98-
let x = matrix.m11 * vector.m1 + matrix.m12 * vector.m2 + matrix.m13 * vector.m3
99-
let y = matrix.m21 * vector.m1 + matrix.m22 * vector.m2 + matrix.m23 * vector.m3
100-
let z = matrix.m31 * vector.m1 + matrix.m32 * vector.m2 + matrix.m33 * vector.m3
101-
102-
return Vector3(x, y, z)
103-
}
104-
105-
private func multiplyMatrix3x3(matrix: Matrix3x3, byMatrix3x3 otherMatrix: Matrix3x3) -> Matrix3x3 {
106-
let column1 = Vector3(otherMatrix.m11, otherMatrix.m21, otherMatrix.m31)
107-
let newColumn1 = multiplyMatrix3x3(matrix, byVector3: column1)
108-
109-
let column2 = Vector3(otherMatrix.m12, otherMatrix.m22, otherMatrix.m32)
110-
let newColumn2 = multiplyMatrix3x3(matrix, byVector3: column2)
111-
112-
let column3 = Vector3(otherMatrix.m13, otherMatrix.m23, otherMatrix.m33)
113-
let newColumn3 = multiplyMatrix3x3(matrix, byVector3: column3)
114-
115-
return Matrix3x3(newColumn1.m1, newColumn2.m1, newColumn3.m1,
116-
newColumn1.m2, newColumn2.m2, newColumn3.m2,
117-
newColumn1.m3, newColumn2.m3, newColumn3.m3)
118-
}
119129

120130
private extension NSAffineTransformStruct {
121-
init(matrix: Matrix3x3) {
122-
self.init(m11: matrix.m11, m12: matrix.m12,
123-
m21: matrix.m21, m22: matrix.m22,
124-
tX: matrix.m13, tY: matrix.m23)
131+
/**
132+
Creates an affine transformation matrix from translation values.
133+
The matrix takes the following form:
134+
135+
[ 1 0 tX ]
136+
[ 0 1 tY ]
137+
[ 0 0 1 ]
138+
*/
139+
static func translation(tX tX: CGFloat, tY: CGFloat) -> NSAffineTransformStruct {
140+
return NSAffineTransformStruct(
141+
m11: CGFloat(1.0), m12: CGFloat(),
142+
m21: CGFloat(), m22: CGFloat(1.0),
143+
tX: tX, tY: tY
144+
)
145+
}
146+
147+
/**
148+
Creates an affine transformation matrix from scaling values.
149+
The matrix takes the following form:
150+
151+
[ sX 0 0 ]
152+
[ 0 sY 0 ]
153+
[ 0 0 1 ]
154+
*/
155+
static func scale(sX sX: CGFloat, sY: CGFloat) -> NSAffineTransformStruct {
156+
return NSAffineTransformStruct(
157+
m11: sX, m12: CGFloat(),
158+
m21: CGFloat(), m22: sY,
159+
tX: CGFloat(), tY: CGFloat()
160+
)
161+
}
162+
163+
/**
164+
Creates an affine transformation matrix from rotation value (angle in radians).
165+
The matrix takes the following form:
166+
167+
[ cos α -sin α 0 ]
168+
[ sin α cos α 0 ]
169+
[ 0 0 1 ]
170+
*/
171+
static func rotation(radians angle: CGFloat) -> NSAffineTransformStruct {
172+
let α = Double(angle)
173+
174+
return NSAffineTransformStruct(
175+
m11: CGFloat(cos(α)), m12: CGFloat(-sin(α)),
176+
m21: CGFloat(sin(α)), m22: CGFloat(cos(α)),
177+
tX: CGFloat(), tY: CGFloat()
178+
)
179+
}
180+
181+
/**
182+
Creates an affine transformation matrix from a rotation value (angle in degrees).
183+
The matrix takes the following form:
184+
185+
[ cos α -sin α 0 ]
186+
[ sin α cos α 0 ]
187+
[ 0 0 1 ]
188+
*/
189+
static func rotation(degrees angle: CGFloat) -> NSAffineTransformStruct {
190+
let α = Double(angle) * M_PI / 180.0
191+
192+
return rotation(radians: CGFloat(α))
125193
}
194+
195+
/**
196+
Creates an affine transformation matrix by combining the receiver with `transformStruct`.
197+
That is, it computes `T * M` and returns the result, where `T` is the receiver's and `M` is
198+
the `transformStruct`'s affine transformation matrix.
199+
The resulting matrix takes the following form:
200+
201+
[ m11_T m12_T tX_T ] [ m11_M m12_M tX_M ]
202+
T * M = [ m21_T m22_T tY_T ] [ m21_M m22_M tY_M ]
203+
[ 0 0 1 ] [ 0 0 1 ]
204+
205+
[ (m11_T*m11_M + m12_T*m21_M) (m11_T*m12_M + m12_T*m22_M) (m11_T*tX_M + m12_T*tY_M + tX_T) ]
206+
= [ (m21_T*m11_M + m22_T*m21_M) (m21_T*m12_M + m22_T*m22_M) (m21_T*tX_M + m22_T*tY_M + tY_T) ]
207+
[ 0 0 1 ]
208+
*/
209+
func concat(transformStruct: NSAffineTransformStruct) -> NSAffineTransformStruct {
210+
let (t, m) = (self, transformStruct)
126211

127-
var matrix3x3: Matrix3x3 {
128-
return Matrix3x3(m11, m12, tX,
129-
m21, m22, tY,
130-
CGFloat(), CGFloat(), CGFloat())
212+
return NSAffineTransformStruct(
213+
m11: (t.m11 * m.m11) + (t.m12 * m.m21), m12: (t.m11 * m.m12) + (t.m12 * m.m22),
214+
m21: (t.m21 * m.m11) + (t.m22 * m.m21), m22: (t.m21 * m.m12) + (t.m22 * m.m22),
215+
tX: (t.m11 * m.tX) + (t.m12 * m.tY) + t.tX,
216+
tY: (t.m21 * m.tX) + (t.m22 * m.tY) + t.tY
217+
)
218+
}
219+
220+
/**
221+
Applies the affine transformation to `toPoint` and returns the result.
222+
The resulting point takes the following form:
223+
224+
[ x' ] [ x ] [ m11 m12 tX ] [ x ] [ m11*x + m12*y + tX ]
225+
[ y' ] = T [ y ] = [ m21 m22 tY ] [ y ] = [ m21*x + m22*y + tY ]
226+
[ 1 ] [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
227+
*/
228+
func applied(toPoint p: NSPoint) -> NSPoint {
229+
let x = (m11 * p.x) + (m12 * p.y) + tX
230+
let y = (m21 * p.x) + (m22 * p.y) + tY
231+
232+
return NSPoint(x: x, y: y)
233+
}
234+
235+
/**
236+
Applies the affine transformation to `toSize` and returns the result.
237+
The resulting size takes the following form:
238+
239+
[ w' ] [ w ] [ m11 m12 tX ] [ w ] [ m11*w + m12*h ]
240+
[ h' ] = T [ h ] = [ m21 m22 tY ] [ h ] = [ m21*w + m22*h ]
241+
[ 0 ] [ 0 ] [ 0 0 1 ] [ 1 ] [ 0 ]
242+
243+
Note: Translation has no effect on the size.
244+
*/
245+
func applied(toSize s: NSSize) -> NSSize {
246+
let w = (m11 * s.width) + (m12 * s.height)
247+
let h = (m21 * s.width) + (m22 * s.height)
248+
249+
return NSSize(width: w, height: h)
250+
}
251+
252+
253+
/**
254+
Returns the inverse affine transformation matrix or `nil` if it has no inverse.
255+
The receiver's affine transformation matrix can be divided into matrix sub-block as
256+
257+
[ M t ]
258+
[ 0 1 ]
259+
260+
where `M` represents the linear map and `t` the translation vector.
261+
262+
The inversion can then be calculated as
263+
264+
[ inv(M) -inv(M) * t ]
265+
[ 0 1 ]
266+
267+
if `M` is invertible.
268+
*/
269+
var inverse: NSAffineTransformStruct? {
270+
get {
271+
// Calculate determinant of M: det(M)
272+
let det = (m11 * m22) - (m12 * m21)
273+
if det == CGFloat() {
274+
return nil
275+
}
276+
277+
let detReciprocal = CGFloat(1.0) / det
278+
279+
// Calculate the inverse of M: inv(M)
280+
let (invM11, invM12) = (detReciprocal * m22, detReciprocal * -m12)
281+
let (invM21, invM22) = (detReciprocal * -m21, detReciprocal * m11)
282+
283+
// Calculate -inv(M)*t
284+
let invTX = ((-invM11 * tX) + (-invM12 * tY))
285+
let invTY = ((-invM21 * tX) + (-invM22 * tY))
286+
287+
return NSAffineTransformStruct(
288+
m11: invM11, m12: invM12,
289+
m21: invM21, m22: invM22,
290+
tX: invTX, tY: invTY
291+
)
292+
}
131293
}
132294
}
295+
296+

Foundation/NSGeometry.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ public func +(lhs: CGFloat, rhs: CGFloat) -> CGFloat {
4545
return CGFloat(lhs.native + rhs.native)
4646
}
4747

48+
public func -(lhs: CGFloat, rhs: CGFloat) -> CGFloat {
49+
return CGFloat(lhs.native - rhs.native)
50+
}
51+
52+
public func /(lhs: CGFloat, rhs: CGFloat) -> CGFloat {
53+
return CGFloat(lhs.native / rhs.native)
54+
}
55+
56+
prefix public func -(x: CGFloat) -> CGFloat {
57+
return CGFloat(-x.native)
58+
}
59+
4860
extension Double {
4961
public init(_ value: CGFloat) {
5062
self = Double(value.native)

0 commit comments

Comments
 (0)