commit 08da5c70e11b549417c1e4e4ce08ef59a80cdaf0 Author: Signal Date: Mon Jun 22 16:13:41 2026 -0400 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..f1946b5 --- /dev/null +++ b/Package.swift @@ -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"] + ), + ] +) diff --git a/Sources/ArtifactMath/Matrix.swift b/Sources/ArtifactMath/Matrix.swift new file mode 100644 index 0000000..ef7ff1b --- /dev/null +++ b/Sources/ArtifactMath/Matrix.swift @@ -0,0 +1,205 @@ + +import Foundation + +// 4x4 Matrix (column-major, common in graphics) +public struct Mat4 { + // 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, 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) -> Vec4 { + 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, vec: Vec3) -> Vec3 { + 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) -> 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) -> 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, target: Vec3, up: Vec3 = Vec3(x: 0, y: 1, z: 0)) -> Mat4 { + 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 +public typealias Mat4d = Mat4 + diff --git a/Sources/ArtifactMath/Misc.swift b/Sources/ArtifactMath/Misc.swift new file mode 100644 index 0000000..90dacd4 --- /dev/null +++ b/Sources/ArtifactMath/Misc.swift @@ -0,0 +1,7 @@ + +postfix operator % + +public postfix func % (_ number: T) -> T { + number / 100 +} + diff --git a/Sources/ArtifactMath/Rect.swift b/Sources/ArtifactMath/Rect.swift new file mode 100644 index 0000000..8672e3e --- /dev/null +++ b/Sources/ArtifactMath/Rect.swift @@ -0,0 +1,94 @@ + +import Foundation + +public struct Rect { + public var origin: Vec2 + public var size: Vec2 + + 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, size: Vec2) { + 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) -> Bool { + left < point.x && point.x < right && top < point.y && point.y < bottom + } + + public func inset(by offset: Component) -> Self { + return Self(origin: Vec2(origin.x + offset, origin.y + offset), size: Vec2(size.x - offset * 2, size.y - offset * 2)) + } + + public func offset(by offset: Vec2) -> Self { + return Self(origin: origin + offset, size: size) + } + + public func clamped(_ lower: Vec2, _ upper: Vec2) -> 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(_ other: Rect) { + origin = Vec2(other.origin) + size = Vec2(other.size) + } + + public init(_ other: Rect) { + origin = Vec2(other.origin) + size = Vec2(other.size) + } +} + +extension Rect where Component: BinaryInteger { + public init(_ other: Rect) { + origin = Vec2(other.origin) + size = Vec2(other.size) + } + + public init(_ other: Rect) { + origin = Vec2(other.origin) + size = Vec2(other.size) + } +} + diff --git a/Sources/ArtifactMath/Vector.swift b/Sources/ArtifactMath/Vector.swift new file mode 100644 index 0000000..71485ec --- /dev/null +++ b/Sources/ArtifactMath/Vector.swift @@ -0,0 +1,632 @@ + +import Foundation + +public struct Vec2: 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(_ other: Vec2) { + x = Component(other.x) + y = Component(other.y) + } + + public init(_ other: Vec2) { + 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(_ other: Vec2) { + x = Component(other.x) + y = Component(other.y) + } + + public init(_ other: Vec2) { + 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 +public typealias Vec2f = Vec2 +public typealias Vec2d = Vec2 + +public struct Vec3: 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(_ other: Vec3) { + x = Component(other.x) + y = Component(other.y) + z = Component(other.z) + } + + public init(_ other: Vec3) { + 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(_ other: Vec3) { + x = Component(other.x) + y = Component(other.y) + z = Component(other.z) + } + + public init(_ other: Vec3) { + 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 +public typealias Vec3f = Vec3 +public typealias Vec3d = Vec3 + + +public struct Vec4: 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(_ other: Vec4) { + x = Component(other.x) + y = Component(other.y) + z = Component(other.z) + w = Component(other.w) + } + + public init(_ other: Vec4) { + 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(_ other: Vec4) { + x = Component(other.x) + y = Component(other.y) + z = Component(other.z) + w = Component(other.w) + } + + public init(_ other: Vec4) { + 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 +public typealias Vec4f = Vec4 +public typealias Vec4d = Vec4 diff --git a/Tests/ArtifactMathTests/ArtifactMathTests.swift b/Tests/ArtifactMathTests/ArtifactMathTests.swift new file mode 100644 index 0000000..58427c5 --- /dev/null +++ b/Tests/ArtifactMathTests/ArtifactMathTests.swift @@ -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 = [] + } +}