Repository navigation
This release adds new tools for triangle mesh topology and quality: vertex-level manifold checks, genus and orientability, cutting meshes open, self-intersection tests and isotropic remeshing. It also fixes several long-standing bugs in the conformal mapping and the GMSH readers, keeps LaPy working with NumPy 2.5, drops Python 3.10 and adds Python 3.14.
New features
Mesh topology (#123)
TriaMesh.is_manifold()now also checks vertices: every vertex needs a single fan of triangles. Pinch vertices, where several cones of triangles touch only at the vertex (two tetrahedra sharing a corner, a bow tie, a figure-8 boundary), used to pass.TriaMesh.pinch_vertices()lists them, andTriaMesh.split_pinch_vertices_()gives every further fan its own copy of the vertex.TriaMesh.cut_(edges)cuts a mesh open along vertex pairs: a closed loop becomes two boundary loops, an open path one loop around it.TriaMesh.genus()returns the genus of closed and open surfaces with any number of components.TriaMesh.is_orientable()tells whether a mesh can be oriented consistently, independent of its current orientation (a Moebius band or a Klein bottle cannot).
Self-intersection tests (#125)
New module lapy.intersect:
triangles_intersect(a, b)tests pairs of triangles (Moeller 1997).self_intersections(tria, subset=None)finds intersecting triangle pairs, optionally only those involving a subset of triangles, which is much faster after a local change.undo_intersections(tria, v_new)moves vertices back where new positions, for example after smoothing, make the surface intersect itself.
Isotropic remeshing (#126)
New module lapy.remesh:
remesh(tria, target_length=None)remeshes a surface to near-equilateral triangles after Botsch and Kobbelt (2004). Edge collapses keep the topology (link condition), new vertices stay on the input surface, boundary vertices stay in place and 2D meshes stay 2D.closest_points(tria, points)finds the closest point on a mesh for each query point.
Both are fully vectorized with NumPy and SciPy and need no new dependency.
CHOLMOD through cholespy
use_cholmod=True now uses cholespy instead of scikit-sparse. Install it with pip install lapy[chol]. The chol extra used to resolve to scikit-sparse 0.5.0, which broke use_cholmod=True. A Solver with dtype=np.float32 works with it and returns float32 results, and a matrix that is not positive definite is reported as a clear ValueError. Thanks @izachp for the proposal and benchmarks (#119, #127).
Other additions
Solver.poissonhas a newintegrateargument to pass a right hand side that is already integrated, without building a new solver (#115, thanks @magnesium2400).lapy.conformal.spherical_tutte_map, whichspherical_conformal_mapnow falls back to when the harmonic map degenerates (#120).TriaMesh.read_gmshworks and returns aTriaMesh.TriaMesh.read_gmshandTetMesh.read_gmshshare one parser that rejects a file early when it holds no cells of the requested type (#121, #122).
Behaviour changes
- Meshes with pinch vertices now fail
is_manifold(), andboundary_loops()raises an error on figure-8 boundaries instead of returning a possibly wrong result. Callsplit_pinch_vertices_()first to keep the old behaviour. genus()raises for non-manifold and non-orientable meshes.- The CHOLMOD backend needs
cholespyinstead ofscikit-sparse. spherical_conformal_mapreturned a mirrored, anti-conformal sphere. The output is now correctly oriented, so results differ from earlier versions (#120).- In-place operations that rebuild the mesh (
rm_free_vertices_,keep_largest_connected_component_,orient_,refine_,split_pinch_vertices_,cut_) keep 2D meshes 2D instead of returning 3D vertices with a zero third coordinate. - Malformed GMSH files raise
ValueErrorinstead of failing anassert. Binary GMSH triangle files raise "binary format not implemented", as tetrahedron files already did; that code path never worked.
Bug fixes
TriaMesh.euler()counted three triangles for meshes with one or two triangles.spherical_conformal_map(#120):- pinning vertices broke the symmetry of the system matrix, so
use_cholmod=Trueandsplugave different results; - the south pole chart was built from a denominator computed before the rescale;
- a healthy fine mesh could be rejected as degenerate;
- the systems are now solved in real instead of complex arithmetic.
- pinning vertices broke the symmetry of the system matrix, so
beltrami_coefficientrejects degenerate triangles and computes planar areas without the removed 2Dnp.cross(#120).- Mesh readers (#121):
- replace the in-place
.shapeassignment deprecated in NumPy 2.5; - replace the binary
np.fromstring, which fails on any NumPy 2.
- replace the in-place
TetMesh.read_gmshhandles a$PhysicalNamesblock and files that hold triangles next to the tetrahedra (#122).- GMSH readers close the file on every error path (#122).
Packaging, CI and documentation (#124)
- Python 3.11 to 3.14 are supported; Python 3.10 is dropped.
- All runtime dependencies have a minimum version (
numpy>=1.23.2,scipy>=1.9.2,nibabel>=4.0,plotly>=5.0,psutil>=5.9), and a new CI job tests exactly these minimums. - Removed:
setup.py; packaging is configured inpyproject.toml;- the
fullextra (useall); - numpy from the build requirements.
- The CI matrix is smaller:
- pytest runs the oldest and newest Python on every OS, and the versions in between on Linux;
- the package is built once and checked with
twine check; - pytest also runs when only
pyproject.tomlchanges.
- Docstrings are checked with ruff instead of the unmaintained pydocstyle.
- The changelog page and the unused sphinx-gallery setup are removed from the documentation. Release notes are now kept on GitHub.
- Deprecation warnings raised inside LaPy now fail the tests (#121).
- GitHub Actions updated: checkout, setup-python and codecov (#114, #116, #117, #118).
Contributors
@m-reuter, @magnesium2400, @izachp
Full changelog: v1.6.0...v1.7.0