v1.0.3 — per-input history Tier 2 + Tier 3 (closes #165)
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 fromunionWithFullHistory/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
absorbAdditivestep fuses into a non-emptycurrent(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