Skip to content

Commit d42d22c

Browse files
stackotterctreffs
andauthored
Implement 2x2 matrices (#11)
* Fix RangeReplaceableCollection conformance for latest Swift version * Add documentation to new replaceSubrange implementations and refactor from fatalError to precondition * Fix incorrect preconditions * Create Matrix2x2f and Matrix2x2d * Implement multiply for 2x2 matrices * Add missing multiplication operators * Fix Mat2x2 related typo in Linux code * Fix compile error in 2x2 multiplication implementation * Fix incorrect preconditions Co-authored-by: Christian Treffs <ctreffs@gmail.com>
1 parent 9782a65 commit d42d22c

File tree

7 files changed

+460
-0
lines changed

7 files changed

+460
-0
lines changed

Sources/FirebladeMath/Matrix/Matrices.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,24 @@ import struct simd.matrix.simd_float4x4
1111
import struct simd.matrix.simd_double4x4
1212
import struct simd.matrix.simd_float3x3
1313
import struct simd.matrix.simd_double3x3
14+
import struct simd.matrix.simd_float2x2
15+
import struct simd.matrix.simd_double2x2
1416

1517
public typealias Mat4x4f = Matrix4x4<simd_float4x4>
1618
public typealias Mat4x4d = Matrix4x4<simd_double4x4>
1719
public typealias Mat3x3f = Matrix3x3<simd_float3x3>
1820
public typealias Mat3x3d = Matrix3x3<simd_double3x3>
21+
public typealias Mat2x2f = Matrix2x2<simd_float2x2>
22+
public typealias Mat2x2d = Matrix2x2<simd_double2x2>
1923

2024
#else
2125

2226
public typealias Mat4x4f = Matrix4x4<Storage4x4<Float>>
2327
public typealias Mat4x4d = Matrix4x4<Storage4x4<Double>>
2428
public typealias Mat3x3f = Matrix3x3<Storage3x3<Float>>
2529
public typealias Mat3x3d = Matrix3x3<Storage3x3<Double>>
30+
public typealias Mat2x2f = Matrix2x2<Storage2x2<Float>>
31+
public typealias Mat2x2d = Matrix2x2<Storage2x2<Double>>
2632

2733
#endif
2834

Sources/FirebladeMath/Matrix/Matrix+Multiplication.swift

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,103 @@ public func multiply(_ lhs: Mat4x4f, _ rhs: Vec4f) -> Vec4f {
148148
return SIMD4<Float>(Q, R, S, T)
149149
#endif
150150
}
151+
152+
// MARK: - Mat2x2d
153+
@inlinable
154+
public func multiply(_ lhs: Mat2x2d, _ rhs: Mat2x2d) -> Mat2x2d {
155+
#if FRB_MATH_USE_SIMD
156+
return Mat2x2d(storage: simd_mul(lhs.storage, rhs.storage))
157+
#else
158+
return Mat2x2d(
159+
Vec2d(
160+
lhs[0, 0] * rhs[0, 0] + lhs[1, 0] * rhs[0, 1],
161+
lhs[0, 1] * rhs[0, 0] + lhs[1, 1] * rhs[0, 1]
162+
),
163+
Vec2d(
164+
lhs[0, 0] * rhs[1, 0] + lhs[1, 0] * rhs[1, 1],
165+
lhs[0, 1] * rhs[1, 0] + lhs[1, 1] * rhs[1, 1]
166+
)
167+
)
168+
#endif
169+
}
170+
171+
@inlinable
172+
public func multiply(_ lhs: Double, _ rhs: Mat2x2d) -> Mat2x2d {
173+
#if FRB_MATH_USE_SIMD
174+
return Mat2x2d(storage: simd_mul(lhs, rhs.storage))
175+
#else
176+
return Mat2x2d(lhs * rhs.storage.columns.0,
177+
lhs * rhs.storage.columns.1)
178+
#endif
179+
}
180+
181+
@inlinable
182+
public func multiply(_ lhs: Vec2d, _ rhs: Mat2x2d) -> Vec2d {
183+
#if FRB_MATH_USE_SIMD
184+
return simd_mul(lhs, rhs.storage)
185+
#else
186+
#warning("implementation missing")
187+
return Vec2d.zero
188+
#endif
189+
}
190+
191+
@inlinable
192+
public func multiply(_ lhs: Mat2x2d, _ rhs: Vec2d) -> Vec2d {
193+
#if FRB_MATH_USE_SIMD
194+
return simd_mul(lhs.storage, rhs)
195+
#else
196+
let Q = lhs[0, 0] * rhs[0] + lhs[1, 0] * rhs[1]
197+
let R = lhs[0, 1] * rhs[0] + lhs[1, 1] * rhs[1]
198+
return SIMD2<Double>(Q, R)
199+
#endif
200+
}
201+
202+
// MARK: - Mat2x2f
203+
@inlinable
204+
public func multiply(_ lhs: Mat2x2f, _ rhs: Mat2x2f) -> Mat2x2f {
205+
#if FRB_MATH_USE_SIMD
206+
return Mat2x2f(storage: simd_mul(lhs.storage, rhs.storage))
207+
#else
208+
return Mat2x2f(
209+
Vec2f(
210+
lhs[0, 0] * rhs[0, 0] + lhs[1, 0] * rhs[0, 1],
211+
lhs[0, 1] * rhs[0, 0] + lhs[1, 1] * rhs[0, 1]
212+
),
213+
Vec2f(
214+
lhs[0, 0] * rhs[1, 0] + lhs[1, 0] * rhs[1, 1],
215+
lhs[0, 1] * rhs[1, 0] + lhs[1, 1] * rhs[1, 1]
216+
)
217+
)
218+
#endif
219+
}
220+
221+
@inlinable
222+
public func multiply(_ lhs: Float, _ rhs: Mat2x2f) -> Mat2x2f {
223+
#if FRB_MATH_USE_SIMD
224+
return Mat2x2f(storage: simd_mul(lhs, rhs.storage))
225+
#else
226+
return Mat2x2f(lhs * rhs.storage.column0,
227+
lhs * rhs.storage.column1)
228+
#endif
229+
}
230+
231+
@inlinable
232+
public func multiply(_ lhs: Vec2f, _ rhs: Mat2x2f) -> Vec2f {
233+
#if FRB_MATH_USE_SIMD
234+
return simd_mul(lhs, rhs.storage)
235+
#else
236+
#warning("implementation missing")
237+
return Vec2f.zero
238+
#endif
239+
}
240+
241+
@inlinable
242+
public func multiply(_ lhs: Mat2x2f, _ rhs: Vec2f) -> Vec2f {
243+
#if FRB_MATH_USE_SIMD
244+
return simd_mul(lhs.storage, rhs)
245+
#else
246+
let Q = lhs[0, 0] * rhs[0] + lhs[1, 0] * rhs[1]
247+
let R = lhs[0, 1] * rhs[0] + lhs[1, 1] * rhs[1]
248+
return SIMD2<Float>(Q, R)
249+
#endif
250+
}

Sources/FirebladeMath/Matrix/Matrix+Operators.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created by Christian Treffs on 26.08.19.
66
//
77

8+
// MARK: 4x4f
89
public func * (lhs: Mat4x4f, rhs: Mat4x4f) -> Mat4x4f {
910
multiply(lhs, rhs)
1011
}
@@ -24,3 +25,66 @@ public func * (lhs: Mat4x4f, rhs: Vec4f) -> Vec4f {
2425
public func *= (lhs: inout Mat4x4f, rhs: Mat4x4f) {
2526
lhs = multiply(lhs, rhs)
2627
}
28+
29+
// MARK: 4x4d
30+
public func * (lhs: Mat4x4d, rhs: Mat4x4d) -> Mat4x4d {
31+
multiply(lhs, rhs)
32+
}
33+
34+
public func * (lhs: Double, rhs: Mat4x4d) -> Mat4x4d {
35+
multiply(lhs, rhs)
36+
}
37+
38+
public func * (lhs: Vec4d, rhs: Mat4x4d) -> Vec4d {
39+
multiply(lhs, rhs)
40+
}
41+
42+
public func * (lhs: Mat4x4d, rhs: Vec4d) -> Vec4d {
43+
multiply(lhs, rhs)
44+
}
45+
46+
public func *= (lhs: inout Mat4x4d, rhs: Mat4x4d) {
47+
lhs = multiply(lhs, rhs)
48+
}
49+
50+
// MARK: 2x2f
51+
public func * (lhs: Mat2x2f, rhs: Mat2x2f) -> Mat2x2f {
52+
multiply(lhs, rhs)
53+
}
54+
55+
public func * (lhs: Float, rhs: Mat2x2f) -> Mat2x2f {
56+
multiply(lhs, rhs)
57+
}
58+
59+
public func * (lhs: Vec2f, rhs: Mat2x2f) -> Vec2f {
60+
multiply(lhs, rhs)
61+
}
62+
63+
public func * (lhs: Mat2x2f, rhs: Vec2f) -> Vec2f {
64+
multiply(lhs, rhs)
65+
}
66+
67+
public func *= (lhs: inout Mat2x2f, rhs: Mat2x2f) {
68+
lhs = multiply(lhs, rhs)
69+
}
70+
71+
// MARK: 2x2d
72+
public func * (lhs: Mat2x2d, rhs: Mat2x2d) -> Mat2x2d {
73+
multiply(lhs, rhs)
74+
}
75+
76+
public func * (lhs: Double, rhs: Mat2x2d) -> Mat2x2d {
77+
multiply(lhs, rhs)
78+
}
79+
80+
public func * (lhs: Vec2d, rhs: Mat2x2d) -> Vec2d {
81+
multiply(lhs, rhs)
82+
}
83+
84+
public func * (lhs: Mat2x2d, rhs: Vec2d) -> Vec2d {
85+
multiply(lhs, rhs)
86+
}
87+
88+
public func *= (lhs: inout Mat2x2d, rhs: Mat2x2d) {
89+
lhs = multiply(lhs, rhs)
90+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//
2+
// Matrix2x2.swift
3+
//
4+
//
5+
// Created by stackotter on 01.11.22.
6+
//
7+
8+
@frozen
9+
public struct Matrix2x2<Storage>: RandomAccessCollection, MutableCollection where Storage: Storage2x2Protocol, Storage.Value == Storage.Element {
10+
public typealias Element = Storage.Element
11+
public typealias Index = Storage.Index
12+
public typealias Value = Storage.Value
13+
public typealias Vector = Storage.Column
14+
15+
@usableFromInline var storage: Storage
16+
17+
public var startIndex: Index { storage.startIndex }
18+
public var endIndex: Index { storage.endIndex }
19+
public func index(after i: Index) -> Index { storage.index(after: i) }
20+
public func index(before i: Index) -> Index { storage.index(before: i) }
21+
22+
@usableFromInline init(storage: Storage) {
23+
self.storage = storage
24+
}
25+
26+
public init(diagonal: Vector) {
27+
self.storage = Storage(diagonal: diagonal)
28+
}
29+
30+
public init(_ columns: [Vector]) {
31+
precondition(columns.count == 2, "Matrix needs exactly 2 Vector vectors")
32+
self.storage = Storage(columns)
33+
}
34+
35+
public init(_ column0: Vector, _ column1: Vector) {
36+
self.init([column0, column1])
37+
}
38+
39+
public init(_ values: [Storage.Value]) {
40+
precondition(values.count == 4, "Matrix needs exactly 4 values")
41+
self.init([Vector(values[0...1]),
42+
Vector(values[2...3])])
43+
}
44+
45+
public subscript(index: Index) -> Element {
46+
get { storage[index] }
47+
set { storage[index] = newValue }
48+
}
49+
50+
public subscript(column: Int, row: Int) -> Value {
51+
get { storage[column, row] }
52+
set { storage[column, row] = newValue }
53+
}
54+
55+
@inlinable public var columns: (Vector, Vector) {
56+
storage.columns
57+
}
58+
59+
@inlinable public var elements: [Value] {
60+
[Value](AnyIterator(self.storage.makeIterator()))
61+
}
62+
63+
@inlinable public func withForcedContiguousStorage<R>(_ body: (UnsafeBufferPointer<Element>) -> R) throws -> R? {
64+
// https://forums.swift.org/t/se-0256-introduce-mutable-contiguouscollection-protocol/22569/7
65+
if let result = withContiguousStorageIfAvailable(body) {
66+
return result
67+
}
68+
69+
return ContiguousArray(self).withContiguousStorageIfAvailable(body)
70+
}
71+
72+
@inlinable public mutating func withForcedContiguousMutableStorage<R>(_ body: (inout UnsafeMutableBufferPointer<Element>) -> R) throws -> R? {
73+
// https://forums.swift.org/t/se-0256-introduce-mutable-contiguouscollection-protocol/22569/7
74+
if let result = withContiguousMutableStorageIfAvailable(body) {
75+
return result
76+
}
77+
78+
var array = ContiguousArray(self)
79+
let result = array.withContiguousMutableStorageIfAvailable(body)
80+
for (idx, arrayIdx) in zip(self.indices, array.indices) {
81+
self[idx] = array[arrayIdx]
82+
}
83+
return result
84+
}
85+
}
86+
87+
extension Matrix2x2: Equatable {
88+
public static func ==(lhs: Matrix2x2<Storage>, rhs: Matrix2x2<Storage>) -> Bool {
89+
lhs.storage == rhs.storage
90+
}
91+
}

Sources/FirebladeMath/Matrix/MatrixStorage+NO_SIMD.swift

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,92 @@
66
//
77

88
#if !FRB_MATH_USE_SIMD
9+
// MARK: - Storage3x3
10+
public struct Storage2x2<Value>: Storage2x2Protocol where Value: StorageScalar {
11+
public typealias Column = SIMD2<Value>
12+
public typealias Storage3x3Ref = Storage3x3<Value>
13+
public typealias Storage4x4Ref = Storage4x4<Value>
14+
15+
@usableFromInline var column0: Column
16+
@usableFromInline var column1: Column
17+
18+
@inlinable public var columns: (Column, Column) {
19+
(column0, column1)
20+
}
21+
22+
@inlinable
23+
public init(_ columns: [Column]) {
24+
precondition(columns.count == 2, "Storage needs exactly 2 column vectors")
25+
column0 = columns[0]
26+
column1 = columns[1]
27+
}
28+
29+
@inlinable
30+
public init(diagonal: Column) {
31+
let zero: Value = 0
32+
column0 = Column(diagonal.x, zero)
33+
column1 = Column(zero, diagonal.y)
34+
}
35+
36+
public init() {
37+
self.init(diagonal: Column(0, 0))
38+
}
39+
40+
public func makeIterator() -> IndexingIterator<[Value]> {
41+
[column0, column1].flatMap { $0 }.makeIterator()
42+
}
43+
44+
@inlinable
45+
public subscript(column: Int, row: Int) -> Value {
46+
get {
47+
switch column {
48+
case 0:
49+
return column0[row]
50+
51+
case 1:
52+
return column1[row]
53+
54+
default:
55+
fatalError("column out of bounds \(column)")
56+
}
57+
}
58+
set {
59+
switch column {
60+
case 0:
61+
column0[row] = newValue
62+
63+
case 1:
64+
column1[row] = newValue
65+
66+
default:
67+
fatalError("column out of bounds \(column)")
68+
}
69+
}
70+
}
71+
72+
public subscript(index: Int) -> Value {
73+
get {
74+
let (column, row) = columnRow(index: index, width: 2)
75+
return self[column, row]
76+
}
77+
set {
78+
let (column, row) = columnRow(index: index, width: 2)
79+
self[column, row] = newValue
80+
}
81+
}
82+
}
83+
84+
extension Storage2x2: Equatable {
85+
public static func == (lhs: Storage2x2<Value>, rhs: Storage2x2<Value>) -> Bool {
86+
lhs.column0 == rhs.column0 &&
87+
lhs.column1 == rhs.column1
88+
}
89+
}
90+
991
// MARK: - Storage3x3
1092
public struct Storage3x3<Value>: Storage3x3Protocol where Value: StorageScalar {
1193
public typealias Column = SIMD3<Value>
94+
public typealias Storage2x2Ref = Storage2x2<Value>
1295
public typealias Storage4x4Ref = Storage4x4<Value>
1396

1497
@usableFromInline var column0: Column
@@ -100,6 +183,7 @@ extension Storage3x3: Equatable {
100183
// MARK: - Storage4x4Ref
101184
public struct Storage4x4<Value>: Storage4x4Protocol where Value: StorageScalar {
102185
public typealias Column = SIMD4<Value>
186+
public typealias Storage2x2Ref = Storage2x2<Value>
103187
public typealias Storage3x3Ref = Storage3x3<Value>
104188

105189
// TODO: we could use SIMD16<Value> here

0 commit comments

Comments
 (0)