Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,18 @@ No tests exist. No linter is configured.
- **Colors** are `[Float]` RGBA (0–1), with predefined constants on `ScriptContext.Colors` (e.g., `.steel`, `.brass`, `.copper`).
- **Geometry types accepted by `ScriptContext.add()`**: `Shape` (solids/shells/compounds/faces), `Wire`, `Edge` (the latter two are converted via `Shape.fromWire`/`Shape.fromEdge`).
- **BREP over STEP**: BREP is the primary format (~1ms vs ~50ms for STEP). STEP export is optional (`ScriptContext(exportSTEP: false)` to disable; `occtkit run --format` controls it via source rewriting).
- **TopologyGraph export**: `ctx.addGraph(_)` writes `graph-N.json` (BREPGraph v1) and optionally `graph-N.sqlite`. `ctx.addGraphsForAllShapes(sqlite:)` is a convenience for batch export.
- **BRepGraph export**: `ctx.addGraph(_)` writes `graph-N.json` (BREPGraph v1) and optionally `graph-N.sqlite`. `ctx.addGraphsForAllShapes(sqlite:)` is a convenience for batch export.
- **occtkit verbs throw, never `exit()`** — required so `--serve` can recover and continue. Use `ScriptError.message(...)` (in `ScriptContext.swift`) for ad-hoc failures; `GraphIO` helpers throw on every failure path. Standalone wrappers catch + exit 1 in their own `main.swift`.
- **Adding a new verb** = one file in `Sources/occtkit/Commands/` plus one entry in `Registry.all` (`Sources/occtkit/Subcommand.swift`). Standalone targets should not gain new verbs; they exist only for legacy compatibility.

## Dependencies

The full cohort graduated to v1.0 on 2026-05-07 alongside OCCT 8.0.0 GA. SemVer-stable from these floors — only bump on documented breaking changes. Pre-1.0 dep history (which API landed in which 0.x tag) lives in git log; consult it when you actually need to support an older floor, otherwise treat the v1.0 surface as the contract.

- **OCCTSwift** — `https://github.com/gsdali/OCCTSwift.git` (>= 1.7.1; xcframework built against **OCCT 8.0.0p1**). The B-Rep kernel: ~400+ methods for parametric CAD, the full ISO drawings stack (Sheet/TitleBlock/ProjectionSymbol/Section2D/Hatch/AutoCentermarks/CuttingPlaneLine/CosmeticThread/SurfaceFinish/GDT/DetailView/DrawingScale), `FeatureReconstructor` for `reconstruct`, the `SheetMetal` namespace for `compose-sheet-metal`, and the XCAF surfaces (`AssemblyNode.labelId`, `Document.node(at:)`) for `inspect-assembly` / `set-metadata`. **Floored at v1.7.1 (= OCCT 8.0.0p1)**: v1.7.0 realigned the BRepGraph wrapper to OCCT's redesigned graph model (definitions vs references/usages, persistent UIDs, controlled layers) and v1.7.1 made the derived graph reads real again — `adjacentFaces`/`faces(of:)`/`edges(of:)`/`sharedEdges`, `faceSameDomain`, `faceIsNaturalRestriction` — plus durable `UID`/`RefUID`/`ItemUID` identity. Our graph verbs (graph-validate/compact/dedup/ml, query-topology) build and run **unchanged** against it. Behaviour changes are **confined to the BRepGraph domain**: `edgeMaxContinuity`/`setEdgeRegularity` are now no-ops (use `Shape.maxContinuity` for continuity); `degenerated`/`closed`/`sameParameter`/`sameRange` setters no-op while their getters return the live derived value. The cookbook ergonomics relied on since v1.3.1 — `Shape.circularPatternCut` (#169), orientation-normalised `Shape.sweep` + `orientedForward`/`signedVolume` (#170), `concaveEdges`/`convexEdges`/`edges(where:)` selectors (#171) — are unchanged. The rest of the cohort still resolves at its existing floors (Viewport 1.0.4 / Tools 1.1.1 / AIS 1.0.2 / Mesh 1.0.0 / IO 1.0.0), all of which compile against 1.7.1.
- **OCCTSwift** — `https://github.com/gsdali/OCCTSwift.git` (>= 1.15.0; xcframework built against **OCCT 8.0.0p1**). The B-Rep kernel: ~400+ methods for parametric CAD, the full ISO drawings stack (Sheet/TitleBlock/ProjectionSymbol/Section2D/Hatch/AutoCentermarks/CuttingPlaneLine/CosmeticThread/SurfaceFinish/GDT/DetailView/DrawingScale), `FeatureReconstructor` for `reconstruct`, the `SheetMetal` namespace for `compose-sheet-metal`, and the XCAF surfaces (`AssemblyNode.labelId`, `Document.node(at:)`) for `inspect-assembly` / `set-metadata`. **Floored at v1.15.0**: v1.15.0 renamed the Swift wrapper class `TopologyGraph` → `BRepGraph` (OCCTSwift#335) to match the C++ package it wraps; this repo has migrated off the deprecated `TopologyGraph` typealias onto `BRepGraph` directly (OCCTSwiftScripts#78), so the floor must guarantee the `BRepGraph` symbol exists. Earlier, v1.7.0 realigned the BRepGraph wrapper to OCCT's redesigned graph model (definitions vs references/usages, persistent UIDs, controlled layers) and v1.7.1 made the derived graph reads real again — `adjacentFaces`/`faces(of:)`/`edges(of:)`/`sharedEdges`, `faceSameDomain`, `faceIsNaturalRestriction` — plus durable `UID`/`RefUID`/`ItemUID` identity. Our graph verbs (graph-validate/compact/dedup/ml, query-topology) build and run **unchanged** against it. Behaviour changes are **confined to the BRepGraph domain**: `edgeMaxContinuity`/`setEdgeRegularity` are now no-ops (use `Shape.maxContinuity` for continuity); `degenerated`/`closed`/`sameParameter`/`sameRange` setters no-op while their getters return the live derived value. The cookbook ergonomics relied on since v1.3.1 — `Shape.circularPatternCut` (#169), orientation-normalised `Shape.sweep` + `orientedForward`/`signedVolume` (#170), `concaveEdges`/`convexEdges`/`edges(where:)` selectors (#171) — are unchanged. The rest of the cohort still resolves at its existing floors (Viewport 1.0.4 / Tools 1.1.1 / AIS 1.0.2 / Mesh 1.0.0 / IO 1.0.0), all of which compile against 1.15.0.
- **OCCTSwiftViewport** — `https://github.com/gsdali/OCCTSwiftViewport.git` (>= 1.0.0). Provides `OffscreenRenderer`, `CameraState`, `DisplayMode`, `ViewportBody` for `render-preview`. Graduated to v1.0.0 on 2026-05-08, one day after the rest of the cohort; floor unblocked by Tools v1.0.2 (closes #45).
- **OCCTSwiftTools** — `https://github.com/gsdali/OCCTSwiftTools.git` (>= 1.0.0; resolves to v1.0.2+ for the widened Viewport constraint). Bridge layer between the B-Rep kernel and the Metal viewport. We use `CADFileLoader.shapeToBodyAndMetadata` in `render-preview` for Shape → `ViewportBody` conversion (both input bodies and highlight sub-shapes). Lives in its own repo since Viewport v0.55.0 split.
- **OCCTSwiftAIS** — `https://github.com/gsdali/OCCTSwiftAIS.git` (>= 1.0.0). Headless-friendly subset only: `Trihedron` / `WorkPlane` / `Axis` / `PointCloud` scene objects (each emits `[ViewportBody]` via `makeBodies()`) for `render-preview`'s `--show-axes` / `--show-workplane` overlays, plus the SubShape selection vocabulary for `--highlight face[N]/edge[M]/vertex[K]`. Selection / Manipulator / SwiftUI surfaces aren't relevant to a CLI. `Dimension` overlays render via a SwiftUI Canvas inside `MetalViewportView` and so can't reach `OffscreenRenderer` — `--annotate-dimensions` is deferred (filed as OCCTSwiftViewport#26). Note: `OCCTSwiftAIS` re-exports a `DisplayMode` enum (3 cases) that collides with `OCCTSwiftViewport.DisplayMode` (6 cases); fully-qualify in `RenderPreview.swift` as `OCCTSwiftViewport.DisplayMode`.
- **OCCTSwiftMesh** — `https://github.com/gsdali/OCCTSwiftMesh.git` (>= 1.0.0). LGPL-2.1 wrapper that vendors `meshoptimizer` (BSD-2-Clause / MIT-equivalent) inside `OCCTMeshOptimizer` for QEM decimation. Powers `simplify-mesh` via `Mesh.simplified(_:)`. Smoothing / repair / remeshing are future work.
- **OCCTSwiftIO** — `https://github.com/gsdali/OCCTSwiftIO.git` (>= 1.0.0). Provides `TopologyGraph.exportForML` / `exportJSON` via extension after OCCTSwift v0.171.0 hoisted them out of the kernel. **Pulled into the `GraphML` standalone target and the `graph-ml` verb only** — the rest of the package keeps its existing `ScriptManifest` type from `Sources/ScriptHarness/Manifest.swift` (which carries a `graphs` field that OCCTSwiftIO's `ScriptManifest` is missing). If a future verb wants progress-aware STEP loading via `ShapeLoader.load(from:format:progress:)`, broaden the dep then.
- **OCCTSwiftIO** — `https://github.com/gsdali/OCCTSwiftIO.git` (>= 1.0.0). Provides `BRepGraph.exportForML` / `exportJSON` via extension after OCCTSwift v0.171.0 hoisted them out of the kernel. **Pulled into the `GraphML` standalone target and the `graph-ml` verb only** — the rest of the package keeps its existing `ScriptManifest` type from `Sources/ScriptHarness/Manifest.swift` (which carries a `graphs` field that OCCTSwiftIO's `ScriptManifest` is missing). If a future verb wants progress-aware STEP loading via `ShapeLoader.load(from:format:progress:)`, broaden the dep then.
- **macOS 15+**, **Swift 6.0+**.
26 changes: 13 additions & 13 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,14 @@ let package = Package(
// orientation #170, concave/convex/edges(where:) #171) are unchanged.
// 1.8.0 adds Exporter.writeBREP(allowInvalid:) for the load-brep /
// import `--allow-invalid` flags (OCCTMCP #41).
occtDep("OCCTSwift", from: "1.12.9"), // ≥1.12.9: OCCT kernel crash/hang fixes through #318 and #323 (patches 0003-0009)
// Bumped 1.12.9 -> 1.15.0 (OCCTSwiftScripts#78): v1.15.0 renamed the
// Swift wrapper class `TopologyGraph` -> `BRepGraph` (OCCTSwift#335) to
// match the C++ package it wraps. This repo has migrated its own
// `TopologyGraph` references to `BRepGraph`, and the bare `BRepGraph`
// symbol doesn't exist before v1.15.0 (only the deprecated typealias
// does, starting there) — the floor must track the rename, not just
// permit it via the open `from:` range.
occtDep("OCCTSwift", from: "1.15.0"), // ≥1.15.0: BRepGraph rename (OCCTSwift#335); also carries kernel crash/hang fixes through #318/#323 (patches 0003-0009) from the prior 1.12.9 floor
// RenderPreview rasterizes through Viewport's OffscreenRenderer.
// Floored at v1.0.4: v1.0.3 fixes an uncatchable quantize() crash on
// body load (Viewport #30) and v1.0.4 makes the published Viewport
Expand Down Expand Up @@ -117,7 +124,7 @@ let package = Package(
// the `simplify-mesh` verb.
occtDep("OCCTSwiftMesh", from: "1.0.0"),
// OCCTSwiftIO v1.0.0 graduated alongside OCCTSwift v1.0.0. Provides
// TopologyGraph.exportForML / exportJSON via extension after OCCTSwift
// BRepGraph.exportForML / exportJSON via extension after OCCTSwift
// v0.171.0 hoisted them out of the kernel. Pulled into GraphML and
// graphml verbs only — the rest of the package keeps its existing
// ScriptManifest type (with the `graphs` field) from ScriptHarness.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ The `occtkit` executable is a separate target — install via the `Makefile` abo

- macOS 15+
- Swift 6.0+
- [OCCTSwift](https://github.com/SecondMouseAU/OCCTSwift) `>= 1.0.1` (xcframework built against OCCT 8.0.0 GA)
- [OCCTSwift](https://github.com/SecondMouseAU/OCCTSwift) `>= 1.15.0` (xcframework built against OCCT 8.0.0 GA; floor required for the `BRepGraph` rename, see OCCTSwift#335)
- [OCCTSwiftViewport](https://github.com/SecondMouseAU/OCCTSwiftViewport) `>= 1.0.0` (powers `render-preview`)
- [OCCTSwiftTools](https://github.com/SecondMouseAU/OCCTSwiftTools) `>= 1.0.0` (bridge layer used by `render-preview` for Shape→ViewportBody conversion)
- [OCCTSwiftAIS](https://github.com/SecondMouseAU/OCCTSwiftAIS) `>= 1.0.0` (headless overlays for `render-preview`'s `--show-axes` / `--show-workplane` / `--highlight`)
Expand Down
2 changes: 1 addition & 1 deletion Sources/GraphML/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation
import OCCTSwift
import OCCTSwiftIO // TopologyGraph.exportForML / GraphExport extension (v0.171.0+)
import OCCTSwiftIO // BRepGraph.exportForML / GraphExport extension (v0.171.0+)
import ScriptHarness

struct MLExport: Codable {
Expand Down
20 changes: 10 additions & 10 deletions Sources/ScriptHarness/BREPGraphJSONExporter.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// BREPGraphJSONExporter.swift
// ScriptHarness
//
// Serializes a TopologyGraph to a JSON file following the BREPGraph v1 schema.
// Serializes a BRepGraph to a JSON file following the BREPGraph v1 schema.

import Foundation
import OCCTSwift

/// Exports a `TopologyGraph` to a structured JSON file.
/// Exports a `BRepGraph` to a structured JSON file.
public enum BREPGraphJSONExporter {

/// Export a topology graph to a JSON file.
/// - Parameters:
/// - graph: The topology graph to export.
/// - url: Destination file URL.
/// - description: Optional description for the metadata.
public static func export(_ graph: TopologyGraph, to url: URL, description: String? = nil) throws {
public static func export(_ graph: BRepGraph, to url: URL, description: String? = nil) throws {
let doc = buildDocument(graph, description: description)
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
Expand All @@ -24,7 +24,7 @@ public enum BREPGraphJSONExporter {

// MARK: - Document Assembly

static func buildDocument(_ g: TopologyGraph, description: String?) -> GraphDocument {
static func buildDocument(_ g: BRepGraph, description: String?) -> GraphDocument {
let s = g.stats

let statsBlock = StatsBlock(
Expand Down Expand Up @@ -84,7 +84,7 @@ public enum BREPGraphJSONExporter {

// MARK: - Nodes

static func buildNodes(_ g: TopologyGraph) -> NodesBlock {
static func buildNodes(_ g: BRepGraph) -> NodesBlock {
// Vertices
var vertices: [VertexNode] = []
for i in 0..<g.vertexCount {
Expand Down Expand Up @@ -248,8 +248,8 @@ public enum BREPGraphJSONExporter {

// MARK: - References

static func buildReferences(_ g: TopologyGraph) -> ReferencesBlock {
func collectRefs(_ refKind: TopologyGraph.RefKind, count: Int) -> [RefEntry] {
static func buildReferences(_ g: BRepGraph) -> ReferencesBlock {
func collectRefs(_ refKind: BRepGraph.RefKind, count: Int) -> [RefEntry] {
var entries: [RefEntry] = []
for i in 0..<count {
guard let childKind = g.refChildNodeKind(refKind, refIndex: i) else { continue }
Expand Down Expand Up @@ -278,7 +278,7 @@ public enum BREPGraphJSONExporter {

// MARK: - Adjacency (COO sparse)

static func buildAdjacency(_ g: TopologyGraph) -> AdjacencyBlock {
static func buildAdjacency(_ g: BRepGraph) -> AdjacencyBlock {
var f2fSrc: [Int] = [], f2fTgt: [Int] = []
var f2eSrc: [Int] = [], f2eTgt: [Int] = []
var e2vSrc: [Int] = [], e2vTgt: [Int] = []
Expand Down Expand Up @@ -306,7 +306,7 @@ public enum BREPGraphJSONExporter {

// MARK: - Assembly

static func buildAssembly(_ g: TopologyGraph) -> AssemblyBlock {
static func buildAssembly(_ g: BRepGraph) -> AssemblyBlock {
var products: [ProductEntry] = []
for i in 0..<g.productCount {
let root = g.productShapeRoot(i)
Expand Down Expand Up @@ -338,7 +338,7 @@ public enum BREPGraphJSONExporter {

// MARK: - Helpers

static func nodeKindName(_ kind: TopologyGraph.NodeKind) -> String {
static func nodeKindName(_ kind: BRepGraph.NodeKind) -> String {
switch kind {
case .solid: return "solid"
case .shell: return "shell"
Expand Down
Loading
Loading