Swift Package that bridges the iShape Rust geometry toolkit (via the bundled i_shape_ffi crate) into native Swift APIs. It ships prebuilt static libraries plus thin Swift wrappers so you can run high-performance polygon Boolean operations on Apple platforms without touching Rust directly.
Current package version: 0.2.6
dependencies: [
.package(url: "https://github.com/iShape-Rust/iShape-swift.git", from: "0.2.6")
],
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "iShapeKit", package: "iShape-swift")
]
)
]Rebuild the FFI artifacts whenever you change the Rust sources:
cd iShape-swift
./build_ffi.shThen build or test with SwiftPM (the script emits static libs for macOS, iOS device, and iOS simulator):
CLANG_MODULE_CACHE_PATH=$(pwd)/.cache/clang swift build --disable-sandbox
CLANG_MODULE_CACHE_PATH=$(pwd)/.cache/clang swift test --disable-sandboximport iShapeKit
let overlay = IntOverlay()
// Add one subject shape (outer contour plus a hole).
overlay.addSubject([[
[IntPoint(0, 0), IntPoint(0, 10), IntPoint(10, 10), IntPoint(10, 0)],
[IntPoint(4, 4), IntPoint(4, 6), IntPoint(6, 6), IntPoint(6, 4)],
]])
overlay.addClip([[
[IntPoint(5, 0), IntPoint(5, 10), IntPoint(12, 10), IntPoint(12, 0)],
]])
let buffer = FlatShapesBuffer()
overlay.overlay(overlayRule: .union, fillRule: .evenOdd, output: buffer)
let shapes: IntShapes = buffer.toIntShapes()
print(shapes.count) // -> 1
print(shapes[0].count) // -> outer contour + one holeFlatShapesBuffer can be reused across calls and passed over the FFI boundary, while the high-level helpers return Swift-native [[[IntPoint]]] structures for convenience.
FloatOverlay mirrors the integer API, but takes and returns CGPoint:
import CoreGraphics
import iShapeKit
let overlay = FloatOverlay()
overlay.addSubject([[
[CGPoint(x: 0, y: 0), CGPoint(x: 0, y: 4), CGPoint(x: 4, y: 4), CGPoint(x: 4, y: 0)],
]])
overlay.addClip([[
[CGPoint(x: 2, y: 0), CGPoint(x: 2, y: 4), CGPoint(x: 6, y: 4), CGPoint(x: 6, y: 0)],
]])
let result: CGPointShapes? = overlay.overlay(overlayRule: .union, fillRule: .evenOdd)Use CGPointStrokeOffset for a constant-width path stroke:
let stroked = CGPointStrokeOffset.offsetContours(
points: [
CGPoint(x: 0, y: 0),
CGPoint(x: 10, y: 0),
CGPoint(x: 10, y: 10),
],
distance: 1,
isClosedPath: false,
style: StrokeOffsetStyle(lineJoin: .round(0.2), lineCap: .round(0.2))
)Use CGPointVariableStrokeOffset when every vertex has its own full stroke width:
let variable = CGPointVariableStrokeOffset.offsetContours(
vertices: [
CGPointVariableStrokeVertex(x: 0, y: 0, width: 2),
CGPointVariableStrokeVertex(x: 10, y: 0, width: 8),
CGPointVariableStrokeVertex(x: 20, y: 10, width: 4),
],
isClosedPath: false
)Use overlayHierarchy when the caller also needs immediate nesting links from
a floating-point Boolean operation:
let hierarchy = overlay.overlayHierarchy(
overlayRule: .union,
fillRule: .nonZero
)
let childIndices = Set(hierarchy?.links.map(\.childShapeIndex) ?? [])
let rootShapes = hierarchy?.shapes.enumerated().compactMap { index, shape in
childIndices.contains(index) ? nil : shape
}parentContourIndex is a global index across the flattened contour order of
shapes, while the parent and child shape indices address shapes directly.
CGPointConvexDecomposition triangulates shapes, applies Delaunay refinement,
and groups the triangles into non-overlapping counter-clockwise convex polygons:
let polygons = CGPointConvexDecomposition.toConvexPolygons(
shape: [[
CGPoint(x: 0, y: 0),
CGPoint(x: 6, y: 0),
CGPoint(x: 6, y: 2),
CGPoint(x: 2, y: 2),
CGPoint(x: 2, y: 6),
CGPoint(x: 0, y: 6),
]]
)Use the input:output: overload with FlatF64ShapesBuffer to reuse allocated
FFI buffers across calls.