Skip to content

Commit 195e113

Browse files
committed
Implement NSAffineTransform.transform{Point,Size}
As part of this change: - The addition and multiplication operators have been implemented for CGFloat so we can compute dot products - A private helper function for matrix-vector products has been implemented
1 parent 492c2e6 commit 195e113

File tree

3 files changed

+77
-10
lines changed

3 files changed

+77
-10
lines changed

Foundation/NSAffineTransform.swift

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,43 @@ public class NSAffineTransform : NSObject, NSCopying, NSSecureCoding {
6262
public func prependTransform(transform: NSAffineTransform) { NSUnimplemented() }
6363

6464
// Transforming points and sizes
65-
public func transformPoint(aPoint: NSPoint) -> NSPoint { NSUnimplemented() }
66-
public func transformSize(aSize: NSSize) -> NSSize { NSUnimplemented() }
67-
65+
public func transformPoint(aPoint: NSPoint) -> NSPoint {
66+
let matrix = transformStruct.matrix3x3
67+
let vector = Vector3(aPoint.x, aPoint.y, CGFloat(1.0))
68+
let resultVector = multiplyMatrix3x3(matrix, byVector3: vector)
69+
return NSMakePoint(resultVector.m1, resultVector.m2)
70+
}
71+
72+
public func transformSize(aSize: NSSize) -> NSSize {
73+
let matrix = transformStruct.matrix3x3
74+
let vector = Vector3(aSize.width, aSize.height, CGFloat(1.0))
75+
let resultVector = multiplyMatrix3x3(matrix, byVector3: vector)
76+
return NSMakeSize(resultVector.m1, resultVector.m2)
77+
}
78+
6879
// Transform Struct
6980
public var transformStruct: NSAffineTransformStruct
7081
}
7182

83+
// Private helper functions and structures for linear algebra operations.
84+
private typealias Vector3 = (m1: CGFloat, m2: CGFloat, m3: CGFloat)
85+
private typealias Matrix3x3 =
86+
(m11: CGFloat, m12: CGFloat, m13: CGFloat,
87+
m21: CGFloat, m22: CGFloat, m23: CGFloat,
88+
m31: CGFloat, m32: CGFloat, m33: CGFloat)
89+
90+
private func multiplyMatrix3x3(matrix: Matrix3x3, byVector3 vector: Vector3) -> Vector3 {
91+
let x = matrix.m11 * vector.m1 + matrix.m12 * vector.m2 + matrix.m13 * vector.m3
92+
let y = matrix.m21 * vector.m1 + matrix.m22 * vector.m2 + matrix.m23 * vector.m3
93+
let z = matrix.m31 * vector.m1 + matrix.m32 * vector.m2 + matrix.m33 * vector.m3
94+
95+
return Vector3(x, y, z)
96+
}
97+
98+
private extension NSAffineTransformStruct {
99+
var matrix3x3: Matrix3x3 {
100+
return Matrix3x3(m11, m12, tX,
101+
m21, m22, tY,
102+
CGFloat(), CGFloat(), CGFloat())
103+
}
104+
}

Foundation/NSGeometry.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ public func <(lhs: CGFloat, rhs: CGFloat) -> Bool {
3737
return lhs.native < rhs.native
3838
}
3939

40+
public func *(lhs: CGFloat, rhs: CGFloat) -> CGFloat {
41+
return CGFloat(lhs.native * rhs.native)
42+
}
43+
44+
public func +(lhs: CGFloat, rhs: CGFloat) -> CGFloat {
45+
return CGFloat(lhs.native + rhs.native)
46+
}
47+
4048
@_transparent extension Double {
4149
public init(_ value: CGFloat) {
4250
self = Double(value.native)

TestFoundation/TestNSAffineTransform.swift

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
#endif
2525

2626
class TestNSAffineTransform : XCTestCase {
27+
private let accuracyThreshold = 0.001
2728

2829
var allTests : [(String, () -> ())] {
2930
return [
30-
("test_BasicConstruction", test_BasicConstruction)
31+
("test_BasicConstruction", test_BasicConstruction),
32+
("test_IdentityTransformation", test_IdentityTransformation)
3133
]
3234
}
3335

@@ -37,12 +39,36 @@ class TestNSAffineTransform : XCTestCase {
3739

3840
// The diagonal entries (1,1) and (2,2) of the identity matrix are ones. The other entries are zeros.
3941
// TODO: These should use DBL_MAX but it's not available as part of Glibc on Linux
40-
XCTAssertEqualWithAccuracy(Double(transformStruct.m11), Double(1), accuracy: 0.001)
41-
XCTAssertEqualWithAccuracy(Double(transformStruct.m22), Double(1), accuracy: 0.001)
42+
XCTAssertEqualWithAccuracy(Double(transformStruct.m11), Double(1), accuracy: accuracyThreshold)
43+
XCTAssertEqualWithAccuracy(Double(transformStruct.m22), Double(1), accuracy: accuracyThreshold)
4244

43-
XCTAssertEqualWithAccuracy(Double(transformStruct.m12), Double(0), accuracy: 0.001)
44-
XCTAssertEqualWithAccuracy(Double(transformStruct.m21), Double(0), accuracy: 0.001)
45-
XCTAssertEqualWithAccuracy(Double(transformStruct.tX), Double(0), accuracy: 0.001)
46-
XCTAssertEqualWithAccuracy(Double(transformStruct.tY), Double(0), accuracy: 0.001)
45+
XCTAssertEqualWithAccuracy(Double(transformStruct.m12), Double(0), accuracy: accuracyThreshold)
46+
XCTAssertEqualWithAccuracy(Double(transformStruct.m21), Double(0), accuracy: accuracyThreshold)
47+
XCTAssertEqualWithAccuracy(Double(transformStruct.tX), Double(0), accuracy: accuracyThreshold)
48+
XCTAssertEqualWithAccuracy(Double(transformStruct.tY), Double(0), accuracy: accuracyThreshold)
49+
}
50+
51+
func test_IdentityTransformation() {
52+
let identityTransform = NSAffineTransform()
53+
54+
func checkIdentityPointTransformation(point: NSPoint) {
55+
let newPoint = identityTransform.transformPoint(point)
56+
XCTAssertEqualWithAccuracy(Double(newPoint.x), Double(point.x), accuracy: accuracyThreshold)
57+
XCTAssertEqualWithAccuracy(Double(newPoint.y), Double(point.y), accuracy: accuracyThreshold)
58+
}
59+
60+
checkIdentityPointTransformation(NSPoint())
61+
checkIdentityPointTransformation(NSMakePoint(CGFloat(24.5), CGFloat(10.0)))
62+
checkIdentityPointTransformation(NSMakePoint(CGFloat(-7.5), CGFloat(2.0)))
63+
64+
func checkIdentitySizeTransformation(size: NSSize) {
65+
let newSize = identityTransform.transformSize(size)
66+
XCTAssertEqualWithAccuracy(Double(newSize.width), Double(size.width), accuracy: accuracyThreshold)
67+
XCTAssertEqualWithAccuracy(Double(newSize.height), Double(size.height), accuracy: accuracyThreshold)
68+
}
69+
70+
checkIdentitySizeTransformation(NSSize())
71+
checkIdentitySizeTransformation(NSMakeSize(CGFloat(13.0), CGFloat(12.5)))
72+
checkIdentitySizeTransformation(NSMakeSize(CGFloat(100.0), CGFloat(-100.0)))
4773
}
4874
}

0 commit comments

Comments
 (0)