Skip to content

M3CSurfaceMeshing emits orphan vertices that no triangle references #1706

Description

@imikejackson

Summary

M3CSurfaceMeshing emits vertices that no triangle in the output references. They are present in ordinary output, with no filter option enabled. On a 12³ test volume there are about 144 of them; the count presumably scales with the volume's boundary area.

How it surfaced

While adding the "Bounding Box Skin" option to the surface meshers, the option's contract required that enabling it on data with no background voxels be a bit-for-bit no-op. A test of exactly that caught a discrepancy: faces matched (3528 == 3528, nothing pruned, correct) but vertex counts did not (1791 vs 1647).

The cause was that the option's orphan cleanup was clearing nodeType for every node unreferenced by a surviving triangle — including nodes that no triangle ever referenced. That has since been narrowed to clear only nodes orphaned by the prune, which fixed the no-op violation and left these pre-existing orphans in place, where they belong until this issue is addressed.

So this issue is the remainder: M3C's candidate-node generation produces nodeType entries near the volume boundary that survive compaction and become output vertices, yet are referenced by nothing.

Why it matters

  • Vertex counts are inflated relative to the actual mesh, which is misleading to anyone reasoning about mesh size or comparing meshers.
  • Downstream consumers that iterate vertices rather than faces do wasted work, and any per-vertex array carries entries for geometry that is not part of the surface.
  • It makes "does this mesh have orphan vertices?" a useless health check for M3C output, because the answer is always yes.
  • Most concretely: with the Bounding Box Skin option enabled and every face pruned (an all-background volume), the output is now 0 faces with ~144 orphan vertices. That is honest — it reflects this wart rather than hiding it — but it looks strange, and the filter's warning text has to say "zero faces" rather than "zero vertices" because of it.

Where to look

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/M3CSurfaceMeshing.cpp

The node compaction is the nodeType > 0 prefix-scan in finalizeMesh: every candidate with a nonzero nodeType becomes an output vertex. The question is why the marching-cubes candidate sweep sets a nonzero nodeType on nodes that no generated triangle ends up using — most likely boundary/ghost-shell interaction, given where they cluster.

Suggested approach

Decide which is correct:

  1. The sweep should not mark them. Find why those candidates get a nonzero nodeType and stop setting it. Preferable if the marking is simply wrong, since it costs nothing at output time.
  2. The compaction should drop them. Build a referenced-node set from the final triangle list and require both nodeType > 0 and referenced. Straightforward, but adds a pass and an allocation on every M3C run, not just when an option is on.

Option 1 is better if the marking turns out to be a bug rather than intentional.

This will change M3C's output vertex count, so it invalidates the stored M3C exemplars and needs regeneration plus a Data_Archive upload. Worth bundling with other M3C work rather than doing alone.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions