bug(compose): synth_cylinder / synth_plate_with_holes produce solid-with-no-hole — CSG never fires on curved primitives inside plane bodies #83
Labels
No labels
blocked:needs-decision
blocked:preflight
bug
in-progress
in-progress
lint-cleanup
needs-decompose
quickfix
triage-approved
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/pc2cad#83
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The compose engine's synth path produces correct single-solid topology for plane-only and curved-only inputs, but on inputs where a curved primitive (cylinder bore, hole, pocket) lives INSIDE a plane-body envelope, the curved primitive never gets CSG-subtracted. The output is a solid plate/cup/block with no hole.
Coverage metric passes (1.000 at 5mm) because the points that should be ON the hole wall are within tolerance of the solid surface — they ARE inside the solid, but the metric only checks distance, not topology. STEP roundtrip reads as 1 MANIFOLD_SOLID_BREP, but with the hole missing.
Reproduction
Same pattern on samples/synth/synth_plate_with_holes.ply: fitter detects 0 holes (even though input has visible through-holes), synth produces a solid plate with no holes, coverage 1.000 at 5mm, 0 CSG features.
Root cause
The graph has edges for plane-to-plane binding (OPPOSES, ADJACENT, SHARES_CAP — wired in #80). It has NO edge for curved-to-plane binding ("this cylinder primitive is enclosed by these planes"). As a result:
The "ENCLOSES" or "AXIS_OF" edge from a curved primitive to its surrounding planes would let cluster_bodies merge them into one body. Without that edge, curved primitives end up in singleton/multi-primitive bodies with axis=None and the synth path returns nothing useful.
Expected
synth_cylinder.ply STEP contains a cup with a through-bore; coverage at 1mm ≥0.85 (some loss expected near the bore wall due to scan noise). synth_plate_with_holes.ply STEP contains a plate with multiple through-holes; coverage at 1mm ≥0.85.
Actual
Both STEPs have 1 solid, 0 holes. Coverage 1.000 at 5mm because the missing-hole points are INSIDE the solid (within tolerance). The metric gate is fooled.
Acceptance
Visual evidence
vision_analyze on /tmp/render_analytic/synth_cylinder/side_by_side.png (--mode analytic forced the synth path; CLI default --mode auto mis-routes these to freeform fallback, which produces a triangulated-mesh STEP — separate but related CLI bug):
Notes
The CLI default --mode auto also routes these samples to the freeform fallback (because the auto-classifier doesn't recognise them as mechanical primitives despite the fitter finding good primitive fits). That routing produces a triangulated mesh STEP (0 MANIFOLD_SOLID_BREP, 2000 PLANE triangles, ~6MB) — a separate issue from this CSG gap, but the visible symptom is similar.
Not a regression: this CSG gap predates #80. Mark/pc2cad#76 added body-aware feature sweep specifically for bolt-circle patterns that RANSAC missed, suggesting the underlying ENCLOSES edge gap was already known but not addressed at the graph level.
Refs: scan-to-cad primitive-graph-phase3 reference (the CSG-with-subtract path documented but not exercised for in-body-bore cases), #80 (same BFS-binding gap class for planes), #76 (bolt-circle body-aware sweep that works around this gap by detecting features post-hoc rather than at the graph level).