├── .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 | [](https://travis-ci.org/nicklockwood/VectorMath)
2 | [](https://developer.apple.com/swift)
3 | [](https://developer.apple.com/swift)
4 | [](https://opensource.org/licenses/MIT)
5 | [](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 |
--------------------------------------------------------------------------------