Skip to content

Commit 57016c8

Browse files
committed
Continue new GameMath implementation
1 parent 1a3a247 commit 57016c8

File tree

8 files changed

+255
-14
lines changed

8 files changed

+255
-14
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright © 2025 Dustin Collins (Strega's Gate)
3+
* All Rights Reserved.
4+
*
5+
* http://stregasgate.com
6+
*/
7+
8+
public extension Position2n where Scalar: BinaryInteger {
9+
var oldVector: Position2 {
10+
return Position2(x: Float(self.x), y: Float(self.y))
11+
}
12+
}
13+
14+
public extension Position2n where Scalar: BinaryFloatingPoint {
15+
var oldVector: Position2 {
16+
return Position2(x: Float(self.x), y: Float(self.y))
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright © 2025 Dustin Collins (Strega's Gate)
3+
* All Rights Reserved.
4+
*
5+
* http://stregasgate.com
6+
*/
7+
8+
public extension Size2n where Scalar: BinaryInteger {
9+
var oldVector: Size2 {
10+
return Size2(x: Float(self.x), y: Float(self.y))
11+
}
12+
}
13+
14+
public extension Size2n where Scalar: BinaryFloatingPoint {
15+
var oldVector: Size2 {
16+
return Size2(x: Float(self.x), y: Float(self.y))
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright © 2025 Dustin Collins (Strega's Gate)
3+
* All Rights Reserved.
4+
*
5+
* http://stregasgate.com
6+
*/
7+
8+
public extension Vector2n where Scalar: BinaryInteger {
9+
init(oldVector vector2: some Vector2) {
10+
self.init(x: Scalar(vector2.x), y: Scalar(vector2.y))
11+
}
12+
}
13+
14+
public extension Vector2n where Scalar: BinaryFloatingPoint {
15+
init(oldVector vector2: some Vector2) {
16+
self.init(x: Scalar(vector2.x), y: Scalar(vector2.y))
17+
}
18+
}

Sources/GameMath/2D Types (New)/Rect2n.swift

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,76 @@
88
public typealias Rect2i = Rect2n<Int>
99
public typealias Rect2f = Rect2n<Float>
1010

11+
@frozen
1112
public struct Rect2n<Scalar: Vector2n.ScalarType> {
12-
public var position: Position2n<Scalar>
13+
public var origin: Position2n<Scalar>
1314
public var size: Size2n<Scalar>
1415

1516
@inlinable
16-
public init(position: Position2n<Scalar>, size: Size2n<Scalar>) {
17-
self.position = position
17+
public init(origin: Position2n<Scalar>, size: Size2n<Scalar>) {
18+
self.origin = origin
1819
self.size = size
1920
}
2021

2122
@inlinable
2223
public init(size: Size2n<Scalar>, center: Position2n<Scalar>) where Scalar: BinaryFloatingPoint {
23-
self.position = center - size * Size2n<Scalar>(width: 0.5, height: 0.5)
24+
self.origin = center - (size / 2)
2425
self.size = size
2526
}
2627
}
2728

29+
extension Rect2n where Scalar: FixedWidthInteger {
30+
@inlinable
31+
public var center: Position2n<Scalar> {
32+
get {
33+
return origin + (size / 2)
34+
}
35+
mutating set {
36+
self.origin = newValue - (size / 2)
37+
}
38+
}
39+
}
40+
41+
extension Rect2n where Scalar: FloatingPoint {
42+
@inlinable
43+
public var center: Position2n<Scalar> {
44+
get {
45+
return origin + (size / 2)
46+
}
47+
mutating set {
48+
self.origin = newValue - (size / 2)
49+
}
50+
}
51+
}
52+
extension Rect2n {
53+
@inlinable
54+
public var min: Position2n<Scalar> {
55+
return self.origin
56+
}
57+
58+
@inlinable
59+
public var max: Position2n<Scalar> {
60+
return self.origin + self.size
61+
}
62+
}
63+
2864
extension Rect2n {
2965
@_transparent
3066
public var x: Scalar {
3167
get {
32-
return position.x
68+
return origin.x
3369
}
3470
mutating set {
35-
position.x = newValue
71+
origin.x = newValue
3672
}
3773
}
3874
@_transparent
3975
public var y: Scalar {
4076
get {
41-
return position.y
77+
return origin.y
4278
}
4379
mutating set {
44-
position.y = newValue
80+
origin.y = newValue
4581
}
4682
}
4783
@_transparent
@@ -63,3 +99,29 @@ extension Rect2n {
6399
}
64100
}
65101
}
102+
103+
extension Rect2n where Scalar: Comparable {
104+
/// `true` if `rhs` is inside `self`
105+
public func contains(_ rhs: Position2n<Scalar>, withThreshold threshold: Scalar = 0) -> Bool {
106+
let min = self.min - threshold
107+
guard rhs.x >= min.x && rhs.y >= min.y else {return false}
108+
109+
let max = self.max + threshold
110+
guard rhs.x < max.x && rhs.y < max.y else {return false}
111+
112+
return true
113+
}
114+
115+
public func nearestSurfacePosition(to point: Position2n<Scalar>) -> Position2n<Scalar> {
116+
return point.clamped(from: self.min, to: self.max)
117+
}
118+
}
119+
120+
121+
// MARK: - Common Protocol Conformances
122+
extension Rect2n: Equatable where Scalar: Equatable { }
123+
extension Rect2n: Hashable where Scalar: Hashable { }
124+
extension Rect2n: Sendable where Scalar: Sendable { }
125+
extension Rect2n: Codable where Scalar: Codable { }
126+
extension Rect2n: BitwiseCopyable where Scalar: BitwiseCopyable { }
127+
extension Rect2n: BinaryCodable where Self: BitwiseCopyable { }

Sources/GameMath/2D Types (New)/Triangle2n.swift

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
* http://stregasgate.com
66
*/
77

8+
public typealias Triangle2f = Triangle2n<Float32>
9+
public typealias Triangle2d = Triangle2n<Float64>
10+
811
@frozen
912
public struct Triangle2n<Scalar: Vector2n.ScalarType & FloatingPoint> {
1013
/// The cartesian position of the triangle's first point.
@@ -19,13 +22,103 @@ public struct Triangle2n<Scalar: Vector2n.ScalarType & FloatingPoint> {
1922
- parameter p2: The cartesian position of the triangle's second point.
2023
- parameter p3: The cartesian position of the triangle's third point.
2124
*/
22-
public init(_ p1: Position2n<Scalar>, p2: Position2n<Scalar>, p3: Position2n<Scalar>) {
25+
public init(p1: Position2n<Scalar>, p2: Position2n<Scalar>, p3: Position2n<Scalar>) {
2326
self.p1 = p1
2427
self.p2 = p2
2528
self.p3 = p3
2629
}
2730
}
2831

32+
public extension Triangle2n where Scalar: ExpressibleByFloatLiteral {
33+
var center: Position2n<Scalar> {
34+
return Position2n<Scalar>(x: (p1.x + p2.x + p3.x) / 3.0, y: (p1.y + p2.y + p3.y) / 3.0)
35+
}
36+
}
37+
38+
public extension Triangle2n {
39+
@inlinable
40+
func contains(_ position: Position2n<Scalar>) -> Bool where Scalar: ExpressibleByFloatLiteral {
41+
let pa = self.p1
42+
let pb = self.p2
43+
let pc = self.p3
44+
45+
let e10 = pb - pa
46+
let e20 = pc - pa
47+
let a = e10.dot(e10)
48+
let b = e10.dot(e20)
49+
let c = e20.dot(e20)
50+
let ac_bb = (a * c) - (b * b)
51+
let vp = Position2n(x: position.x - pa.x, y: position.y - pa.y)
52+
let d = vp.dot(e10)
53+
let e = vp.dot(e20)
54+
let x = (d * c) - (e * b)
55+
let y = (e * a) - (d * b)
56+
let z = x + y - ac_bb
57+
58+
return z < 0.0 && x >= 0.0 && y >= 0.0
59+
}
60+
61+
/**
62+
Locates a position on the surface of this triangle that is as close to the given point as possible.
63+
- parameter position: A point in space to use as an reference
64+
- returns: The point on the triangle's surface that is nearest to `p`
65+
*/
66+
func nearestSurfacePosition(to position: Position2n<Scalar>) -> Position2n<Scalar> where Scalar: ExpressibleByFloatLiteral {
67+
let a = self.p1
68+
let b = self.p2
69+
let c = self.p3
70+
let p = position
71+
72+
// Check if P in vertex region outside A
73+
let ab = b - a
74+
let ac = c - a
75+
let ap = p - a
76+
77+
let d1 = ab.dot(ap)
78+
let d2 = ac.dot(ap)
79+
if d1 <= 0 && d2 <= 0 {
80+
return a // barycentric coordinates (1,0,0)
81+
}
82+
// Check if P in vertex region outside B
83+
let bp = p - b
84+
let d3 = ab.dot(bp)
85+
let d4 = ac.dot(bp)
86+
if d3 >= 0 && d4 <= d3 {
87+
return b // barycentric coordinates (0,1,0)
88+
}
89+
// Check if P in edge region of AB, if so return projection of P onto AB
90+
let vc = d1 * d4 - d3 * d2
91+
if vc <= 0 && d1 >= 0 && d3 <= 0 {
92+
let v = d1 / (d1 - d3)
93+
return a + ab * v // barycentric coordinates (1-v,v,0)
94+
}
95+
// Check if P in vertex region outside C
96+
let cp = p - c
97+
let d5 = ab.dot(cp)
98+
let d6 = ac.dot(cp)
99+
if d6 >= 0 && d5 <= d6 {
100+
return c // barycentric coordinates (0,0,1)
101+
}
102+
// Check if P in edge region of AC, if so return projection of P onto AC
103+
let vb = d5 * d2 - d1 * d6
104+
if vb <= 0 && d2 >= 0 && d6 <= 0 {
105+
let w = d2 / (d2 - d6)
106+
return a + ac * w // barycentric coordinates (1-w,0,w)
107+
}
108+
// Check if P in edge region of BC, if so return projection of P onto BC
109+
let va = d3 * d6 - d5 * d4
110+
if va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0 {
111+
let w = (d4 - d3) / ((d4 - d3) + (d5 - d6))
112+
return b + (c - b) * w // barycentric coordinates (0,1-w,w)
113+
}
114+
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
115+
let denom = 1.0 / (va + vb + vc)
116+
let v = vb * denom
117+
let w = vc * denom
118+
return a + ab * v + ac * w //=u*a+v*b+w*c,u=va*denom=1.0f-v-w
119+
}
120+
}
121+
29122
// MARK: - Barycentric
30123
public extension Triangle2n where Scalar: ExpressibleByFloatLiteral {
31124
/**

Sources/GameMath/2D Types (New)/Vector2n.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,38 @@ extension Vector2n where Scalar: Hashable {
337337
}
338338
}
339339

340+
public extension Vector2n where Scalar: Comparable {
341+
@inlinable
342+
nonmutating func clamped(from lowerBound: Self, to upperBound: Self) -> Self {
343+
var x = self.x
344+
if x < lowerBound.x { x = lowerBound.x }
345+
if x > upperBound.x { x = upperBound.x }
346+
347+
var y = self.y
348+
if y < lowerBound.y { y = lowerBound.y }
349+
if y > upperBound.y { y = upperBound.y }
350+
351+
return Self(x: x, y: y)
352+
}
353+
354+
@inlinable
355+
mutating func clamp(from lowerBound: Self, to upperBound: Self) {
356+
self = self.clamped(from: lowerBound, to: upperBound)
357+
}
358+
}
359+
360+
public extension Vector2n {
361+
@inlinable
362+
nonmutating func dot<V: Vector2n>(_ vector: V) -> Scalar where V.Scalar == Scalar {
363+
return (x * vector.x) + (y * vector.y)
364+
}
365+
366+
@inlinable
367+
nonmutating func cross<V: Vector2n>(_ vector: V) -> Scalar where V.Scalar == Scalar {
368+
return (x * vector.y) - (y * vector.x)
369+
}
370+
}
371+
340372
extension Vector2n where Scalar: BinaryCodable {
341373
public func encode(into data: inout ContiguousArray<UInt8>, version: BinaryCodableVersion) throws {
342374
try self.x.encode(into: &data, version: version)

Sources/GateEngine/Types/Material.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,16 @@ public struct Material {
106106
)
107107
if Game.shared.renderer.api.origin == .bottomLeft {
108108
self.offset = Position2(
109-
(Float(subRect.position.x) + xRoundingOffset) / Float(texture.size.width),
110-
(Float(subRect.position.y) - yRoundingOffset) / Float(texture.size.height)
109+
(Float(subRect.origin.x) + xRoundingOffset) / Float(texture.size.width),
110+
(Float(subRect.origin.y) - yRoundingOffset) / Float(texture.size.height)
111111
)
112112
if texture.isRenderTarget {
113113
self.scale.y *= -1
114114
}
115115
} else {
116116
self.offset = Position2(
117-
(Float(subRect.position.x) + xRoundingOffset) / Float(texture.size.width),
118-
(Float(subRect.position.y) + yRoundingOffset) / Float(texture.size.height)
117+
(Float(subRect.origin.x) + xRoundingOffset) / Float(texture.size.width),
118+
(Float(subRect.origin.y) + yRoundingOffset) / Float(texture.size.height)
119119
)
120120
}
121121
}

Sources/GateEngine/UI/View.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ open class View {
535535
var material = self.offScreenRepresentationMaterial
536536
material.channel(0) { channel in
537537
channel.texture = offScreenRendering.renderTarget.texture
538-
channel.setSubRect(.init(position: .init(offscreenFrame.position), size: .init(offscreenFrame.size)))
538+
channel.setSubRect(.init(origin: .init(oldVector: offscreenFrame.position), size: .init(oldVector: offscreenFrame.size)))
539539
}
540540
#if DEBUG
541541
material.channel(1) { channel in

0 commit comments

Comments
 (0)