blob: f58d9377001d671d519895e1774eea2316c9b55d [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
#if os(OSX) || os(iOS)
import Darwin
#elseif os(Linux)
import Glibc
#endif
public struct NSAffineTransformStruct {
public var m11: CGFloat
public var m12: CGFloat
public var m21: CGFloat
public var m22: CGFloat
public var tX: CGFloat
public var tY: CGFloat
public init() {
self.init(m11: CGFloat(), m12: CGFloat(), m21: CGFloat(), m22: CGFloat(), tX: CGFloat(), tY: CGFloat())
}
public init(m11: CGFloat, m12: CGFloat, m21: CGFloat, m22: CGFloat, tX: CGFloat, tY: CGFloat) {
(self.m11, self.m12, self.m21, self.m22) = (m11, m12, m21, m22)
(self.tX, self.tY) = (tX, tY)
}
}
public class NSAffineTransform : NSObject, NSCopying, NSSecureCoding {
public func encodeWithCoder(aCoder: NSCoder) {
NSUnimplemented()
}
public func copyWithZone(zone: NSZone) -> AnyObject {
return NSAffineTransform(transform: self)
}
// Necessary because `NSObject.copy()` returns `self`.
public override func copy() -> AnyObject {
return copyWithZone(nil)
}
public required init?(coder aDecoder: NSCoder) {
NSUnimplemented()
}
public static func supportsSecureCoding() -> Bool {
return true
}
// Initialization
public convenience init(transform: NSAffineTransform) {
self.init()
transformStruct = transform.transformStruct
}
public override init() {
transformStruct = NSAffineTransformStruct(
m11: CGFloat(1.0), m12: CGFloat(),
m21: CGFloat(), m22: CGFloat(1.0),
tX: CGFloat(), tY: CGFloat()
)
}
// Translating
public func translateXBy(deltaX: CGFloat, yBy deltaY: CGFloat) {
let translation = NSAffineTransformStruct.translation(tX: deltaX, tY: deltaY)
transformStruct = translation.concat(transformStruct)
}
// Rotating
public func rotateByDegrees(angle: CGFloat) {
let rotation = NSAffineTransformStruct.rotation(degrees: angle)
transformStruct = rotation.concat(transformStruct)
}
public func rotateByRadians(angle: CGFloat) {
let rotation = NSAffineTransformStruct.rotation(radians: angle)
transformStruct = rotation.concat(transformStruct)
}
// Scaling
public func scaleBy(scale: CGFloat) {
scaleXBy(scale, yBy: scale)
}
public func scaleXBy(scaleX: CGFloat, yBy scaleY: CGFloat) {
let scale = NSAffineTransformStruct.scale(sX: scaleX, sY: scaleY)
transformStruct = scale.concat(transformStruct)
}
// Inverting
public func invert() {
if let inverse = transformStruct.inverse {
transformStruct = inverse
}
else {
preconditionFailure("NSAffineTransform: Transform has no inverse")
}
}
// Transforming with transform
public func appendTransform(transform: NSAffineTransform) {
transformStruct = transformStruct.concat(transform.transformStruct)
}
public func prependTransform(transform: NSAffineTransform) {
transformStruct = transform.transformStruct.concat(transformStruct)
}
// Transforming points and sizes
public func transformPoint(aPoint: NSPoint) -> NSPoint {
return transformStruct.applied(toPoint: aPoint)
}
public func transformSize(aSize: NSSize) -> NSSize {
return transformStruct.applied(toSize: aSize)
}
// Transform Struct
public var transformStruct: NSAffineTransformStruct
}
/**
NSAffineTransformStruct represents an affine transformation matrix of the following form:
[ m11 m12 0 ]
[ m21 m22 0 ]
[ tX tY 1 ]
*/
private extension NSAffineTransformStruct {
/**
Creates an affine transformation matrix from translation values.
The matrix takes the following form:
[ 1 0 0 ]
[ 0 1 0 ]
[ tX tY 1 ]
*/
static func translation(tX tX: CGFloat, tY: CGFloat) -> NSAffineTransformStruct {
return NSAffineTransformStruct(
m11: CGFloat(1.0), m12: CGFloat(),
m21: CGFloat(), m22: CGFloat(1.0),
tX: tX, tY: tY
)
}
/**
Creates an affine transformation matrix from scaling values.
The matrix takes the following form:
[ sX 0 0 ]
[ 0 sY 0 ]
[ 0 0 1 ]
*/
static func scale(sX sX: CGFloat, sY: CGFloat) -> NSAffineTransformStruct {
return NSAffineTransformStruct(
m11: sX, m12: CGFloat(),
m21: CGFloat(), m22: sY,
tX: CGFloat(), tY: CGFloat()
)
}
/**
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 ]
*/
static func rotation(radians angle: CGFloat) -> NSAffineTransformStruct {
let α = Double(angle)
let sine = CGFloat(sin(α))
let cosine = CGFloat(cos(α))
return NSAffineTransformStruct(
m11: cosine, m12: sine,
m21: -sine, m22: cosine,
tX: CGFloat(), tY: CGFloat()
)
}
/**
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 ]
*/
static func rotation(degrees angle: CGFloat) -> NSAffineTransformStruct {
let α = Double(angle) * M_PI / 180.0
return rotation(radians: CGFloat(α))
}
/**
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 ]
*/
func concat(transformStruct: NSAffineTransformStruct) -> NSAffineTransformStruct {
let (t, m) = (self, transformStruct)
return NSAffineTransformStruct(
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
)
}
/**
Applies the affine transformation to `toPoint` and returns the result.
The resulting point takes the following form:
[ x' y' 1 ] = [ x y 1 ] * T
[ m11 m12 0 ]
= [ x y 1 ] * [ m21 m22 0 ]
[ tX tY 1 ]
= [ (x*m11 + y*m21 + tX) (x*m12 + y*m22 + tY) 1 ]
*/
func applied(toPoint p: NSPoint) -> NSPoint {
let x = (p.x * m11) + (p.y * m21) + tX
let y = (p.x * m12) + (p.y * m22) + tY
return NSPoint(x: x, y: y)
}
/**
Applies the affine transformation to `toSize` and returns the result.
The resulting size takes the following form:
[ w' h' 0 ] = [ w h 0] * T
[ m11 m12 0 ]
= [ w h 0 ] [ m21 m22 0 ]
[ tX tY 1 ]
= [ (w*m11 + h*m21) (w*m12 + h*m22) 0 ]
Note: Translation has no effect on the size.
*/
func applied(toSize s: NSSize) -> NSSize {
let w = (m11 * s.width) + (m12 * s.height)
let h = (m21 * s.width) + (m22 * s.height)
return NSSize(width: w, height: h)
}
/**
Returns the inverse affine transformation matrix or `nil` if it has no inverse.
The receiver's affine transformation matrix can be divided into matrix sub-block as
[ M 0 ]
[ t 1 ]
where `M` represents the linear map and `t` the translation vector.
The inversion can then be calculated as
[ inv(M) 0 ]
[ -t*inv(M) 1 ]
if `M` is invertible.
*/
var inverse: NSAffineTransformStruct? {
// Calculate determinant of M: det(M)
let det = (m11 * m22) - (m12 * m21)
if det == CGFloat() {
return nil
}
// Calculate the inverse of M: inv(M)
let (invM11, invM12) = ( m22 / det, -m12 / det)
let (invM21, invM22) = (-m21 / det, m11 / det)
// Calculate -t*inv(M)
let invTX = (-tX * invM11) + (-tY * invM21)
let invTY = (-tX * invM12) + (-tY * invM22)
return NSAffineTransformStruct(
m11: invM11, m12: invM12,
m21: invM21, m22: invM22,
tX: invTX, tY: invTY
)
}
}