Initial Commit
This commit is contained in:
commit
08da5c70e1
7 changed files with 984 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
24
Package.swift
Normal file
24
Package.swift
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
// swift-tools-version: 6.1
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "ArtifactMath",
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, making them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "ArtifactMath",
|
||||||
|
targets: ["ArtifactMath"]),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||||
|
// Targets can depend on other targets in this package and products from dependencies.
|
||||||
|
.target(
|
||||||
|
name: "ArtifactMath"),
|
||||||
|
.testTarget(
|
||||||
|
name: "ArtifactMathTests",
|
||||||
|
dependencies: ["ArtifactMath"]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
205
Sources/ArtifactMath/Matrix.swift
Normal file
205
Sources/ArtifactMath/Matrix.swift
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// 4x4 Matrix (column-major, common in graphics)
|
||||||
|
public struct Mat4<Component: BinaryFloatingPoint> {
|
||||||
|
// 16 elements in column-major order: m[0] = m00, m[4] = m10, etc.
|
||||||
|
private var m: [Component] = Array(repeating: 0, count: 16)
|
||||||
|
|
||||||
|
public var bytes: [UInt8] {
|
||||||
|
m.withUnsafeBytes { Array($0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscript for easy access: mat[col][row]
|
||||||
|
public subscript(col: Int, row: Int) -> Component {
|
||||||
|
get {
|
||||||
|
assert(col >= 0 && col < 4 && row >= 0 && row < 4, "Index out of bounds")
|
||||||
|
return m[col * 4 + row]
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
assert(col >= 0 && col < 4 && row >= 0 && row < 4, "Index out of bounds")
|
||||||
|
m[col * 4 + row] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
// Identity matrix
|
||||||
|
public static func identity() -> Mat4 {
|
||||||
|
var mat = Mat4()
|
||||||
|
mat[0, 0] = 1
|
||||||
|
mat[1, 1] = 1
|
||||||
|
mat[2, 2] = 1
|
||||||
|
mat[3, 3] = 1
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize from raw 16 values (column-major)
|
||||||
|
public init(_ values: [Component]) {
|
||||||
|
assert(values.count == 16, "Mat4 requires exactly 16 values")
|
||||||
|
self.m = values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience init from 2D array (row-major input for readability)
|
||||||
|
public init(rows: [[Component]]) {
|
||||||
|
assert(rows.count == 4 && rows.allSatisfy { $0.count == 4 }, "Must provide 4x4 rows")
|
||||||
|
for row in 0..<4 {
|
||||||
|
for col in 0..<4 {
|
||||||
|
self[col, row] = rows[row][col]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix multiplication: self * other
|
||||||
|
public static func * (lhs: Mat4<Component>, rhs: Mat4) -> Mat4 {
|
||||||
|
var result = Mat4()
|
||||||
|
for i in 0..<4 { // column of result
|
||||||
|
for j in 0..<4 { // row of result
|
||||||
|
var sum: Component = 0
|
||||||
|
for k in 0..<4 {
|
||||||
|
sum += lhs[k, j] * rhs[i, k] // Note: column-major access
|
||||||
|
}
|
||||||
|
result[i, j] = sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix * Vector4
|
||||||
|
public static func * (mat: Mat4, vec: Vec4<Component>) -> Vec4<Component> {
|
||||||
|
let x = mat[0,0]*vec.x + mat[1,0]*vec.y + mat[2,0]*vec.z + mat[3,0]*vec.w
|
||||||
|
let y = mat[0,1]*vec.x + mat[1,1]*vec.y + mat[2,1]*vec.z + mat[3,1]*vec.w
|
||||||
|
let z = mat[0,2]*vec.x + mat[1,2]*vec.y + mat[2,2]*vec.z + mat[3,2]*vec.w
|
||||||
|
let w = mat[0,3]*vec.x + mat[1,3]*vec.y + mat[2,3]*vec.z + mat[3,3]*vec.w
|
||||||
|
return Vec4(x: x, y: y, z: z, w: w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix * Vector3 (treats as point, w=1)
|
||||||
|
public static func * (mat: Mat4<Component>, vec: Vec3<Component>) -> Vec3<Component> {
|
||||||
|
let v4 = mat * Vec4(x: vec.x, y: vec.y, z: vec.z, w: 1)
|
||||||
|
return Vec3(x: v4.x / v4.w, y: v4.y / v4.w, z: v4.z / v4.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Transformations (return new matrices)
|
||||||
|
|
||||||
|
public static func translation(_ t: Vec3<Component>) -> Mat4 {
|
||||||
|
var mat = Mat4.identity()
|
||||||
|
mat[3, 0] = t.x
|
||||||
|
mat[3, 1] = t.y
|
||||||
|
mat[3, 2] = t.z
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func scale(_ s: Vec3<Component>) -> Mat4 {
|
||||||
|
var mat = Mat4.identity()
|
||||||
|
mat[0, 0] = s.x
|
||||||
|
mat[1, 1] = s.y
|
||||||
|
mat[2, 2] = s.z
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func rotationX(angleRadians: Float) -> Mat4 {
|
||||||
|
let c = Component(cos(angleRadians))
|
||||||
|
let s = Component(sin(angleRadians))
|
||||||
|
var mat = Mat4.identity()
|
||||||
|
mat[1, 1] = c
|
||||||
|
mat[2, 1] = -s
|
||||||
|
mat[1, 2] = s
|
||||||
|
mat[2, 2] = c
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func rotationY(angleRadians: Float) -> Mat4 {
|
||||||
|
let c = Component(cos(angleRadians))
|
||||||
|
let s = Component(sin(angleRadians))
|
||||||
|
var mat = Mat4.identity()
|
||||||
|
mat[0, 0] = c
|
||||||
|
mat[2, 0] = s
|
||||||
|
mat[0, 2] = -s
|
||||||
|
mat[2, 2] = c
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func rotationZ(angleRadians: Float) -> Mat4 {
|
||||||
|
let c = Component(cos(angleRadians))
|
||||||
|
let s = Component(sin(angleRadians))
|
||||||
|
var mat = Mat4.identity()
|
||||||
|
mat[0, 0] = c
|
||||||
|
mat[1, 0] = -s
|
||||||
|
mat[0, 1] = s
|
||||||
|
mat[1, 1] = c
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combined rotation (Euler angles in XYZ order)
|
||||||
|
public static func rotation(x: Float, y: Float, z: Float) -> Mat4 {
|
||||||
|
return rotationX(angleRadians: x) * rotationY(angleRadians: y) * rotationZ(angleRadians: z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look-at matrix (common for cameras)
|
||||||
|
public static func lookAt(eye: Vec3<Component>, target: Vec3<Component>, up: Vec3<Component> = Vec3<Component>(x: 0, y: 1, z: 0)) -> Mat4<Component> {
|
||||||
|
let zAxis = (eye - target).normalized() // Note: direction from eye to target is usually negative, but convention varies
|
||||||
|
let xAxis = Vec3.cross(up, zAxis).normalized()
|
||||||
|
let yAxis = Vec3.cross(zAxis, xAxis)
|
||||||
|
|
||||||
|
var mat = Mat4.identity()
|
||||||
|
mat[0, 0] = xAxis.x; mat[1, 0] = xAxis.y; mat[2, 0] = xAxis.z
|
||||||
|
mat[0, 1] = yAxis.x; mat[1, 1] = yAxis.y; mat[2, 1] = yAxis.z
|
||||||
|
mat[0, 2] = zAxis.x; mat[1, 2] = zAxis.y; mat[2, 2] = zAxis.z
|
||||||
|
mat[3, 0] = -Vec3.dot(xAxis, eye)
|
||||||
|
mat[3, 1] = -Vec3.dot(yAxis, eye)
|
||||||
|
mat[3, 2] = -Vec3.dot(zAxis, eye)
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perspective projection
|
||||||
|
public static func perspective(fovRadians: Component, aspect: Component, near: Component, far: Component) -> Mat4 {
|
||||||
|
let f = 1.0 / tan(Double(fovRadians) / 2.0)
|
||||||
|
var mat = Mat4()
|
||||||
|
|
||||||
|
mat[0, 0] = Component(f) / aspect
|
||||||
|
mat[1, 1] = Component(f)
|
||||||
|
mat[2, 2] = (far + near) / (near - far)
|
||||||
|
mat[3, 2] = (2 * far * near) / (near - far)
|
||||||
|
mat[2, 3] = -1.0
|
||||||
|
// Others remain 0
|
||||||
|
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ortho(left: Component, right: Component, bottom: Component, top: Component, near: Component, far: Component) -> Mat4 {
|
||||||
|
var mat = Mat4()
|
||||||
|
|
||||||
|
let rl = right - left
|
||||||
|
let tb = top - bottom
|
||||||
|
let fn = far - near
|
||||||
|
|
||||||
|
mat[0, 0] = 2 / rl
|
||||||
|
mat[1, 1] = 2 / tb
|
||||||
|
mat[2, 2] = -2 / fn
|
||||||
|
mat[3, 0] = -(right + left) / rl
|
||||||
|
mat[3, 1] = -(top + bottom) / tb
|
||||||
|
mat[3, 2] = -(far + near) / fn
|
||||||
|
mat[3, 3] = 1.0
|
||||||
|
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenient version using width/height + center
|
||||||
|
public static func ortho(width: Component, height: Component, near: Component, far: Component, centerX: Component = 0, centerY: Component = 0) -> Mat4 {
|
||||||
|
let left = centerX - width / 2
|
||||||
|
let right = centerX + width / 2
|
||||||
|
let bottom = centerY - height / 2
|
||||||
|
let top = centerY + height / 2
|
||||||
|
return ortho(left: left, right: right, bottom: bottom, top: top, near: near, far: far)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Common 2D ortho (z-range -1..1, origin at center)
|
||||||
|
public static func ortho2D(width: Component, height: Component) -> Mat4 {
|
||||||
|
ortho(width: width, height: height, near: -1, far: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public typealias Mat4f = Mat4<Float>
|
||||||
|
public typealias Mat4d = Mat4<Double>
|
||||||
|
|
||||||
7
Sources/ArtifactMath/Misc.swift
Normal file
7
Sources/ArtifactMath/Misc.swift
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
postfix operator %
|
||||||
|
|
||||||
|
public postfix func %<T: BinaryFloatingPoint> (_ number: T) -> T {
|
||||||
|
number / 100
|
||||||
|
}
|
||||||
|
|
||||||
94
Sources/ArtifactMath/Rect.swift
Normal file
94
Sources/ArtifactMath/Rect.swift
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct Rect<Component: Numeric & Hashable & Comparable> {
|
||||||
|
public var origin: Vec2<Component>
|
||||||
|
public var size: Vec2<Component>
|
||||||
|
|
||||||
|
public var left: Component {
|
||||||
|
origin.x
|
||||||
|
}
|
||||||
|
|
||||||
|
public var right: Component {
|
||||||
|
origin.x + size.x
|
||||||
|
}
|
||||||
|
|
||||||
|
public var top: Component {
|
||||||
|
origin.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bottom: Component {
|
||||||
|
origin.y + size.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public var width: Component {
|
||||||
|
size.x
|
||||||
|
}
|
||||||
|
|
||||||
|
public var height: Component {
|
||||||
|
size.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
origin = .zero
|
||||||
|
size = .zero
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(origin: Vec2<Component>, size: Vec2<Component>) {
|
||||||
|
self.origin = origin
|
||||||
|
self.size = size
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(x: Component, y: Component, width: Component, height: Component) {
|
||||||
|
origin = Vec2(x, y)
|
||||||
|
size = Vec2(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(left: Component, top: Component, right: Component, bottom: Component) {
|
||||||
|
origin = [left, top]
|
||||||
|
size = [right - left, bottom - top]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func contains(_ point: Vec2<Component>) -> Bool {
|
||||||
|
left < point.x && point.x < right && top < point.y && point.y < bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
public func inset(by offset: Component) -> Self {
|
||||||
|
return Self(origin: Vec2<Component>(origin.x + offset, origin.y + offset), size: Vec2(size.x - offset * 2, size.y - offset * 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func offset(by offset: Vec2<Component>) -> Self {
|
||||||
|
return Self(origin: origin + offset, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func clamped(_ lower: Vec2<Component>, _ upper: Vec2<Component>) -> Self {
|
||||||
|
// let origin: Vec2 = [max(origin.x, lower.x), max(origin.y, lower.y)]
|
||||||
|
// return Self(origin: origin, size: [min(upper.x, right) - origin.x, min(upper.y, bottom) - origin.y])
|
||||||
|
Self(left: min(max(left, lower.x), upper.x), top: min(max(top, lower.y), upper.y), right: min(max(right, lower.x), upper.x), bottom: min(max(bottom, lower.y), upper.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Rect where Component: BinaryFloatingPoint {
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Rect<T>) {
|
||||||
|
origin = Vec2(other.origin)
|
||||||
|
size = Vec2(other.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Rect<T>) {
|
||||||
|
origin = Vec2(other.origin)
|
||||||
|
size = Vec2(other.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Rect where Component: BinaryInteger {
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Rect<T>) {
|
||||||
|
origin = Vec2(other.origin)
|
||||||
|
size = Vec2(other.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Rect<T>) {
|
||||||
|
origin = Vec2(other.origin)
|
||||||
|
size = Vec2(other.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
632
Sources/ArtifactMath/Vector.swift
Normal file
632
Sources/ArtifactMath/Vector.swift
Normal file
|
|
@ -0,0 +1,632 @@
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct Vec2<Component: Numeric & Hashable>: Hashable, ExpressibleByArrayLiteral {
|
||||||
|
public typealias ArrayLiteralElement = Component
|
||||||
|
public typealias Tuple = (x: Component, y: Component)
|
||||||
|
public static var zero: Self { Self() }
|
||||||
|
|
||||||
|
public var x: Component
|
||||||
|
public var y: Component
|
||||||
|
|
||||||
|
public var tuple: (x: Component, y: Component) {
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bytes: [UInt8] {
|
||||||
|
var out: [UInt8] = []
|
||||||
|
writeBytes(into: &out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ x: Component, _ y: Component) {
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(x: Component, y: Component) {
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(arrayLiteral elements: Component...) {
|
||||||
|
x = elements[0]
|
||||||
|
y = elements[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func applying(_ transform: (Component) -> Component) -> Self {
|
||||||
|
Self(transform(x), transform(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func apply(_ transform: (Component) -> Component) {
|
||||||
|
x = transform(x)
|
||||||
|
y = transform(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func writeBytes(into list: inout [UInt8]) {
|
||||||
|
withUnsafeBytes(of: x, { list.append(contentsOf: $0) })
|
||||||
|
withUnsafeBytes(of: y, { list.append(contentsOf: $0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func dot(_ left: Self, _ right: Self) -> Component {
|
||||||
|
left.x * right.x + left.y * right.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(_ left: Self, _ right: Self) -> Bool {
|
||||||
|
left.x == right.x && left.y == right.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Self, _ right: Self) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Self, _ right: (x: Component, y: Component)) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: (x: Component, y: Component), _ right: Self) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +=(_ left: inout Self, _ right: Self) {
|
||||||
|
left.x += right.x
|
||||||
|
left.y += right.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +=(_ left: inout Self, _ right: (x: Component, y: Component)) {
|
||||||
|
left.x += right.x
|
||||||
|
left.y += right.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Self, _ right: Self) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Self, _ right: (x: Component, y: Component)) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: (x: Component, y: Component), _ right: Self) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -=(_ left: inout Self, _ right: Self) {
|
||||||
|
left.x -= right.x
|
||||||
|
left.y -= right.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -=(_ left: inout Self, _ right: Tuple) {
|
||||||
|
left.x -= right.x
|
||||||
|
left.y -= right.y
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func *(_ left: Self, _ right: Self) -> Self {
|
||||||
|
Self(left.x * right.x, left.y * right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func *(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x * right, left.y * right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func *=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x *= right
|
||||||
|
left.y *= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec2 where Component: BinaryFloatingPoint {
|
||||||
|
public var length: Component {
|
||||||
|
sqrt(x * x + y * y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lengthSquared: Component {
|
||||||
|
x * x + y * y
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Vec2<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Vec2<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ x: any BinaryFloatingPoint, _ y: any BinaryFloatingPoint) {
|
||||||
|
self.x = Component(x)
|
||||||
|
self.y = Component(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ x: any BinaryInteger, _ y: any BinaryInteger) {
|
||||||
|
self.x = Component(x)
|
||||||
|
self.y = Component(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func normalized() -> Self {
|
||||||
|
self / length
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x / right, left.y / right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x /= right
|
||||||
|
left.y /= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec2 where Component: BinaryInteger {
|
||||||
|
public var length: Double {
|
||||||
|
Double(x * x + y * y).squareRoot()
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lengthSquared: Component {
|
||||||
|
x * x + y * y
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Vec2<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Vec2<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ x: any BinaryFloatingPoint, _ y: any BinaryFloatingPoint) {
|
||||||
|
self.x = Component(x)
|
||||||
|
self.y = Component(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ x: any BinaryInteger, _ y: any BinaryInteger) {
|
||||||
|
self.x = Component(x)
|
||||||
|
self.y = Component(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func normalized() -> Self {
|
||||||
|
self / Component(length)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x / right, left.y / right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x /= right
|
||||||
|
left.y /= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec2: Codable where Component: Codable {}
|
||||||
|
|
||||||
|
public typealias Vec2i = Vec2<Int>
|
||||||
|
public typealias Vec2f = Vec2<Float>
|
||||||
|
public typealias Vec2d = Vec2<Double>
|
||||||
|
|
||||||
|
public struct Vec3<Component: Numeric & Hashable>: Hashable, ExpressibleByArrayLiteral {
|
||||||
|
public typealias ArrayLiteralElement = Component
|
||||||
|
public typealias Tuple = (x: Component, y: Component, z: Component)
|
||||||
|
public static var zero: Self { Self() }
|
||||||
|
|
||||||
|
public var x: Component
|
||||||
|
public var y: Component
|
||||||
|
public var z: Component
|
||||||
|
|
||||||
|
public var tuple: (x: Component, y: Component, z: Component) {
|
||||||
|
(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bytes: [UInt8] {
|
||||||
|
var out: [UInt8] = []
|
||||||
|
writeBytes(into: &out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
z = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ x: Component, _ y: Component, _ z: Component) {
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(x: Component, y: Component, z: Component) {
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(arrayLiteral elements: Component...) {
|
||||||
|
x = elements[0]
|
||||||
|
y = elements[1]
|
||||||
|
z = elements[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(x)
|
||||||
|
hasher.combine(y)
|
||||||
|
hasher.combine(z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func writeBytes(into list: inout [UInt8]) {
|
||||||
|
withUnsafeBytes(of: x, { list.append(contentsOf: $0) })
|
||||||
|
withUnsafeBytes(of: y, { list.append(contentsOf: $0) })
|
||||||
|
withUnsafeBytes(of: z, { list.append(contentsOf: $0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func dot(_ a: Self, _ b: Self) -> Component {
|
||||||
|
a.x * b.x + a.y * b.y + a.z * b.z
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func cross(_ a: Vec3, _ b: Vec3) -> Self {
|
||||||
|
Self(
|
||||||
|
x: a.y * b.z - a.z * b.y,
|
||||||
|
y: a.z * b.x - a.x * b.z,
|
||||||
|
z: a.x * b.y - a.y * b.x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(_ left: Self, _ right: Self) -> Bool {
|
||||||
|
left.x == right.x && left.y == right.y && left.z == right.z
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Self, _ right: Self) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y, left.z + right.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Self, _ right: Tuple) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y, left.z + right.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Tuple, _ right: Self) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y, left.z + right.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +=(_ left: inout Self, _ right: Self) {
|
||||||
|
left.x += right.x
|
||||||
|
left.y += right.y
|
||||||
|
left.z += right.z
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +=(_ left: inout Self, _ right: Tuple) {
|
||||||
|
left.x += right.x
|
||||||
|
left.y += right.y
|
||||||
|
left.z += right.z
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Self, _ right: Self) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y, left.z - right.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Self, _ right: Tuple) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y, left.z - right.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Tuple, _ right: Self) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y, left.z - right.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -=(_ left: inout Self, _ right: Self) {
|
||||||
|
left.x -= right.x
|
||||||
|
left.y -= right.y
|
||||||
|
left.z -= right.z
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -=(_ left: inout Self, _ right: Tuple) {
|
||||||
|
left.x -= right.x
|
||||||
|
left.y -= right.y
|
||||||
|
left.z -= right.z
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func *(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x * right, left.y * right, left.z * right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func *=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x *= right
|
||||||
|
left.y *= right
|
||||||
|
left.z *= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec3 where Component: BinaryFloatingPoint {
|
||||||
|
public var length: Component {
|
||||||
|
sqrt(x * x + y * y + z * z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lengthSquared: Component {
|
||||||
|
x * x + y * y + z * z
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Vec3<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Vec3<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func normalized() -> Self {
|
||||||
|
self / Component(length)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x / right, left.y / right, left.z / right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x /= right
|
||||||
|
left.y /= right
|
||||||
|
left.z /= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec3 where Component: BinaryInteger {
|
||||||
|
public var length: Double {
|
||||||
|
Double(x * x + y * y + z * z).squareRoot()
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lengthSquared: Component {
|
||||||
|
x * x + y * y + z * z
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Vec3<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Vec3<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func normalized() -> Self {
|
||||||
|
self / Component(length)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x / right, left.y / right, left.z / right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x /= right
|
||||||
|
left.y /= right
|
||||||
|
left.z /= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec3: Codable where Component: Codable {}
|
||||||
|
|
||||||
|
public typealias Vec3i = Vec3<Int>
|
||||||
|
public typealias Vec3f = Vec3<Float>
|
||||||
|
public typealias Vec3d = Vec3<Double>
|
||||||
|
|
||||||
|
|
||||||
|
public struct Vec4<Component: Numeric & Hashable>: Hashable, ExpressibleByArrayLiteral {
|
||||||
|
public typealias ArrayLiteralElement = Component
|
||||||
|
public typealias Tuple = (x: Component, y: Component, z: Component, w: Component)
|
||||||
|
public static var zero: Self { Self() }
|
||||||
|
|
||||||
|
public var x: Component
|
||||||
|
public var y: Component
|
||||||
|
public var z: Component
|
||||||
|
public var w: Component
|
||||||
|
|
||||||
|
public var tuple: (x: Component, y: Component, z: Component) {
|
||||||
|
(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var bytes: [UInt8] {
|
||||||
|
var out: [UInt8] = []
|
||||||
|
writeBytes(into: &out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
z = 0
|
||||||
|
w = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(_ x: Component, _ y: Component, _ z: Component, _ w: Component) {
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
self.w = w
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(x: Component, y: Component, z: Component, w: Component) {
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.z = z
|
||||||
|
self.w = w
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(arrayLiteral elements: Component...) {
|
||||||
|
x = elements[0]
|
||||||
|
y = elements[1]
|
||||||
|
z = elements[2]
|
||||||
|
w = elements[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func writeBytes(into list: inout [UInt8]) {
|
||||||
|
withUnsafeBytes(of: x, { list.append(contentsOf: $0) })
|
||||||
|
withUnsafeBytes(of: y, { list.append(contentsOf: $0) })
|
||||||
|
withUnsafeBytes(of: z, { list.append(contentsOf: $0) })
|
||||||
|
withUnsafeBytes(of: w, { list.append(contentsOf: $0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func dot(_ a: Self, _ b: Self) -> Component {
|
||||||
|
a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(_ left: Self, _ right: Self) -> Bool {
|
||||||
|
left.x == right.x && left.y == right.y && left.z == right.z && left.w == right.w
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Self, _ right: Self) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Self, _ right: Tuple) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +(_ left: Tuple, _ right: Self) -> Self {
|
||||||
|
Self(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +=(_ left: inout Self, _ right: Self) {
|
||||||
|
left.x += right.x
|
||||||
|
left.y += right.y
|
||||||
|
left.z += right.z
|
||||||
|
left.w += right.w
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func +=(_ left: inout Self, _ right: Tuple) {
|
||||||
|
left.x += right.x
|
||||||
|
left.y += right.y
|
||||||
|
left.z += right.z
|
||||||
|
left.w += right.w
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Self, _ right: Self) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Self, _ right: Tuple) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -(_ left: Tuple, _ right: Self) -> Self {
|
||||||
|
Self(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -=(_ left: inout Self, _ right: Self) {
|
||||||
|
left.x -= right.x
|
||||||
|
left.y -= right.y
|
||||||
|
left.z -= right.z
|
||||||
|
left.w -= right.w
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func -=(_ left: inout Self, _ right: Tuple) {
|
||||||
|
left.x -= right.x
|
||||||
|
left.y -= right.y
|
||||||
|
left.z -= right.z
|
||||||
|
left.w -= right.w
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func *(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x * right, left.y * right, left.z * right, left.w * right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func *=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x *= right
|
||||||
|
left.y *= right
|
||||||
|
left.z *= right
|
||||||
|
left.w *= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec4 where Component: BinaryFloatingPoint {
|
||||||
|
public var length: Component {
|
||||||
|
sqrt(x * x + y * y + z * z)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lengthSquared: Component {
|
||||||
|
x * x + y * y + z * z
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Vec4<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
w = Component(other.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Vec4<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
w = Component(other.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func normalized() -> Self {
|
||||||
|
self / length
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x / right, left.y / right, left.z / right, left.w / right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x /= right
|
||||||
|
left.y /= right
|
||||||
|
left.z /= right
|
||||||
|
left.w /= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec4 where Component: BinaryInteger {
|
||||||
|
public var length: Double {
|
||||||
|
Double(x * x + y * y + z * z).squareRoot()
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lengthSquared: Component {
|
||||||
|
x * x + y * y + z * z
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryFloatingPoint>(_ other: Vec4<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
w = Component(other.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init<T: BinaryInteger>(_ other: Vec4<T>) {
|
||||||
|
x = Component(other.x)
|
||||||
|
y = Component(other.y)
|
||||||
|
z = Component(other.z)
|
||||||
|
w = Component(other.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func normalized() -> Self {
|
||||||
|
self / Component(length)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /(_ left: Self, _ right: Component) -> Self {
|
||||||
|
Self(left.x / right, left.y / right, left.z / right, left.w / right)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func /=(_ left: inout Self, _ right: Component) {
|
||||||
|
left.x /= right
|
||||||
|
left.y /= right
|
||||||
|
left.z /= right
|
||||||
|
left.w /= right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Vec4: Codable where Component: Codable {}
|
||||||
|
|
||||||
|
public typealias Vec4i = Vec4<Int>
|
||||||
|
public typealias Vec4f = Vec4<Float>
|
||||||
|
public typealias Vec4d = Vec4<Double>
|
||||||
14
Tests/ArtifactMathTests/ArtifactMathTests.swift
Normal file
14
Tests/ArtifactMathTests/ArtifactMathTests.swift
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import XCTest
|
||||||
|
@testable import ArtifactMath
|
||||||
|
|
||||||
|
final class ArtifactMathTests: XCTestCase {
|
||||||
|
func testExample() throws {
|
||||||
|
// XCTest Documentation
|
||||||
|
// https://developer.apple.com/documentation/xctest
|
||||||
|
|
||||||
|
// Defining Test Cases and Test Methods
|
||||||
|
// https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
|
||||||
|
|
||||||
|
let set: Set<Vec3i> = []
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue