mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
357 lines
12 KiB
Swift
357 lines
12 KiB
Swift
//===----------------------------------------------------------------------===//
|
||
//
|
||
// This source file is part of the Swift.org open source project
|
||
//
|
||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||
//
|
||
// See https://swift.org/LICENSE.txt for license information
|
||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
@_exported import Foundation // Clang module
|
||
|
||
#if os(macOS)
|
||
|
||
private let ε: CGFloat = 2.22045e-16
|
||
|
||
public struct AffineTransform : ReferenceConvertible, Hashable, CustomStringConvertible {
|
||
public var m11, m12, m21, m22, tX, tY: CGFloat
|
||
|
||
public typealias ReferenceType = NSAffineTransform
|
||
|
||
/**
|
||
Creates an affine transformation.
|
||
*/
|
||
public init(m11: CGFloat, m12: CGFloat, m21: CGFloat, m22: CGFloat, tX: CGFloat, tY: CGFloat) {
|
||
self.m11 = m11
|
||
self.m12 = m12
|
||
self.m21 = m21
|
||
self.m22 = m22
|
||
self.tX = tX
|
||
self.tY = tY
|
||
}
|
||
|
||
fileprivate init(reference: NSAffineTransform) {
|
||
m11 = reference.transformStruct.m11
|
||
m12 = reference.transformStruct.m12
|
||
m21 = reference.transformStruct.m21
|
||
m22 = reference.transformStruct.m22
|
||
tX = reference.transformStruct.tX
|
||
tY = reference.transformStruct.tY
|
||
}
|
||
|
||
fileprivate var reference : NSAffineTransform {
|
||
let ref = NSAffineTransform()
|
||
ref.transformStruct = NSAffineTransformStruct(m11: m11, m12: m12, m21: m21, m22: m22, tX: tX, tY: tY)
|
||
return ref
|
||
}
|
||
|
||
/**
|
||
Creates an affine transformation matrix with identity values.
|
||
- seealso: identity
|
||
*/
|
||
public init() {
|
||
self.init(m11: CGFloat(1.0), m12: CGFloat(0.0),
|
||
m21: CGFloat(0.0), m22: CGFloat(1.0),
|
||
tX: CGFloat(0.0), tY: CGFloat(0.0))
|
||
}
|
||
|
||
/**
|
||
Creates an affine transformation matrix from translation values.
|
||
The matrix takes the following form:
|
||
|
||
[ 1 0 0 ]
|
||
[ 0 1 0 ]
|
||
[ x y 1 ]
|
||
*/
|
||
public init(translationByX x: CGFloat, byY y: CGFloat) {
|
||
self.init(m11: CGFloat(1.0), m12: CGFloat(0.0),
|
||
m21: CGFloat(0.0), m22: CGFloat(1.0),
|
||
tX: x, tY: y)
|
||
}
|
||
|
||
/**
|
||
Creates an affine transformation matrix from scaling values.
|
||
The matrix takes the following form:
|
||
|
||
[ x 0 0 ]
|
||
[ 0 y 0 ]
|
||
[ 0 0 1 ]
|
||
*/
|
||
public init(scaleByX x: CGFloat, byY y: CGFloat) {
|
||
self.init(m11: x, m12: CGFloat(0.0),
|
||
m21: CGFloat(0.0), m22: y,
|
||
tX: CGFloat(0.0), tY: CGFloat(0.0))
|
||
}
|
||
|
||
/**
|
||
Creates an affine transformation matrix from scaling a single value.
|
||
The matrix takes the following form:
|
||
|
||
[ f 0 0 ]
|
||
[ 0 f 0 ]
|
||
[ 0 0 1 ]
|
||
*/
|
||
public init(scale factor: CGFloat) {
|
||
self.init(scaleByX: factor, byY: factor)
|
||
}
|
||
|
||
/**
|
||
Creates an affine transformation matrix from rotation value (angle in radians).
|
||
The matrix takes the following form:
|
||
|
||
[ cos α sin α 0 ]
|
||
[ -sin α cos α 0 ]
|
||
[ 0 0 1 ]
|
||
*/
|
||
public init(rotationByRadians angle: CGFloat) {
|
||
let α = Double(angle)
|
||
|
||
let sine = CGFloat(sin(α))
|
||
let cosine = CGFloat(cos(α))
|
||
|
||
self.init(m11: cosine, m12: sine, m21: -sine, m22: cosine, tX: 0, tY: 0)
|
||
}
|
||
|
||
/**
|
||
Creates an affine transformation matrix from a rotation value (angle in degrees).
|
||
The matrix takes the following form:
|
||
|
||
[ cos α sin α 0 ]
|
||
[ -sin α cos α 0 ]
|
||
[ 0 0 1 ]
|
||
*/
|
||
public init(rotationByDegrees angle: CGFloat) {
|
||
let α = angle * .pi / 180.0
|
||
self.init(rotationByRadians: α)
|
||
}
|
||
|
||
/**
|
||
An identity affine transformation matrix
|
||
|
||
[ 1 0 0 ]
|
||
[ 0 1 0 ]
|
||
[ 0 0 1 ]
|
||
*/
|
||
public static let identity = AffineTransform(m11: 1, m12: 0, m21: 0, m22: 1, tX: 0, tY: 0)
|
||
|
||
// Translating
|
||
|
||
public mutating func translate(x: CGFloat, y: CGFloat) {
|
||
tX += m11 * x + m21 * y
|
||
tY += m12 * x + m22 * y
|
||
}
|
||
|
||
/**
|
||
Mutates an affine transformation matrix from a rotation value (angle α in degrees).
|
||
The matrix takes the following form:
|
||
|
||
[ cos α sin α 0 ]
|
||
[ -sin α cos α 0 ]
|
||
[ 0 0 1 ]
|
||
*/
|
||
public mutating func rotate(byDegrees angle: CGFloat) {
|
||
let α = angle * .pi / 180.0
|
||
return rotate(byRadians: α)
|
||
}
|
||
|
||
/**
|
||
Mutates an affine transformation matrix from a rotation value (angle α in radians).
|
||
The matrix takes the following form:
|
||
|
||
[ cos α sin α 0 ]
|
||
[ -sin α cos α 0 ]
|
||
[ 0 0 1 ]
|
||
*/
|
||
public mutating func rotate(byRadians angle: CGFloat) {
|
||
let t2 = self
|
||
let t1 = AffineTransform(rotationByRadians: angle)
|
||
|
||
var t = AffineTransform.identity
|
||
|
||
t.m11 = t1.m11 * t2.m11 + t1.m12 * t2.m21
|
||
t.m12 = t1.m11 * t2.m12 + t1.m12 * t2.m22
|
||
t.m21 = t1.m21 * t2.m11 + t1.m22 * t2.m21
|
||
t.m22 = t1.m21 * t2.m12 + t1.m22 * t2.m22
|
||
t.tX = t1.tX * t2.m11 + t1.tY * t2.m21 + t2.tX
|
||
t.tY = t1.tX * t2.m12 + t1.tY * t2.m22 + t2.tY
|
||
|
||
self = t
|
||
}
|
||
|
||
/**
|
||
Creates an affine transformation matrix by combining the receiver with `transformStruct`.
|
||
That is, it computes `T * M` and returns the result, where `T` is the receiver's and `M` is
|
||
the `transformStruct`'s affine transformation matrix.
|
||
The resulting matrix takes the following form:
|
||
|
||
[ m11_T m12_T 0 ] [ m11_M m12_M 0 ]
|
||
T * M = [ m21_T m22_T 0 ] [ m21_M m22_M 0 ]
|
||
[ tX_T tY_T 1 ] [ tX_M tY_M 1 ]
|
||
|
||
[ (m11_T*m11_M + m12_T*m21_M) (m11_T*m12_M + m12_T*m22_M) 0 ]
|
||
= [ (m21_T*m11_M + m22_T*m21_M) (m21_T*m12_M + m22_T*m22_M) 0 ]
|
||
[ (tX_T*m11_M + tY_T*m21_M + tX_M) (tX_T*m12_M + tY_T*m22_M + tY_M) 1 ]
|
||
*/
|
||
private func concatenated(_ other: AffineTransform) -> AffineTransform {
|
||
let (t, m) = (self, other)
|
||
|
||
// this could be optimized with a vector version
|
||
return AffineTransform(
|
||
m11: (t.m11 * m.m11) + (t.m12 * m.m21), m12: (t.m11 * m.m12) + (t.m12 * m.m22),
|
||
m21: (t.m21 * m.m11) + (t.m22 * m.m21), m22: (t.m21 * m.m12) + (t.m22 * m.m22),
|
||
tX: (t.tX * m.m11) + (t.tY * m.m21) + m.tX,
|
||
tY: (t.tX * m.m12) + (t.tY * m.m22) + m.tY
|
||
)
|
||
}
|
||
|
||
// Scaling
|
||
public mutating func scale(_ scale: CGFloat) {
|
||
self.scale(x: scale, y: scale)
|
||
}
|
||
|
||
public mutating func scale(x: CGFloat, y: CGFloat) {
|
||
m11 *= x
|
||
m12 *= x
|
||
m21 *= y
|
||
m22 *= y
|
||
}
|
||
|
||
/**
|
||
Inverts the transformation matrix if possible. Matrices with a determinant that is less than
|
||
the smallest valid representation of a double value greater than zero are considered to be
|
||
invalid for representing as an inverse. If the input AffineTransform can potentially fall into
|
||
this case then the inverted() method is suggested to be used instead since that will return
|
||
an optional value that will be nil in the case that the matrix cannot be inverted.
|
||
|
||
D = (m11 * m22) - (m12 * m21)
|
||
|
||
D < ε the inverse is undefined and will be nil
|
||
*/
|
||
public mutating func invert() {
|
||
guard let inverse = inverted() else {
|
||
fatalError("Transform has no inverse")
|
||
}
|
||
self = inverse
|
||
}
|
||
|
||
public func inverted() -> AffineTransform? {
|
||
let determinant = (m11 * m22) - (m12 * m21)
|
||
if fabs(determinant) <= ε {
|
||
return nil
|
||
}
|
||
var inverse = AffineTransform()
|
||
inverse.m11 = m22 / determinant
|
||
inverse.m12 = -m12 / determinant
|
||
inverse.m21 = -m21 / determinant
|
||
inverse.m22 = m11 / determinant
|
||
inverse.tX = (m21 * tY - m22 * tX) / determinant
|
||
inverse.tY = (m12 * tX - m11 * tY) / determinant
|
||
return inverse
|
||
}
|
||
|
||
// Transforming with transform
|
||
public mutating func append(_ transform: AffineTransform) {
|
||
self = concatenated(transform)
|
||
}
|
||
|
||
public mutating func prepend(_ transform: AffineTransform) {
|
||
self = transform.concatenated(self)
|
||
}
|
||
|
||
// Transforming points and sizes
|
||
public func transform(_ point: NSPoint) -> NSPoint {
|
||
var newPoint = NSPoint()
|
||
newPoint.x = (m11 * point.x) + (m21 * point.y) + tX
|
||
newPoint.y = (m12 * point.x) + (m22 * point.y) + tY
|
||
return newPoint
|
||
}
|
||
|
||
public func transform(_ size: NSSize) -> NSSize {
|
||
var newSize = NSSize()
|
||
newSize.width = (m11 * size.width) + (m21 * size.height)
|
||
newSize.height = (m12 * size.width) + (m22 * size.height)
|
||
return newSize
|
||
}
|
||
|
||
public var hashValue : Int {
|
||
return Int(m11 + m12 + m21 + m22 + tX + tY)
|
||
}
|
||
|
||
public var description: String {
|
||
return "{m11:\(m11), m12:\(m12), m21:\(m21), m22:\(m22), tX:\(tX), tY:\(tY)}"
|
||
}
|
||
|
||
public var debugDescription: String {
|
||
return description
|
||
}
|
||
|
||
public static func ==(lhs: AffineTransform, rhs: AffineTransform) -> Bool {
|
||
return lhs.m11 == rhs.m11 && lhs.m12 == rhs.m12 &&
|
||
lhs.m21 == rhs.m21 && lhs.m22 == rhs.m22 &&
|
||
lhs.tX == rhs.tX && lhs.tY == rhs.tY
|
||
}
|
||
|
||
}
|
||
|
||
extension AffineTransform : _ObjectiveCBridgeable {
|
||
public static func _getObjectiveCType() -> Any.Type {
|
||
return NSAffineTransform.self
|
||
}
|
||
|
||
@_semantics("convertToObjectiveC")
|
||
public func _bridgeToObjectiveC() -> NSAffineTransform {
|
||
return self.reference
|
||
}
|
||
|
||
public static func _forceBridgeFromObjectiveC(_ x: NSAffineTransform, result: inout AffineTransform?) {
|
||
if !_conditionallyBridgeFromObjectiveC(x, result: &result) {
|
||
fatalError("Unable to bridge type")
|
||
}
|
||
}
|
||
|
||
public static func _conditionallyBridgeFromObjectiveC(_ x: NSAffineTransform, result: inout AffineTransform?) -> Bool {
|
||
result = AffineTransform(reference: x)
|
||
return true // Can't fail
|
||
}
|
||
|
||
public static func _unconditionallyBridgeFromObjectiveC(_ x: NSAffineTransform?) -> AffineTransform {
|
||
guard let src = x else { return AffineTransform.identity }
|
||
return AffineTransform(reference: src)
|
||
}
|
||
}
|
||
|
||
extension NSAffineTransform : _HasCustomAnyHashableRepresentation {
|
||
// Must be @nonobjc to avoid infinite recursion during bridging.
|
||
@nonobjc
|
||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||
return AnyHashable(self as AffineTransform)
|
||
}
|
||
}
|
||
|
||
extension AffineTransform : Codable {
|
||
public init(from decoder: Decoder) throws {
|
||
var container = try decoder.unkeyedContainer()
|
||
m11 = try container.decode(CGFloat.self)
|
||
m12 = try container.decode(CGFloat.self)
|
||
m21 = try container.decode(CGFloat.self)
|
||
m22 = try container.decode(CGFloat.self)
|
||
tX = try container.decode(CGFloat.self)
|
||
tY = try container.decode(CGFloat.self)
|
||
}
|
||
|
||
public func encode(to encoder: Encoder) throws {
|
||
var container = encoder.unkeyedContainer()
|
||
try container.encode(self.m11)
|
||
try container.encode(self.m12)
|
||
try container.encode(self.m21)
|
||
try container.encode(self.m22)
|
||
try container.encode(self.tX)
|
||
try container.encode(self.tY)
|
||
}
|
||
}
|
||
|
||
#endif
|