From bb2b7c64065a79afab902d954b560c35b3815757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Franze=CC=81n?= Date: Sat, 21 Sep 2024 19:29:12 +0200 Subject: [PATCH] Add offsettingBounds and settingBoundsSize --- .../Operations/Bounds/OffsetBounds.swift | 54 ++++++++++++ .../Operations/Bounds/SetBoundsSize.swift | 84 +++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 Sources/SwiftSCAD/Operations/Bounds/OffsetBounds.swift create mode 100644 Sources/SwiftSCAD/Operations/Bounds/SetBoundsSize.swift diff --git a/Sources/SwiftSCAD/Operations/Bounds/OffsetBounds.swift b/Sources/SwiftSCAD/Operations/Bounds/OffsetBounds.swift new file mode 100644 index 0000000..13a3a56 --- /dev/null +++ b/Sources/SwiftSCAD/Operations/Bounds/OffsetBounds.swift @@ -0,0 +1,54 @@ +import Foundation + +public extension Geometry2D { + /// Offsets the boundary of this geometry, leaving the geometry itself unmodified. + /// + /// The geometry remains in its current position, while its boundary is moved by the specified offset. + /// - Parameter offset: A vector describing the amount by which to translate the boundary. + /// - Returns: A geometry with the boundary translated by the given offset. + + func offsettingBounds(_ offset: Vector2D) -> any Geometry2D { + ReadBoundary2D(body: self) { snapshot, boundary in + SetBounds2D(body: snapshot, boundary: boundary.translated(offset)) + } + } + + /// Offsets the boundary of this geometry, leaving the geometry itself unmodified. + /// + /// The geometry remains in its current position, while its boundary is moved by the specified offset in the `x` and `y` directions. + /// - Parameters: + /// - x: The offset along the X axis to apply to the boundary. Defaults to `0`. + /// - y: The offset along the Y axis to apply to the boundary. Defaults to `0`. + /// - Returns: A geometry with the boundary translated by the given offsets. + + func offsettingBounds(x: Double = 0, y: Double = 0) -> any Geometry2D { + offsettingBounds(.init(x: x, y: y)) + } +} + +public extension Geometry3D { + /// Offsets the boundary of this geometry, leaving the geometry itself unmodified. + /// + /// The geometry remains in its current position, while its boundary is moved by the specified offset. + /// - Parameter offset: A vector describing the amount by which to translate the boundary. + /// - Returns: A geometry with the boundary translated by the given offset. + + func offsettingBounds(_ offset: Vector3D) -> any Geometry3D { + ReadBoundary3D(body: self) { snapshot, boundary in + SetBounds3D(body: snapshot, boundary: boundary.translated(offset)) + } + } + + /// Offsets the boundary of this geometry, leaving the geometry itself unmodified. + /// + /// The geometry remains in its current position, while its boundary is moved by the specified offset in the `x` and `y` directions. + /// - Parameters: + /// - x: The offset along the X axis to apply to the boundary. Defaults to `0`. + /// - y: The offset along the Y axis to apply to the boundary. Defaults to `0`. + /// - z: The offset along the Z axis to apply to the boundary. Defaults to `0`. + /// - Returns: A geometry with the boundary translated by the given offsets. + + func offsettingBounds(x: Double = 0, y: Double = 0, z: Double = 0) -> any Geometry3D { + offsettingBounds(.init(x: x, y: y, z: z)) + } +} diff --git a/Sources/SwiftSCAD/Operations/Bounds/SetBoundsSize.swift b/Sources/SwiftSCAD/Operations/Bounds/SetBoundsSize.swift new file mode 100644 index 0000000..b2df8e6 --- /dev/null +++ b/Sources/SwiftSCAD/Operations/Bounds/SetBoundsSize.swift @@ -0,0 +1,84 @@ +import Foundation + +public extension Geometry2D { + private func settingBounds(snapshot: GeometrySnapshot2D, currentSize: Vector2D, targetSize: Vector2D, alignment: GeometryAlignment2D) -> any Geometry2D { + let translation = (targetSize - currentSize) * alignment.factors + return snapshot + .aligned(at: .origin) + .translated(translation) + .settingBounds(.init(minimum: .zero, maximum: targetSize)) + } + + /// Set the size of the bounding box of this geometry. + /// + /// The resulting bounding box is aligned at the origin, with a size specified by the given parameters. If a dimension is not provided, the current size for that dimension is preserved. + /// The geometry is translated within the new bounding box according to the specified alignment. + /// - Parameters: + /// - x: The new X size for the bounding box. If `nil`, the current X size is preserved. + /// - y: The new Y size for the bounding box. If `nil`, the current Y size is preserved. + /// - alignment: The alignment specification that controls how the geometry is positioned within the new bounding box. + /// - Returns: A modified geometry with the updated bounding box. + + func settingBoundsSize(x: Double? = nil, y: Double? = nil, alignment: GeometryAlignment2D...) -> any Geometry2D { + measuringBounds { snapshot, box in + let newSize = Vector2D(x ?? box.size.x, y ?? box.size.y) + settingBounds(snapshot: snapshot, currentSize: box.size, targetSize: newSize, alignment: alignment.merged) + } + } + + /// Set the size of the bounding box of this geometry. + /// + /// The resulting bounding box is aligned at the origin with the specified size. The geometry is translated within the new bounding box according to the specified alignment. + /// - Parameters: + /// - targetSize: The new size of the bounding box. + /// - alignment: The alignment specification that controls how the geometry is positioned within the new bounding box. + /// - Returns: A modified geometry with the updated bounding box. + + func settingBoundsSize(_ targetSize: Vector2D, alignment: GeometryAlignment2D...) -> any Geometry2D { + measuringBounds { snapshot, box in + settingBounds(snapshot: snapshot, currentSize: box.size, targetSize: targetSize, alignment: alignment.merged) + } + } +} + +public extension Geometry3D { + private func settingBounds(snapshot: GeometrySnapshot3D, currentSize: Vector3D, targetSize: Vector3D, alignment: GeometryAlignment3D) -> any Geometry3D { + let translation = (targetSize - currentSize) * alignment.factors + return snapshot + .aligned(at: .origin) + .translated(translation) + .settingBounds(.init(minimum: .zero, maximum: targetSize)) + } + + /// Set the size of the bounding box of this geometry. + /// + /// The resulting bounding box is aligned at the origin, with a size specified by the given parameters. If a dimension is not provided, the current size for that dimension is preserved. + /// The geometry is translated within the new bounding box according to the specified alignment. + /// - Parameters: + /// - x: The new X size for the bounding box. If `nil`, the current X size is preserved. + /// - y: The new Y size for the bounding box. If `nil`, the current Y size is preserved. + /// - z: The new Z size for the bounding box. If `nil`, the current Z size is preserved. + /// - alignment: The alignment specification that controls how the geometry is positioned within the new bounding box. + /// - Returns: A modified geometry with the updated bounding box. + + func settingBoundsSize(x: Double? = nil, y: Double? = nil, z: Double? = nil, alignment: GeometryAlignment3D...) -> any Geometry3D { + measuringBounds { snapshot, box in + let newSize = Vector3D(x ?? box.size.x, y ?? box.size.y, z ?? box.size.z) + settingBounds(snapshot: snapshot, currentSize: box.size, targetSize: newSize, alignment: alignment.merged) + } + } + + /// Set the size of the bounding box of this geometry. + /// + /// The resulting bounding box is aligned at the origin with the specified size. The geometry is translated within the new bounding box according to the specified alignment. + /// - Parameters: + /// - targetSize: The new size of the bounding box. + /// - alignment: The alignment specification that controls how the geometry is positioned within the new bounding box. + /// - Returns: A modified geometry with the updated bounding box. + + func settingBoundsSize(_ targetSize: Vector3D, alignment: GeometryAlignment3D...) -> any Geometry3D { + measuringBounds { snapshot, box in + settingBounds(snapshot: snapshot, currentSize: box.size, targetSize: targetSize, alignment: alignment.merged) + } + } +}