๐ ฟ๏ธ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