VectorMath is a Swift library for Mac and iOS that implements common 2D and 3D vector and matrix functions

Related tags

Math VectorMath
Overview

Travis Swift 4.2 Swift 5.0 License Twitter

Purpose

VectorMath is a Swift library for Mac and iOS that implements common 2D and 3D vector and matrix functions, useful for games or vector-based graphics.

VectorMath takes advantage of Swift language features such as function and operator overloading and struct methods to provide a more elegant interface than most C, C++ or Cocoa-based graphics APIs.

VectorMath also provides a handy replacement for the GLKit vector math types and functions, which are not available yet in Swift due to their reliance on union types.

VectorMath is a completely standalone library, relying only on the Foundation framework. However, it provides optional compatibility extensions for SceneKit and Quartz (CoreGraphics/CoreAnimation) for interoperability with UIKit, AppKit, SpriteKit and SceneKit.

VectorMath is designed to be efficient, but has not been heavily optimized yet, and does not yet take advantage of architecture-specific hardware acceleration using the Accelerate framework.

Supported OS & SDK Versions

  • Supported build target - iOS 12.0, Mac OS 10.14 (Xcode 11.1)
  • Earliest supported deployment target - iOS 9.0, Mac OS 10.13
  • Earliest compatible deployment target - iOS 7.0, Mac OS 10.9

NOTE: 'Supported' means that the library has been tested with this version. 'Compatible' means that the library should work on this OS version (i.e. it doesn't rely on any unavailable SDK features) but is no longer being tested for compatibility and may require tweaking or bug fixes to run correctly.

Installation

To use the VectorMath functions in an app, drag the VectorMath.swift file (demo/test files and assets are not needed) into your project. You may also wish to include the VectorMath+SceneKit.swift and/or VectorMath+Quartz.swift compatibility extensions.

Types

VectorMath declares the following types:

Scalar

This is a typealias used for the scalar floating point values in the VectorMath library. It is set to Float by default, but you can change it to Double or CGFloat to improve performance for your specific application.

Vector2
Vector3
Vector4

These represent 2D, 3D and 4D vectors, respectively.

Matrix3
Matrix4

These represent homogenous 3x3 and 4x4 transform matrices, respectively.

Quaternion

This represents a rotation in 3D space. It has the same structure as Vector4D, but is defined as a different type due to the different use cases and methods.

All the VectorMath types conform to Equatable and Hashable, so they can be stored in Swift dictionaries.

Constants

VectorMath declares a number of namespaced constants for your convenience. They are as follows:

Scalar.pi
Scalar.halfPi
Scalar.quarterPi
Scalar.twoPi

These should be self-explanatory.

Scalar.degreesPerRadian
Scalar.radiansPerDegree

Conversion factors between degrees and radians. E.g. to convert 40 degrees to radians, you would say let r = 40 * .degreesPerRadian, or to convert Pi/2 radians to degrees, say let d = .halfPi * .radiansPerDegree

Scalar.epsilon = 0.0001

This is a floating point error value used by the approx-equal operator. You can change this if it's insufficiently (or excessively) precise for your needs.

Vector2.zero
Vector3.zero
Vector4.zero
Quaternion.Zero

These are zero vector constants, useful as default values for vectors

Vector2.x
Vector2.y
Vector3.x
Vector3.y
Vector3.z
Vector4.x
Vector4.y
Vector4.z
Vector4.w

These are unit vectors along various axes. For example Vector3.z has the value Vector3(0, 0, 1)

Matrix3.identity
Matrix4.identity
Quaternion.identity

These are identity matrices, which have the property that multiplying them by another matrix or vector has no effect.

Methods

The complete list of VectorMath properties and methods is given below. These are mostly self-explanatory. If you can't find a method you are looking for (e.g. a method to rotate a vector using a quaternion), it's probably implemented as an operator (see "Operators" below).

Vector2
    init(x: Scalar, y: Scalar)
    init(_: Scalar, _: Scalar)
    init(_: [Scalar])
    lengthSquared: Scalar
    length: Scalar
    inverse: Vector2
    toArray() -> [Scalar]
    dot(Vector2) -> Scalar
    cross(Vector2) -> Scalar
    normalized() -> Vector2
    rotated(by: Scalar) -> Vector2
    rotated(by: Scalar, around: Vector2) -> Vector2
    angle(with: Vector2) -> Scalar
    interpolated(with: Vector2, by: Scalar) -> Vector2

Vector3
    init(x: Scalar, y: Scalar, z: Scalar)
    init(_: Scalar, _: Scalar, _: Scalar)
    init(_: [Scalar])
    lengthSquared: Scalar
    length: Scalar
    inverse: Vector3
    xy: Vector2
    xz: Vector2
    yz: Vector2
    toArray() -> [Scalar]
    dot(Vector3) -> Scalar
    cross(Vector3) -> Vector3
    normalized() -> Vector3
    interpolated(with: Vector3, by: Scalar) -> Vector3

Vector4
    init(x: Scalar, y: Scalar, z: Scalar, w: Scalar)
    init(_: Scalar, _: Scalar, _: Scalar, _: Scalar)
    init(_: Vector3, w: Scalar)
    init(_: [Scalar])
    lengthSquared: Scalar
    length: Scalar
    inverse: Vector4
    xyz: Vector3
    xy: Vector2
    xz: Vector2
    yz: Vector2
    toArray() -> [Scalar]
    toVector3() -> Vector3
    dot(Vector4) -> Scalar
    normalized() -> Vector4
    interpolated(with: Vector4, by: Scalar) -> Vector4

Matrix3
    init(m11: Scalar, m12: Scalar, ... m33: Scalar)
    init(_: Scalar, _: Scalar, ... _: Scalar)
    init(scale: Vector2)
    init(translation: Vector2)
    init(rotation: Scalar)
    init(_: [Scalar])
    adjugate: Matrix3
    determinant: Scalar
    transpose: Matrix3
    inverse: Matrix3
    toArray() -> [Scalar]
    interpolated(with: Matrix3, by: Scalar) -> Matrix3

Matrix4
    init(m11: Scalar, m12: Scalar, ... m33: Scalar)
    init(_: Scalar, _: Scalar, ... _: Scalar)
    init(scale: Vector3)
    init(translation: Vector3)
    init(rotation: Vector4)
    init(quaternion: Quaternion)
    init(fovx: Scalar, fovy: Scalar, near: Scalar, far: Scalar)
    init(fovx: Scalar, aspect: Scalar, near: Scalar, far: Scalar)
    init(fovy: Scalar, aspect: Scalar, near: Scalar, far: Scalar)
    init(top: Scalar, right: Scalar, bottom: Scalar, left: Scalar, near: Scalar, far: Scalar)
    init(_: [Scalar])
    adjugate: Matrix4
    determinant: Scalar
    transpose: Matrix4
    inverse: Matrix4
    toArray() -> [Scalar]
    interpolated(with: Matrix3, by: Scalar) -> Matrix3

Quaternion
    init(x: Scalar, y: Scalar, z: Scalar, w: Scalar)
    init(_: Scalar, _: Scalar, _: Scalar, _: Scalar)
    init(axisAngle: Vector4)
    init(pitch: Scalar, yaw: Scalar, roll: Scalar)
    init(rotationMatrix m: Matrix4)
    init(_: [Scalar])
    lengthSquared: Scalar
    length: Scalar
    inverse: Quaternion
    xyz: Vector3
    pitch: Scalar
    yaw: Scalar
    roll: Scalar
    toAxisAngle() -> Vector4
    toPitchYawRoll() -> (pitch: Scalar, yaw: Scalar, roll: Scalar)
    toArray() -> [Scalar]
    dot(Quaternion) -> Scalar
    normalized() -> Quaternion
    interpolated(with: Quaternion, by: Scalar) -> Quaternion

Operators

VectorMath makes extensive use of operator overloading, but I've tried not to go overboard with custom operators. The only nonstandard operator defined is ~=, meaning "approximately equal", which is extremely useful for comparing Scalar, Vector or Matrix values for equality, as, due to floating point imprecision, they are rarely identical.

The *, /, +, - and == operators are implemented for most of the included types. * in particular is useful for matrix and vector transforms. For example, to apply a matrix transform "m" to a vector "v" you can write m * v. * can also be used in conjunction with a Scalar value to scale a vector.

Unary minus is supported for inversion/negation on vectors and matrices.

Dot product, cross product and normalization are not available in operator form, but are supplied as methods on the various types.

Acknowledgements

Many of the algorithms used in VectorMath were ported or adapted from the Kazmath vector math library for C (https://github.com/Kazade/kazmath), or derived from the awesome Matrix and Quaternion FAQ (http://www.j3d.org/matrix_faq/matrfaq_latest.html).

In addition, the following people have contributed directly to the project:

  • @harlanhaskins - SPM and Linux support
  • @milpitas - CocoaPods support
  • @billhsu / @ismailbozk - Bug fixes and test coverage
  • @jiropole - MapKit integration
  • @nicklockwood - Everything else

(Full list of contributors)

Comments
  • Make extensions public

    Make extensions public

    put the "public" keyword before the extension declarations, so that they export properly from bundle to bundle/app

    example:

    public extension SCNVector3 {
        init(_ v: Vector3) {
            self.init(x: SCNFloat(v.x), y: SCNFloat(v.y), z: SCNFloat(v.z))
        }
    }
    
    opened by timprepscius 4
  • normalize() maybe wrong?

    normalize() maybe wrong?

    func normalized() -> Vector4 {
        let lengthSquared = self.lengthSquared
        if lengthSquared ~= 0 || lengthSquared ~= 1 {
            return self
        }
        return self / sqrt(lengthSquared)
    }
    
    

    should be " lengthSquared == 0 || lengthSquared == 1 "

    opened by freedom12 3
  • Use auto-derived implementations of Hashable and Equatable

    Use auto-derived implementations of Hashable and Equatable

    The original versions of hashValue only used addition, so 0.5, -0.5, 0.0 and -0.5, 0.5, 0.0 hash to the same value.

    Just use the auto-synthesized hash value.

    opened by harlanhaskins 2
  • Matrix3 / 4 multiplier order

    Matrix3 / 4 multiplier order

    Correct me if I'm wrong or midunderstangind something, but I think the order of matrix multiplication of VectorMath library seems reversed.

        public static func *(lhs: Matrix3, rhs: Matrix3) -> Matrix3 {
            
            return Matrix3(
                lhs.m11 * rhs.m11 + lhs.m21 * rhs.m12 + lhs.m31 * rhs.m13,
                .....
        }
    
        public static func *(lhs: Matrix4, rhs: Matrix4) -> Matrix4 {
            
            var m = Matrix4.identity
            
            m.m11 = lhs.m11 * rhs.m11 + lhs.m21 * rhs.m12
            m.m11 += lhs.m31 * rhs.m13 + lhs.m41 * rhs.m14
           ....
        }
    

    According to this, algorithm of two matrices is something like this:

    screen shot 2017-03-13 at 9 22 22 pm

    where I believe VectorMath's * operator represents ABin this equation. (meaning you can write A * B in swift to do same calculation.) If so, the function's lhs and rhs is reversed and since Matrix multiplication is not commutative, it'll produce different results.

    I did write a simple Matrix4 <-> CATransform3D conversion code and check something like this:

        let m1 = Matrix4( /* init with some values */)
        let m2 = Matrix4( /* init with some other values */)
    
        let con1 = CATransform3DConcat(CATransform3D(m1), CATransform3D(m2) )
        let con2 = m1 * m2
        XCTAssertTrue(Matrix4(con1) == con2)  // this should pass, but fails
    
        let con3 = m2 * m1
        XCTAssertFalse(Matrix4(con1) == con3)  // this should pass, but fails
    
    

    Here's what CATransform3DConcats document says:

    Concatenate 'b' to 'a' and return the result: t' = a * b.

    I think it would be better to have reversed order for those methods.

    opened by naan 2
  • Add Swift Package Manager and Linux support

    Add Swift Package Manager and Linux support

    This patch adds Linux support via Swift Package Manager and conditionally compiles the SceneKit and Quartz extensions only for platforms that can import them.

    opened by harlanhaskins 1
  • Quaternion <-> Euler angle converting back and forth does not return original value

    Quaternion <-> Euler angle converting back and forth does not return original value

    Insert the following testing code to testEulerConversion test case and observe it to fail

    let quat2 = Quaternion(pitch: 0.12334412, yaw: 1.3521468, roll: -0.53435323)
    let (pitch, yaw, roll) = quat2.toPitchYawRoll()
    let quat2Ref = Quaternion(pitch: pitch, yaw: yaw, roll: roll)
    XCTAssertTrue(quat2 ~= quat2Ref)
    
    opened by DJBen 1
  • add helper functions to Vector4

    add helper functions to Vector4

    These two helper functions are pretty helpful to me, hence adding it to the Vector4 class.

    1. Vector4.init(vec3: Vector3, w: Scalar)
    2. Vector4.toVector3()
    opened by billhsu 1
  • Update creating quaternion from (pitch, yaw, roll)

    Update creating quaternion from (pitch, yaw, roll)

    I believe the previous implementation for Quaternion.init(pitch: Scalar, yaw: Scalar, roll: Scalar) is not correct. Updated the function with another implementation.

    opened by billhsu 1
  • Make all extensions public so they're accessible via frameworks

    Make all extensions public so they're accessible via frameworks

    This commit addresses issue #15, "Make extensions public."

    • All extensions are made public, so they can be referenced when VectorMath is added to a project as a framework via Cocoapods.

    • Extensions that declare conformance to Hashable are broken out as separate extensions, because extensions that declare protocol conformance cannot be declared public.

    • The explicit definition of Scalar.pi is removed, because Swift already declares it as Float.pi and Double.pi. When VectorMath is added to a project as a framework and Scalar.pi is referenced, the duplicate definition of pi causes a compiler error about ambiguous symbols.

    opened by drewolbrich 0
Owner
Nick Lockwood
Nick Lockwood
Swift Matrix Library

Swift Matrix and Machine Learning Library Note: tensorflow/swift and apple/swift-numerics/issues/6 have or will have more complete support for NumPy-l

Scott Sievert 591 Sep 5, 2022
Swift Matrix Library

Swift Matrix and Machine Learning Library Note: tensorflow/swift and apple/swift-numerics/issues/6 have or will have more complete support for NumPy-l

Scott Sievert 591 Sep 5, 2022
SwiftMath is a Swift framework providing some useful math constructs and functions

SwiftMath is a Swift framework providing some useful math constructs and functions, like complex numbers, vectors, matrices, quaternions, and polynomials.

Matteo Battaglio 175 Dec 2, 2022
A collection of functions for statistical calculation written in Swift.

σ (sigma) - statistics library written in Swift This library is a collection of functions that perform statistical calculations in Swift. It can be us

Evgenii Neumerzhitckii 658 Jan 5, 2023
Metron is a comprehensive collection of geometric functions and types that extend the 2D geometric primitives

Metron Geometry, simplified. Metron is a comprehensive collection of geometric functions and types that extend the 2D geometric primitives provided by

Toine Heuvelmans 1k Dec 5, 2022
MRFoundation - A library to complement the Swift Standard Library

MRFoundation MRFoundation is a library to complement the Swift Standard Library.

Roman Mogutnov 2 Feb 12, 2022
A cross-platform Swift library for evaluating mathematical expressions at runtime

Introduction What? Why? How? Usage Installation Integration Symbols Variables Operators Functions Arrays Performance Caching Optimization Standard Lib

Nick Lockwood 738 Jan 7, 2023
Beautiful math equation rendering on iOS and MacOS

iosMath iosMath is a library for displaying beautifully rendered math equations in iOS and MacOS applications. It typesets formulae written using the

Kostub Deshmukh 1.3k Dec 21, 2022
Overload +-*/ operator for Swift, make it easier to use (and not so strict)

Easy-Cal-Swift Overview This file is an overloading of +-*/ operator for Swift, to make it easier to use (and not so strict) It can make your life wit

Wei Wang 272 Jun 29, 2022
Arbitrary-precision arithmetic in pure Swift

Overview API Documentation License Requirements and Integration Implementation Notes Full-width multiplication and division primitives Why is there no

null 707 Dec 19, 2022
Multi-dimensional Swift math

Upsurge Upsurge implements multi-dimensional data structures and operations. It brings numpy-like operations to Swift. Upsurge no longer supports DSP

Alejandro Isaza 180 Dec 20, 2022
Swift Custom Operators for Mathematical Notation

Euler Euler uses custom operators in the "Math Symbols" character set to implement functions using traditional mathematical notation. Please keep in m

Mattt 1.1k Jan 4, 2023
Numeric facilities for Swift

NumericAnnex NumericAnnex supplements the numeric facilities provided in the Swift standard library. Features The exponentiation operator ** and the c

Xiaodi Wu 69 Nov 3, 2022
Math expression parser built with Point•Free's swift-parsing package

swift-math-parser Basic math expression parser built with Point•Free's swift-parsing package. NOTE: currently, this uses a fork of that fixes a parsin

Brad Howes 36 Dec 14, 2022
A set of protocols for Arithmetic, Statistics and Logical operations

Arithmosophi - Arithmosoϕ Arithmosophi is a set of missing protocols that simplify arithmetic and statistics on generic objects or functions. As Equat

Eric Marchand 66 Jul 9, 2022
Numpy-like library in swift. (Multi-dimensional Array, ndarray, matrix and vector library)

Matft Matft is Numpy-like library in Swift. Function name and usage is similar to Numpy. Matft Feature & Usage Declaration MfArray MfType Subscription

null 80 Dec 21, 2022
Matrix-rust-components-swift - Swift package providing components from the matrix-rust-sdk

Swift package for Matrix Rust components This repository is a Swift Package for

matrix.org 10 Nov 4, 2022
Sweet-swift - Make Swift Sweet by Gracefully Introducing Syntactic Sugar, Helper Functions and Common Utilities

Sweet Swift Make Swift Sweet by Gracefully Introducing Syntactic Sugar, Helper F

Yanzhan Yang 2 Feb 6, 2022
Safe and easy wrappers for common Firebase Realtime Database functions.

FirebaseHelper FirebaseHelper is a small wrapper over Firebase's realtime database, providing streamlined methods for get, set, delete, and increment

Quan Vo 15 Apr 9, 2022
Swift Matrix Library

Swift Matrix and Machine Learning Library Note: tensorflow/swift and apple/swift-numerics/issues/6 have or will have more complete support for NumPy-l

Scott Sievert 591 Sep 5, 2022