import Foundation import simd public extension FloatingPoint { @inline(__always) var saturate: Self { min(max(self , 0), 1) } @inline(__always) static func lerp(_ a: Self, _ b: Self, _ x: Self) -> Self { a * (1 - x) + b * x } @inline(__always) static func deg(fromRad: Self) -> Self { fromRad * (180 / Self.pi) } @inline(__always) static func rad(fromDeg: Self) -> Self { fromDeg * (Self.pi / 180) } fileprivate func axisDeadzone(_ min: Self, _ max: Self) -> Self { let xabs = abs(self) return if xabs <= min { 0 } else if xabs >= max { Self(signOf: self, magnitudeOf: 1) } else { Self(signOf: self, magnitudeOf: xabs - min) / (max - min) } } } public extension SIMD2 where Scalar: FloatingPoint { @inline(__always) var len2: Scalar { x * x + y * y } //@inline(__always) var len2: Scalar { simd_dot(self, self) } @inline(__always) var len: Scalar { len2.squareRoot() } @inline(__always) var normalised: Self { self / len } @inline(__always) func dot(_ b: Self) -> Scalar { x * b.x + y * b.y } @inline(__always) func reflect(_ n: Self) -> Self { self - (n * 2 * self.dot(n)) } @inline(__always) func project(_ n: Self) -> Self { n * self.dot(n) } @inline(__always) func cross(_ b: Self) -> Scalar { x * b.y - y * b.x } @inline(__always) func lerp(_ b: Self, _ x: Scalar) -> Self { let invX = 1 - x return Self(self.x * invX + b.x * x, self.y * invX + b.y * x) } @inline(__always) func distance(_ b: Self) -> Scalar { return (b - self).len } func cardinalDeadzone(min: Scalar, max: Scalar) -> Self { Self(self.x.axisDeadzone(min, max), self.y.axisDeadzone(min, max)) } func radialDeadzone(min: Scalar, max: Scalar) -> Self { let magnitude = self.len if magnitude == .zero || magnitude < min { return Self.zero } if magnitude > max { return self / magnitude } let rescale = (magnitude - min) / (max - min) return self / magnitude * rescale } } public extension SIMD3 where Scalar: FloatingPoint { @inline(__always) static var X: Self { Self(1, 0, 0) } @inline(__always) static var Y: Self { Self(0, 1, 0) } @inline(__always) static var Z: Self { Self(0, 0, 1) } @inline(__always) static var up: Self { Y } @inline(__always) static var down: Self { -Y } @inline(__always) static var left: Self { -X } @inline(__always) static var right: Self { X } @inline(__always) static var forward: Self { Z } @inline(__always) static var back: Self { -Z } @inline(__always) var len2: Scalar { x * x + y * y + z * z } @inline(__always) var len: Scalar { len2.squareRoot() } @inline(__always) var normalised: Self { self / len } @inline(__always) mutating func normalise() { self /= len } @inline(__always) func lerp(_ b: Self, _ x: Scalar) -> Self { let invX = 1 - x return Self(self.x * invX + b.x * x, self.y * invX + b.y * x, self.z * invX + b.z * x) } @inline(__always) func dot(_ b: Self) -> Scalar { x * b.x + y * b.y + z * b.z } @inline(__always) func cross(_ b: Self) -> Self { Self(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x) } @inline(__always) func project(_ n: Self) -> Self { n * self.dot(n) } } public extension SIMD4 where Scalar: FloatingPoint { @inline(__always) static var X: Self { Self(1, 0, 0, 0) } @inline(__always) static var Y: Self { Self(0, 1, 0, 0) } @inline(__always) static var Z: Self { Self(0, 0, 1, 0) } @inline(__always) static var W: Self { Self(0, 0, 0, 1) } } public extension simd_float4x4 { @inline(__always) static var identity: Self { Self(diagonal: .one) } @inline(__always) static func translate(_ v: Vec3f) -> Self { Self( .init( 1, 0, 0, 0), .init( 0, 1, 0 ,0), .init( 0, 0, 1, 0), .init(v.x, v.y, v.z, 1)) } @inline(__always) static func scale(_ v: Vec3f) -> Self { Self( .init(v.x, 0, 0, 0), .init( 0, v.y, 0, 0), .init( 0, 0, v.z, 0), .init( 0, 0, 0, 1)) } @inline(__always) static func scale(scalar v: Float) -> Self { Self( .init(v, 0, 0, 0), .init(0, v, 0, 0), .init(0, 0, v, 0), .init(0, 0, 0, 1)) } static func rotate(axis v: Vec3f, angle theta: Float) -> Self { //FIXME: THIS IS FUCKED UP FOR EVERYTHING OTHER THAN X AXIS ROTATION LOL /* let vv = v * v let xy = v.x * v.y, xz = v.x * v.z, yz = v.y * v.z let ts = sin(angle), tc = cos(angle) return Self( .init( vv.x + (tc * (1 - vv.x)), (xz - (tc * xy)) + (ts * v.z), (xz - (tc * xz)) - (ts * v.y), 0), .init( (xy - (tc * xy)) - (ts * v.z), vv.y + (tc * (1 - vv.y)), (xz - (tc * xz)) + (ts * v.x), 0), .init( (xz - (tc * xz)) + (ts * v.y), (xz - (tc * xz)) - (ts * v.y), vv.z + (tc * (1 - vv.z)), 1), .init(0, 0, 0, 1)) */ let vxx = v.x * v.x, vxy = v.x * v.y, vxz = v.x * v.z let vyy = v.y * v.y, vyz = v.y * v.z let vzz = v.z * v.z let ts = sin(theta), tc = cos(theta) let ic = 1 - tc return Self( .init( ic * vxx + tc, ic * vxy - v.z * ts, ic * vxz + v.z * ts, 0), .init( ic * vxy + v.z * ts, ic * vyy + tc, ic * vyz - v.x * ts, 0), .init( ic * vxz - v.y * ts, ic * vyz + v.x * ts, ic * vzz + tc, 0), .init(0, 0, 0, 1)) } @inline(__always) static func rotate(yawPitch: Vec2f) -> Self { return rotate(yaw: yawPitch.x, pitch: yawPitch.y) } static func rotate(yaw ytheta: Float, pitch xtheta: Float) -> Self { let xc = cos(xtheta), xs = sin(xtheta) let yc = cos(ytheta), ys = sin(ytheta) return Self( .init(yc, ys * xs, -ys * xc, 0), .init( 0, xc, xs, 0), .init(ys, yc * -xs, yc * xc, 0), .init( 0, 0, 0, 1)) } static func rotate(yaw ytheta: Float, pitch xtheta: Float, roll ztheta: Float) -> Self { //FIXME: this doesn't null against control let xc = cos(xtheta), xs = sin(xtheta) let yc = cos(ytheta), ys = sin(ytheta) let zc = cos(ztheta), zs = sin(ztheta) let ysxs = ys * xs, ycxs = yc * xs let result = Mat4f( .init(yc * zc + ysxs * zs, yc * -zs + ysxs * zc, -ys * xc, 0), .init( xc * zs, xc * zc, xs, 0), .init(ys * zc - ycxs * zs, ys * -zs - ycxs * zc, yc * xc, 0), .init( 0, 0, 0, 1)) let shouldBe = .rotate(z: ztheta) * .rotate(x: xtheta) * .rotate(y: ytheta) let epsilon: Float = .ulpOfOne if (result != shouldBe) { assert(abs(result[0][0] - shouldBe[0][0]) <= epsilon) // epsilon assert(result[1][0] == shouldBe[1][0]) assert(abs(result[2][0] - shouldBe[2][0]) <= epsilon) // epsilon assert(result[3][0] == shouldBe[3][0]) assert(abs(result[0][1] - shouldBe[0][1]) <= epsilon) // epsilon assert(result[1][1] == shouldBe[1][1]) assert(abs(result[2][1] - shouldBe[2][1]) <= epsilon) // epsilon assert(result[3][1] == shouldBe[3][1]) assert(result[0][2] == shouldBe[0][2]) assert(result[1][2] == shouldBe[1][2]) assert(result[2][2] == shouldBe[2][2]) assert(result[3][2] == shouldBe[3][2]) assert(result[0][3] == shouldBe[0][3]) assert(result[1][3] == shouldBe[1][3]) assert(result[2][3] == shouldBe[2][3]) assert(result[3][3] == shouldBe[3][3]) } return result } @inline(__always) static func rotate(x theta: Float) -> Self { let c = cos(theta), s = sin(theta) return Self( .init(1, 0, 0, 0), .init(0, c, s, 0), .init(0, -s, c, 0), .init(0, 0, 0, 1)) } @inline(__always) static func rotate(y theta: Float) -> Self { let c = cos(theta), s = sin(theta) return Self( .init(c, 0, -s, 0), .init(0, 1, 0, 0), .init(s, 0, c, 0), .init(0, 0, 0, 1)) } @inline(__always) static func rotate(z theta: Float) -> Self { let c = cos(theta), s = sin(theta) return Self( .init(c, -s, 0, 0), .init(s, c, 0, 0), .init(0, 0, 1, 0), .init(0, 0, 0, 1)) } static func perspective(fovY: Float, aspect: Float, zNear: Float, zFar: Float) -> Self { let h = 1 / tanf(fovY * 0.5) let w = h / aspect let invClipRange = 1 / (zFar - zNear) let z = -(zFar + zNear) * invClipRange let z2 = -(2 * zFar * zNear) * invClipRange return simd_matrix( .init(w, 0, 0, 0), .init(0, h, 0, 0), .init(0, 0, z, -1), .init(0, 0, z2, 0)) } static func lookAt(from: Vec3f = .zero, to: Vec3f, up: Vec3f = .up) -> Self { let forward = (to - from).normalised let bitangent = forward.cross(up).normalised let tangent = bitangent.cross(forward).normalised let normal = -forward return simd_matrix( .init(bitangent.x, tangent.x, normal.x, 0.0), .init(bitangent.y, tangent.y, normal.y, 0.0), .init(bitangent.z, tangent.z, normal.z, 0.0), .init( 0.0, 0.0, 0.0, 1.0)) } } public typealias Vec2f = SIMD2 public typealias Vec2d = SIMD2 public typealias Vec3f = SIMD3 public typealias Vec3d = SIMD3 public typealias Vec4f = SIMD4 public typealias Vec4d = SIMD4 public typealias Mat4f = simd_float4x4