|
| 1 | +// |
| 2 | +// SIMD.swift |
| 3 | +// allonet2 |
| 4 | +// |
| 5 | +// Created by Nevyn Bengtsson on 2025-11-20. |
| 6 | +// |
| 7 | + |
| 8 | +import simd |
| 9 | + |
| 10 | +public extension simd_float3x3 { |
| 11 | + static func * (lhs: simd_float3x3, rhs: SIMD2<Float>) -> SIMD2<Float> { |
| 12 | + let vec3 = SIMD3<Float>(rhs, 1) |
| 13 | + let transformed = lhs * vec3 |
| 14 | + return transformed.xy |
| 15 | + } |
| 16 | +} |
| 17 | + |
| 18 | +extension simd_float4x4 : Codable |
| 19 | +{ |
| 20 | + public func encode(to encoder: Encoder) throws |
| 21 | + { |
| 22 | + var container = encoder.unkeyedContainer() |
| 23 | + // Encode in row-major order: for each row, encode all columns. |
| 24 | + for row in 0..<4 |
| 25 | + { |
| 26 | + for col in 0..<4 |
| 27 | + { |
| 28 | + try container.encode(self[col][row]) |
| 29 | + } |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + public init(from decoder: Decoder) throws |
| 34 | + { |
| 35 | + var container = try decoder.unkeyedContainer() |
| 36 | + var matrix = simd_float4x4() |
| 37 | + // Decode in row-major order. |
| 38 | + for row in 0..<4 |
| 39 | + { |
| 40 | + for col in 0..<4 |
| 41 | + { |
| 42 | + let value = try container.decode(Float.self) |
| 43 | + matrix[col][row] = value |
| 44 | + } |
| 45 | + } |
| 46 | + self = matrix |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +extension simd_float4x4 { |
| 51 | + public static var identity: simd_float4x4 { |
| 52 | + return matrix_identity_float4x4 |
| 53 | + } |
| 54 | + public var translation: SIMD3<Float> { |
| 55 | + get { |
| 56 | + return SIMD3<Float>(columns.3.x, columns.3.y, columns.3.z) |
| 57 | + } |
| 58 | + set { |
| 59 | + columns.3 = SIMD4<Float>(newValue.x, newValue.y, newValue.z, columns.3.w) |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + public var scale: SIMD3<Float> { |
| 64 | + get { |
| 65 | + // The scale is the length of each column vector (ignoring the homogeneous component). |
| 66 | + let scaleX = length(SIMD3<Float>(columns.0.x, columns.0.y, columns.0.z)) |
| 67 | + let scaleY = length(SIMD3<Float>(columns.1.x, columns.1.y, columns.1.z)) |
| 68 | + let scaleZ = length(SIMD3<Float>(columns.2.x, columns.2.y, columns.2.z)) |
| 69 | + return SIMD3<Float>(scaleX, scaleY, scaleZ) |
| 70 | + } |
| 71 | + set { |
| 72 | + // Update the rotation part to apply the new scale while preserving the current rotation. |
| 73 | + let currentRotation = self.rotation |
| 74 | + let rotationMatrix = float3x3(currentRotation) |
| 75 | + columns.0 = SIMD4<Float>(rotationMatrix.columns.0 * newValue.x, 0) |
| 76 | + columns.1 = SIMD4<Float>(rotationMatrix.columns.1 * newValue.y, 0) |
| 77 | + columns.2 = SIMD4<Float>(rotationMatrix.columns.2 * newValue.z, 0) |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + /// The rotation component as a quaternion. |
| 82 | + public var rotation: simd_quatf { |
| 83 | + get { |
| 84 | + // Remove the scaling from the upper-left 3x3 part. |
| 85 | + let currentScale = self.scale |
| 86 | + let col0 = SIMD3<Float>(columns.0.x, columns.0.y, columns.0.z) / (currentScale.x != 0 ? currentScale.x : 1) |
| 87 | + let col1 = SIMD3<Float>(columns.1.x, columns.1.y, columns.1.z) / (currentScale.y != 0 ? currentScale.y : 1) |
| 88 | + let col2 = SIMD3<Float>(columns.2.x, columns.2.y, columns.2.z) / (currentScale.z != 0 ? currentScale.z : 1) |
| 89 | + let rotationMatrix = float3x3(col0, col1, col2) |
| 90 | + return simd_quatf(rotationMatrix) |
| 91 | + } |
| 92 | + set { |
| 93 | + // Preserve the current scale while setting a new rotation. |
| 94 | + let currentScale = self.scale |
| 95 | + let rotationMatrix = float3x3(newValue) |
| 96 | + columns.0 = SIMD4<Float>(rotationMatrix.columns.0 * currentScale.x, 0) |
| 97 | + columns.1 = SIMD4<Float>(rotationMatrix.columns.1 * currentScale.y, 0) |
| 98 | + columns.2 = SIMD4<Float>(rotationMatrix.columns.2 * currentScale.z, 0) |
| 99 | + } |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +public extension simd_float4x4 |
| 104 | +{ |
| 105 | + static func * (lhs: simd_float4x4, rhs: SIMD3<Float>) -> SIMD3<Float> { |
| 106 | + let vec4 = SIMD4<Float>(rhs, 1) |
| 107 | + let transformed = lhs * vec4 |
| 108 | + return transformed.xyz |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +public extension simd_quatf |
| 113 | +{ |
| 114 | + public static var identity: simd_quatf |
| 115 | + { |
| 116 | + simd_quatf(ix: 0, iy: 0, iz: 0, r: 1) |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +public extension SIMD3 |
| 121 | +{ |
| 122 | + var xy: SIMD2<Scalar> |
| 123 | + { |
| 124 | + SIMD2<Scalar>(self.x, self.y) |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +public extension SIMD4 |
| 129 | +{ |
| 130 | + var xyz: SIMD3<Scalar> |
| 131 | + { |
| 132 | + SIMD3<Scalar>(self.x, self.y, self.z) |
| 133 | + } |
| 134 | +} |
0 commit comments