🅿️Vector2D
swift ⟩ type ⟩ custom ⟩ package ⟩ GeometryKit ⟩ Vector2D
// History:
// • 2024.12.09 - draft
// • 2024.12.11 - version 1.0
// Inheritance Hierarchy
// • MetricSpace -> Vector -> Vector2D -> ComplextNumber
// ---------------------
// 🅿️ Vector2D
// ---------------------
/// 通用的二維向量協定,可適用於 `CGPoint`, `CGSize` 等
///
/// ```
/// import CoreGraphics
///
/// // CGPoint + Vector2D
/// extension CGPoint: Vector2D {
/// public typealias Scalar = CGFloat
/// }
///
/// // CGSize + Vector2D
/// extension CGSize: Vector2D {
/// public typealias Scalar = CGFloat
/// public var y: CGFloat { height }
/// public init(x: CGFloat, y: CGFloat) {
/// self.init(width: x, height: y)
/// }
/// }
/// ```
public protocol Vector2D: Vector {
// ⭐️ required properties
var x: Scalar { get }
var y: Scalar { get }
// ⭐️ required initializer
init(x: Scalar, y: Scalar)
}
// ------------------------------------------
// 🌀 Vector2D: Vector Operations
// ------------------------------------------
// MARK: - Vector Operations -
public extension Vector2D {
/* ------- Constants ------- */
/// `Self.zero = (0, 0)`
static var zero: Self {
return Self.init(x: 0, y: 0)
}
/// `Self.i = (1, 0)`
static var i: Self {
return Self.init(x: 1, y: 0)
}
/// `Self.j = (0, 1)`
static var j: Self {
return Self.init(x: 0, y: 1)
}
/* ------- Initializers ------- */
/// convenience initializer, ex: `CGPoint(1, 2`
init(_ x: Scalar, _ y: Scalar) {
self.init(x: x, y: y)
}
/* ------- Geometry Properties ------- */
/// 長度平方 |z|² = x² + y²
var normSquared: Scalar {
x * x + y * y
}
/// 長度 |z| = √(x² + y²)
var magnitude: Scalar {
(x * x + y * y).squareRoot()
}
/// 長度 |z|
var norm: Scalar {
(x * x + y * y).squareRoot()
}
/// 單位向量
var unitVector: Self {
precondition(self.magnitude != 0, "⛔ Zero vector can not be normalized.")
return self / self.magnitude
}
/* ------- Linear Combinations ------- */
/// u + v (vector addition)
@inlinable // 減少運算頻繁調用的開銷
static func + (u: Self, v: Self) -> Self {
return Self.init(x: u.x + v.x, y: u.y + v.y)
}
/// u += v (vector addition)
static func += (u: inout Self, v: Self) {
u = u + v
}
/// -v (additive inverse)
static prefix func - (v: Self) -> Self {
return Self.init(x: -v.x, y: -v.y)
}
/// u - v (vector subtraction)
@inlinable // 減少運算頻繁調用的開銷
static func - (u: Self, v: Self) -> Self {
return Self.init(x: u.x - v.x, y: u.y - v.y)
}
/// u -= v (vector subtraction)
static func -= (u: inout Self, v: Self) {
u = u - v
}
/* ------- Scalar Multiplication ------- */
/// v * a (scalar multiplication)
/// (a * v : default behavior provided by `Vector`)
@inlinable // 減少運算頻繁調用的開銷
static func * (v: Self, a: Scalar) -> Self {
return Self.init(x: a * v.x, y: a * v.y)
}
/// v *= a (scalar multiplicatio)
static func *= (v: inout Self, a: Scalar) {
v = v * a
}
// v / a (scalar multiplication)
// default behavior provided by `Vector`
/// v /= a (scalar multiplicatio)
static func /= (v: inout Self, a: Scalar) {
precondition(a != 0, "⛔ Division by zero is not allowed.")
v = v / a
}
/* ------- Dot Product ------- */
/// dot product
/// u.dot(v) = u • v = u.x * v.x + u.y * v.y
func dot(_ v: Self) -> Scalar {
return x * v.x + y * v.y
}
/* ------- Cross Product ------- */
/// cross product:
/// u × v = u.x * v.y - u.y * v.x
///
/// ```
/// // Example:
/// let u = CGPoint(1, 2) // u = (1, 2)
/// let v = CGPoint(3, 4) // v = (3, 4)
/// u.cross(v) // u × v = -2
/// ```
func cross(_ v: Self) -> Scalar {
return x * v.y - y * v.x
}
}
// ---------------------------------------
// 🌀 Vector2D: Other Utilities
// ---------------------------------------
// MARK: - Utility -
public extension Vector2D {
/* ------- non-proportional scale ------- */
/// ```
/// // non-proportional scale
/// let u = CGPoint(1, 2) // u = (1, 2)
/// let v = CGPoint(3, 4) // v = (3, 4)
/// u.scale(v) // (1*3, 2*4) = (3, 8)
/// ```
func scaled(by v: Self) -> Self {
return Self.init(x: x * v.x, y: y * v.y)
}
/// non-proportional scale
/// u = (a, b)
/// v = (c, d)
/// u.divided(by: v) = (a/c, b/d)
func divided(by v: Self) -> Self {
precondition(v.x != 0 && v.y != 0, "⛔ Division by zero is not allowed.")
return Self.init(x: x / v.x, y: y / v.y)
}
}
// ----------------------------------------------
// 🌀 Vector2D: Geometry Utilities
// ----------------------------------------------
// MARK: - Geometry -
extension Vector2D {
// ⭐️ required by MetricSpace
// p.distance(to: q)
public func distance(to other: Self) -> Scalar {
return (self - other).magnitude
}
}
// 需要 cos(), sin(), atan2() 等等函數者放這裡
import CoreGraphics // for CGFloat, CGPoint , etc
// cos(), sin(), etc use CGFloat
extension Vector2D where Scalar == CGFloat {
/* ------- Geometry Utilities ------- */
/// 將向量旋轉某個角度 (in radians)
/// `vector.rotated(by: angle)`
public func rotated(by angle: Scalar) -> Self {
let cosA = cos(angle)
let sinA = sin(angle)
// transformed by rotation matrix
return Self.init(
x: x * cosA - y * sinA,
y: x * sinA + y * cosA
)
}
/// u 到 v 的有向角(in radians),
/// 範圍:-𝜋 < θ <= 𝜋
public func angle(to v: Self) -> Scalar {
// dot = |u||v|cosθ
let dot = self.dot(v)
// u ⨉ v = |u||v|sinθ
let area = self.cross(v)
// |u||v|
let mn = self.magnitude * v.magnitude
precondition(mn != 0,
"⛔ Angle to/from zero vector is not defined."
)
// (cosθ, sinθ)
let (c, s) = (dot/mn, area/mn)
// atan2(y, x)
return atan2(s, c)
}
/* ------- Polar Coordinates ------- */
// cos(x), sin(x) -> Foundation framework
/// polar form: `Self.polar(radius: 2, angle: .pi/3)`
public static func polar(radius: Scalar, angle: Scalar) -> Self {
let (r, a) = (radius, angle)
return Self.init(x: r * cos(a), y: r * sin(a))
}
/// convert to CGPoint: `v.point`
public var point: CGPoint { CGPoint(x: x, y: y) }
/* ------- Frame related ------- */
/// vector's origin: `v.origin == Self.zero`
public var origin: CGPoint { .zero }
/// convert to CGSize: `v.size`
public var size : CGSize { CGSize(width: x, height: y) }
}Vector:comformed by Vector2D
UpArrowShape uses Vector2D.
used in Drag gesture to add drag offset.
History
// 封存日期:2020.10.10
//
// 封存記錄:
// 1. 將 CGFloat 改為 associatedtype Field
// 2. 將 Conforming Types 搬到獨立的檔案:🌀 CoreGraphics + Vector2D
// 3. 將牽涉到此協定的 type constraint
// `T: Vector2D` 改為 `T: Vector where T.Field == CGFloat`
// -----------------------------------------
/*
* ⭐️ Conforming Types:
* - CGPoint, CGVector, CGSize
*/
import CoreGraphics // for CGFloat
import Foundation // for String(format:)
// ⭐️ define custom operators
infix operator × : MultiplicationPrecedence // cross (determinant)
infix operator • : MultiplicationPrecedence // dot (• : opt + 8)
infix operator ** : MultiplicationPrecedence // non-proportional scale
infix operator **=: MultiplicationPrecedence
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// x, y coordinates
var x: CGFloat { get }
var y: CGFloat { get }
// initializer
init(x: CGFloat, y: CGFloat)
// vector addition
static func + (u: Self, v: Self) -> Self
// additive inverse
static prefix func - (u: Self) -> Self
}
// default behaviors
extension Vector2D {
/* ------- Constants ------- */
public static var i: Self { .init(x: 1, y: 0) }
public static var j: Self { .init(x: 0, y: 1) }
/* ------- Initializers ------- */
// ExpressibleByArrayLiteral
/// Example:
/// - `let p: CGPoint = [1, 2]`
public init(arrayLiteral elements: CGFloat...) {
self.init(x: elements[0], y: elements[1])
}
/// # convenience initializer
/// Example:
/// - `let p = CGPoint(1, 2)`
public init(_ x: CGFloat, _ y: CGFloat) { self.init(x: x, y: y) }
/* ------- Linear Combinations ------- */
/// u + v
public static func + (u: Self, v: Self) -> Self {
.init(u.x + v.x, u.y + v.y)
}
/// -v (additive inverse)
public static prefix func - (v: Self) -> Self {
.init(-v.x, -v.y)
}
/// # vector subtraction
/// `u - v == u + (-v)`
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
/* ------- Scalar Multiplication ------- */
/// a * v
public static func * (a: CGFloat, v: Self) -> Self {
.init(a * v.x, a * v.y)
}
/// v * a
public static func * (v: Self, a: CGFloat) -> Self {
a * v
}
/// v / a
public static func / (v: Self, a: CGFloat) -> Self {
(1/a) * v
}
/* ------- Dot Product, Cross Product ------- */
/// # cross product: u × v
public static func × (u: Self, v: Self) -> CGFloat {
u.x * v.y - u.y * v.x // x1 y2 - x2 y1
}
/// # dot product: u • v
public static func • (u: Self, v: Self) -> CGFloat {
u.x * v.x + u.y * v.y // x1 x2 + y1 y2
}
/* ------- Complex Number ------- */
/// multiplication: u * v
public static func * (u: Self, v: Self) -> Self {
let (a,b,c,d) = (u.x, u.y, v.x, v.y) // u = a + bi, v = a - bi
return .init(a * c - b * d, a * d + b * c) // uv = (ac-bd) + (ad+bc)i
}
/// multiplication: u *= v
public static func *= (u: inout Self, v: Self) { u = u * v }
/* ------- Other Operations ------- */
/// # Non-proportional scale
/// If `u = [a,b], v = [c,d]`, then `u ** v = [ac, bd]`
public static func ** (u: Self, v: Self) -> Self {
.init(u.x * v.x, u.y * v.y)
}
}
// CustomStringConvertible
extension Vector2D {
public var description: String {
"(\(String(format: "%.2f", x)), \(String(format: "%.2f", y)))"
}
}
/*
* 🅿️ Vector2D Conformance
* - CGPoint
* - CGVector
* - CGSize
*/
// 🌀CGPoint + Vector2D
extension CGPoint: Vector2D {}
// 🌀CGSize + Vector2D
extension CGSize: Vector2D {
public var x: CGFloat { width }
public var y: CGFloat { height }
public init(x: CGFloat, y: CGFloat) {
self.init(width: x, height: y)
}
}
// 🌀CGVector + Vector2D
extension CGVector: Vector2D {
public var x: CGFloat { dx }
public var y: CGFloat { dy }
public init(x: CGFloat, y: CGFloat) {
self.init(dx: x, dy: y)
}
}👉 GitHub ⟩ Vector2D (https://github.com/lochiwei/Vector2D)
👉 replit
/*
* ⭐️ 為了配合 Swift 5.0 (repl.it),函數的回傳記得要用「return」!
*/
import CoreGraphics
// ⭐️ define custom operators
infix operator × : MultiplicationPrecedence // cross (determinant)
infix operator • : MultiplicationPrecedence // dot (• : opt + 8)
infix operator ** : MultiplicationPrecedence // non-proportional scale
infix operator **=: MultiplicationPrecedence
// 🅿️ Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral {
associatedtype Field: FloatingPoint // support +,-,*,/
// x, y coordinates
var x: Field { get }
var y: Field { get }
// initializer
init(x: Field, y: Field)
// vector addition: u + v
static func + (u: Self, v: Self) -> Self
// additive inverse: -v
static prefix func - (u: Self) -> Self
// scalar multiplication: a * v
// has default implementation in extension
}
// default behaviors
extension Vector2D {
/* ------- Constants ------- */
public static var i: Self { return Self.init(x: 1, y: 0) }
public static var j: Self { return Self.init(x: 0, y: 1) }
/* ------- Initializers ------- */
// ExpressibleByArrayLiteral
/// Example:
/// - `let p: CGPoint = [1, 2]`
public init(arrayLiteral elements: Field...) {
var a = elements + [0, 0] // padding 0's
self.init(x: a[0], y: a[1])
}
/// # convenience initializer
/// Example:
/// - `let p = CGPoint(1, 2)`
public init(_ x: Field, _ y: Field) {
self.init(x: x, y: y)
}
/* ------- Linear Combinations ------- */
/// u + v
public static func + (u: Self, v: Self) -> Self {
return Self.init(x: u.x + v.x, y: u.y + v.y)
}
/// -v (additive inverse)
public static prefix func - (v: Self) -> Self {
return Self.init(x: -v.x, y: -v.y)
}
/// # vector subtraction
/// `u - v == u + (-v)`
public static func - (u: Self, v: Self) -> Self {
return u + (-v)
}
/* ------- Scalar Multiplication ------- */
/// a * v
public static func * (a: Field, v: Self) -> Self {
return Self.init(x: a * v.x, y: a * v.y)
}
/// v * a
public static func * (v: Self, a: Field) -> Self {
return a * v
}
/// v / a
public static func / (v: Self, a: Field) -> Self {
return Self.init(x: v.x / a, y: v.y / a)
}
/* ------- Dot Product, Cross Product ------- */
/// # cross product: u × v
public static func × (u: Self, v: Self) -> Field {
return u.x * v.y - u.y * v.x // x1 y2 - x2 y1
}
/// # dot product: u • v
public static func • (u: Self, v: Self) -> Field {
return u.x * v.x + u.y * v.y // x1 x2 + y1 y2
}
/* ------- Complex Number ------- */
/// multiplication: u * v
public static func * (u: Self, v: Self) -> Self {
let (a,b,c,d) = (u.x, u.y, v.x, v.y) // u = a + bi, v = a - bi
return Self.init(x: a * c - b * d, y: a * d + b * c) // uv = (ac-bd) + (ad+bc)i
}
/// multiplication: u *= v
public static func *= (u: inout Self, v: Self) {
u = u * v
}
/* ------- Other Operations ------- */
/// # Non-proportional scale
/// If `u = [a,b], v = [c,d]`, then `u ** v = [ac, bd]`
public static func ** (u: Self, v: Self) -> Self {
return Self.init(x: u.x * v.x, y: u.y * v.y)
}
}
extension Vector2D where Field == CGFloat {
/* ------- Polar Coordinates ------- */
// .polar(r, a)
public static func polar(_ r: Field, _ a: Field) -> Self {
Self.init(x: r * cos(a), y: r * sin(a))
}
}// 2020.10.10 + vec.point (Vector2D -> CGPoint)
//
// ---------------------
// 🅿️ Vector2D
// ---------------------
import SwiftUI
// ⭐️ define custom operators
infix operator × : MultiplicationPrecedence // cross (determinant)
infix operator • : MultiplicationPrecedence // dot (• : opt + 8)
infix operator ** : MultiplicationPrecedence // non-proportional scale
infix operator **=: MultiplicationPrecedence
// 🅿️ Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, Rectangular {
associatedtype Field: FloatingPoint // support +,-,*,/
// x, y coordinates
var x: Field { get }
var y: Field { get }
// initializer
init(x: Field, y: Field)
// vector addition: u + v
// (default implementation provided)
static func + (u: Self, v: Self) -> Self
// additive inverse: -v
// (default implementation provided)
static prefix func - (u: Self) -> Self
}
// MARK: - Vector2D protocol extension -
// default behaviors
extension Vector2D {
/* ------- Constants ------- */
public static var i: Self { return Self.init(x: 1, y: 0) }
public static var j: Self { return Self.init(x: 0, y: 1) }
/* ------- Initializers ------- */
/// make `Vector2D` conform to `ExpressibleByArrayLiteral`
///
/// Example:
/// ```
/// let p: CGPoint = [1, 2]
/// ```
public init(arrayLiteral elements: Field...) {
let a = elements + [0, 0] // padding 0's
self.init(x: a[0], y: a[1])
}
/// convenience initializer
///
/// example:
/// ```
/// let p = CGPoint(1, 2)
/// ```
public init(_ x: Field, _ y: Field) {
self.init(x: x, y: y)
}
/* ------- Linear Combinations ------- */
/// u + v
public static func + (u: Self, v: Self) -> Self {
return Self.init(x: u.x + v.x, y: u.y + v.y)
}
/// -v (additive inverse)
public static prefix func - (v: Self) -> Self {
return Self.init(x: -v.x, y: -v.y)
}
/// vector subtraction
/// `u - v == u + (-v)`
public static func - (u: Self, v: Self) -> Self {
return u + (-v)
}
/* ------- Scalar Multiplication ------- */
/// a * v
public static func * (a: Field, v: Self) -> Self {
return Self.init(x: a * v.x, y: a * v.y)
}
/// v * a
public static func * (v: Self, a: Field) -> Self {
return a * v
}
/// v / a
public static func / (v: Self, a: Field) -> Self {
return Self.init(x: v.x / a, y: v.y / a)
}
/* ------- Dot Product, Cross Product ------- */
/// cross product: `u × v`
public static func × (u: Self, v: Self) -> Field {
return u.x * v.y - u.y * v.x // x1 y2 - x2 y1
}
/// dot product: `u • v`
public static func • (u: Self, v: Self) -> Field {
return u.x * v.x + u.y * v.y // x1 x2 + y1 y2
}
/* ------- Complex Number ------- */
/// complex multiplication: `u * v`
public static func * (u: Self, v: Self) -> Self {
let (a,b,c,d) = (u.x, u.y, v.x, v.y) // u = a + bi, v = a - bi
return Self.init(x: a * c - b * d, y: a * d + b * c) // uv = (ac-bd) + (ad+bc)i
}
/// complex multiplication: u *= v
public static func *= (u: inout Self, v: Self) {
u = u * v
}
/* ------- Other Operations ------- */
/// (non-proportional scale)
/// if `u = [a,b], v = [c,d]`, then `u ** v = [ac, bd]`
public static func ** (u: Self, v: Self) -> Self {
return Self.init(x: u.x * v.x, y: u.y * v.y)
}
}
// MARK: - Field == CGFloat -
extension Vector2D where Field == CGFloat {
/* ------- Polar Coordinates ------- */
/// polar form of a 2D vector
/// - Parameters:
/// - r: radius
/// - a: angle (in radian)
/// - Returns: `Self`: the conforming type
public static func polar(_ r: Field, _ a: Field) -> Self {
Self.init(x: r * cos(a), y: r * sin(a))
}
/* ------- Vector2D to CGPoint ------- */
/// turn a `Vector2D` into `CGPoint`
///
/// Example:
/// ```
/// let size = CGSize(width: 20, height: 30)
/// let p = size.point // CGPoint(x: 20, y: 30)
/// ```
public var point: CGPoint { CGPoint(x: x, y: y) }
/* ------- `Rectangular` conformance ------- */
// automatically conforms to `Rectangular`
public var origin: CGPoint { .zero }
public var size : CGSize { CGSize(width: x, height: y) }
}
// MARK: - Conforming Types -
// 🌀CGPoint + Vector2D
extension CGPoint: Vector2D {}
// 🌀CGSize + Vector2D
extension CGSize: Vector2D {
public var x: CGFloat { width }
public var y: CGFloat { height }
public init(x: CGFloat, y: CGFloat) {
self.init(width: x, height: y)
}
}
// 🌀CGVector + Vector2D
extension CGVector: Vector2D {
public var x: CGFloat { dx }
public var y: CGFloat { dy }
public init(x: CGFloat, y: CGFloat) {
self.init(dx: x, dy: y)
}
}
// 🌀UnitPoint + Vector2D
@available(iOS 13, macOS 10.15, *)
extension UnitPoint: Vector2D {}// 2020.10.10 + vec.point (Vector2D -> CGPoint)
// 2020.03.09 + operator "u ÷ v", "u ÷= v", "u += v"
// ---------------------
// 🅿️ Vector2D
// ---------------------
import SwiftUI
// ⭐️ define custom operators
infix operator × : MultiplicationPrecedence // cross (determinant)
infix operator • : MultiplicationPrecedence // dot (• : opt + 8)
infix operator ** : MultiplicationPrecedence // non-proportional scale
infix operator **=: MultiplicationPrecedence
// 2022.03.09
// (a,b) ÷ (c,d) == (a/c, b/d)
infix operator ÷ : MultiplicationPrecedence
infix operator ÷= : MultiplicationPrecedence
// 🅿️ Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, Rectangular {
associatedtype Field: FloatingPoint // support +,-,*,/
// x, y coordinates
var x: Field { get }
var y: Field { get }
// initializer
init(x: Field, y: Field)
// vector addition: u + v
// (default implementation provided)
static func + (u: Self, v: Self) -> Self
// additive inverse: -v
// (default implementation provided)
static prefix func - (u: Self) -> Self
}
// MARK: - Vector2D protocol extension -
// default behaviors
extension Vector2D {
/* ------- Constants ------- */
public static var i: Self { return Self.init(x: 1, y: 0) }
public static var j: Self { return Self.init(x: 0, y: 1) }
/* ------- Initializers ------- */
/// make `Vector2D` conform to `ExpressibleByArrayLiteral`
///
/// Example:
/// ```
/// let p: CGPoint = [1, 2]
/// ```
public init(arrayLiteral elements: Field...) {
let a = elements + [0, 0] // padding 0's
self.init(x: a[0], y: a[1])
}
/// convenience initializer
///
/// example:
/// ```
/// let p = CGPoint(1, 2)
/// ```
public init(_ x: Field, _ y: Field) {
self.init(x: x, y: y)
}
/* ------- Linear Combinations ------- */
/// u + v
public static func + (u: Self, v: Self) -> Self {
return Self.init(x: u.x + v.x, y: u.y + v.y)
}
// 2022.03.09
/// u += v
public static func += (u: inout Self, v: Self) {
u = u + v
}
/// -v (additive inverse)
public static prefix func - (v: Self) -> Self {
return Self.init(x: -v.x, y: -v.y)
}
/// vector subtraction
/// `u - v == u + (-v)`
public static func - (u: Self, v: Self) -> Self {
return u + (-v)
}
/* ------- Scalar Multiplication ------- */
/// a * v
public static func * (a: Field, v: Self) -> Self {
return Self.init(x: a * v.x, y: a * v.y)
}
/// v * a
public static func * (v: Self, a: Field) -> Self {
return a * v
}
/// v / a
public static func / (v: Self, a: Field) -> Self {
return Self.init(x: v.x / a, y: v.y / a)
}
/* ------- Dot Product, Cross Product ------- */
/// cross product: `u × v`
public static func × (u: Self, v: Self) -> Field {
return u.x * v.y - u.y * v.x // x1 y2 - x2 y1
}
/// dot product: `u • v`
public static func • (u: Self, v: Self) -> Field {
return u.x * v.x + u.y * v.y // x1 x2 + y1 y2
}
/* ------- Complex Number ------- */
/// complex multiplication: `u * v`
public static func * (u: Self, v: Self) -> Self {
let (a,b,c,d) = (u.x, u.y, v.x, v.y) // u = a + bi, v = a - bi
return Self.init(x: a * c - b * d, y: a * d + b * c) // uv = (ac-bd) + (ad+bc)i
}
/// complex multiplication: u *= v
public static func *= (u: inout Self, v: Self) {
u = u * v
}
/* ------- non-proportional scale ------- */
/// `(a,b) ** (c,d) == (ac, bd)`
public static func ** (u: Self, v: Self) -> Self {
return Self.init(x: u.x * v.x, y: u.y * v.y)
}
// 2022.03.09
/// `(a,b) ÷ (c,d) == (a/c, b/d)`
public static func ÷ (u: Self, v: Self) -> Self {
return Self.init(x: u.x / v.x, y: u.y / v.y)
}
/// `(a,b) ÷= (c,d): (a,b) => (a/c, b/d)`
public static func ÷= (u: inout Self, v: Self) {
u = u ÷ v
}
}
// MARK: - Field == CGFloat -
extension Vector2D where Field == CGFloat {
/* ------- Polar Coordinates ------- */
/// polar form of a 2D vector
/// - Parameters:
/// - r: radius
/// - a: angle (in radian)
/// - Returns: `Self`: the conforming type
public static func polar(_ r: Field, _ a: Field) -> Self {
Self.init(x: r * cos(a), y: r * sin(a))
}
/* ------- Vector2D to CGPoint ------- */
/// turn a `Vector2D` into `CGPoint`
///
/// Example:
/// ```
/// let size = CGSize(width: 20, height: 30)
/// let p = size.point // CGPoint(x: 20, y: 30)
/// ```
public var point: CGPoint { CGPoint(x: x, y: y) }
/* ------- `Rectangular` conformance ------- */
// automatically conforms to `Rectangular`
public var origin: CGPoint { .zero }
public var size : CGSize { CGSize(width: x, height: y) }
}
// MARK: - Conforming Types -
// 🌀CGPoint + Vector2D
extension CGPoint: Vector2D {}
// 🌀CGSize + Vector2D
extension CGSize: Vector2D {
public var x: CGFloat { width }
public var y: CGFloat { height }
public init(x: CGFloat, y: CGFloat) {
self.init(width: x, height: y)
}
}
// 🌀CGVector + Vector2D
extension CGVector: Vector2D {
public var x: CGFloat { dx }
public var y: CGFloat { dy }
public init(x: CGFloat, y: CGFloat) {
self.init(dx: x, dy: y)
}
}
// 🌀UnitPoint + Vector2D
@available(iOS 13, macOS 10.15, *)
extension UnitPoint: Vector2D {}Last updated
Was this helpful?