2

如果我对变量 x, y 的唯一了解是它们T是符合FloatingPoint协议的类型,我如何计算的值atan2(y, x)并将其作为类型的实例返回T

实际上,我试图实现一个Vector2D协议,但我遇到了以下方法/属性的问题:

.init(radius r:Component, angle a:Component) 
.angle

以下是我的实现:

/*
 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
}

4

1 回答 1

1

我的解决方法:(包括 2 个文件 - Vector2D.swift 和 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 {}
于 2019-12-03T10:59:19.293 回答