bug(compose): cone apex lost — output is a truncated cylinder with hallucinated hole (synth_cone coverage 0.503, methodology_cone 0.367) #74

Closed
opened 2026-09-19 01:46:17 +00:00 by Mark · 0 comments
Owner

Three coupled bugs surfaced together in the Sep-18 visual-regression sweep on samples/synth/synth_cone.ply (and the same shape on samples/methodology_cone.ply) — fixed in commit 4f2d3a5 on main:

1. _synthesize_curved_only (pc2cad_compose.py) — the apex was lost.

The cone primitive's inlier range (t_min, t_max) was treated as the cone's actual extent, with r_bottom = abs(t_min) * tan(angle). When the fitter's t_min was small but non-zero (the typical noisy-data RANSAC output, e.g. t_min=0.0081 for synth_cone), the synth produced a frustum with two non-zero radii — the apex was rendered as a small ring instead of a single vertex.

Fix: the apex radius is exactly 0; the base radius is at the FAR end of the inlier range (max(|t_min|, |t_max|) * tan(angle)); the build123d Cone is positioned so the apex vertex lands on the fit's apex point.

2. synthesize_body (pc2cad_compose.py) — no branch for a cone-dominant cluster.

With synth_cone the cluster has 1 plane (base) + 2 cones (redundant lateral-surface fits). The previous code fell through to the planar-extrusion-then-SUBTRACT-curved path and produced a frustum-with-hole shape.

Fix: when the cluster contains cone primitives, the best-cone (most inliers) is emitted as the body. Coincident planes are redundant — the cone primitive already includes its base circle.

3. synthesize_with_fallback (pc2cad_compose.py) — unit mismatch in validation.

pts (raw pcd.points, in input units — mm) was compared against the synthesized solid (model units — m). Every distance comparison was off by ~1000×, validation reported coverage=0.000 against the correct cone, and the orchestrator fell through to the legacy path (which doesn't recognise cones and re-emitted the broken frustum-with-hole).

Fix: rescale pts by units_scale_factor from fit_result.report before validation.

4. apply_plane_axial_holes (pc2cad.py) — plane_cone ≠ hole.

Stopped emitting 'hole' features for plane_cone intersections. The cone's base circle is the cone's base, not a hole — flagging it as a hole produced the 'hallucinated hole near the top' symptom called out in the issue.


Coverage status (not fully meeting the ≥0.85 acceptance):

The synth cone is now well-shaped (clean Cone primitive with apex vertex, no hole, no frustum) and well-positioned for clean fits, but the cone RANSAC itself is non-deterministic and produces fits with apex offsets of ~5-10 mm from origin on these samples. The synth faithfully reflects the fit's parameters — when the fit's apex is at (3.5, -3.4, 85.7) instead of (0, 0, 80), the synth cone is positioned accordingly. Coverage at 1 mm tolerance is 0.61 (with fine tessellation, mesh_tol=0.05) on synth_cone; 0.37 on methodology_cone (where the fit lands further off-center).

The cone-RANSAC accuracy is a separate bug — fixing it requires a more robust axis estimation than the current PCA-on-inliers seed. The synth code is now correct and ready for that improvement.

Verified:

  • --engine compose now emits a Cone primitive for synth_cone (was: 55-face polygon extrusion). STEP has 6 faces, 1 apex vertex.
  • report.features no longer contains a 'hole' with source: plane_cone.
  • report.trimmed_holes is empty.
  • All 74 compose/reconstruction/methodology/holes/pre-dispatch/determinism tests pass; 2 new regression tests for the cone-apex and multi-cone-cluster paths.
Three coupled bugs surfaced together in the Sep-18 visual-regression sweep on samples/synth/synth_cone.ply (and the same shape on samples/methodology_cone.ply) — fixed in commit 4f2d3a5 on main: **1. `_synthesize_curved_only` (pc2cad_compose.py) — the apex was lost.** The cone primitive's inlier range `(t_min, t_max)` was treated as the cone's actual extent, with `r_bottom = abs(t_min) * tan(angle)`. When the fitter's `t_min` was small but non-zero (the typical noisy-data RANSAC output, e.g. `t_min=0.0081` for synth_cone), the synth produced a frustum with two non-zero radii — the apex was rendered as a small ring instead of a single vertex. Fix: the apex radius is exactly 0; the base radius is at the FAR end of the inlier range (`max(|t_min|, |t_max|) * tan(angle)`); the build123d `Cone` is positioned so the apex vertex lands on the fit's apex point. **2. `synthesize_body` (pc2cad_compose.py) — no branch for a cone-dominant cluster.** With synth_cone the cluster has 1 plane (base) + 2 cones (redundant lateral-surface fits). The previous code fell through to the planar-extrusion-then-SUBTRACT-curved path and produced a frustum-with-hole shape. Fix: when the cluster contains cone primitives, the best-cone (most inliers) is emitted as the body. Coincident planes are redundant — the cone primitive already includes its base circle. **3. `synthesize_with_fallback` (pc2cad_compose.py) — unit mismatch in validation.** `pts` (raw `pcd.points`, in input units — mm) was compared against the synthesized solid (model units — m). Every distance comparison was off by ~1000×, validation reported coverage=0.000 against the correct cone, and the orchestrator fell through to the legacy path (which doesn't recognise cones and re-emitted the broken frustum-with-hole). Fix: rescale `pts` by `units_scale_factor` from `fit_result.report` before validation. **4. `apply_plane_axial_holes` (pc2cad.py) — `plane_cone` ≠ hole.** Stopped emitting `'hole'` features for `plane_cone` intersections. The cone's base circle is the cone's base, not a hole — flagging it as a hole produced the 'hallucinated hole near the top' symptom called out in the issue. --- **Coverage status (not fully meeting the ≥0.85 acceptance):** The synth cone is now well-shaped (clean `Cone` primitive with apex vertex, no hole, no frustum) and well-positioned for clean fits, but the cone RANSAC itself is non-deterministic and produces fits with apex offsets of ~5-10 mm from origin on these samples. The synth faithfully reflects the fit's parameters — when the fit's apex is at `(3.5, -3.4, 85.7)` instead of `(0, 0, 80)`, the synth cone is positioned accordingly. Coverage at 1 mm tolerance is 0.61 (with fine tessellation, mesh_tol=0.05) on synth_cone; 0.37 on methodology_cone (where the fit lands further off-center). The cone-RANSAC accuracy is a separate bug — fixing it requires a more robust axis estimation than the current PCA-on-inliers seed. The synth code is now correct and ready for that improvement. Verified: - `--engine compose` now emits a `Cone` primitive for synth_cone (was: 55-face polygon extrusion). STEP has 6 faces, 1 apex vertex. - `report.features` no longer contains a `'hole'` with `source: plane_cone`. - `report.trimmed_holes` is empty. - All 74 compose/reconstruction/methodology/holes/pre-dispatch/determinism tests pass; 2 new regression tests for the cone-apex and multi-cone-cluster paths.
Mark closed this issue 2026-09-19 03:27:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Mark/pc2cad#74
No description provided.