If the only thing I know about the variables x, y is that they are of type T
which is conforming to the FloatingPoint
protocol, how can I calculate the value of atan2(y, x)
and return it as an instance of type T
?
Actually, I was trying to implement a Vector2D
protocol, but I met problems with the following methods/properties:
.init(radius r:Component, angle a:Component)
.angle
The following is my implementation:
/*
Swift Tip: Vector Algebra with Protocols
https://www.objc.io/blog/2018/11/29/vector-calculus-with-protocols/
FloatingPoint
https://developer.apple.com/documentation/swift/floatingpoint
*/
import Foundation // cos, sin
// math constants
extension FloatingPoint {
public static var deg: Self { .pi / 180 }
}
/// Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// vector component type
associatedtype Component: FloatingPoint
// (x,y) coordinates
var x: Component { get }
var y: Component { get }
// required initializer
init(x: Component, y: Component)
}
// custom operators (declaration)
// ⭐️ can only be declared at file scope
infix operator • : MultiplicationPrecedence // inner product
infix operator × : MultiplicationPrecedence // outer product
// ExpressibleByArrayLiteral
public extension Vector2D {
public init(arrayLiteral a: Component...) {
assert(a.count >= 2, "Must initialize vector with at least 2 values.")
self.init(x: a[0], y: a[1])
}
}
// CustomStringConvertible
public extension Vector2D {
public var description: String { "(\(x), \(y))" }
}
// initializers & factory methods
public extension Vector2D {
// usage: Self(x, y)
public init(_ x:Component, _ y:Component) {
self.init(x: x, y: y)
}
// usage: Self(radius: r, angle: a)
public init(radius r:Component, angle a:Component) {
let r = r as! Double // ⛔️doesn't work‼️
let a = a as! Double
let x = r * cos(a) as! Component
let y = r * sin(a) as! Component
self.init(x: x, y: y)
}
// usage: Self.polar(r, a)
public static func polar(_ r: Component, _ a: Component) -> Self {
Self(radius: r, angle: a)
}
}
// constant vectors
public extension Vector2D {
// zero vector: (0,0)
public static var zero: Self {
Self(x: Component.zero, y: Component.zero)
}
}
// vector operations
public extension Vector2D {
// u + v
public static func + (u: Self, v: Self) -> Self {
Self(x: u.x + v.x, y: u.y + v.y)
}
// -u (prefix)
public static prefix func - (v: Self) -> Self {
Self(x: -v.x, y: -v.y)
}
// u - v
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
// a * v, v * a, v / a (scalar product)
public static func * (a: Component, v: Self) -> Self {
Self(x:a * v.x, y: a * v.y)
}
public static func * (v: Self, a: Component) -> Self {
a * v
}
public static func / (v: Self, a: Component) -> Self {
(1/a) * v
}
// u • v (dot product)
public static func • (u: Self, v: Self) -> Component {
u.x * v.x + u.y * v.y // x1x2 + y1y2
}
// u × v (cross product)
public static func × (u: Self, v: Self) -> Component {
u.x * v.y - u.y * v.x // ad - bc
}
}
// complex numbers
public extension Vector2D {
// z1 * z2 (complex product)
public static func * (z1: Self, z2: Self) -> Self {
let (a,b) = (z1.x, z1.y) // z1 = a + bi
let (c,d) = (z2.x, z2.y) // z2 = c + di
return Self(x: a*c - b*d, y: a*d + b*c) // z1 * z2 = (ac-bd) + (ad+bc)i
}
// z.conjugate
public var conjugate: Self { Self(x: x, y: -y) } // a - bi
// z1 / z2 (complex division)
public static func / (z1: Self, z2: Self) -> Self {
z1 * z2.conjugate / (z2 • z2)
}
}
// vector properties
public extension Vector2D {
public var length: Component { sqrt(self • self) } // |v|
public var magnitude: Component { length } // |v|
public var angle: Component { // in radians
atan2(y as! Double, x as! Double) as! Component // ⛔️doesn't work‼️
}
public var degrees: Component { angle / .deg } // in degrees
}
My workaround: (including 2 files - Vector2D.swift and CGPoint+Vector2D.swift)
Vector2D.swift
import Foundation
// math constants
extension FloatingPoint {
public static var deg: Self { .pi / 180 }
}
// protocol for Vector2D.Component
public protocol VectorComponent: FloatingPoint {
static func cos(_ x: Self) -> Self
static func sin(_ x: Self) -> Self
static func atan2(_ y: Self, _ x: Self) -> Self
}
/// Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// vector component type
associatedtype Component: VectorComponent
// (x,y) coordinates
var x: Component { get }
var y: Component { get }
// required initializer
init(x: Component, y: Component)
}
// custom operators (declaration)
// ⭐️ can only be declared at file scope
infix operator • : MultiplicationPrecedence // inner product
infix operator × : MultiplicationPrecedence // outer product
// ExpressibleByArrayLiteral
public extension Vector2D {
public init(arrayLiteral elements: Component...) {
let a = elements + [0, 0] // make sure that a.count >= 2
self.init(x: a[0], y: a[1])
}
}
// CustomStringConvertible
public extension Vector2D {
public var description: String { "(\(x), \(y))" }
}
// initializers & factory methods
public extension Vector2D {
// usage: Self(x, y)
public init(_ x:Component, _ y:Component) {
self.init(x: x, y: y)
}
// usage: Self(radius: r, angle: a)
public init(radius r: Component, angle a: Component) {
self.init(x: r * Component.cos(a), y: r * Component.sin(a))
}
// usage: Self.polar(r, a)
public static func polar(_ r: Component, _ a: Component) -> Self {
Self(radius: r, angle: a)
}
}
// constant vectors
public extension Vector2D {
// zero vector: (0,0)
public static var zero: Self {
Self(x: Component.zero, y: Component.zero)
}
}
// vector operations
public extension Vector2D {
// u + v
public static func + (u: Self, v: Self) -> Self {
Self(x: u.x + v.x, y: u.y + v.y)
}
// -u (prefix)
public static prefix func - (v: Self) -> Self {
Self(x: -v.x, y: -v.y)
}
// u - v
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
// a * v, v * a, v / a (scalar product)
public static func * (a: Component, v: Self) -> Self {
Self(x:a * v.x, y: a * v.y)
}
public static func * (v: Self, a: Component) -> Self {
a * v
}
public static func / (v: Self, a: Component) -> Self {
(1/a) * v
}
// u • v (dot product)
public static func • (u: Self, v: Self) -> Component {
u.x * v.x + u.y * v.y // x1x2 + y1y2
}
// u × v (cross product)
public static func × (u: Self, v: Self) -> Component {
u.x * v.y - u.y * v.x // ad - bc
}
}
// complex numbers
public extension Vector2D {
// z1 * z2 (complex product)
public static func * (z1: Self, z2: Self) -> Self {
let (a,b) = (z1.x, z1.y) // z1 = a + bi
let (c,d) = (z2.x, z2.y) // z2 = c + di
return Self(x: a*c - b*d, y: a*d + b*c) // z1 * z2 = (ac-bd) + (ad+bc)i
}
// z.conjugate
public var conjugate: Self { Self(x: x, y: -y) } // a - bi
// z1 / z2 (complex division)
public static func / (z1: Self, z2: Self) -> Self {
z1 * z2.conjugate / (z2 • z2)
}
}
// vector properties
public extension Vector2D {
public var length: Component { sqrt(self • self) } // |v|
public var magnitude: Component { length } // |v|
public var angle: Component { Component.atan2(y, x) } // in radians
public var degrees: Component { angle / .deg } // in degrees
}
// vector functions
public func abs<T: Vector2D>(_ v: T) -> T.Component {
v.length
}
CGPoint+Vector2D.swift
import CoreGraphics // for CGPoint, cos, sin, atan2
// CGFloat (protocol conformance)
extension CGFloat: VectorComponent {
public static func cos(_ x: CGFloat) -> CGFloat { CoreGraphics.cos(x) }
public static func sin(_ x: CGFloat) -> CGFloat { CoreGraphics.sin(x) }
public static func atan2(_ y: CGFloat, _ x: CGFloat) -> CGFloat { CoreGraphics.atan2(y,x) }
}
// CGPoint (protocol conformance)
extension CGPoint: Vector2D {}