Skip to content

Fix IndexError in replay_simplification for meshes with unreferenced vertices (#60) - #97

Merged
akaszynski merged 1 commit into
mainfrom
fix/replay-unreferenced-vertices
Aug 12, 2026
Merged

Fix IndexError in replay_simplification for meshes with unreferenced vertices (#60)#97
akaszynski merged 1 commit into
mainfrom
fix/replay-unreferenced-vertices

Conversation

@akaszynski

Copy link
Copy Markdown
Member

Summary

Fixes #60replay_simplification raised IndexError: index N is out of bounds for axis 0 with size N inside _map_isolated_points when the input mesh contained vertices not referenced by any triangle.

Root cause

The decimation core drops unreferenced vertices, so _replay.return_points() never includes them. But the pure-array bookkeeping compute_indice_mapping computes ranks over all non-collapsed vertices — including the unreferenced ones — and assigns each an index. Because ranks are assigned in ascending original-index order, an unreferenced vertex shifts the decimated index of every vertex that follows it. The resulting indice_mapping therefore points one-or-more past the end of the decimated point set, and:

  • the final indice_mapping = mapping[indice_mapping] remap (or, when an unreferenced vertex is touched by a degenerate edge, _map_isolated_points) indexes out of bounds → the crash.
  • even when it doesn't crash (unreferenced vertex at the very end), the shift silently corrupts the mapping for all following vertices.

This is common for meshes carrying UV/texture data (the issue reporter loaded via trimesh), which frequently contain unreferenced vertices.

Fix

Strip unreferenced vertices up front so the vertex numbering is dense, replay on the compacted mesh, then re-expand indice_mapping back to the original vertex count with unreferenced vertices mapped to -1 (they are not part of the triangle-only decimated mesh). Collapses are remapped to the compacted numbering; since an unreferenced vertex has no incident edge it can never be a collapse origin, so a negative entry there indicates collapses that don't match the mesh and raises a clear ValueError.

Meshes whose vertices are all referenced are untouched (same code path as before).

Reproducer (before this PR)

import numpy as np, fast_simplification as fs
# triangulated grid + a few UNREFERENCED vertices
pts = np.random.rand(50, 3)                      # 50 unreferenced points ...
grid_pts, faces = ...                            # ... appended to a real grid
points = np.vstack([grid_pts, pts])
_, _, coll = fs.simplify(points, faces, target_reduction=0.75, return_collapses=True)
fs.replay_simplification(points.astype(np.float32), faces, coll)  # -> IndexError

Testing

Two regression tests (offline, no VTK/network): unreferenced vertices at the end of the array, and inserted in the middle (the silent-shift case) — asserting no crash, that unreferenced vertices map to -1, and that the decimated mesh and the referenced-vertex mapping are bit-for-bit identical to the same mesh without the isolated vertices. Full suite: 41 tests pass locally (PyVista 0.48.4 / VTK 9.6.2).

🤖 Generated with Claude Opus 4.8 (Claude Code)

…vertices (#60)

Vertices not referenced by any triangle are dropped by the decimation
core, but the pure-array index bookkeeping (compute_indice_mapping)
retained them and assigned each an index. That shifted the decimated
index of every vertex following an unreferenced one, so indice_mapping
pointed past the end of the decimated points and _map_isolated_points
(or the final remap) raised:

    IndexError: index N is out of bounds for axis 0 with size N

This is common with meshes carrying UV/texture data (e.g. loaded via
trimesh), which frequently contain unreferenced vertices.

Strip unreferenced vertices up front so the vertex numbering is dense,
run the replay on the compacted mesh, then re-expand indice_mapping to
the original vertex count with unreferenced vertices mapped to -1. The
collapses are remapped to the compacted numbering; an unreferenced
vertex can never be a collapse origin, so a negative entry there means
the collapses do not match the mesh and raises a clear ValueError.

Meshes whose vertices are all referenced take an unchanged code path.

Adds two regression tests: unreferenced vertices at the end of the
array, and inserted in the middle (which previously shifted the mapping
silently) with the decimated mesh and referenced-vertex mapping asserted
identical to the isolated-free mesh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@akaszynski
akaszynski merged commit cc6f246 into main Aug 12, 2026
13 checks passed
@akaszynski
akaszynski deleted the fix/replay-unreferenced-vertices branch August 12, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

IndexError in _map_isolated_points

1 participant