├── .gitignore ├── .swift-version ├── .swiftformat ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── Package.swift ├── README.md ├── Tests ├── LinuxMain.swift ├── UnitTests.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── Mac Tests.xcscheme │ │ └── iOS Tests.xcscheme └── VectorMathTests │ ├── Mac-Info.plist │ ├── QuartzTests.swift │ ├── VectorMathTests.swift │ ├── XCTestManifests.swift │ └── iOS-Info.plist ├── VectorMath.podspec.json └── VectorMath ├── VectorMath+MapKit.swift ├── VectorMath+Quartz.swift ├── VectorMath+SceneKit.swift └── VectorMath.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .build/ 3 | xcuserdata/ 4 | project.xcworkspace/ 5 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --hexgrouping ignore 2 | --decimalgrouping ignore 3 | --ifdef noindent 4 | --enable isEmpty 5 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | - LC_CTYPE=en_US.UTF-8 4 | matrix: 5 | include: 6 | - os: osx 7 | language: objective-c 8 | osx_image: xcode10.1 9 | script: xcodebuild -project Tests/UnitTests.xcodeproj -scheme "iOS Tests" -sdk iphonesimulator 10 | - os: linux 11 | language: generic 12 | sudo: required 13 | dist: trusty 14 | install: eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" 15 | script: swift test 16 | 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.4.1](https://github.com/nicklockwood/VectorMath/releases/tag/0.4.1) (2019-11-10) 4 | 5 | - Reduced epsilon value to fix precision issues 6 | - Fixed warnings in Xcode 11.1 7 | 8 | ## [0.4.0](https://github.com/nicklockwood/VectorMath/releases/tag/0.4.0) (2019-05-18) 9 | 10 | - Updated for Swift 5 and Xcode 10.2 11 | - Added MapKit integration 12 | - Minimum supported Swift version is now 4.2 13 | 14 | ## [0.3.3](https://github.com/nicklockwood/VectorMath/releases/tag/0.3.3) (2018-05-08) 15 | 16 | - Switched to a more conventional MIT license 17 | - Added Swift Package Manager and Linux support 18 | 19 | ## [0.3.2](https://github.com/nicklockwood/VectorMath/releases/tag/0.3.2) (2017-07-01) 20 | 21 | - Added Cocoapods subspecs for the Quartz and SceneKit extensions 22 | - Quartz and SceneKit extensions are now marked public 23 | 24 | ## [0.3.1](https://github.com/nicklockwood/VectorMath/releases/tag/0.3.1) (2017-06-20) 25 | 26 | - Fixed bugs in Quaternion `init(pitch:, yaw:, roll:)` and `init(rotationMatrix:)` constructors 27 | - Added support for watchOS and tvOS 28 | - Added CocoaPods podspec 29 | 30 | ## [0.3](https://github.com/nicklockwood/VectorMath/releases/tag/0.3) (2016-09-24) 31 | 32 | - Updated for Swift 3 33 | - Added `init(_: Vector3, w: Scalar)` and `toVector3() -> Vector3` methods to Vector4 34 | - Renamed some methods and properties in accordance with modern conventions 35 | 36 | ## [0.2](https://github.com/nicklockwood/VectorMath/releases/tag/0.2) (2016-06-09) 37 | 38 | - Added Mac support 39 | - Fixed Vector4, Matrix4 multiplication bug 40 | - Fixed bug in `Quaternion(pitch:, yaw:, roll:)` initializer 41 | 42 | ## [0.1](https://github.com/nicklockwood/VectorMath/releases/tag/0.1) (2014-11-27) 43 | 44 | - First release 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014 Nick Lockwood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "VectorMath", 7 | products: [ 8 | .library( 9 | name: "VectorMath", 10 | targets: ["VectorMath"] 11 | ), 12 | ], 13 | dependencies: [], 14 | targets: [ 15 | .target( 16 | name: "VectorMath", 17 | dependencies: [], 18 | path: "VectorMath" 19 | ), 20 | .testTarget( 21 | name: "VectorMathTests", 22 | dependencies: ["VectorMath"] 23 | ), 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Travis](https://img.shields.io/travis/nicklockwood/VectorMath.svg?maxAge=2592000)](https://travis-ci.org/nicklockwood/VectorMath) 2 | [![Swift 4.2](https://img.shields.io/badge/swift-4.2-orange.svg?style=flat)](https://developer.apple.com/swift) 3 | [![Swift 5.0](https://img.shields.io/badge/swift-5.0-red.svg?style=flat)](https://developer.apple.com/swift) 4 | [![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://opensource.org/licenses/MIT) 5 | [![Twitter](https://img.shields.io/badge/twitter-@nicklockwood-blue.svg?maxAge=2592000)](http://twitter.com/nicklockwood) 6 | 7 | 8 | Purpose 9 | -------------- 10 | 11 | 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. 12 | 13 | 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. 14 | 15 | 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. 16 | 17 | 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. 18 | 19 | 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. 20 | 21 | 22 | Supported OS & SDK Versions 23 | ----------------------------- 24 | 25 | * Supported build target - iOS 12.0, Mac OS 10.14 (Xcode 11.1) 26 | * Earliest supported deployment target - iOS 9.0, Mac OS 10.13 27 | * Earliest compatible deployment target - iOS 7.0, Mac OS 10.9 28 | 29 | 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. 30 | 31 | 32 | Installation 33 | -------------- 34 | 35 | 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. 36 | 37 | 38 | Types 39 | -------------- 40 | 41 | VectorMath declares the following types: 42 | 43 | ```swift 44 | Scalar 45 | ``` 46 | 47 | 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. 48 | 49 | ```swift 50 | Vector2 51 | Vector3 52 | Vector4 53 | ``` 54 | 55 | These represent 2D, 3D and 4D vectors, respectively. 56 | 57 | ```swift 58 | Matrix3 59 | Matrix4 60 | ``` 61 | 62 | These represent homogenous 3x3 and 4x4 transform matrices, respectively. 63 | 64 | ```swift 65 | Quaternion 66 | ``` 67 | 68 | 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. 69 | 70 | All the VectorMath types conform to Equatable and Hashable, so they can be stored in Swift dictionaries. 71 | 72 | 73 | Constants 74 | ------------- 75 | 76 | VectorMath declares a number of namespaced constants for your convenience. They are as follows: 77 | 78 | ```swift 79 | Scalar.pi 80 | Scalar.halfPi 81 | Scalar.quarterPi 82 | Scalar.twoPi 83 | ``` 84 | 85 | These should be self-explanatory. 86 | 87 | ```swift 88 | Scalar.degreesPerRadian 89 | Scalar.radiansPerDegree 90 | ``` 91 | 92 | 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` 93 | 94 | ```swift 95 | Scalar.epsilon = 0.0001 96 | ``` 97 | 98 | 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. 99 | 100 | ```swift 101 | Vector2.zero 102 | Vector3.zero 103 | Vector4.zero 104 | Quaternion.Zero 105 | ``` 106 | 107 | These are zero vector constants, useful as default values for vectors 108 | 109 | ```swift 110 | Vector2.x 111 | Vector2.y 112 | Vector3.x 113 | Vector3.y 114 | Vector3.z 115 | Vector4.x 116 | Vector4.y 117 | Vector4.z 118 | Vector4.w 119 | ``` 120 | 121 | These are unit vectors along various axes. For example Vector3.z has the value `Vector3(0, 0, 1)` 122 | 123 | ```swift 124 | Matrix3.identity 125 | Matrix4.identity 126 | Quaternion.identity 127 | ``` 128 | 129 | These are identity matrices, which have the property that multiplying them by another matrix or vector has no effect. 130 | 131 | 132 | Methods 133 | ------------ 134 | 135 | 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). 136 | 137 | ```swift 138 | Vector2 139 | init(x: Scalar, y: Scalar) 140 | init(_: Scalar, _: Scalar) 141 | init(_: [Scalar]) 142 | lengthSquared: Scalar 143 | length: Scalar 144 | inverse: Vector2 145 | toArray() -> [Scalar] 146 | dot(Vector2) -> Scalar 147 | cross(Vector2) -> Scalar 148 | normalized() -> Vector2 149 | rotated(by: Scalar) -> Vector2 150 | rotated(by: Scalar, around: Vector2) -> Vector2 151 | angle(with: Vector2) -> Scalar 152 | interpolated(with: Vector2, by: Scalar) -> Vector2 153 | 154 | Vector3 155 | init(x: Scalar, y: Scalar, z: Scalar) 156 | init(_: Scalar, _: Scalar, _: Scalar) 157 | init(_: [Scalar]) 158 | lengthSquared: Scalar 159 | length: Scalar 160 | inverse: Vector3 161 | xy: Vector2 162 | xz: Vector2 163 | yz: Vector2 164 | toArray() -> [Scalar] 165 | dot(Vector3) -> Scalar 166 | cross(Vector3) -> Vector3 167 | normalized() -> Vector3 168 | interpolated(with: Vector3, by: Scalar) -> Vector3 169 | 170 | Vector4 171 | init(x: Scalar, y: Scalar, z: Scalar, w: Scalar) 172 | init(_: Scalar, _: Scalar, _: Scalar, _: Scalar) 173 | init(_: Vector3, w: Scalar) 174 | init(_: [Scalar]) 175 | lengthSquared: Scalar 176 | length: Scalar 177 | inverse: Vector4 178 | xyz: Vector3 179 | xy: Vector2 180 | xz: Vector2 181 | yz: Vector2 182 | toArray() -> [Scalar] 183 | toVector3() -> Vector3 184 | dot(Vector4) -> Scalar 185 | normalized() -> Vector4 186 | interpolated(with: Vector4, by: Scalar) -> Vector4 187 | 188 | Matrix3 189 | init(m11: Scalar, m12: Scalar, ... m33: Scalar) 190 | init(_: Scalar, _: Scalar, ... _: Scalar) 191 | init(scale: Vector2) 192 | init(translation: Vector2) 193 | init(rotation: Scalar) 194 | init(_: [Scalar]) 195 | adjugate: Matrix3 196 | determinant: Scalar 197 | transpose: Matrix3 198 | inverse: Matrix3 199 | toArray() -> [Scalar] 200 | interpolated(with: Matrix3, by: Scalar) -> Matrix3 201 | 202 | Matrix4 203 | init(m11: Scalar, m12: Scalar, ... m33: Scalar) 204 | init(_: Scalar, _: Scalar, ... _: Scalar) 205 | init(scale: Vector3) 206 | init(translation: Vector3) 207 | init(rotation: Vector4) 208 | init(quaternion: Quaternion) 209 | init(fovx: Scalar, fovy: Scalar, near: Scalar, far: Scalar) 210 | init(fovx: Scalar, aspect: Scalar, near: Scalar, far: Scalar) 211 | init(fovy: Scalar, aspect: Scalar, near: Scalar, far: Scalar) 212 | init(top: Scalar, right: Scalar, bottom: Scalar, left: Scalar, near: Scalar, far: Scalar) 213 | init(_: [Scalar]) 214 | adjugate: Matrix4 215 | determinant: Scalar 216 | transpose: Matrix4 217 | inverse: Matrix4 218 | toArray() -> [Scalar] 219 | interpolated(with: Matrix3, by: Scalar) -> Matrix3 220 | 221 | Quaternion 222 | init(x: Scalar, y: Scalar, z: Scalar, w: Scalar) 223 | init(_: Scalar, _: Scalar, _: Scalar, _: Scalar) 224 | init(axisAngle: Vector4) 225 | init(pitch: Scalar, yaw: Scalar, roll: Scalar) 226 | init(rotationMatrix m: Matrix4) 227 | init(_: [Scalar]) 228 | lengthSquared: Scalar 229 | length: Scalar 230 | inverse: Quaternion 231 | xyz: Vector3 232 | pitch: Scalar 233 | yaw: Scalar 234 | roll: Scalar 235 | toAxisAngle() -> Vector4 236 | toPitchYawRoll() -> (pitch: Scalar, yaw: Scalar, roll: Scalar) 237 | toArray() -> [Scalar] 238 | dot(Quaternion) -> Scalar 239 | normalized() -> Quaternion 240 | interpolated(with: Quaternion, by: Scalar) -> Quaternion 241 | ``` 242 | 243 | Operators 244 | ------------ 245 | 246 | 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. 247 | 248 | 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. 249 | 250 | Unary minus is supported for inversion/negation on vectors and matrices. 251 | 252 | Dot product, cross product and normalization are not available in operator form, but are supplied as methods on the various types. 253 | 254 | 255 | Acknowledgements 256 | ---------------- 257 | 258 | 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). 259 | 260 | In addition, the following people have contributed directly to the project: 261 | 262 | * @harlanhaskins - SPM and Linux support 263 | * @milpitas - CocoaPods support 264 | * @billhsu / @ismailbozk - Bug fixes and test coverage 265 | * @jiropole - MapKit integration 266 | * @nicklockwood - Everything else 267 | 268 | ([Full list of contributors](https://github.com/nicklockwood/VectorMath/graphs/contributors)) 269 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import VectorMathTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += VectorMathTests.__allTests() 7 | 8 | XCTMain(tests) 9 | -------------------------------------------------------------------------------- /Tests/UnitTests.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 015FC9761CF6E6CA00C9A42A /* VectorMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6561A23EF1400DA8FB3 /* VectorMathTests.swift */; }; 11 | 015FC9781CF6E72100C9A42A /* Mac-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 015FC9771CF6E72100C9A42A /* Mac-Info.plist */; }; 12 | 015FC97B1CF6E7B500C9A42A /* VectorMath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6631A23F08000DA8FB3 /* VectorMath.swift */; }; 13 | 015FC97C1CF6E8EA00C9A42A /* VectorMath+Quartz.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6621A23F08000DA8FB3 /* VectorMath+Quartz.swift */; }; 14 | 015FC97D1CF6E90400C9A42A /* VectorMath+SceneKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6611A23F08000DA8FB3 /* VectorMath+SceneKit.swift */; }; 15 | 01B6A6571A23EF1400DA8FB3 /* VectorMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6561A23EF1400DA8FB3 /* VectorMathTests.swift */; }; 16 | 01B6A6641A23F08000DA8FB3 /* VectorMath+SceneKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6611A23F08000DA8FB3 /* VectorMath+SceneKit.swift */; }; 17 | 01B6A6651A23F08000DA8FB3 /* VectorMath+Quartz.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6621A23F08000DA8FB3 /* VectorMath+Quartz.swift */; }; 18 | 01B6A6661A23F08000DA8FB3 /* VectorMath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6A6631A23F08000DA8FB3 /* VectorMath.swift */; }; 19 | DC1BCD7320A0F26700B75CCA /* QuartzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1BCD7220A0F26600B75CCA /* QuartzTests.swift */; }; 20 | DC1BCD7420A0F26700B75CCA /* QuartzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1BCD7220A0F26600B75CCA /* QuartzTests.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 015FC96E1CF6E68400C9A42A /* MacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 015FC9771CF6E72100C9A42A /* Mac-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Mac-Info.plist"; sourceTree = ""; }; 26 | 01B6A6501A23EF1400DA8FB3 /* iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 01B6A6551A23EF1400DA8FB3 /* iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOS-Info.plist"; sourceTree = ""; }; 28 | 01B6A6561A23EF1400DA8FB3 /* VectorMathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorMathTests.swift; sourceTree = ""; }; 29 | 01B6A6611A23F08000DA8FB3 /* VectorMath+SceneKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "VectorMath+SceneKit.swift"; sourceTree = ""; }; 30 | 01B6A6621A23F08000DA8FB3 /* VectorMath+Quartz.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "VectorMath+Quartz.swift"; sourceTree = ""; }; 31 | 01B6A6631A23F08000DA8FB3 /* VectorMath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VectorMath.swift; sourceTree = ""; }; 32 | DC1BCD7220A0F26600B75CCA /* QuartzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuartzTests.swift; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 015FC96B1CF6E68400C9A42A /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | 01B6A64D1A23EF1400DA8FB3 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 01B6A6321A23EF1400DA8FB3 = { 54 | isa = PBXGroup; 55 | children = ( 56 | 01B6A6601A23F08000DA8FB3 /* VectorMath */, 57 | 01B6A6531A23EF1400DA8FB3 /* UnitTests */, 58 | 01B6A63C1A23EF1400DA8FB3 /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | 01B6A63C1A23EF1400DA8FB3 /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 01B6A6501A23EF1400DA8FB3 /* iOSTests.xctest */, 66 | 015FC96E1CF6E68400C9A42A /* MacTests.xctest */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 01B6A6531A23EF1400DA8FB3 /* UnitTests */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | DC1BCD7220A0F26600B75CCA /* QuartzTests.swift */, 75 | 01B6A6561A23EF1400DA8FB3 /* VectorMathTests.swift */, 76 | 01B6A6551A23EF1400DA8FB3 /* iOS-Info.plist */, 77 | 015FC9771CF6E72100C9A42A /* Mac-Info.plist */, 78 | ); 79 | name = UnitTests; 80 | path = VectorMathTests; 81 | sourceTree = ""; 82 | }; 83 | 01B6A6601A23F08000DA8FB3 /* VectorMath */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 01B6A6611A23F08000DA8FB3 /* VectorMath+SceneKit.swift */, 87 | 01B6A6621A23F08000DA8FB3 /* VectorMath+Quartz.swift */, 88 | 01B6A6631A23F08000DA8FB3 /* VectorMath.swift */, 89 | ); 90 | name = VectorMath; 91 | path = ../VectorMath; 92 | sourceTree = ""; 93 | }; 94 | /* End PBXGroup section */ 95 | 96 | /* Begin PBXNativeTarget section */ 97 | 015FC96D1CF6E68400C9A42A /* MacTests */ = { 98 | isa = PBXNativeTarget; 99 | buildConfigurationList = 015FC9751CF6E68400C9A42A /* Build configuration list for PBXNativeTarget "MacTests" */; 100 | buildPhases = ( 101 | 015FC96A1CF6E68400C9A42A /* Sources */, 102 | 015FC96B1CF6E68400C9A42A /* Frameworks */, 103 | 015FC96C1CF6E68400C9A42A /* Resources */, 104 | ); 105 | buildRules = ( 106 | ); 107 | dependencies = ( 108 | ); 109 | name = MacTests; 110 | productName = MacTests; 111 | productReference = 015FC96E1CF6E68400C9A42A /* MacTests.xctest */; 112 | productType = "com.apple.product-type.bundle.unit-test"; 113 | }; 114 | 01B6A64F1A23EF1400DA8FB3 /* iOSTests */ = { 115 | isa = PBXNativeTarget; 116 | buildConfigurationList = 01B6A65D1A23EF1400DA8FB3 /* Build configuration list for PBXNativeTarget "iOSTests" */; 117 | buildPhases = ( 118 | 01B6A64C1A23EF1400DA8FB3 /* Sources */, 119 | 01B6A64D1A23EF1400DA8FB3 /* Frameworks */, 120 | 01B6A64E1A23EF1400DA8FB3 /* Resources */, 121 | ); 122 | buildRules = ( 123 | ); 124 | dependencies = ( 125 | ); 126 | name = iOSTests; 127 | productName = VectorMathTests; 128 | productReference = 01B6A6501A23EF1400DA8FB3 /* iOSTests.xctest */; 129 | productType = "com.apple.product-type.bundle.unit-test"; 130 | }; 131 | /* End PBXNativeTarget section */ 132 | 133 | /* Begin PBXProject section */ 134 | 01B6A6331A23EF1400DA8FB3 /* Project object */ = { 135 | isa = PBXProject; 136 | attributes = { 137 | LastSwiftUpdateCheck = 0730; 138 | LastUpgradeCheck = 1020; 139 | ORGANIZATIONNAME = "Nick Lockwood"; 140 | TargetAttributes = { 141 | 015FC96D1CF6E68400C9A42A = { 142 | CreatedOnToolsVersion = 7.3; 143 | LastSwiftMigration = 0930; 144 | }; 145 | 01B6A64F1A23EF1400DA8FB3 = { 146 | CreatedOnToolsVersion = 6.1; 147 | LastSwiftMigration = 1020; 148 | TestTargetID = 01B6A63A1A23EF1400DA8FB3; 149 | }; 150 | }; 151 | }; 152 | buildConfigurationList = 01B6A6361A23EF1400DA8FB3 /* Build configuration list for PBXProject "UnitTests" */; 153 | compatibilityVersion = "Xcode 3.2"; 154 | developmentRegion = en; 155 | hasScannedForEncodings = 0; 156 | knownRegions = ( 157 | en, 158 | Base, 159 | ); 160 | mainGroup = 01B6A6321A23EF1400DA8FB3; 161 | productRefGroup = 01B6A63C1A23EF1400DA8FB3 /* Products */; 162 | projectDirPath = ""; 163 | projectRoot = ""; 164 | targets = ( 165 | 01B6A64F1A23EF1400DA8FB3 /* iOSTests */, 166 | 015FC96D1CF6E68400C9A42A /* MacTests */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | 015FC96C1CF6E68400C9A42A /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | 01B6A64E1A23EF1400DA8FB3 /* Resources */ = { 180 | isa = PBXResourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | 015FC9781CF6E72100C9A42A /* Mac-Info.plist in Resources */, 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | }; 187 | /* End PBXResourcesBuildPhase section */ 188 | 189 | /* Begin PBXSourcesBuildPhase section */ 190 | 015FC96A1CF6E68400C9A42A /* Sources */ = { 191 | isa = PBXSourcesBuildPhase; 192 | buildActionMask = 2147483647; 193 | files = ( 194 | 015FC97B1CF6E7B500C9A42A /* VectorMath.swift in Sources */, 195 | 015FC97C1CF6E8EA00C9A42A /* VectorMath+Quartz.swift in Sources */, 196 | 015FC97D1CF6E90400C9A42A /* VectorMath+SceneKit.swift in Sources */, 197 | 015FC9761CF6E6CA00C9A42A /* VectorMathTests.swift in Sources */, 198 | DC1BCD7420A0F26700B75CCA /* QuartzTests.swift in Sources */, 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | 01B6A64C1A23EF1400DA8FB3 /* Sources */ = { 203 | isa = PBXSourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | 01B6A6661A23F08000DA8FB3 /* VectorMath.swift in Sources */, 207 | 01B6A6641A23F08000DA8FB3 /* VectorMath+SceneKit.swift in Sources */, 208 | 01B6A6651A23F08000DA8FB3 /* VectorMath+Quartz.swift in Sources */, 209 | 01B6A6571A23EF1400DA8FB3 /* VectorMathTests.swift in Sources */, 210 | DC1BCD7320A0F26700B75CCA /* QuartzTests.swift in Sources */, 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | /* End PBXSourcesBuildPhase section */ 215 | 216 | /* Begin XCBuildConfiguration section */ 217 | 015FC9731CF6E68400C9A42A /* Debug */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | CLANG_ANALYZER_NONNULL = YES; 221 | CODE_SIGN_IDENTITY = "-"; 222 | COMBINE_HIDPI_IMAGES = YES; 223 | DEBUG_INFORMATION_FORMAT = dwarf; 224 | GCC_NO_COMMON_BLOCKS = YES; 225 | INFOPLIST_FILE = "VectorMathTests/Mac-Info.plist"; 226 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 227 | MACOSX_DEPLOYMENT_TARGET = 10.11; 228 | PRODUCT_BUNDLE_IDENTIFIER = com.charcoaldesign.MacTests; 229 | PRODUCT_NAME = "$(TARGET_NAME)"; 230 | SDKROOT = macosx; 231 | SWIFT_VERSION = 4.2; 232 | }; 233 | name = Debug; 234 | }; 235 | 015FC9741CF6E68400C9A42A /* Release */ = { 236 | isa = XCBuildConfiguration; 237 | buildSettings = { 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CODE_SIGN_IDENTITY = "-"; 240 | COMBINE_HIDPI_IMAGES = YES; 241 | COPY_PHASE_STRIP = NO; 242 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 243 | GCC_NO_COMMON_BLOCKS = YES; 244 | INFOPLIST_FILE = "VectorMathTests/Mac-Info.plist"; 245 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 246 | MACOSX_DEPLOYMENT_TARGET = 10.11; 247 | PRODUCT_BUNDLE_IDENTIFIER = com.charcoaldesign.MacTests; 248 | PRODUCT_NAME = "$(TARGET_NAME)"; 249 | SDKROOT = macosx; 250 | SWIFT_VERSION = 4.2; 251 | }; 252 | name = Release; 253 | }; 254 | 01B6A6581A23EF1400DA8FB3 /* Debug */ = { 255 | isa = XCBuildConfiguration; 256 | buildSettings = { 257 | ALWAYS_SEARCH_USER_PATHS = NO; 258 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 264 | CLANG_WARN_BOOL_CONVERSION = YES; 265 | CLANG_WARN_COMMA = YES; 266 | CLANG_WARN_CONSTANT_CONVERSION = YES; 267 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 269 | CLANG_WARN_EMPTY_BODY = YES; 270 | CLANG_WARN_ENUM_CONVERSION = YES; 271 | CLANG_WARN_INFINITE_RECURSION = YES; 272 | CLANG_WARN_INT_CONVERSION = YES; 273 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 274 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 277 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 278 | CLANG_WARN_STRICT_PROTOTYPES = YES; 279 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 283 | COPY_PHASE_STRIP = NO; 284 | ENABLE_STRICT_OBJC_MSGSEND = YES; 285 | ENABLE_TESTABILITY = YES; 286 | GCC_C_LANGUAGE_STANDARD = gnu99; 287 | GCC_DYNAMIC_NO_PIC = NO; 288 | GCC_NO_COMMON_BLOCKS = YES; 289 | GCC_OPTIMIZATION_LEVEL = 0; 290 | GCC_PREPROCESSOR_DEFINITIONS = ( 291 | "DEBUG=1", 292 | "$(inherited)", 293 | ); 294 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 295 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 296 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 297 | GCC_WARN_UNDECLARED_SELECTOR = YES; 298 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 299 | GCC_WARN_UNUSED_FUNCTION = YES; 300 | GCC_WARN_UNUSED_VARIABLE = YES; 301 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 302 | MTL_ENABLE_DEBUG_INFO = YES; 303 | ONLY_ACTIVE_ARCH = YES; 304 | SDKROOT = iphoneos; 305 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 306 | SWIFT_VERSION = 4.0; 307 | }; 308 | name = Debug; 309 | }; 310 | 01B6A6591A23EF1400DA8FB3 /* Release */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 315 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 316 | CLANG_CXX_LIBRARY = "libc++"; 317 | CLANG_ENABLE_MODULES = YES; 318 | CLANG_ENABLE_OBJC_ARC = YES; 319 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 320 | CLANG_WARN_BOOL_CONVERSION = YES; 321 | CLANG_WARN_COMMA = YES; 322 | CLANG_WARN_CONSTANT_CONVERSION = YES; 323 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 324 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 325 | CLANG_WARN_EMPTY_BODY = YES; 326 | CLANG_WARN_ENUM_CONVERSION = YES; 327 | CLANG_WARN_INFINITE_RECURSION = YES; 328 | CLANG_WARN_INT_CONVERSION = YES; 329 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 330 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 331 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = YES; 340 | ENABLE_NS_ASSERTIONS = NO; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = iphoneos; 353 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 354 | SWIFT_VERSION = 4.0; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | 01B6A65E1A23EF1400DA8FB3 /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | BUNDLE_LOADER = "$(TEST_HOST)"; 363 | FRAMEWORK_SEARCH_PATHS = ( 364 | "$(SDKROOT)/Developer/Library/Frameworks", 365 | "$(inherited)", 366 | ); 367 | GCC_PREPROCESSOR_DEFINITIONS = ( 368 | "DEBUG=1", 369 | "$(inherited)", 370 | ); 371 | INFOPLIST_FILE = "VectorMathTests/iOS-Info.plist"; 372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 373 | PRODUCT_BUNDLE_IDENTIFIER = "com.charcoaldesign.$(PRODUCT_NAME:rfc1034identifier)"; 374 | PRODUCT_NAME = iOSTests; 375 | SWIFT_VERSION = 4.2; 376 | }; 377 | name = Debug; 378 | }; 379 | 01B6A65F1A23EF1400DA8FB3 /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | BUNDLE_LOADER = "$(TEST_HOST)"; 383 | FRAMEWORK_SEARCH_PATHS = ( 384 | "$(SDKROOT)/Developer/Library/Frameworks", 385 | "$(inherited)", 386 | ); 387 | INFOPLIST_FILE = "VectorMathTests/iOS-Info.plist"; 388 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 389 | PRODUCT_BUNDLE_IDENTIFIER = "com.charcoaldesign.$(PRODUCT_NAME:rfc1034identifier)"; 390 | PRODUCT_NAME = iOSTests; 391 | SWIFT_VERSION = 4.2; 392 | }; 393 | name = Release; 394 | }; 395 | /* End XCBuildConfiguration section */ 396 | 397 | /* Begin XCConfigurationList section */ 398 | 015FC9751CF6E68400C9A42A /* Build configuration list for PBXNativeTarget "MacTests" */ = { 399 | isa = XCConfigurationList; 400 | buildConfigurations = ( 401 | 015FC9731CF6E68400C9A42A /* Debug */, 402 | 015FC9741CF6E68400C9A42A /* Release */, 403 | ); 404 | defaultConfigurationIsVisible = 0; 405 | defaultConfigurationName = Release; 406 | }; 407 | 01B6A6361A23EF1400DA8FB3 /* Build configuration list for PBXProject "UnitTests" */ = { 408 | isa = XCConfigurationList; 409 | buildConfigurations = ( 410 | 01B6A6581A23EF1400DA8FB3 /* Debug */, 411 | 01B6A6591A23EF1400DA8FB3 /* Release */, 412 | ); 413 | defaultConfigurationIsVisible = 0; 414 | defaultConfigurationName = Release; 415 | }; 416 | 01B6A65D1A23EF1400DA8FB3 /* Build configuration list for PBXNativeTarget "iOSTests" */ = { 417 | isa = XCConfigurationList; 418 | buildConfigurations = ( 419 | 01B6A65E1A23EF1400DA8FB3 /* Debug */, 420 | 01B6A65F1A23EF1400DA8FB3 /* Release */, 421 | ); 422 | defaultConfigurationIsVisible = 0; 423 | defaultConfigurationName = Release; 424 | }; 425 | /* End XCConfigurationList section */ 426 | }; 427 | rootObject = 01B6A6331A23EF1400DA8FB3 /* Project object */; 428 | } 429 | -------------------------------------------------------------------------------- /Tests/UnitTests.xcodeproj/xcshareddata/xcschemes/Mac Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Tests/UnitTests.xcodeproj/xcshareddata/xcschemes/iOS Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Tests/VectorMathTests/Mac-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/VectorMathTests/QuartzTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuartzTests.swift 3 | // VectorMathTests 4 | // 5 | // Created by Harlan Haskins on 7/5/2018. 6 | // Copyright (c) 2014 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | #if canImport(QuartzCore) 10 | import QuartzCore 11 | import XCTest 12 | 13 | #if SWIFT_PACKAGE 14 | @testable import VectorMath 15 | #endif 16 | 17 | class Matrix3QuartzTests: XCTestCase { 18 | func testScale() { 19 | let transform = CGAffineTransform(scaleX: 0.3, y: 0.4) 20 | let matrix = Matrix3(transform) 21 | let compare = Matrix3(scale: Vector2(0.3, 0.4)) 22 | 23 | XCTAssertTrue(matrix ~= compare) 24 | } 25 | 26 | func testTranslation() { 27 | let transform = CGAffineTransform(translationX: 0.3, y: 0.4) 28 | let matrix = Matrix3(transform) 29 | let compare = Matrix3(translation: Vector2(0.3, 0.4)) 30 | 31 | XCTAssertTrue(matrix ~= compare) 32 | } 33 | 34 | func testRotation() { 35 | let transform = CGAffineTransform(rotationAngle: .pi / 2) 36 | let matrix = Matrix3(transform) 37 | let compare = Matrix3(rotation: .halfPi) 38 | 39 | XCTAssertTrue(matrix ~= compare) 40 | } 41 | } 42 | 43 | class Matrix4QuartzTests: XCTestCase { 44 | func testScale() { 45 | let transform = CATransform3DMakeScale(0.3, 0.4, 0.5) 46 | let matrix = Matrix4(transform) 47 | let compare = Matrix4(scale: Vector3(0.3, 0.4, 0.5)) 48 | 49 | XCTAssertTrue(matrix ~= compare) 50 | } 51 | 52 | func testTranslation() { 53 | let transform = CATransform3DMakeTranslation(0.3, 0.4, 0.5) 54 | let matrix = Matrix4(transform) 55 | let compare = Matrix4(translation: Vector3(0.3, 0.4, 0.5)) 56 | 57 | XCTAssertTrue(matrix ~= compare) 58 | } 59 | 60 | func testRotation() { 61 | let transform = CATransform3DMakeRotation(.pi / 2, 1, 0, 0) 62 | let matrix = Matrix4(transform) 63 | let compare = Matrix4(rotation: Vector4(1, 0, 0, .halfPi)) 64 | 65 | XCTAssertTrue(matrix ~= compare) 66 | } 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /Tests/VectorMathTests/VectorMathTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VectorMathTests.swift 3 | // VectorMathTests 4 | // 5 | // Created by Nick Lockwood on 24/11/2014. 6 | // Copyright (c) 2014 Nick Lockwood. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | #if SWIFT_PACKAGE 12 | @testable import VectorMath 13 | #endif 14 | 15 | class Vector2Tests: XCTestCase { 16 | func testRotatedBy() { 17 | let a = Vector2(1, 0) 18 | let b = Vector2(0, 1) 19 | let c = a.rotated(by: .halfPi) 20 | 21 | XCTAssertTrue(b ~= c) 22 | 23 | let d = Vector2(0.5, 1.5) 24 | let e = a.rotated(by: .halfPi, around: Vector2(0, 0.5)) 25 | 26 | XCTAssertTrue(d ~= e) 27 | } 28 | 29 | func testAngleWith() { 30 | let a = Vector2(1, 0) 31 | let b = Vector2(0, 1) 32 | let angle = a.angle(with: b) 33 | 34 | XCTAssertTrue(angle ~= .halfPi) 35 | } 36 | } 37 | 38 | class Matrix3Tests: XCTestCase { 39 | func testMatrix3Multiplication() { 40 | let a = Matrix3(rotation: .halfPi) 41 | let b = Matrix3(rotation: .quarterPi) 42 | let c = Matrix3(rotation: .halfPi + .quarterPi) 43 | let d = a * b 44 | 45 | XCTAssertTrue(c ~= d) 46 | } 47 | 48 | func testVector3Multiplication() { 49 | let m = Matrix3(rotation: .halfPi) 50 | let a = Vector3(1, 0, 1) 51 | let b = Vector3(0, 1, 1) 52 | let c = a * m 53 | 54 | XCTAssertTrue(b ~= c) 55 | } 56 | 57 | func testVector2Multiplication() { 58 | let m = Matrix3(rotation: .halfPi) 59 | let a = Vector2(1, 0) 60 | let b = Vector2(0, 1) 61 | let c = a * m 62 | 63 | XCTAssertTrue(b ~= c) 64 | } 65 | } 66 | 67 | class Matrix4Tests: XCTestCase { 68 | func testRotationAndTranslation() { 69 | let point = Vector4(0.0, -1.0, 0.0, 1.0) 70 | let euclideanTransformation = Matrix4(0.0, 1.0, 0.0, 0.0, 71 | -1.0, 0.0, 0.0, 0.0, 72 | 0.0, 0.0, 0.0, 0.0, 73 | -1.0, 0.0, 0.0, 1.0) 74 | 75 | let result = euclideanTransformation * point 76 | let expectedResult = Vector4(0.0, 0.0, 0.0, 1.0) 77 | 78 | XCTAssertTrue(result ~= expectedResult) 79 | } 80 | 81 | func testTransformationMatrixMultiplication() { 82 | let somePoint = Vector4(2.0, 2.0, 2.0, 1.0) 83 | let zAxisTransformationMaxtrix90Positive = Matrix4(0.0, 1.0, 0.0, 0.0, 84 | -1.0, 0.0, 0.0, 0.0, 85 | 0.0, 0.0, 0.0, 0.0, 86 | 0.0, 0.0, 0.0, 1.0) 87 | let yAxisTransformationMaxtrix90Positive = Matrix4(0.0, 0.0, 1.0, 0.0, 88 | 0.0, 1.0, 0.0, 0.0, 89 | -1.0, 0.0, 0.0, 0.0, 90 | 0.0, 0.0, 0.0, 1.0) 91 | let xAxisTransformationMaxtrix90Positive = Matrix4(1.0, 0.0, 0.0, 0.0, 92 | 0.0, 0.0, -1.0, 0.0, 93 | 0.0, 1.0, 0.0, 0.0, 94 | 0.0, 0.0, 0.0, 1.0) 95 | 96 | let resultPoint = (xAxisTransformationMaxtrix90Positive * (yAxisTransformationMaxtrix90Positive * (zAxisTransformationMaxtrix90Positive * somePoint))) 97 | 98 | let comparePoint = (xAxisTransformationMaxtrix90Positive * yAxisTransformationMaxtrix90Positive * zAxisTransformationMaxtrix90Positive) * somePoint 99 | 100 | XCTAssertTrue(resultPoint ~= comparePoint) 101 | } 102 | } 103 | 104 | class QuaternionTests: XCTestCase { 105 | func testAxisAngleConversion() { 106 | let aaa = Vector4(1, 0, 0, .halfPi) 107 | let q = Quaternion(axisAngle: aaa) 108 | let aab = q.toAxisAngle() 109 | 110 | XCTAssertTrue(aaa ~= aab) 111 | } 112 | 113 | func testVector3Multiplication() { 114 | let q = Quaternion(axisAngle: Vector4(0, 0, 1, .halfPi)) 115 | let a = Vector3(1, 0, 1) 116 | let b = Vector3(0, 1, 1) 117 | let c = a * q 118 | 119 | XCTAssertTrue(b ~= c) 120 | } 121 | 122 | func testEulerConversion() { 123 | var quat = Quaternion(pitch: .quarterPi, yaw: 0, roll: 0) 124 | XCTAssertTrue(quat.pitch ~= .quarterPi) 125 | 126 | quat = Quaternion(pitch: 0, yaw: .quarterPi, roll: 0) 127 | XCTAssertTrue(quat.yaw ~= .quarterPi) 128 | 129 | quat = Quaternion(pitch: 0, yaw: 0, roll: .quarterPi) 130 | XCTAssertTrue(quat.roll ~= .quarterPi) 131 | 132 | quat = Quaternion(pitch: 0.12334412, yaw: 1.3521468, roll: -0.53435323) 133 | let (pitch, yaw, roll) = quat.toPitchYawRoll() 134 | let quat2Ref = Quaternion(pitch: pitch, yaw: yaw, roll: roll) 135 | XCTAssertTrue(quat ~= quat2Ref) 136 | } 137 | 138 | func testMatrix4Conversion() { 139 | let quat = Quaternion(rotationMatrix: Matrix4.identity) 140 | let matr = Matrix4(quaternion: quat) 141 | XCTAssertTrue(matr ~= .identity) 142 | } 143 | } 144 | 145 | class PerformanceTests: XCTestCase { 146 | func testMatrix3MultiplicationPerformance() { 147 | let a = Matrix3(rotation: .halfPi) 148 | var b = Matrix3(translation: Vector2(1, 10)) 149 | 150 | measure { 151 | for _ in 0 ..< 100000 { 152 | b = a * b 153 | } 154 | } 155 | } 156 | 157 | func testMatrix4MultiplicationPerformance() { 158 | let a = Matrix4(rotation: Vector4(1, 0, 0, .halfPi)) 159 | var b = Matrix4(translation: Vector3(1, 10, 24)) 160 | 161 | measure { 162 | for _ in 0 ..< 100000 { 163 | b = a * b 164 | } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Tests/VectorMathTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | #if !canImport(ObjectiveC) 2 | import XCTest 3 | 4 | extension Matrix3Tests { 5 | // DO NOT MODIFY: This is autogenerated, use: 6 | // `swift test --generate-linuxmain` 7 | // to regenerate. 8 | static let __allTests__Matrix3Tests = [ 9 | ("testMatrix3Multiplication", testMatrix3Multiplication), 10 | ("testVector2Multiplication", testVector2Multiplication), 11 | ("testVector3Multiplication", testVector3Multiplication), 12 | ] 13 | } 14 | 15 | extension Matrix4Tests { 16 | // DO NOT MODIFY: This is autogenerated, use: 17 | // `swift test --generate-linuxmain` 18 | // to regenerate. 19 | static let __allTests__Matrix4Tests = [ 20 | ("testRotationAndTranslation", testRotationAndTranslation), 21 | ("testTransformationMatrixMultiplication", testTransformationMatrixMultiplication), 22 | ] 23 | } 24 | 25 | extension PerformanceTests { 26 | // DO NOT MODIFY: This is autogenerated, use: 27 | // `swift test --generate-linuxmain` 28 | // to regenerate. 29 | static let __allTests__PerformanceTests = [ 30 | ("testMatrix3MultiplicationPerformance", testMatrix3MultiplicationPerformance), 31 | ("testMatrix4MultiplicationPerformance", testMatrix4MultiplicationPerformance), 32 | ] 33 | } 34 | 35 | extension QuaternionTests { 36 | // DO NOT MODIFY: This is autogenerated, use: 37 | // `swift test --generate-linuxmain` 38 | // to regenerate. 39 | static let __allTests__QuaternionTests = [ 40 | ("testAxisAngleConversion", testAxisAngleConversion), 41 | ("testEulerConversion", testEulerConversion), 42 | ("testMatrix4Conversion", testMatrix4Conversion), 43 | ("testVector3Multiplication", testVector3Multiplication), 44 | ] 45 | } 46 | 47 | extension Vector2Tests { 48 | // DO NOT MODIFY: This is autogenerated, use: 49 | // `swift test --generate-linuxmain` 50 | // to regenerate. 51 | static let __allTests__Vector2Tests = [ 52 | ("testAngleWith", testAngleWith), 53 | ("testRotatedBy", testRotatedBy), 54 | ] 55 | } 56 | 57 | public func __allTests() -> [XCTestCaseEntry] { 58 | return [ 59 | testCase(Matrix3Tests.__allTests__Matrix3Tests), 60 | testCase(Matrix4Tests.__allTests__Matrix4Tests), 61 | testCase(PerformanceTests.__allTests__PerformanceTests), 62 | testCase(QuaternionTests.__allTests__QuaternionTests), 63 | testCase(Vector2Tests.__allTests__Vector2Tests), 64 | ] 65 | } 66 | #endif 67 | -------------------------------------------------------------------------------- /Tests/VectorMathTests/iOS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /VectorMath.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VectorMath", 3 | "version": "0.4.1", 4 | "license": { 5 | "type": "zlib", 6 | "file": "LICENSE.md" 7 | }, 8 | "summary": "Vector math library for Mac and iOS", 9 | "homepage": "https://github.com/nicklockwood/VectorMath", 10 | "authors": "Nick Lockwood", 11 | "source": { 12 | "git": "https://github.com/nicklockwood/VectorMath.git", 13 | "tag": "0.4.1" 14 | }, 15 | "subspecs": [ 16 | { 17 | "name": "Core", 18 | "source_files": "VectorMath/VectorMath.swift" 19 | }, 20 | { 21 | "name": "Quartz", 22 | "source_files": "VectorMath/VectorMath+Quartz.swift", 23 | "dependencies": { "VectorMath/Core": [] } 24 | }, 25 | { 26 | "name": "SceneKit", 27 | "source_files": "VectorMath/VectorMath+SceneKit.swift", 28 | "dependencies": { "VectorMath/Core": [] } 29 | }, 30 | { 31 | "name": "MapKit", 32 | "source_files": "VectorMath/VectorMath+MapKit.swift", 33 | "dependencies": { "VectorMath/Core": [] } 34 | } 35 | ], 36 | "platforms": { 37 | "ios": "9.0", 38 | "tvos": "9.0", 39 | "osx": "10.11" 40 | }, 41 | "requires_arc": true 42 | } 43 | -------------------------------------------------------------------------------- /VectorMath/VectorMath+MapKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VectorMath+MapKit.swift 3 | // VectorMath 4 | // 5 | // Version 0.3.3 6 | // 7 | // Created by Nick Lockwood on 24/11/2014. 8 | // Copyright (c) 2014 Nick Lockwood. All rights reserved. 9 | // 10 | // Distributed under the permissive MIT License 11 | // Get the latest version from here: 12 | // 13 | // https://github.com/nicklockwood/VectorMath 14 | // 15 | // Permission is hereby granted, free of charge, to any person obtaining a copy 16 | // of this software and associated documentation files (the "Software"), to deal 17 | // in the Software without restriction, including without limitation the rights 18 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | // copies of the Software, and to permit persons to whom the Software is 20 | // furnished to do so, subject to the following conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be included in all 23 | // copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | // SOFTWARE. 32 | // 33 | 34 | #if canImport(MapKit) 35 | 36 | import MapKit 37 | 38 | // MARK: MapKit extensions 39 | 40 | public extension MKMapPoint { 41 | init(_ v: Vector2) { 42 | self.init(x: Double(v.x), y: Double(v.y)) 43 | } 44 | } 45 | 46 | public extension MKMapSize { 47 | init(_ v: Vector2) { 48 | self.init(width: Double(v.x), height: Double(v.y)) 49 | } 50 | } 51 | 52 | // MARK: VectorMath extensions 53 | 54 | public extension Vector2 { 55 | init(_ v: MKMapPoint) { 56 | self.init(x: Scalar(v.x), y: Scalar(v.y)) 57 | } 58 | 59 | init(_ v: MKMapSize) { 60 | self.init(x: Scalar(v.width), y: Scalar(v.height)) 61 | } 62 | } 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /VectorMath/VectorMath+Quartz.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VectorMath+Quartz.swift 3 | // VectorMath 4 | // 5 | // Version 0.4.0 6 | // 7 | // Created by Nick Lockwood on 24/11/2014. 8 | // Copyright (c) 2014 Nick Lockwood. All rights reserved. 9 | // 10 | // Distributed under the permissive MIT License 11 | // Get the latest version from here: 12 | // 13 | // https://github.com/nicklockwood/VectorMath 14 | // 15 | // Permission is hereby granted, free of charge, to any person obtaining a copy 16 | // of this software and associated documentation files (the "Software"), to deal 17 | // in the Software without restriction, including without limitation the rights 18 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | // copies of the Software, and to permit persons to whom the Software is 20 | // furnished to do so, subject to the following conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be included in all 23 | // copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | // SOFTWARE. 32 | // 33 | 34 | #if canImport(QuartzCore) 35 | 36 | import QuartzCore 37 | 38 | // MARK: SceneKit extensions 39 | 40 | public extension CGPoint { 41 | init(_ v: Vector2) { 42 | self.init(x: CGFloat(v.x), y: CGFloat(v.y)) 43 | } 44 | } 45 | 46 | public extension CGSize { 47 | init(_ v: Vector2) { 48 | self.init(width: CGFloat(v.x), height: CGFloat(v.y)) 49 | } 50 | } 51 | 52 | public extension CGVector { 53 | init(_ v: Vector2) { 54 | self.init(dx: CGFloat(v.x), dy: CGFloat(v.y)) 55 | } 56 | } 57 | 58 | public extension CGAffineTransform { 59 | init(_ m: Matrix3) { 60 | self.init( 61 | a: CGFloat(m.m11), b: CGFloat(m.m12), 62 | c: CGFloat(m.m21), d: CGFloat(m.m22), 63 | tx: CGFloat(m.m31), ty: CGFloat(m.m32) 64 | ) 65 | } 66 | } 67 | 68 | public extension CATransform3D { 69 | init(_ m: Matrix4) { 70 | self.init( 71 | m11: CGFloat(m.m11), m12: CGFloat(m.m12), m13: CGFloat(m.m13), m14: CGFloat(m.m14), 72 | m21: CGFloat(m.m21), m22: CGFloat(m.m22), m23: CGFloat(m.m23), m24: CGFloat(m.m24), 73 | m31: CGFloat(m.m31), m32: CGFloat(m.m32), m33: CGFloat(m.m33), m34: CGFloat(m.m34), 74 | m41: CGFloat(m.m41), m42: CGFloat(m.m42), m43: CGFloat(m.m43), m44: CGFloat(m.m44) 75 | ) 76 | } 77 | } 78 | 79 | // MARK: VectorMath extensions 80 | 81 | public extension Vector2 { 82 | init(_ v: CGPoint) { 83 | self.init(x: Scalar(v.x), y: Scalar(v.y)) 84 | } 85 | 86 | init(_ v: CGSize) { 87 | self.init(x: Scalar(v.width), y: Scalar(v.height)) 88 | } 89 | 90 | init(_ v: CGVector) { 91 | self.init(x: Scalar(v.dx), y: Scalar(v.dy)) 92 | } 93 | } 94 | 95 | public extension Matrix3 { 96 | init(_ m: CGAffineTransform) { 97 | self.init( 98 | m11: Scalar(m.a), m12: Scalar(m.b), m13: 0, 99 | m21: Scalar(m.c), m22: Scalar(m.d), m23: 0, 100 | m31: Scalar(m.tx), m32: Scalar(m.ty), m33: 1 101 | ) 102 | } 103 | } 104 | 105 | public extension Matrix4 { 106 | init(_ m: CATransform3D) { 107 | self.init( 108 | m11: Scalar(m.m11), m12: Scalar(m.m12), m13: Scalar(m.m13), m14: Scalar(m.m14), 109 | m21: Scalar(m.m21), m22: Scalar(m.m22), m23: Scalar(m.m23), m24: Scalar(m.m24), 110 | m31: Scalar(m.m31), m32: Scalar(m.m32), m33: Scalar(m.m33), m34: Scalar(m.m34), 111 | m41: Scalar(m.m41), m42: Scalar(m.m42), m43: Scalar(m.m43), m44: Scalar(m.m44) 112 | ) 113 | } 114 | } 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /VectorMath/VectorMath+SceneKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VectorMath+SceneKit.swift 3 | // VectorMath 4 | // 5 | // Version 0.4.0 6 | // 7 | // Created by Nick Lockwood on 24/11/2014. 8 | // Copyright (c) 2014 Nick Lockwood. All rights reserved. 9 | // 10 | // Distributed under the permissive MIT License 11 | // Get the latest version from here: 12 | // 13 | // https://github.com/nicklockwood/VectorMath 14 | /// 15 | // Permission is hereby granted, free of charge, to any person obtaining a copy 16 | // of this software and associated documentation files (the "Software"), to deal 17 | // in the Software without restriction, including without limitation the rights 18 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | // copies of the Software, and to permit persons to whom the Software is 20 | // furnished to do so, subject to the following conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be included in all 23 | // copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | // SOFTWARE. 32 | // 33 | 34 | #if canImport(SceneKit) 35 | 36 | #if os(iOS) || os(tvOS) 37 | typealias SCNFloat = Float 38 | #else 39 | typealias SCNFloat = CGFloat 40 | #endif 41 | 42 | import SceneKit 43 | 44 | // MARK: SceneKit extensions 45 | 46 | public extension SCNVector3 { 47 | init(_ v: Vector3) { 48 | self.init(x: SCNFloat(v.x), y: SCNFloat(v.y), z: SCNFloat(v.z)) 49 | } 50 | } 51 | 52 | public extension SCNVector4 { 53 | init(_ v: Vector4) { 54 | self.init(x: SCNFloat(v.x), y: SCNFloat(v.y), z: SCNFloat(v.z), w: SCNFloat(v.w)) 55 | } 56 | } 57 | 58 | #if os(iOS) // SCNMatrix4 = CATransform3D on Mac 59 | 60 | public extension SCNMatrix4 { 61 | init(_ m: Matrix4) { 62 | self.init( 63 | m11: SCNFloat(m.m11), m12: SCNFloat(m.m12), m13: SCNFloat(m.m13), m14: SCNFloat(m.m14), 64 | m21: SCNFloat(m.m21), m22: SCNFloat(m.m22), m23: SCNFloat(m.m23), m24: SCNFloat(m.m24), 65 | m31: SCNFloat(m.m31), m32: SCNFloat(m.m32), m33: SCNFloat(m.m33), m34: SCNFloat(m.m34), 66 | m41: SCNFloat(m.m41), m42: SCNFloat(m.m42), m43: SCNFloat(m.m43), m44: SCNFloat(m.m44) 67 | ) 68 | } 69 | } 70 | 71 | #endif 72 | 73 | public extension SCNQuaternion { 74 | init(_ q: Quaternion) { 75 | self.init(x: SCNFloat(q.x), y: SCNFloat(q.y), z: SCNFloat(q.z), w: SCNFloat(q.w)) 76 | } 77 | } 78 | 79 | // MARK: VectorMath extensions 80 | 81 | public extension Vector3 { 82 | init(_ v: SCNVector3) { 83 | self.init(x: Scalar(v.x), y: Scalar(v.y), z: Scalar(v.z)) 84 | } 85 | } 86 | 87 | public extension Vector4 { 88 | init(_ v: SCNVector4) { 89 | self.init(x: Scalar(v.x), y: Scalar(v.y), z: Scalar(v.z), w: Scalar(v.w)) 90 | } 91 | } 92 | 93 | #if os(iOS) // SCNMatrix4 = CATransform3D on Mac 94 | 95 | public extension Matrix4 { 96 | init(_ m: SCNMatrix4) { 97 | self.init( 98 | m11: Scalar(m.m11), m12: Scalar(m.m12), m13: Scalar(m.m13), m14: Scalar(m.m14), 99 | m21: Scalar(m.m21), m22: Scalar(m.m22), m23: Scalar(m.m23), m24: Scalar(m.m24), 100 | m31: Scalar(m.m31), m32: Scalar(m.m32), m33: Scalar(m.m33), m34: Scalar(m.m34), 101 | m41: Scalar(m.m41), m42: Scalar(m.m42), m43: Scalar(m.m43), m44: Scalar(m.m44) 102 | ) 103 | } 104 | } 105 | 106 | #endif 107 | 108 | public extension Quaternion { 109 | init(_ q: SCNQuaternion) { 110 | self.init(x: Scalar(q.x), y: Scalar(q.y), z: Scalar(q.z), w: Scalar(q.w)) 111 | } 112 | } 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /VectorMath/VectorMath.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VectorMath.swift 3 | // VectorMath 4 | // 5 | // Version 0.4.0 6 | // 7 | // Created by Nick Lockwood on 24/11/2014. 8 | // Copyright (c) 2014 Nick Lockwood. All rights reserved. 9 | // 10 | // Distributed under the permissive MIT License 11 | // Get the latest version from here: 12 | // 13 | // https://github.com/nicklockwood/VectorMath 14 | // 15 | // Permission is hereby granted, free of charge, to any person obtaining a copy 16 | // of this software and associated documentation files (the "Software"), to deal 17 | // in the Software without restriction, including without limitation the rights 18 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | // copies of the Software, and to permit persons to whom the Software is 20 | // furnished to do so, subject to the following conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be included in all 23 | // copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | // SOFTWARE. 32 | // 33 | 34 | import Foundation 35 | 36 | // MARK: Types 37 | 38 | public typealias Scalar = Float 39 | 40 | public struct Vector2: Hashable { 41 | public var x: Scalar 42 | public var y: Scalar 43 | } 44 | 45 | public struct Vector3: Hashable { 46 | public var x: Scalar 47 | public var y: Scalar 48 | public var z: Scalar 49 | } 50 | 51 | public struct Vector4: Hashable { 52 | public var x: Scalar 53 | public var y: Scalar 54 | public var z: Scalar 55 | public var w: Scalar 56 | } 57 | 58 | public struct Matrix3: Hashable { 59 | public var m11: Scalar 60 | public var m12: Scalar 61 | public var m13: Scalar 62 | public var m21: Scalar 63 | public var m22: Scalar 64 | public var m23: Scalar 65 | public var m31: Scalar 66 | public var m32: Scalar 67 | public var m33: Scalar 68 | } 69 | 70 | public struct Matrix4: Hashable { 71 | public var m11: Scalar 72 | public var m12: Scalar 73 | public var m13: Scalar 74 | public var m14: Scalar 75 | public var m21: Scalar 76 | public var m22: Scalar 77 | public var m23: Scalar 78 | public var m24: Scalar 79 | public var m31: Scalar 80 | public var m32: Scalar 81 | public var m33: Scalar 82 | public var m34: Scalar 83 | public var m41: Scalar 84 | public var m42: Scalar 85 | public var m43: Scalar 86 | public var m44: Scalar 87 | } 88 | 89 | public struct Quaternion: Hashable { 90 | public var x: Scalar 91 | public var y: Scalar 92 | public var z: Scalar 93 | public var w: Scalar 94 | } 95 | 96 | // MARK: Scalar 97 | 98 | public extension Scalar { 99 | static let halfPi = pi / 2 100 | static let quarterPi = pi / 4 101 | static let twoPi = pi * 2 102 | static let degreesPerRadian = 180 / pi 103 | static let radiansPerDegree = pi / 180 104 | static let epsilon: Scalar = 0.0001 105 | 106 | static func ~= (lhs: Scalar, rhs: Scalar) -> Bool { 107 | return Swift.abs(lhs - rhs) < .epsilon 108 | } 109 | 110 | fileprivate var sign: Scalar { 111 | return self > 0 ? 1 : -1 112 | } 113 | } 114 | 115 | // MARK: Vector2 116 | 117 | public extension Vector2 { 118 | static let zero = Vector2(0, 0) 119 | static let x = Vector2(1, 0) 120 | static let y = Vector2(0, 1) 121 | 122 | var lengthSquared: Scalar { 123 | return x * x + y * y 124 | } 125 | 126 | var length: Scalar { 127 | return sqrt(lengthSquared) 128 | } 129 | 130 | var inverse: Vector2 { 131 | return -self 132 | } 133 | 134 | init(_ x: Scalar, _ y: Scalar) { 135 | self.init(x: x, y: y) 136 | } 137 | 138 | init(_ v: [Scalar]) { 139 | assert(v.count == 2, "array must contain 2 elements, contained \(v.count)") 140 | self.init(v[0], v[1]) 141 | } 142 | 143 | func toArray() -> [Scalar] { 144 | return [x, y] 145 | } 146 | 147 | func dot(_ v: Vector2) -> Scalar { 148 | return x * v.x + y * v.y 149 | } 150 | 151 | func cross(_ v: Vector2) -> Scalar { 152 | return x * v.y - y * v.x 153 | } 154 | 155 | func normalized() -> Vector2 { 156 | let lengthSquared = self.lengthSquared 157 | if lengthSquared ~= 0 || lengthSquared ~= 1 { 158 | return self 159 | } 160 | return self / sqrt(lengthSquared) 161 | } 162 | 163 | func rotated(by radians: Scalar) -> Vector2 { 164 | let cs = cos(radians) 165 | let sn = sin(radians) 166 | return Vector2(x * cs - y * sn, x * sn + y * cs) 167 | } 168 | 169 | func rotated(by radians: Scalar, around pivot: Vector2) -> Vector2 { 170 | return (self - pivot).rotated(by: radians) + pivot 171 | } 172 | 173 | func angle(with v: Vector2) -> Scalar { 174 | if self == v { 175 | return 0 176 | } 177 | 178 | let t1 = normalized() 179 | let t2 = v.normalized() 180 | let cross = t1.cross(t2) 181 | let dot = max(-1, min(1, t1.dot(t2))) 182 | 183 | return atan2(cross, dot) 184 | } 185 | 186 | func interpolated(with v: Vector2, by t: Scalar) -> Vector2 { 187 | return self + (v - self) * t 188 | } 189 | 190 | static prefix func - (v: Vector2) -> Vector2 { 191 | return Vector2(-v.x, -v.y) 192 | } 193 | 194 | static func + (lhs: Vector2, rhs: Vector2) -> Vector2 { 195 | return Vector2(lhs.x + rhs.x, lhs.y + rhs.y) 196 | } 197 | 198 | static func - (lhs: Vector2, rhs: Vector2) -> Vector2 { 199 | return Vector2(lhs.x - rhs.x, lhs.y - rhs.y) 200 | } 201 | 202 | static func * (lhs: Vector2, rhs: Vector2) -> Vector2 { 203 | return Vector2(lhs.x * rhs.x, lhs.y * rhs.y) 204 | } 205 | 206 | static func * (lhs: Vector2, rhs: Scalar) -> Vector2 { 207 | return Vector2(lhs.x * rhs, lhs.y * rhs) 208 | } 209 | 210 | static func * (lhs: Vector2, rhs: Matrix3) -> Vector2 { 211 | return Vector2( 212 | lhs.x * rhs.m11 + lhs.y * rhs.m21 + rhs.m31, 213 | lhs.x * rhs.m12 + lhs.y * rhs.m22 + rhs.m32 214 | ) 215 | } 216 | 217 | static func / (lhs: Vector2, rhs: Vector2) -> Vector2 { 218 | return Vector2(lhs.x / rhs.x, lhs.y / rhs.y) 219 | } 220 | 221 | static func / (lhs: Vector2, rhs: Scalar) -> Vector2 { 222 | return Vector2(lhs.x / rhs, lhs.y / rhs) 223 | } 224 | 225 | static func ~= (lhs: Vector2, rhs: Vector2) -> Bool { 226 | return lhs.x ~= rhs.x && lhs.y ~= rhs.y 227 | } 228 | } 229 | 230 | // MARK: Vector3 231 | 232 | public extension Vector3 { 233 | static let zero = Vector3(0, 0, 0) 234 | static let x = Vector3(1, 0, 0) 235 | static let y = Vector3(0, 1, 0) 236 | static let z = Vector3(0, 0, 1) 237 | 238 | var lengthSquared: Scalar { 239 | return x * x + y * y + z * z 240 | } 241 | 242 | var length: Scalar { 243 | return sqrt(lengthSquared) 244 | } 245 | 246 | var inverse: Vector3 { 247 | return -self 248 | } 249 | 250 | var xy: Vector2 { 251 | get { 252 | return Vector2(x, y) 253 | } 254 | set(v) { 255 | x = v.x 256 | y = v.y 257 | } 258 | } 259 | 260 | var xz: Vector2 { 261 | get { 262 | return Vector2(x, z) 263 | } 264 | set(v) { 265 | x = v.x 266 | z = v.y 267 | } 268 | } 269 | 270 | var yz: Vector2 { 271 | get { 272 | return Vector2(y, z) 273 | } 274 | set(v) { 275 | y = v.x 276 | z = v.y 277 | } 278 | } 279 | 280 | init(_ x: Scalar, _ y: Scalar, _ z: Scalar) { 281 | self.init(x: x, y: y, z: z) 282 | } 283 | 284 | init(_ v: [Scalar]) { 285 | assert(v.count == 3, "array must contain 3 elements, contained \(v.count)") 286 | self.init(v[0], v[1], v[2]) 287 | } 288 | 289 | func toArray() -> [Scalar] { 290 | return [x, y, z] 291 | } 292 | 293 | func dot(_ v: Vector3) -> Scalar { 294 | return x * v.x + y * v.y + z * v.z 295 | } 296 | 297 | func cross(_ v: Vector3) -> Vector3 { 298 | return Vector3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x) 299 | } 300 | 301 | func normalized() -> Vector3 { 302 | let lengthSquared = self.lengthSquared 303 | if lengthSquared ~= 0 || lengthSquared ~= 1 { 304 | return self 305 | } 306 | return self / sqrt(lengthSquared) 307 | } 308 | 309 | func interpolated(with v: Vector3, by t: Scalar) -> Vector3 { 310 | return self + (v - self) * t 311 | } 312 | 313 | static prefix func - (v: Vector3) -> Vector3 { 314 | return Vector3(-v.x, -v.y, -v.z) 315 | } 316 | 317 | static func + (lhs: Vector3, rhs: Vector3) -> Vector3 { 318 | return Vector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z) 319 | } 320 | 321 | static func - (lhs: Vector3, rhs: Vector3) -> Vector3 { 322 | return Vector3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z) 323 | } 324 | 325 | static func * (lhs: Vector3, rhs: Vector3) -> Vector3 { 326 | return Vector3(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z) 327 | } 328 | 329 | static func * (lhs: Vector3, rhs: Scalar) -> Vector3 { 330 | return Vector3(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs) 331 | } 332 | 333 | static func * (lhs: Vector3, rhs: Matrix3) -> Vector3 { 334 | return Vector3( 335 | lhs.x * rhs.m11 + lhs.y * rhs.m21 + lhs.z * rhs.m31, 336 | lhs.x * rhs.m12 + lhs.y * rhs.m22 + lhs.z * rhs.m32, 337 | lhs.x * rhs.m13 + lhs.y * rhs.m23 + lhs.z * rhs.m33 338 | ) 339 | } 340 | 341 | static func * (lhs: Vector3, rhs: Matrix4) -> Vector3 { 342 | return Vector3( 343 | lhs.x * rhs.m11 + lhs.y * rhs.m21 + lhs.z * rhs.m31 + rhs.m41, 344 | lhs.x * rhs.m12 + lhs.y * rhs.m22 + lhs.z * rhs.m32 + rhs.m42, 345 | lhs.x * rhs.m13 + lhs.y * rhs.m23 + lhs.z * rhs.m33 + rhs.m43 346 | ) 347 | } 348 | 349 | static func * (v: Vector3, q: Quaternion) -> Vector3 { 350 | let qv = q.xyz 351 | let uv = qv.cross(v) 352 | let uuv = qv.cross(uv) 353 | return v + (uv * 2 * q.w) + (uuv * 2) 354 | } 355 | 356 | static func / (lhs: Vector3, rhs: Vector3) -> Vector3 { 357 | return Vector3(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z) 358 | } 359 | 360 | static func / (lhs: Vector3, rhs: Scalar) -> Vector3 { 361 | return Vector3(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs) 362 | } 363 | 364 | static func ~= (lhs: Vector3, rhs: Vector3) -> Bool { 365 | return lhs.x ~= rhs.x && lhs.y ~= rhs.y && lhs.z ~= rhs.z 366 | } 367 | } 368 | 369 | // MARK: Vector4 370 | 371 | public extension Vector4 { 372 | static let zero = Vector4(0, 0, 0, 0) 373 | static let x = Vector4(1, 0, 0, 0) 374 | static let y = Vector4(0, 1, 0, 0) 375 | static let z = Vector4(0, 0, 1, 0) 376 | static let w = Vector4(0, 0, 0, 1) 377 | 378 | var lengthSquared: Scalar { 379 | return x * x + y * y + z * z + w * w 380 | } 381 | 382 | var length: Scalar { 383 | return sqrt(lengthSquared) 384 | } 385 | 386 | var inverse: Vector4 { 387 | return -self 388 | } 389 | 390 | var xyz: Vector3 { 391 | get { 392 | return Vector3(x, y, z) 393 | } 394 | set(v) { 395 | x = v.x 396 | y = v.y 397 | z = v.z 398 | } 399 | } 400 | 401 | var xy: Vector2 { 402 | get { 403 | return Vector2(x, y) 404 | } 405 | set(v) { 406 | x = v.x 407 | y = v.y 408 | } 409 | } 410 | 411 | var xz: Vector2 { 412 | get { 413 | return Vector2(x, z) 414 | } 415 | set(v) { 416 | x = v.x 417 | z = v.y 418 | } 419 | } 420 | 421 | var yz: Vector2 { 422 | get { 423 | return Vector2(y, z) 424 | } 425 | set(v) { 426 | y = v.x 427 | z = v.y 428 | } 429 | } 430 | 431 | init(_ x: Scalar, _ y: Scalar, _ z: Scalar, _ w: Scalar) { 432 | self.init(x: x, y: y, z: z, w: w) 433 | } 434 | 435 | init(_ v: [Scalar]) { 436 | assert(v.count == 4, "array must contain 4 elements, contained \(v.count)") 437 | self.init(v[0], v[1], v[2], v[3]) 438 | } 439 | 440 | init(_ v: Vector3, w: Scalar) { 441 | self.init(v.x, v.y, v.z, w) 442 | } 443 | 444 | func toArray() -> [Scalar] { 445 | return [x, y, z, w] 446 | } 447 | 448 | func toVector3() -> Vector3 { 449 | if w ~= 0 { 450 | return xyz 451 | } else { 452 | return xyz / w 453 | } 454 | } 455 | 456 | func dot(_ v: Vector4) -> Scalar { 457 | return x * v.x + y * v.y + z * v.z + w * v.w 458 | } 459 | 460 | func normalized() -> Vector4 { 461 | let lengthSquared = self.lengthSquared 462 | if lengthSquared ~= 0 || lengthSquared ~= 1 { 463 | return self 464 | } 465 | return self / sqrt(lengthSquared) 466 | } 467 | 468 | func interpolated(with v: Vector4, by t: Scalar) -> Vector4 { 469 | return self + (v - self) * t 470 | } 471 | 472 | static prefix func - (v: Vector4) -> Vector4 { 473 | return Vector4(-v.x, -v.y, -v.z, -v.w) 474 | } 475 | 476 | static func + (lhs: Vector4, rhs: Vector4) -> Vector4 { 477 | return Vector4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w) 478 | } 479 | 480 | static func - (lhs: Vector4, rhs: Vector4) -> Vector4 { 481 | return Vector4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w) 482 | } 483 | 484 | static func * (lhs: Vector4, rhs: Vector4) -> Vector4 { 485 | return Vector4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w) 486 | } 487 | 488 | static func * (lhs: Vector4, rhs: Scalar) -> Vector4 { 489 | return Vector4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs) 490 | } 491 | 492 | static func * (lhs: Vector4, rhs: Matrix4) -> Vector4 { 493 | return Vector4( 494 | lhs.x * rhs.m11 + lhs.y * rhs.m21 + lhs.z * rhs.m31 + lhs.w * rhs.m41, 495 | lhs.x * rhs.m12 + lhs.y * rhs.m22 + lhs.z * rhs.m32 + lhs.w * rhs.m42, 496 | lhs.x * rhs.m13 + lhs.y * rhs.m23 + lhs.z * rhs.m33 + lhs.w * rhs.m43, 497 | lhs.x * rhs.m14 + lhs.y * rhs.m24 + lhs.z * rhs.m34 + lhs.w * rhs.m44 498 | ) 499 | } 500 | 501 | static func / (lhs: Vector4, rhs: Vector4) -> Vector4 { 502 | return Vector4(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w) 503 | } 504 | 505 | static func / (lhs: Vector4, rhs: Scalar) -> Vector4 { 506 | return Vector4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs) 507 | } 508 | 509 | static func ~= (lhs: Vector4, rhs: Vector4) -> Bool { 510 | return lhs.x ~= rhs.x && lhs.y ~= rhs.y && lhs.z ~= rhs.z && lhs.w ~= rhs.w 511 | } 512 | } 513 | 514 | // MARK: Matrix3 515 | 516 | public extension Matrix3 { 517 | static let identity = Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1) 518 | 519 | init(_ m11: Scalar, _ m12: Scalar, _ m13: Scalar, 520 | _ m21: Scalar, _ m22: Scalar, _ m23: Scalar, 521 | _ m31: Scalar, _ m32: Scalar, _ m33: Scalar) { 522 | self.m11 = m11 // 0 523 | self.m12 = m12 // 1 524 | self.m13 = m13 // 2 525 | self.m21 = m21 // 3 526 | self.m22 = m22 // 4 527 | self.m23 = m23 // 5 528 | self.m31 = m31 // 6 529 | self.m32 = m32 // 7 530 | self.m33 = m33 // 8 531 | } 532 | 533 | init(scale: Vector2) { 534 | self.init( 535 | scale.x, 0, 0, 536 | 0, scale.y, 0, 537 | 0, 0, 1 538 | ) 539 | } 540 | 541 | init(translation: Vector2) { 542 | self.init( 543 | 1, 0, 0, 544 | 0, 1, 0, 545 | translation.x, translation.y, 1 546 | ) 547 | } 548 | 549 | init(rotation radians: Scalar) { 550 | let cs = cos(radians) 551 | let sn = sin(radians) 552 | self.init( 553 | cs, sn, 0, 554 | -sn, cs, 0, 555 | 0, 0, 1 556 | ) 557 | } 558 | 559 | init(_ m: [Scalar]) { 560 | assert(m.count == 9, "array must contain 9 elements, contained \(m.count)") 561 | self.init(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]) 562 | } 563 | 564 | func toArray() -> [Scalar] { 565 | return [m11, m12, m13, m21, m22, m23, m31, m32, m33] 566 | } 567 | 568 | var adjugate: Matrix3 { 569 | return Matrix3( 570 | m22 * m33 - m23 * m32, 571 | m13 * m32 - m12 * m33, 572 | m12 * m23 - m13 * m22, 573 | m23 * m31 - m21 * m33, 574 | m11 * m33 - m13 * m31, 575 | m13 * m21 - m11 * m23, 576 | m21 * m32 - m22 * m31, 577 | m12 * m31 - m11 * m32, 578 | m11 * m22 - m12 * m21 579 | ) 580 | } 581 | 582 | var determinant: Scalar { 583 | return (m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32) 584 | - (m13 * m22 * m31 + m11 * m23 * m32 + m12 * m21 * m33) 585 | } 586 | 587 | var transpose: Matrix3 { 588 | return Matrix3(m11, m21, m31, m12, m22, m32, m13, m23, m33) 589 | } 590 | 591 | var inverse: Matrix3 { 592 | return adjugate * (1 / determinant) 593 | } 594 | 595 | func interpolated(with m: Matrix3, by t: Scalar) -> Matrix3 { 596 | return Matrix3( 597 | m11 + (m.m11 - m11) * t, 598 | m12 + (m.m12 - m12) * t, 599 | m13 + (m.m13 - m13) * t, 600 | m21 + (m.m21 - m21) * t, 601 | m22 + (m.m22 - m22) * t, 602 | m23 + (m.m23 - m23) * t, 603 | m31 + (m.m31 - m31) * t, 604 | m32 + (m.m32 - m32) * t, 605 | m33 + (m.m33 - m33) * t 606 | ) 607 | } 608 | 609 | static prefix func - (m: Matrix3) -> Matrix3 { 610 | return m.inverse 611 | } 612 | 613 | static func * (lhs: Matrix3, rhs: Matrix3) -> Matrix3 { 614 | return Matrix3( 615 | lhs.m11 * rhs.m11 + lhs.m21 * rhs.m12 + lhs.m31 * rhs.m13, 616 | lhs.m12 * rhs.m11 + lhs.m22 * rhs.m12 + lhs.m32 * rhs.m13, 617 | lhs.m13 * rhs.m11 + lhs.m23 * rhs.m12 + lhs.m33 * rhs.m13, 618 | lhs.m11 * rhs.m21 + lhs.m21 * rhs.m22 + lhs.m31 * rhs.m23, 619 | lhs.m12 * rhs.m21 + lhs.m22 * rhs.m22 + lhs.m32 * rhs.m23, 620 | lhs.m13 * rhs.m21 + lhs.m23 * rhs.m22 + lhs.m33 * rhs.m23, 621 | lhs.m11 * rhs.m31 + lhs.m21 * rhs.m32 + lhs.m31 * rhs.m33, 622 | lhs.m12 * rhs.m31 + lhs.m22 * rhs.m32 + lhs.m32 * rhs.m33, 623 | lhs.m13 * rhs.m31 + lhs.m23 * rhs.m32 + lhs.m33 * rhs.m33 624 | ) 625 | } 626 | 627 | static func * (lhs: Matrix3, rhs: Vector2) -> Vector2 { 628 | return rhs * lhs 629 | } 630 | 631 | static func * (lhs: Matrix3, rhs: Vector3) -> Vector3 { 632 | return rhs * lhs 633 | } 634 | 635 | static func * (lhs: Matrix3, rhs: Scalar) -> Matrix3 { 636 | return Matrix3( 637 | lhs.m11 * rhs, lhs.m12 * rhs, lhs.m13 * rhs, 638 | lhs.m21 * rhs, lhs.m22 * rhs, lhs.m23 * rhs, 639 | lhs.m31 * rhs, lhs.m32 * rhs, lhs.m33 * rhs 640 | ) 641 | } 642 | 643 | static func ~= (lhs: Matrix3, rhs: Matrix3) -> Bool { 644 | if !(lhs.m11 ~= rhs.m11) { return false } 645 | if !(lhs.m12 ~= rhs.m12) { return false } 646 | if !(lhs.m13 ~= rhs.m13) { return false } 647 | if !(lhs.m21 ~= rhs.m21) { return false } 648 | if !(lhs.m22 ~= rhs.m22) { return false } 649 | if !(lhs.m23 ~= rhs.m23) { return false } 650 | if !(lhs.m31 ~= rhs.m31) { return false } 651 | if !(lhs.m32 ~= rhs.m32) { return false } 652 | if !(lhs.m33 ~= rhs.m33) { return false } 653 | return true 654 | } 655 | } 656 | 657 | // MARK: Matrix4 658 | 659 | public extension Matrix4 { 660 | static let identity = Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) 661 | 662 | init(_ m11: Scalar, _ m12: Scalar, _ m13: Scalar, _ m14: Scalar, 663 | _ m21: Scalar, _ m22: Scalar, _ m23: Scalar, _ m24: Scalar, 664 | _ m31: Scalar, _ m32: Scalar, _ m33: Scalar, _ m34: Scalar, 665 | _ m41: Scalar, _ m42: Scalar, _ m43: Scalar, _ m44: Scalar) { 666 | self.m11 = m11 // 0 667 | self.m12 = m12 // 1 668 | self.m13 = m13 // 2 669 | self.m14 = m14 // 3 670 | self.m21 = m21 // 4 671 | self.m22 = m22 // 5 672 | self.m23 = m23 // 6 673 | self.m24 = m24 // 7 674 | self.m31 = m31 // 8 675 | self.m32 = m32 // 9 676 | self.m33 = m33 // 10 677 | self.m34 = m34 // 11 678 | self.m41 = m41 // 12 679 | self.m42 = m42 // 13 680 | self.m43 = m43 // 14 681 | self.m44 = m44 // 15 682 | } 683 | 684 | init(scale s: Vector3) { 685 | self.init( 686 | s.x, 0, 0, 0, 687 | 0, s.y, 0, 0, 688 | 0, 0, s.z, 0, 689 | 0, 0, 0, 1 690 | ) 691 | } 692 | 693 | init(translation t: Vector3) { 694 | self.init( 695 | 1, 0, 0, 0, 696 | 0, 1, 0, 0, 697 | 0, 0, 1, 0, 698 | t.x, t.y, t.z, 1 699 | ) 700 | } 701 | 702 | init(rotation axisAngle: Vector4) { 703 | self.init(quaternion: Quaternion(axisAngle: axisAngle)) 704 | } 705 | 706 | init(quaternion q: Quaternion) { 707 | self.init( 708 | 1 - 2 * (q.y * q.y + q.z * q.z), 2 * (q.x * q.y + q.z * q.w), 2 * (q.x * q.z - q.y * q.w), 0, 709 | 2 * (q.x * q.y - q.z * q.w), 1 - 2 * (q.x * q.x + q.z * q.z), 2 * (q.y * q.z + q.x * q.w), 0, 710 | 2 * (q.x * q.z + q.y * q.w), 2 * (q.y * q.z - q.x * q.w), 1 - 2 * (q.x * q.x + q.y * q.y), 0, 711 | 0, 0, 0, 1 712 | ) 713 | } 714 | 715 | init(fovx: Scalar, fovy: Scalar, near: Scalar, far: Scalar) { 716 | self.init(fovy: fovy, aspect: fovx / fovy, near: near, far: far) 717 | } 718 | 719 | init(fovx: Scalar, aspect: Scalar, near: Scalar, far: Scalar) { 720 | self.init(fovy: fovx / aspect, aspect: aspect, near: near, far: far) 721 | } 722 | 723 | init(fovy: Scalar, aspect: Scalar, near: Scalar, far: Scalar) { 724 | let dz = far - near 725 | 726 | assert(dz > 0, "far value must be greater than near") 727 | assert(fovy > 0, "field of view must be nonzero and positive") 728 | assert(aspect > 0, "aspect ratio must be nonzero and positive") 729 | 730 | let r = fovy / 2 731 | let cotangent = cos(r) / sin(r) 732 | 733 | self.init( 734 | cotangent / aspect, 0, 0, 0, 735 | 0, cotangent, 0, 0, 736 | 0, 0, -(far + near) / dz, -1, 737 | 0, 0, -2 * near * far / dz, 0 738 | ) 739 | } 740 | 741 | init(top: Scalar, right: Scalar, bottom: Scalar, left: Scalar, near: Scalar, far: Scalar) { 742 | let dx = right - left 743 | let dy = top - bottom 744 | let dz = far - near 745 | 746 | self.init( 747 | 2 / dx, 0, 0, 0, 748 | 0, 2 / dy, 0, 0, 749 | 0, 0, -2 / dz, 0, 750 | -(right + left) / dx, -(top + bottom) / dy, -(far + near) / dz, 1 751 | ) 752 | } 753 | 754 | init(_ m: [Scalar]) { 755 | assert(m.count == 16, "array must contain 16 elements, contained \(m.count)") 756 | 757 | m11 = m[0] 758 | m12 = m[1] 759 | m13 = m[2] 760 | m14 = m[3] 761 | m21 = m[4] 762 | m22 = m[5] 763 | m23 = m[6] 764 | m24 = m[7] 765 | m31 = m[8] 766 | m32 = m[9] 767 | m33 = m[10] 768 | m34 = m[11] 769 | m41 = m[12] 770 | m42 = m[13] 771 | m43 = m[14] 772 | m44 = m[15] 773 | } 774 | 775 | func toArray() -> [Scalar] { 776 | return [m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44] 777 | } 778 | 779 | var adjugate: Matrix4 { 780 | var m = Matrix4.identity 781 | 782 | m.m11 = m22 * m33 * m44 - m22 * m34 * m43 783 | m.m11 += -m32 * m23 * m44 + m32 * m24 * m43 784 | m.m11 += m42 * m23 * m34 - m42 * m24 * m33 785 | 786 | m.m21 = -m21 * m33 * m44 + m21 * m34 * m43 787 | m.m21 += m31 * m23 * m44 - m31 * m24 * m43 788 | m.m21 += -m41 * m23 * m34 + m41 * m24 * m33 789 | 790 | m.m31 = m21 * m32 * m44 - m21 * m34 * m42 791 | m.m31 += -m31 * m22 * m44 + m31 * m24 * m42 792 | m.m31 += m41 * m22 * m34 - m41 * m24 * m32 793 | 794 | m.m41 = -m21 * m32 * m43 + m21 * m33 * m42 795 | m.m41 += m31 * m22 * m43 - m31 * m23 * m42 796 | m.m41 += -m41 * m22 * m33 + m41 * m23 * m32 797 | 798 | m.m12 = -m12 * m33 * m44 + m12 * m34 * m43 799 | m.m12 += m32 * m13 * m44 - m32 * m14 * m43 800 | m.m12 += -m42 * m13 * m34 + m42 * m14 * m33 801 | 802 | m.m22 = m11 * m33 * m44 - m11 * m34 * m43 803 | m.m22 += -m31 * m13 * m44 + m31 * m14 * m43 804 | m.m22 += m41 * m13 * m34 - m41 * m14 * m33 805 | 806 | m.m32 = -m11 * m32 * m44 + m11 * m34 * m42 807 | m.m32 += m31 * m12 * m44 - m31 * m14 * m42 808 | m.m32 += -m41 * m12 * m34 + m41 * m14 * m32 809 | 810 | m.m42 = m11 * m32 * m43 - m11 * m33 * m42 811 | m.m42 += -m31 * m12 * m43 + m31 * m13 * m42 812 | m.m42 += m41 * m12 * m33 - m41 * m13 * m32 813 | 814 | m.m13 = m12 * m23 * m44 - m12 * m24 * m43 815 | m.m13 += -m22 * m13 * m44 + m22 * m14 * m43 816 | m.m13 += m42 * m13 * m24 - m42 * m14 * m23 817 | 818 | m.m23 = -m11 * m23 * m44 + m11 * m24 * m43 819 | m.m23 += m21 * m13 * m44 - m21 * m14 * m43 820 | m.m23 += -m41 * m13 * m24 + m41 * m14 * m23 821 | 822 | m.m33 = m11 * m22 * m44 - m11 * m24 * m42 823 | m.m33 += -m21 * m12 * m44 + m21 * m14 * m42 824 | m.m33 += m41 * m12 * m24 - m41 * m14 * m22 825 | 826 | m.m43 = -m11 * m22 * m43 + m11 * m23 * m42 827 | m.m43 += m21 * m12 * m43 - m21 * m13 * m42 828 | m.m43 += -m41 * m12 * m23 + m41 * m13 * m22 829 | 830 | m.m14 = -m12 * m23 * m34 + m12 * m24 * m33 831 | m.m14 += m22 * m13 * m34 - m22 * m14 * m33 832 | m.m14 += -m32 * m13 * m24 + m32 * m14 * m23 833 | 834 | m.m24 = m11 * m23 * m34 - m11 * m24 * m33 835 | m.m24 += -m21 * m13 * m34 + m21 * m14 * m33 836 | m.m24 += m31 * m13 * m24 - m31 * m14 * m23 837 | 838 | m.m34 = -m11 * m22 * m34 + m11 * m24 * m32 839 | m.m34 += m21 * m12 * m34 - m21 * m14 * m32 840 | m.m34 += -m31 * m12 * m24 + m31 * m14 * m22 841 | 842 | m.m44 = m11 * m22 * m33 - m11 * m23 * m32 843 | m.m44 += -m21 * m12 * m33 + m21 * m13 * m32 844 | m.m44 += m31 * m12 * m23 - m31 * m13 * m22 845 | 846 | return m 847 | } 848 | 849 | private func determinant(forAdjugate m: Matrix4) -> Scalar { 850 | return m11 * m.m11 + m12 * m.m21 + m13 * m.m31 + m14 * m.m41 851 | } 852 | 853 | var determinant: Scalar { 854 | return determinant(forAdjugate: adjugate) 855 | } 856 | 857 | var transpose: Matrix4 { 858 | return Matrix4( 859 | m11, m21, m31, m41, 860 | m12, m22, m32, m42, 861 | m13, m23, m33, m43, 862 | m14, m24, m34, m44 863 | ) 864 | } 865 | 866 | var inverse: Matrix4 { 867 | let adjugate = self.adjugate // avoid recalculating 868 | return adjugate * (1 / determinant(forAdjugate: adjugate)) 869 | } 870 | 871 | static prefix func - (m: Matrix4) -> Matrix4 { 872 | return m.inverse 873 | } 874 | 875 | static func * (lhs: Matrix4, rhs: Matrix4) -> Matrix4 { 876 | var m = Matrix4.identity 877 | 878 | m.m11 = lhs.m11 * rhs.m11 + lhs.m21 * rhs.m12 879 | m.m11 += lhs.m31 * rhs.m13 + lhs.m41 * rhs.m14 880 | 881 | m.m12 = lhs.m12 * rhs.m11 + lhs.m22 * rhs.m12 882 | m.m12 += lhs.m32 * rhs.m13 + lhs.m42 * rhs.m14 883 | 884 | m.m13 = lhs.m13 * rhs.m11 + lhs.m23 * rhs.m12 885 | m.m13 += lhs.m33 * rhs.m13 + lhs.m43 * rhs.m14 886 | 887 | m.m14 = lhs.m14 * rhs.m11 + lhs.m24 * rhs.m12 888 | m.m14 += lhs.m34 * rhs.m13 + lhs.m44 * rhs.m14 889 | 890 | m.m21 = lhs.m11 * rhs.m21 + lhs.m21 * rhs.m22 891 | m.m21 += lhs.m31 * rhs.m23 + lhs.m41 * rhs.m24 892 | 893 | m.m22 = lhs.m12 * rhs.m21 + lhs.m22 * rhs.m22 894 | m.m22 += lhs.m32 * rhs.m23 + lhs.m42 * rhs.m24 895 | 896 | m.m23 = lhs.m13 * rhs.m21 + lhs.m23 * rhs.m22 897 | m.m23 += lhs.m33 * rhs.m23 + lhs.m43 * rhs.m24 898 | 899 | m.m24 = lhs.m14 * rhs.m21 + lhs.m24 * rhs.m22 900 | m.m24 += lhs.m34 * rhs.m23 + lhs.m44 * rhs.m24 901 | 902 | m.m31 = lhs.m11 * rhs.m31 + lhs.m21 * rhs.m32 903 | m.m31 += lhs.m31 * rhs.m33 + lhs.m41 * rhs.m34 904 | 905 | m.m32 = lhs.m12 * rhs.m31 + lhs.m22 * rhs.m32 906 | m.m32 += lhs.m32 * rhs.m33 + lhs.m42 * rhs.m34 907 | 908 | m.m33 = lhs.m13 * rhs.m31 + lhs.m23 * rhs.m32 909 | m.m33 += lhs.m33 * rhs.m33 + lhs.m43 * rhs.m34 910 | 911 | m.m34 = lhs.m14 * rhs.m31 + lhs.m24 * rhs.m32 912 | m.m34 += lhs.m34 * rhs.m33 + lhs.m44 * rhs.m34 913 | 914 | m.m41 = lhs.m11 * rhs.m41 + lhs.m21 * rhs.m42 915 | m.m41 += lhs.m31 * rhs.m43 + lhs.m41 * rhs.m44 916 | 917 | m.m42 = lhs.m12 * rhs.m41 + lhs.m22 * rhs.m42 918 | m.m42 += lhs.m32 * rhs.m43 + lhs.m42 * rhs.m44 919 | 920 | m.m43 = lhs.m13 * rhs.m41 + lhs.m23 * rhs.m42 921 | m.m43 += lhs.m33 * rhs.m43 + lhs.m43 * rhs.m44 922 | 923 | m.m44 = lhs.m14 * rhs.m41 + lhs.m24 * rhs.m42 924 | m.m44 += lhs.m34 * rhs.m43 + lhs.m44 * rhs.m44 925 | 926 | return m 927 | } 928 | 929 | static func * (lhs: Matrix4, rhs: Vector3) -> Vector3 { 930 | return rhs * lhs 931 | } 932 | 933 | static func * (lhs: Matrix4, rhs: Vector4) -> Vector4 { 934 | return rhs * lhs 935 | } 936 | 937 | static func * (lhs: Matrix4, rhs: Scalar) -> Matrix4 { 938 | return Matrix4( 939 | lhs.m11 * rhs, lhs.m12 * rhs, lhs.m13 * rhs, lhs.m14 * rhs, 940 | lhs.m21 * rhs, lhs.m22 * rhs, lhs.m23 * rhs, lhs.m24 * rhs, 941 | lhs.m31 * rhs, lhs.m32 * rhs, lhs.m33 * rhs, lhs.m34 * rhs, 942 | lhs.m41 * rhs, lhs.m42 * rhs, lhs.m43 * rhs, lhs.m44 * rhs 943 | ) 944 | } 945 | 946 | static func ~= (lhs: Matrix4, rhs: Matrix4) -> Bool { 947 | if !(lhs.m11 ~= rhs.m11) { return false } 948 | if !(lhs.m12 ~= rhs.m12) { return false } 949 | if !(lhs.m13 ~= rhs.m13) { return false } 950 | if !(lhs.m14 ~= rhs.m14) { return false } 951 | if !(lhs.m21 ~= rhs.m21) { return false } 952 | if !(lhs.m22 ~= rhs.m22) { return false } 953 | if !(lhs.m23 ~= rhs.m23) { return false } 954 | if !(lhs.m24 ~= rhs.m24) { return false } 955 | if !(lhs.m31 ~= rhs.m31) { return false } 956 | if !(lhs.m32 ~= rhs.m32) { return false } 957 | if !(lhs.m33 ~= rhs.m33) { return false } 958 | if !(lhs.m34 ~= rhs.m34) { return false } 959 | if !(lhs.m41 ~= rhs.m41) { return false } 960 | if !(lhs.m42 ~= rhs.m42) { return false } 961 | if !(lhs.m43 ~= rhs.m43) { return false } 962 | if !(lhs.m44 ~= rhs.m44) { return false } 963 | return true 964 | } 965 | } 966 | 967 | // MARK: Quaternion 968 | 969 | public extension Quaternion { 970 | static let zero = Quaternion(0, 0, 0, 0) 971 | static let identity = Quaternion(0, 0, 0, 1) 972 | 973 | var lengthSquared: Scalar { 974 | return x * x + y * y + z * z + w * w 975 | } 976 | 977 | var length: Scalar { 978 | return sqrt(lengthSquared) 979 | } 980 | 981 | var inverse: Quaternion { 982 | return -self 983 | } 984 | 985 | var xyz: Vector3 { 986 | get { 987 | return Vector3(x, y, z) 988 | } 989 | set(v) { 990 | x = v.x 991 | y = v.y 992 | z = v.z 993 | } 994 | } 995 | 996 | var pitch: Scalar { 997 | return asin(min(1, max(-1, 2 * (w * y - z * x)))) 998 | } 999 | 1000 | var yaw: Scalar { 1001 | return atan2(2 * (w * z + x * y), 1 - 2 * (y * y + z * z)) 1002 | } 1003 | 1004 | var roll: Scalar { 1005 | return atan2(2 * (w * x + y * z), 1 - 2 * (x * x + y * y)) 1006 | } 1007 | 1008 | init(_ x: Scalar, _ y: Scalar, _ z: Scalar, _ w: Scalar) { 1009 | self.init(x: x, y: y, z: z, w: w) 1010 | } 1011 | 1012 | init(axisAngle: Vector4) { 1013 | let r = axisAngle.w * 0.5 1014 | let scale = sin(r) 1015 | let a = axisAngle.xyz * scale 1016 | self.init(a.x, a.y, a.z, cos(r)) 1017 | } 1018 | 1019 | init(pitch: Scalar, yaw: Scalar, roll: Scalar) { 1020 | let t0 = cos(yaw * 0.5) 1021 | let t1 = sin(yaw * 0.5) 1022 | let t2 = cos(roll * 0.5) 1023 | let t3 = sin(roll * 0.5) 1024 | let t4 = cos(pitch * 0.5) 1025 | let t5 = sin(pitch * 0.5) 1026 | self.init( 1027 | t0 * t3 * t4 - t1 * t2 * t5, 1028 | t0 * t2 * t5 + t1 * t3 * t4, 1029 | t1 * t2 * t4 - t0 * t3 * t5, 1030 | t0 * t2 * t4 + t1 * t3 * t5 1031 | ) 1032 | } 1033 | 1034 | init(rotationMatrix m: Matrix4) { 1035 | let x = sqrt(max(0, 1 + m.m11 - m.m22 - m.m33)) / 2 1036 | let y = sqrt(max(0, 1 - m.m11 + m.m22 - m.m33)) / 2 1037 | let z = sqrt(max(0, 1 - m.m11 - m.m22 + m.m33)) / 2 1038 | let w = sqrt(max(0, 1 + m.m11 + m.m22 + m.m33)) / 2 1039 | self.init( 1040 | x * (x * (m.m32 - m.m23)).sign, 1041 | y * (y * (m.m13 - m.m31)).sign, 1042 | z * (z * (m.m21 - m.m12)).sign, 1043 | w 1044 | ) 1045 | } 1046 | 1047 | init(_ v: [Scalar]) { 1048 | assert(v.count == 4, "array must contain 4 elements, contained \(v.count)") 1049 | 1050 | x = v[0] 1051 | y = v[1] 1052 | z = v[2] 1053 | w = v[3] 1054 | } 1055 | 1056 | func toAxisAngle() -> Vector4 { 1057 | let scale = xyz.length 1058 | if scale ~= 0 || scale ~= .twoPi { 1059 | return .z 1060 | } else { 1061 | return Vector4(x / scale, y / scale, z / scale, acos(w) * 2) 1062 | } 1063 | } 1064 | 1065 | func toPitchYawRoll() -> (pitch: Scalar, yaw: Scalar, roll: Scalar) { 1066 | return (pitch, yaw, roll) 1067 | } 1068 | 1069 | func toArray() -> [Scalar] { 1070 | return [x, y, z, w] 1071 | } 1072 | 1073 | func dot(_ v: Quaternion) -> Scalar { 1074 | return x * v.x + y * v.y + z * v.z + w * v.w 1075 | } 1076 | 1077 | func normalized() -> Quaternion { 1078 | let lengthSquared = self.lengthSquared 1079 | if lengthSquared ~= 0 || lengthSquared ~= 1 { 1080 | return self 1081 | } 1082 | return self / sqrt(lengthSquared) 1083 | } 1084 | 1085 | func interpolated(with q: Quaternion, by t: Scalar) -> Quaternion { 1086 | let dot = max(-1, min(1, self.dot(q))) 1087 | if dot ~= 1 { 1088 | return (self + (q - self) * t).normalized() 1089 | } 1090 | 1091 | let theta = acos(dot) * t 1092 | let t1 = self * cos(theta) 1093 | let t2 = (q - (self * dot)).normalized() * sin(theta) 1094 | return t1 + t2 1095 | } 1096 | 1097 | static prefix func - (q: Quaternion) -> Quaternion { 1098 | return Quaternion(-q.x, -q.y, -q.z, q.w) 1099 | } 1100 | 1101 | static func + (lhs: Quaternion, rhs: Quaternion) -> Quaternion { 1102 | return Quaternion(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w) 1103 | } 1104 | 1105 | static func - (lhs: Quaternion, rhs: Quaternion) -> Quaternion { 1106 | return Quaternion(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w) 1107 | } 1108 | 1109 | static func * (lhs: Quaternion, rhs: Quaternion) -> Quaternion { 1110 | return Quaternion( 1111 | lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y, 1112 | lhs.w * rhs.y + lhs.y * rhs.w + lhs.z * rhs.x - lhs.x * rhs.z, 1113 | lhs.w * rhs.z + lhs.z * rhs.w + lhs.x * rhs.y - lhs.y * rhs.x, 1114 | lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z 1115 | ) 1116 | } 1117 | 1118 | static func * (lhs: Quaternion, rhs: Vector3) -> Vector3 { 1119 | return rhs * lhs 1120 | } 1121 | 1122 | static func * (lhs: Quaternion, rhs: Scalar) -> Quaternion { 1123 | return Quaternion(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs) 1124 | } 1125 | 1126 | static func / (lhs: Quaternion, rhs: Scalar) -> Quaternion { 1127 | return Quaternion(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs) 1128 | } 1129 | 1130 | static func ~= (lhs: Quaternion, rhs: Quaternion) -> Bool { 1131 | return lhs.x ~= rhs.x && lhs.y ~= rhs.y && lhs.z ~= rhs.z && lhs.w ~= rhs.w 1132 | } 1133 | } 1134 | --------------------------------------------------------------------------------