Found while diagnosing the Route B failure in #86.
The defect
recipes/03-pipe-flange/main.swift:45 builds the flange by revolving a wire:
let section = Wire.polygon([...])!
var flange = Shape.revolve(profile: section, axisOrigin: .zero,
axisDirection: SIMD3(0, 1, 0), angle: 2 * .pi)!
Shape.revolve(profile:) revolves a curve, so it correctly returns a shell, not a solid. This
is standard BRepPrimAPI_MakeRevol behaviour and not an OCCTSwift bug. Rebuilt and measured:
recipe03 flange: type=shell valid=true vol=228707.95 faces=6
The recipe then chamfers it and cuts a bolt circle, treating it as a solid throughout. Its README
describes the output as a solid body.
Why CI does not catch it
Scripts/recipe-check.sh asserts the emitted volume is greater than zero. A closed shell
returns a correct positive volume from volumeInertia, so the check passes. Nothing currently
asserts the result is a solid.
The committed recipes/03-pipe-flange/output.brep reports shapeType: compound with volume
241242.9, which looks healthy by every metric the recipe suite measures.
Why it matters
A shell is a surface model. It will not fillet reliably, exports to STEP as surfaces rather than a
solid body, and misbehaves as a boolean operand. In #86 the same pattern produced disconnected
surface patches once real cuts were attempted against it, with Shape.volume returning nil and
the written BREP giving a negative volume. Recipe 03 gets away with it because its bolt-circle cut
happens to leave a closed shell, but it is the same latent error and it is being shipped as a
reference example.
Fix
Revolve a face:
guard let section = Wire.polygon([...]),
let face = Shape.face(from: section),
let flange = face.revolved(axisOrigin: .zero, axisDirection: SIMD3(0, 1, 0)) else { ... }
Verified on the #86 blank: Shape.face(from: wire).revolved(...) yields type=solid with the
same volume, and downstream booleans then behave correctly.
Suggested follow-up
Two things worth doing alongside the fix:
- Audit the other recipes for the same pattern. Any
Shape.revolve(profile:) or
Shape.extrude(profile:) whose result is treated as a solid should be checked.
- Strengthen
recipe-check.sh to assert the emitted body is a solid, not merely that its
volume is positive. That is the assertion which would have caught this, and it is the same
class of gap as a check that cannot fail.
Found while diagnosing the Route B failure in #86.
The defect
recipes/03-pipe-flange/main.swift:45builds the flange by revolving a wire:Shape.revolve(profile:)revolves a curve, so it correctly returns a shell, not a solid. Thisis standard
BRepPrimAPI_MakeRevolbehaviour and not an OCCTSwift bug. Rebuilt and measured:The recipe then chamfers it and cuts a bolt circle, treating it as a solid throughout. Its README
describes the output as a solid body.
Why CI does not catch it
Scripts/recipe-check.shasserts the emitted volume is greater than zero. A closed shellreturns a correct positive volume from
volumeInertia, so the check passes. Nothing currentlyasserts the result is a solid.
The committed
recipes/03-pipe-flange/output.brepreportsshapeType: compoundwith volume241242.9, which looks healthy by every metric the recipe suite measures.
Why it matters
A shell is a surface model. It will not fillet reliably, exports to STEP as surfaces rather than a
solid body, and misbehaves as a boolean operand. In #86 the same pattern produced disconnected
surface patches once real cuts were attempted against it, with
Shape.volumereturning nil andthe written BREP giving a negative volume. Recipe 03 gets away with it because its bolt-circle cut
happens to leave a closed shell, but it is the same latent error and it is being shipped as a
reference example.
Fix
Revolve a face:
Verified on the #86 blank:
Shape.face(from: wire).revolved(...)yieldstype=solidwith thesame volume, and downstream booleans then behave correctly.
Suggested follow-up
Two things worth doing alongside the fix:
Shape.revolve(profile:)orShape.extrude(profile:)whose result is treated as a solid should be checked.recipe-check.shto assert the emitted body is a solid, not merely that itsvolume is positive. That is the assertion which would have caught this, and it is the same
class of gap as a check that cannot fail.