organise maths helpers

This commit is contained in:
2024-08-22 06:24:30 +10:00
parent b24d154c93
commit cad6418cff
6 changed files with 29 additions and 21 deletions

View File

@ -0,0 +1,58 @@
import simd
struct AABB {
private var _bounds: simd_float2x3
var lower: SIMD3<Float> {
get { _bounds[0] }
set(row) { self._bounds[0] = row }
}
var upper: SIMD3<Float> {
get { _bounds[1] }
set(row) { self._bounds[1] = row }
}
var center: SIMD3<Float> {
get { (self._bounds[0] + self._bounds[1]) / 2 }
}
var size: SIMD3<Float> {
get { self._bounds[1] - self._bounds[0] }
}
var left: Float { self._bounds[0].x }
var bottom: Float { self._bounds[0].y }
var far: Float { self._bounds[0].z }
var right: Float { self._bounds[1].x }
var top: Float { self._bounds[1].y }
var near: Float { self._bounds[1].z }
private init(bounds: simd_float2x3) {
self._bounds = bounds
}
init(from: SIMD3<Float>, to: SIMD3<Float>) {
self.init(bounds: .init(from, to))
}
static func fromUnitCube(position: SIMD3<Float> = .zero, scale: SIMD3<Float> = .one) -> Self {
self.init(
from: position - scale,
to: position + scale)
}
func touching(_ other: Self) -> Bool{
let distLower = other._bounds[0] - self._bounds[1] // x: left, y: bottom, z: far
let distUpper = self._bounds[0] - other._bounds[1] // x: right, y: top, z: near
if distLower.x > 0 || distUpper.x > 0 { return false }
if distLower.y > 0 || distUpper.y > 0 { return false }
if distLower.z > 0 || distUpper.z > 0 { return false }
return true
}
}
extension AABB {
static func + (lhs: Self, rhs: SIMD3<Float>) -> Self {
.init(bounds: lhs._bounds + .init(rhs, rhs))
}
}

View File

@ -0,0 +1,10 @@
public extension FloatingPoint {
@inline(__always) var degrees: Self { self * (180 / Self.pi) }
@inline(__always) var radians: Self { self * (Self.pi / 180) }
@inline(__always) func lerp(_ a: Self, _ b: Self) -> Self { b * self + a * (1 - self) }
@inline(__always) func mlerp(_ a: Self, _ b: Self) -> Self { a + (b - a) * self }
@inline(__always) func clamp(_ a: Self, _ b: Self) -> Self { min(max(self, a), b) }
@inline(__always) var saturated: Self { self.clamp(0, 1) }
}

View File

@ -0,0 +1,95 @@
import simd
public extension simd_float4x4 {
typealias T = Float
@inline(__always) static var identity: Self { matrix_identity_float4x4 }
@inline(__always) static func translate(_ v: SIMD3<T>) -> 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: SIMD3<T>) -> Self { Self(diagonal: .init(v.x, v.y, v.z, 1)) }
@inline(__always) static func scale(_ s: T) -> Self { Self(diagonal: .init(s, s, s, 1)) }
static func rotate(x theta: T) -> 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))
}
static func rotate(y theta: T) -> 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))
}
static func rotate(z theta: T) -> 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))
}
@inline(__always) static func rotate(yawPitch yp: SIMD2<T>) -> Self { rotate(yaw: yp.x, pitch: yp.y) }
static func rotate(yaw ytheta: T, pitch xtheta: T) -> Self {
let xc = cos(xtheta), xs = sin(xtheta)
let yc = cos(ytheta), ys = sin(ytheta)
return .init(
.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 orthographic(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self {
let
invWidth = 1 / (right - left),
invHeight = 1 / (top - bottom),
invDepth = 1 / (far - near)
let
tx = -(right + left) * invWidth,
ty = -(top + bottom) * invHeight,
tz = -near * invDepth
let x = 2 * invWidth, y = 2 * invHeight, z = invDepth
return .init(
.init( x, 0, 0, 0),
.init( 0, y, 0, 0),
.init( 0, 0, z, 0),
.init(tx, ty, tz, 1))
}
static func perspective(verticalFov fovY: T, aspect: T, near zNear: T, far zFar: T) -> Self {
let tanHalfFovY = tan(fovY * T(0.5))
let invClipRange = 1 / (zNear - zFar)
let y = 1 / tanHalfFovY
let x = y / aspect
let z = zFar * invClipRange
let w = zNear * z
return .init(
.init(x, 0, 0, 0),
.init(0, y, 0, 0),
.init(0, 0, z, -1),
.init(0, 0, w, 0))
}
}
extension simd_quatf {
static var identity: Self { .init(real: 1, imag: .zero) }
}

View File

@ -0,0 +1,103 @@
struct Point<T: AdditiveArithmetic>: Equatable {
var x: T, y: T
static var zero: Self { .init(.zero, .zero) }
init(_ x: T, _ y: T) {
self.x = x
self.y = y
}
@inline(__always) static func == (lhs: Self, rhs: Self) -> Bool { lhs.x == rhs.x && lhs.y == rhs.y }
@inline(__always) static func != (lhs: Self, rhs: Self) -> Bool { lhs.x != rhs.x || lhs.y != rhs.y }
}
extension Point where T: AdditiveArithmetic {
@inline(__always) static func + (lhs: Self, rhs: Self) -> Self { Self(lhs.x + rhs.x, lhs.y + rhs.y) }
@inline(__always) static func - (lhs: Self, rhs: Self) -> Self { Self(lhs.x - rhs.x, lhs.y - rhs.y) }
@inline(__always) static func += (lhs: inout Self, rhs: Self) { lhs.x += rhs.x; lhs.y += rhs.y }
@inline(__always) static func -= (lhs: inout Self, rhs: Self) { lhs.x -= rhs.x; lhs.y -= rhs.y }
}
extension SIMD2 where Scalar: AdditiveArithmetic {
init(_ point: Point<Scalar>) {
self.init(point.x, point.y)
}
}
public struct Size<T: AdditiveArithmetic>: Equatable {
var w: T, h: T
static var zero: Self { .init(.zero, .zero) }
init(_ w: T, _ h: T) {
self.w = w
self.h = h
}
@inline(__always) public static func == (lhs: Self, rhs: Self) -> Bool { lhs.w == rhs.w && lhs.h == rhs.h }
@inline(__always) public static func != (lhs: Self, rhs: Self) -> Bool { lhs.w != rhs.w || lhs.h != rhs.h }
}
extension Size where T: BinaryInteger {
static var one: Self { .init(T(1), T(1)) }
init<O>(_ other: Size<O>) where O: BinaryInteger {
self.init(T(other.w), T(other.h))
}
}
struct Rect<T: AdditiveArithmetic>: Equatable {
var x: T, y: T, w: T, h: T
var origin: Point<T> {
get { .init(self.x, self.y) }
set(point) { self.x = point.x; self.y = point.y }
}
var size: Size<T> {
get { .init(self.w, self.h) }
set(size) { self.w = size.w; self.h = size.h }
}
static var zero: Self { .init(origin: .zero, size: .zero) }
init(x: T, y: T, width: T, height: T) {
self.x = x
self.y = y
self.w = width
self.h = height
}
init(origin: Point<T>, size: Size<T>) {
self.x = origin.x
self.y = origin.y
self.w = size.w
self.h = size.h
}
@inline(__always) static func == (lhs: Self, rhs: Self) -> Bool {
lhs.x == rhs.x && lhs.y == rhs.y && lhs.w == rhs.w && lhs.h == rhs.h
}
}
extension Rect where T: AdditiveArithmetic {
var left: T { x }
var right: T { x + w }
var up: T { y }
var down: T { y + h }
}
struct Extent<T: AdditiveArithmetic>: Equatable {
var top: T, bottom: T, left: T, right: T
@inline(__always) static func == (lhs: Self, rhs: Self) -> Bool {
lhs.left == rhs.left && lhs.right == rhs.right && lhs.top == rhs.top && lhs.bottom == rhs.bottom
}
}
extension Extent where T: Comparable {
var size: Size<T> { .init(
right > left ? right - left : left - right,
bottom > top ? bottom - top : top - bottom) }
}

View File

@ -0,0 +1,12 @@
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 }
}