Bug C: pc2cad does not route organic vs mechanical scans differently #49

Closed
opened 2026-06-24 22:25:57 +00:00 by Mark · 1 comment
Owner

Strategy gap: pc2cad does not route organic vs mechanical scans differently

What is missing

pc2cad.py runs the same fitting pipeline on every input. For a
pure-organic scan (e.g. samples/organic_bunny.ply, 100k+ points),
RANSAC primitives are mostly noise — the right output is the Poisson
freeform mesh (export_residual_mesh), not a primitive list. For a
mechanical scan with sharp edges and cylindrical faces, the right
output is RANSAC primitives plus feature cuts (Bug A).

Currently both inputs go through the same fitting pipeline and
produce mediocre results on both:

  • samples/organic_bunny.ply → flat primitive list, even though
    primitive coverage is < 30% and Poisson would explain > 95%
  • samples/strategy_plate_with_hole.ply → freeform mesh fallback,
    even though RANSAC primitives would explain > 85%

There is no classify_scan(pointcloud) -> "organic" | "mechanical" | "mixed" that gates the routing decision, and no strategy that uses
the classification to pick a fitting path.

This is a strategy gap (no scan-classification / routing
algorithm exists), not a function bug (the fitter works on the
samples it was tested on).

Sample

Organic side: samples/organic_bunny.ply (existing on disk)
Mechanical side: samples/strategy_plate_with_hole.ply and
samples/strategy_l_bracket.ply (commit b239497)

What a real fix has to produce

A classify_scan(pointcloud) -> dict that returns at minimum
{"type": "organic" | "mechanical" | "mixed", "confidence": float, features: {...}}. A strategy router (route_strategy(classification, config)) that picks between freeform-Poisson, primitive+features, or
hybrid pipelines based on classification. Wire the router into the
top-level fit_primitives (or a new reconstruct_pointcloud) entry
point so the CLI gets the right output without manual flags.

Acceptance — agentic / visual verification

The implementer must run both commands and attach both output
PNGs and metrics to the closing comment:

# Organic side — must NOT emit a flat primitive list as primary output
python pc2cad_cli.py samples/organic_bunny.ply --out /tmp/bug_C_bunny.step \
    --out-format build123d
python tests/render_compare.py \
    samples/organic_bunny.ply /tmp/bug_C_bunny.step \
    --out-dir /tmp/bug_C_bunny --tolerance 2.0 --min-coverage 0.50

# Mechanical side — must NOT fall through to freeform-only
python pc2cad_cli.py samples/strategy_plate_with_hole.ply \
    --out /tmp/bug_C_plate.step --out-format build123d
python tests/render_compare.py \
    samples/strategy_plate_with_hole.ply /tmp/bug_C_plate.step \
    --spec samples/specs/strategy_plate_with_hole_spec.py \
    --out-dir /tmp/bug_C_plate --tolerance 1.0 --min-coverage 0.85

The closing comment must include:

  1. /tmp/bug_C_bunny/side_by_side.png and
    /tmp/bug_C_plate/side_by_side.png
  2. Numerical coverage values from each metrics.json
  3. A 3-line explanation of the classification: for each sample, what
    feature(s) triggered the routing decision and which pipeline path
    was taken.

A commit that ships classify_scan but does not wire it into the CLI
default path is not a real fix — the bug is about routing, and
routing that the user has to opt into via a flag does not address
it.

Why this is a strategy gap and not a function bug

The current pipeline works on samples it was tuned for. The gap is
that there is no per-scan decision about which strategy to apply. A
real fix requires a classification + routing layer that is on by
default.

  • Bug A (body+features composition)
  • Bug B (inlier coverage metric)
  • Bug D (visual harness committed)

How to regenerate the synthetic samples

python scripts/gen_strategy_samples.py
## Strategy gap: pc2cad does not route organic vs mechanical scans differently ### What is missing `pc2cad.py` runs the same fitting pipeline on every input. For a pure-organic scan (e.g. `samples/organic_bunny.ply`, 100k+ points), RANSAC primitives are mostly noise — the right output is the Poisson freeform mesh (`export_residual_mesh`), not a primitive list. For a mechanical scan with sharp edges and cylindrical faces, the right output is RANSAC primitives plus feature cuts (Bug A). Currently both inputs go through the same fitting pipeline and produce mediocre results on both: - `samples/organic_bunny.ply` → flat primitive list, even though primitive coverage is < 30% and Poisson would explain > 95% - `samples/strategy_plate_with_hole.ply` → freeform mesh fallback, even though RANSAC primitives would explain > 85% There is no `classify_scan(pointcloud) -> "organic" | "mechanical" | "mixed"` that gates the routing decision, and no strategy that uses the classification to pick a fitting path. This is a **strategy gap** (no scan-classification / routing algorithm exists), not a function bug (the fitter works on the samples it was tested on). ### Sample Organic side: `samples/organic_bunny.ply` (existing on disk) Mechanical side: `samples/strategy_plate_with_hole.ply` and `samples/strategy_l_bracket.ply` (commit `b239497`) ### What a real fix has to produce A `classify_scan(pointcloud) -> dict` that returns at minimum `{"type": "organic" | "mechanical" | "mixed", "confidence": float, features: {...}}`. A strategy router (`route_strategy(classification, config)`) that picks between freeform-Poisson, primitive+features, or hybrid pipelines based on classification. Wire the router into the top-level `fit_primitives` (or a new `reconstruct_pointcloud`) entry point so the CLI gets the right output without manual flags. ### Acceptance — agentic / visual verification The implementer must run **both** commands and attach both output PNGs and metrics to the closing comment: ```bash # Organic side — must NOT emit a flat primitive list as primary output python pc2cad_cli.py samples/organic_bunny.ply --out /tmp/bug_C_bunny.step \ --out-format build123d python tests/render_compare.py \ samples/organic_bunny.ply /tmp/bug_C_bunny.step \ --out-dir /tmp/bug_C_bunny --tolerance 2.0 --min-coverage 0.50 # Mechanical side — must NOT fall through to freeform-only python pc2cad_cli.py samples/strategy_plate_with_hole.ply \ --out /tmp/bug_C_plate.step --out-format build123d python tests/render_compare.py \ samples/strategy_plate_with_hole.ply /tmp/bug_C_plate.step \ --spec samples/specs/strategy_plate_with_hole_spec.py \ --out-dir /tmp/bug_C_plate --tolerance 1.0 --min-coverage 0.85 ``` The closing comment must include: 1. `/tmp/bug_C_bunny/side_by_side.png` and `/tmp/bug_C_plate/side_by_side.png` 2. Numerical coverage values from each `metrics.json` 3. A 3-line explanation of the classification: for each sample, what feature(s) triggered the routing decision and which pipeline path was taken. A commit that ships `classify_scan` but does not wire it into the CLI default path is **not** a real fix — the bug is about routing, and routing that the user has to opt into via a flag does not address it. ### Why this is a strategy gap and not a function bug The current pipeline works on samples it was tuned for. The gap is that there is no per-scan decision about which strategy to apply. A real fix requires a classification + routing layer that is on by default. ### Related - Bug A (body+features composition) - Bug B (inlier coverage metric) - Bug D (visual harness committed) ### How to regenerate the synthetic samples ```bash python scripts/gen_strategy_samples.py ```
Author
Owner

This bug is part of a four-bug batch on the pc2cad reconstruction
strategy gaps. They are designed to be solved together because each
one's verification gate depends on the harness from Bug D and the
synthetic samples from Bug A.

Bug Title Depends on
Bug D (#50) render-compare harness committed —
Bug A (#47) body+features composition Bug D
Bug B (#48) inlier-coverage gate Bug D
Bug C (#49) organic vs mechanical routing Bug A + Bug B

Closing this bug requires running the verification commands in its body
and attaching the resulting side-by-side PNGs to the closing comment.
Closing any of A/B/C without Bug D's harness producing falsifiable
evidence is a workaround, not a fix.

## Cross-references — related strategy bugs This bug is part of a four-bug batch on the pc2cad reconstruction strategy gaps. They are designed to be solved together because each one's verification gate depends on the harness from Bug D and the synthetic samples from Bug A. | Bug | Title | Depends on | |---|---|---| | Bug D (#50) | render-compare harness committed | — | | Bug A (#47) | body+features composition | Bug D | | Bug B (#48) | inlier-coverage gate | Bug D | | Bug C (#49) | organic vs mechanical routing | Bug A + Bug B | Closing this bug requires running the verification commands in its body and attaching the resulting side-by-side PNGs to the closing comment. Closing any of A/B/C without Bug D's harness producing falsifiable evidence is a workaround, not a fix.
Mark 2026-07-07 20:02:26 +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#49
No description provided.