๐ ฟ๏ธVector2D
import SwiftUI
import Vector2D
let u = CGPoint(1, 2) // convenience init
let v = CGPoint(2,-1)
let w: CGPoint = [2, 3] // expressible by array literal
// linear combinations
u + v // ( 3, 1)
u - 2 * v // (-3, 4)
3 * u - 2 * v // (-1, 8)
// products
u * v // 4 + 3i : complex number multiplication
u โข v // 0.0 : inner product
u ร v // -5.0 : cross product (directed area)
u ** v // (2,-2) : memberwise multiplication
let p = CGPoint(1,2) // (1,2)
p + 5 * .i // (1,2) + 5(1,0) = (6,2)
2 * p - 3 * .j // (2,4) - 3(0,1) = (2,1)
// 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 {}
๐ Path
๐ Path + .transform
๐ CoreGraphics + 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 {}
Last updated