Skip to content

Expose the coplanar-group map of the input triangles - #24

Merged
danielepanozzo merged 1 commit into
mainfrom
expose-coplanar-groups
Aug 12, 2026
Merged

Expose the coplanar-group map of the input triangles#24
danielepanozzo merged 1 commit into
mainfrom
expose-coplanar-groups

Conversation

@danielepanozzo

Copy link
Copy Markdown
Collaborator

What

out_triangle_provenance is indexed by coplanar group, and nothing in the API said which group an input triangle landed in. A caller therefore received per-group lists of output faces with no way back to the input — and so no way to carry a per-triangle attribute (a material tag, which input file a surface came from) through the arrangement.

The mapping was already computed: BSPcomplex::constraint_coplanar_group, filled by computeCoplanarGroups(), and even written out by the -g debug dump. It was simply never returned.

This adds one output to embed_tri_in_poly_mesh:

std::vector<uint32_t>& out_triangle_group,   // per input triangle -> index into out_triangle_provenance

Why it is composed, not returned raw

Constraint indices are not input triangle indices. read_nodes_and_constraints drops degenerate (misAlignment) triangles as it copies them, compacting everything after them, and records the original index in tri_original_index. So the new output composes the two maps and hands back the caller's own numbering:

  • a triangle dropped as degenerate maps to UINT32_MAX — it contributed nothing to the arrangement and belongs to no group;
  • edge- and point-forcing triangles are grouped internally but carry no original index, so they never name a slot.

Tests

tests/unit_tests_3d.cpp is new — the first unit tests of the 3D embedding API that CI actually runs. embed_regression covers a real model but is hidden behind Catch2's [.] tag because it takes over an hour; these use a six-tet cube and four triangles and finish in well under a second.

They pin the three cases that matter:

input expected
two triangles sharing an edge, exactly coplanar same group
a third sharing an edge but folded out of plane different group
a degenerate (collinear) triangle UINT32_MAX, later triangles still on their own input indices

The degenerate triangle sits in the middle of the input on purpose: with it last, a map that returned the internal constraint numbering would pass by coincidence. Verified as a negative control — reverting the composition to the identity makes the test fail.

embed_regression also gained a cheap invariant check on the new output.

Local ctest: full suite green.

out_triangle_provenance is indexed by coplanar group, and until now nothing in
the API said which group an input triangle landed in. A caller therefore got
per-group lists of output faces with no way back to the input, and so no way to
carry a per-triangle attribute -- a material tag, which input file a surface came
from -- through the arrangement. The mapping was already computed
(BSPcomplex::constraint_coplanar_group, filled by computeCoplanarGroups) and even
dumped by the -g debug writer; it was just never returned.

Add out_triangle_group, one entry per input triangle. It is composed with
constraint_original_index rather than returned raw: constraint indices are not
input triangle indices, because read_nodes_and_constraints drops degenerate
(misAlignment) triangles as it copies, compacting everything after them. A
dropped triangle gets UINT32_MAX -- it contributed nothing to the arrangement and
belongs to no group. Edge- and point-forcing triangles are grouped internally but
carry no original index, so they never name a slot.

Also add tests/unit_tests_3d.cpp: the first unit tests for the 3D embedding API
that CI actually runs. embed_regression covers a real model but is hidden behind
Catch2's [.] because it takes over an hour; these use a six-tet cube and four
triangles and finish in well under a second. They pin the three cases that
matter -- edge-adjacent and coplanar triangles share a group, an edge-adjacent
but folded triangle does not, and a degenerate triangle maps to UINT32_MAX with
the surviving triangles still on their own input indices. The degenerate triangle
sits in the middle of the input on purpose, so a map that returned the internal
constraint numbering fails rather than passing by coincidence (verified: it does).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@danielepanozzo
danielepanozzo merged commit 64c52aa into main Aug 12, 2026
6 checks passed
@danielepanozzo
danielepanozzo deleted the expose-coplanar-groups branch August 12, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant