Skip to content

Commit 7a4fe0f

Browse files
committed
feat: create Scalable3D protocol; conform Size3D, AffineTransform3D and Vecto3D with Scalable3D protocol
1 parent 67f3571 commit 7a4fe0f

7 files changed

Lines changed: 203 additions & 0 deletions

File tree

Sources/OpenSpatial/3D primitives/Size3D.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,31 @@ extension Size3D : Primitive3D {
248248
public func applying(_ transform: AffineTransform3D) -> Size3D {
249249
fatalError("Size3D does not support applying affine transforms.")
250250
}
251+
}
252+
253+
extension Size3D : Scalable3D {
254+
255+
/// Returns a new entity scaled by the specified size.
256+
///
257+
/// - Parameter size: A size that contains the scale factors for each axis.
258+
/// - Returns: A new scaled entity.
259+
/// - Complexity: O(1)
260+
@inline(__always)
261+
public func scaled(by size: Size3D) -> Size3D {
262+
.init(width: self.width * size.width,
263+
height: self.height * size.height,
264+
depth: self.depth * size.depth)
265+
}
266+
267+
/// Returns a new entity scaled uniformly by the specified factor.
268+
///
269+
/// - Parameter scale: A double-precision value that specifies the uniform scale factor.
270+
/// - Returns: A new scaled entity.
271+
/// - Complexity: O(1)
272+
@inline(__always)
273+
public func uniformlyScaled(by scale: Double) -> Size3D {
274+
.init(width: self.width * scale,
275+
height: self.height * scale,
276+
depth: self.depth * scale)
277+
}
251278
}

Sources/OpenSpatial/Affine and projective transforms/AffineTransform3D.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,33 @@ extension AffineTransform3D {
9090
public static func *= (lhs: inout AffineTransform3D, rhs: AffineTransform3D) {
9191
lhs = lhs * rhs
9292
}
93+
}
94+
95+
extension AffineTransform3D : Scalable3D {
96+
97+
// MARK: - Scaling transforms
98+
99+
/// Returns a new affine transform scaled by the specified size.
100+
///
101+
/// - Parameter size: A size that contains the scale factors for each axis.
102+
/// - Returns: A new scaled affine transform.
103+
/// - Complexity: O(1)
104+
@inline(__always)
105+
public func scaled(by size: Size3D) -> AffineTransform3D {
106+
var scaleTransform = AffineTransform3D()
107+
scaleTransform[0, 0] = size.width
108+
scaleTransform[1, 1] = size.height
109+
scaleTransform[2, 2] = size.depth
110+
return self * scaleTransform
111+
}
112+
113+
/// Returns a new entity scaled uniformly by the specified factor.
114+
///
115+
/// - Parameter scale: A double-precision value that specifies the uniform scale factor.
116+
/// - Returns: A new scaled entity.
117+
/// - Complexity: O(1)
118+
@inline(__always)
119+
public func uniformlyScaled(by scale: Double) -> AffineTransform3D {
120+
self.scaled(by: Size3D(width: scale, height: scale, depth: scale))
121+
}
93122
}

Sources/OpenSpatial/Data structures/Vector3D.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,29 @@ extension Vector3D : Primitive3D {
315315
let newZ = x * transform.matrix[0][2] + y * transform.matrix[1][2] + z * transform.matrix[2][2] + transform.matrix[3][2]
316316
return Vector3D(x: newX, y: newY, z: newZ)
317317
}
318+
}
319+
320+
extension Vector3D : Scalable3D {
321+
322+
// MARK: - Instance methods
323+
324+
/// Returns a new entity scaled by the specified size.
325+
///
326+
/// - Parameter size: A size that contains the scale factors for each axis.
327+
/// - Returns: A new scaled entity.
328+
/// - Complexity: O(1)
329+
@inline(__always)
330+
public func scaled(by size: Size3D) -> Vector3D {
331+
.init(x: x * size.width, y: y * size.height, z: z * size.depth)
332+
}
333+
334+
/// Returns a new entity scaled uniformly by the specified factor.
335+
///
336+
/// - Parameter scale: A double-precision value that specifies the uniform scale factor.
337+
/// - Returns: A new scaled entity.
338+
/// - Complexity: O(1)
339+
@inline(__always)
340+
public func uniformlyScaled(by scale: Double) -> Vector3D {
341+
.init(x: x * scale, y: y * scale, z: z * scale)
342+
}
318343
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import Foundation
2+
3+
/// A protocol that defines scalable 3D types.
4+
public protocol Scalable3D {
5+
6+
// MARK: - Instance methods
7+
8+
/// Scales the entity by the specified size.
9+
///
10+
/// - Parameter size: A size that contains the scale factors for each axis.
11+
mutating func scale(by size: Size3D)
12+
13+
/// Scales the entity by the specified factors.
14+
///
15+
/// - Parameters:
16+
/// - x: A double-precision value that specifies the scale factor for the x-axis.
17+
/// - y: A double-precision value that specifies the scale factor for the y-axis.
18+
/// - z: A double-precision value that specifies the scale factor for the z-axis.
19+
mutating func scaleBy(x: Double, y: Double, z: Double)
20+
21+
/// Returns a new entity scaled by the specified size.
22+
///
23+
/// - Parameter size: A size that contains the scale factors for each axis.
24+
/// - Returns: A new scaled entity.
25+
func scaled(by size: Size3D) -> Self
26+
27+
/// Scales the entity uniformly by the specified factor.
28+
///
29+
/// - Parameter scale: A double-precision value that specifies the uniform scale factor.
30+
mutating func uniformlyScale(by scale: Double)
31+
32+
/// Returns a new entity scaled uniformly by the specified factor.
33+
///
34+
/// - Parameter scale: A double-precision value that specifies the uniform scale factor.
35+
/// - Returns: A new scaled entity.
36+
func uniformlyScaled(by scale: Double) -> Self
37+
}
38+
39+
extension Scalable3D {
40+
41+
/// Scales the entity by the specified size.
42+
///
43+
/// - Parameter size: A size that contains the scale factors for each axis.
44+
public mutating func scale(by size: Size3D) {
45+
self = self.scaled(by: size)
46+
}
47+
48+
/// Scales the entity by the specified factors.
49+
///
50+
/// - Parameters:
51+
/// - x: A double-precision value that specifies the scale factor for the x-axis.
52+
/// - y: A double-precision value that specifies the scale factor for the y-axis.
53+
/// - z: A double-precision value that specifies the scale factor for the z-axis.
54+
public mutating func scaleBy(x: Double, y: Double, z: Double) {
55+
self.scale(by: Size3D(width: x, height: y, depth: z))
56+
}
57+
58+
/// Scales the entity uniformly by the specified factor.
59+
///
60+
/// - Parameter scale: A double-precision value that specifies the uniform scale factor.
61+
public mutating func uniformlyScale(by scale: Double) {
62+
self.scale(by: Size3D(width: scale, height: scale, depth: scale))
63+
}
64+
}

Tests/OpenSpatialTests/3D primitives/Size3DTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,22 @@ struct Size3DTests {
169169
#expect(infinitySize.height == Double.infinity)
170170
#expect(infinitySize.depth == Double.infinity)
171171
}
172+
173+
// MARK: - Scalable3D tests
174+
175+
@Test func testScaledBy() {
176+
let size = Size3D(width: 2.0, height: 3.0, depth: 4.0)
177+
let scaledSize = size.scaled(by: .init(width: 3, height: 3, depth: 3))
178+
#expect(scaledSize.width == 6.0)
179+
#expect(scaledSize.height == 9.0)
180+
#expect(scaledSize.depth == 12.0)
181+
}
182+
183+
@Test func testUniformScale() {
184+
let size = Size3D(width: 2.0, height: 3.0, depth: 4.0)
185+
let uniformScaledSize = size.uniformlyScaled(by: 2.0)
186+
#expect(uniformScaledSize.width == 4.0)
187+
#expect(uniformScaledSize.height == 6.0)
188+
#expect(uniformScaledSize.depth == 8.0)
189+
}
172190
}

Tests/OpenSpatialTests/Affine and projective transforms/AffineTransform3DTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,30 @@ struct AffineTransform3DTests {
5151
]
5252
#expect(result.matrix == expectedMatrix)
5353
}
54+
55+
// MARK: - Scalable3D tests
56+
57+
@Test func testScalingAffineTransform3D() {
58+
var transform = AffineTransform3D()
59+
transform.scaleBy(x: 2.0, y: 3.0, z: 4.0)
60+
let expectedMatrix: [[Double]] = [
61+
[2.0, 0.0, 0.0, 0.0],
62+
[0.0, 3.0, 0.0, 0.0],
63+
[0.0, 0.0, 4.0, 0.0],
64+
[0.0, 0.0, 0.0, 1.0]
65+
]
66+
#expect(transform.matrix == expectedMatrix)
67+
}
68+
69+
@Test func testUniformScalingAffineTransform3D() {
70+
var transform = AffineTransform3D()
71+
transform.uniformlyScale(by: 5.0)
72+
let expectedMatrix: [[Double]] = [
73+
[5.0, 0.0, 0.0, 0.0],
74+
[0.0, 5.0, 0.0, 0.0],
75+
[0.0, 0.0, 5.0, 0.0],
76+
[0.0, 0.0, 0.0, 1.0]
77+
]
78+
#expect(transform.matrix == expectedMatrix)
79+
}
5480
}

Tests/OpenSpatialTests/Data structures/Vector3DTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,18 @@ struct Vector3DTests {
228228
let transformedVector = vector.applying(transform)
229229
#expect(transformedVector == Vector3D(x: 10.0, y: 40.0, z: 90.0))
230230
}
231+
232+
// MARK: - Scalable3D tests
233+
234+
@Test func testScalingVector3D() {
235+
var vector = Vector3D(x: 1.0, y: 2.0, z: 3.0)
236+
vector.scaleBy(x: 2.0, y: 3.0, z: 4.0)
237+
#expect(vector == Vector3D(x: 2.0, y: 6.0, z: 12.0))
238+
}
239+
240+
@Test func testUniformScalingVector3D() {
241+
var vector = Vector3D(x: 1.0, y: 2.0, z: 3.0)
242+
vector.uniformlyScale(by: 3.0)
243+
#expect(vector == Vector3D(x: 3.0, y: 6.0, z: 9.0))
244+
}
231245
}

0 commit comments

Comments
 (0)