Skip to content

Add self-intersection tests for triangle meshes - #125

Merged
m-reuter merged 3 commits into
Deep-MI:mainfrom
m-reuter:self-intersections
Oct 10, 2026
Merged

m-reuter merged 3 commits into
Deep-MI:mainfrom
m-reuter:self-intersections

Conversation

@m-reuter

Copy link
Copy Markdown
Member

LaPy had no way to tell whether a triangle mesh intersects itself. That matters after anything that moves vertices, like
smoothing, mean curvature flow or a surface repair: a surface that crosses itself is still a valid manifold for
is_manifold(), but it is not an embedded surface anymore. This PR adds a module lapy.intersect with a vectorized
self-intersection test and a function to undo vertex moves that create intersections.

Changes

New module lapy/intersect.py, listed on the API page under Modules:

  • triangles_intersect(a, b) tests pairs of triangles, given as corner arrays of shape (k, 3, 3), with the interval
    test of Moeller (1997). Triangles that only touch in a point, and coplanar triangles, do not count as intersecting.
  • self_intersections(tria, subset=None) returns the pairs of triangles that intersect and share no vertex, sorted
    within each row and lexicographically. Candidate pairs come from a KD-tree on the triangle centroids; the few much
    larger triangles are searched one by one, so that they do not inflate the search radius for all others. With subset,
    only pairs involving those triangles are tested, and only triangles in their bounding box are searched, which is much
    faster after a local change.
  • undo_intersections(tria, v_new, rings=2, max_rounds=10) takes new vertex positions, for example after smoothing, and
    moves the vertices of intersecting triangles and rings rings of their neighbors back to their positions in tria.
    It repeats on the triangles that changed until nothing intersects, and returns the positions and the number of
    vertices moved back. The mesh itself is not modified.

The functions follow the other analysis modules (diffgeo, conformal) and take the mesh as their first argument.
Everything is vectorized over candidate pairs with numpy and scipy, so no new dependency. On a 286k triangle cortical
surface self_intersections takes about 3.5 s.

Tests

New tests in lapy/utils/tests/test_intersect.py:

  • single triangle pairs: piercing, separated, touching at a corner, side by side and coplanar;
  • two copies of a torus, which intersect each other where they overlap, never within one copy, and not at all when far
    apart;
  • a torus with a vertex pushed through the opposite tube, where the search restricted to the triangles around the vertex
    finds the same pairs as the full search;
  • undo_intersections on that torus, which moves back only the vertices near the crossing, keeps a small change
    elsewhere and leaves the mesh unchanged.

The docs build with -W and numpydoc validation passes with the new module.

@codecov

codecov Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.52381% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.67%. Comparing base (cbb70b4) to head (9b6524f).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
lapy/intersect.py 89.52% 5 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #125      +/-   ##
==========================================
+ Coverage   61.84%   62.67%   +0.82%     
==========================================
  Files          16       17       +1     
  Lines        3182     3287     +105     
  Branches      417      429      +12     
==========================================
+ Hits         1968     2060      +92     
- Misses       1046     1053       +7     
- Partials      168      174       +6     
Flag Coverage Δ
unittests 62.67% <89.52%> (+0.82%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Single-point contacts and differently scaled triangles can produce incorrect intersection results.

1 open finding
What changed in this PR

Adds triangle-mesh self-intersection detection and local rollback support.

Changes:

  • Implements vectorized triangle and mesh intersection tests.
  • Adds vertex rollback for newly introduced intersections.
  • Adds API documentation and tests.
File Description
lapy/​intersect.py Implements detection and rollback algorithms.
lapy/​utils/​tests/​test_intersect.py Tests intersection and rollback behavior.
doc/​api/​lapy.modules.rst Lists the new module.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lapy/intersect.py Outdated
@m-reuter
m-reuter merged commit 1da47c8 into Deep-MI:main Oct 10, 2026
15 checks passed
@m-reuter
m-reuter deleted the self-intersections branch October 10, 2026 13:56
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.

2 participants