Skip to content

No recoverable mapping from torn seam-duplicate vertices back to the consumer's pre-HEM mesh #100

Description

@csparker247

Use case

Downstream consumers do not use HalfEdgeMesh as their primary mesh type.
They keep their own mesh (the "raw" / pre-HEM mesh — their own vertex array and
face list) and wrap it in a HalfEdgeMesh only to flatten: build the HEM with
insert_vertices/insert_faces, tear it (split_path), extract components,
parameterize, and pack/merge. They then build a per-wedge UV map from the
packed result and expect to apply it back onto their pre-HEM mesh, keyed by
their own (face, corner) identities.

Summary

The round-trip is atlas corner → (F2 maps) → torn-HEM corner → pre-HEM mesh.
Three of the four hops already round-trip cleanly:

  • Face index. insert_faces inserts faces in order
    (HalfEdgeMesh.hpp:1040-1049) and split_path only tears edges (never
    re-inserts faces), so pre-HEM face i == HEM face i, carried back by F2's
    face_map/face_source.
  • Corner order. insert_face may auto-reverse a mis-wound face to stay
    manifold (HalfEdgeMesh.hpp:1706-1748), permuting a face's stored corner
    order. This is not a blocker: the documented per-wedge recipe resolves
    corners by vertex identity against the consumer's own face, never by raw
    traversal index (ChartPacking.hpp:108-123), so the reversal is absorbed,
    not inverted.
  • Non-seam vertices. insert_vertices preserves order and tearing only
    appends vertices, so a non-duplicated HEM vertex keeps its pre-HEM index;
    carried back by F2's vertex_map/vertex_source.

The one hop that breaks is seam-vertex identity. Tearing duplicates seam
vertices via insert_vertex(oldStart->pos) (HalfEdgeMesh.hpp:1445,1465),
appending a new index whose only link to its origin is a copied position.
split_path/split_edge return void and record no duplicate → original
map. So a torn-HEM corner that lands on a seam duplicate cannot be expressed
in the consumer's pre-HEM vertex namespace
— and identity-based corner
resolution (the very mechanism that absorbs the winding reversal) fails for
exactly those corners.

Why this matters

The per-wedge UV recipe in MultiChartFlatten.cpp is self-consistent within
the torn HEM's own namespace, but the consumer's goal is to land UVs on their
pre-HEM mesh. Non-seam corners already work; a seam corner silently cannot,
because the duplicate has no recoverable pre-HEM vertex identity. The same gap
blocks scattering per-vertex attributes captured on the pre-HEM mesh and
emitting output indexed by the consumer's original vertices. Surfaced during
PR #99 (multi-chart packing).

Goal

Make every torn/extracted vertex — original or seam duplicate — name its
pre-HEM (input) vertex, so the documented identity-keyed per-wedge recipe
resolves corners against the consumer's own faces for all corners, seams
included. Composed with F2's vertex_map/face_map and
vertex_source/face_source, a consumer can take any packed/merged atlas
corner and name the pre-HEM face, corner, and vertex it came from.

Out of scope

  • Removing auto-rewinding or seam duplication (both intentional).
  • Recording the insert_face corner-order permutation / a reversed flag.
    Identity-keyed corner resolution (ChartPacking.hpp:108-123) absorbs the
    reversal, so it does not need to be inverted for this use case — the
    corner order is recovered implicitly once seam-duplicate vertices carry their
    pre-HEM identity, by locating each vertex within the consumer's own face.

Candidate approaches (to decide in the track)

  1. Vertex origin tracking (primary) — store an origin (pre-HEM vertex
    index) set on construction and copied to duplicates by split_edge, so every
    vertex names its pre-HEM vertex. Survives extraction via the clone_face_
    vertex copy path. With this, corners are located by identity in the
    consumer's own face and the winding reversal needs no separate record.
  2. Returned remapssplit_edge/split_path return/accumulate
    duplicate → original pairs; lighter-weight but does not survive extraction
    without the caller threading it through, and gives no uniform "every vertex
    names its input vertex" accessor.

References

  • include/OpenABF/HalfEdgeMesh.hpp:1445,1465 (seam-vertex duplication — the gap)
  • include/OpenABF/HalfEdgeMesh.hpp:1706-1748 (winding reversal — absorbed, not a blocker)
  • include/OpenABF/ChartPacking.hpp:108-123 (identity-keyed per-wedge recipe)
  • PR feat: Multi-chart UV packing (PackCharts) #99 (multi-chart packing): MultiChartFlatten.cpp reference comment +
    PackCharts/MergeMeshes provenance maps (the downstream half of the chain).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions