Skip to content

v1.0.3 — per-input history Tier 2 + Tier 3 (closes #165)

Choose a tag to compare

@gsdali gsdali released this 09 May 05:40
· 110 commits to main since this release

v1.0.3 — full per-input history Tier 2 & Tier 3 (closes #165)

Completes #165. Builds on the boolean-history surface from v1.0.2 by extending the same opaque-handle pattern to modification ops and threading history capture through FeatureReconstructor.

Tier 2 — modification ops with full per-input history

Five new Shape extensions, all returning (result: Shape, history: ShapeHistoryRef)?:

extension Shape {
    func filletedWithFullHistory(radius: Double, edges: [Int])
    func filletedWithFullHistory(edge: Int, startRadius: Double, endRadius: Double)  // variable radius
    func chamferedWithFullHistory(distance: Double, edges: [Int])
    func shelledWithFullHistory(facesToRemove: [Int], thickness: Double, tolerance: Double = 1e-3)
    func defeaturedWithFullHistory(faces: [Int])
}

Each retains its OCCT builder so Modified / Generated / IsDeleted stay queryable per input sub-shape. Filleted edges typically appear in record.generated (new fillet faces generated FROM the edge) plus record.isDeleted == true for the original edge; defeatured faces appear in record.isDeleted == true; shelled inputs split between modified (the offset shell) and deleted (the removed face).

The ShapeHistoryRef class itself is unchanged — same record(of:) lookup contract as v1.0.2. The bridge reuses the same OCCTBooleanHistory opaque handle since the underlying type stores a unique_ptr<BRepBuilderAPI_MakeShape>, and every OCCT modification builder (BRepFilletAPI_MakeFillet, BRepFilletAPI_MakeChamfer, BRepOffsetAPI_MakeThickSolid, BRepAlgoAPI_Defeaturing) inherits from that base.

Tier 3 — FeatureReconstructor.BuildResult.histories

public struct BuildResult: Sendable {
    // … existing fields …
    public let histories: [String: ShapeHistoryRef]
}

Per-feature ShapeHistoryRef keyed by the feature id. Populated when:

  • A boolean spec (FeatureSpec.Boolean) with a non-nil id resolves successfully — captured from unionWithFullHistory / subtractedWithFullHistory / intersectionWithFullHistory.
  • A hole spec (FeatureSpec.Hole) with a non-nil id — captured from the underlying subtract.
  • An additive feature (revolve / extrude / sheet-metal) with a non-nil id whose absorbAdditive step fuses into a non-empty current (i.e. the second additive feature onward) — captured from the union.

Features without an id aren't keyed (no key to attach the history under). The existing applyFillet / applyChamfer paths still go through the non-history primitives — wiring them through requires edge/face index computation in those paths and is tracked as a separate refinement.

This unblocks OCCTMCP's remap_selection for the apply_feature tool: instead of falling back to centroid-distance heuristics on splits / merges / deletions, the consumer can now walk BuildResult.histories[feature_id].record(of: subshape) for the exact OCCT-recorded mapping.

Binary

xcframework unchanged from v1.0.0. SPM consumers using the remote URL continue resolving against the v1.0.0 asset; SPM picks up the Swift-side additions directly from this tag.

Op count

4,279 → 4,284 (+5 Tier 2 entry points).

🤖 Generated with Claude Code