rmesh
rmesh is an experimental reimplementation of trimesh in Rust using PyO3. It is not currently released and may never be. The general idea of rmesh is to be mostly API compatible and pass trimesh's large test suite with the exception of behavior changes made for quality reasons.
Background
Trimesh was originally a research codebase that grew organically to solve problems I care about. trimesh is probably popular because it tries quite hard to avoid being annoying: pip install always works, expensive values are cached automatically on the mesh object, and it uses occasionally convoluted indexing tricks to avoid Evil Python Behavior such as loops. trimesh usually performs comparably to carefully written compiled mesh codebases for many common tasks, which is achieved with the careful use of numpy. It also is opinionated and maybe a little too magical sometimes, given my Very Strong Opinions that most applications that consume meshes should be like 10 lines.
trimesh also predates type hints and has a larger than ideal number of optional dependencies, an upstream burden which requires effort to maintain. There's also been a revolution in the Python ecosystem where some great Python libraries are now written in Rust, thanks to the awesome work of PyO3 and cibuildwheel. Rmesh is intended to package similarly to polars, where it is written in Rust but usable from both Python and Rust. Rust is also a nice low-level language with great tooling and is fast enough to implement very sensitive iterative algorithms.
Project Status
This is experimental, and doesn't do anything at the moment. It hasn't been released to package indexes and may never be if it turns out to be a dead end. trimesh isn't going anywhere as it has a ton of surface area. This won't be released to indexes until the following MVP feature list exists:
- Load an STL, OBJ, and PLY file from Python 3.0x faster than
trimeshin Python. - Implement the following for a mesh object:
edges,euler_number,merge_vertices,face_normals,face_adjacency,face_adjacency_angles,extents,bounds,split,is_watertight,is_winding_consistant,is_volume,is_convex, mass properties using the same algorithm,principal_inertia_components(using nalgebrahermitian_eigen),
Goals
- Targeting use as a Rust crate, a nicely type hinted Python module, and WASM. WASM is mostly because
wasm-packmade it kind of easy, and keeping the build in CI from the start makes sure we don't add things that break WASM builds. - Be generally faster than trimesh and pass many-to-most of trimesh's unit tests.
- Have a relatively small number of carefully chosen dependencies, and prefer to vendor/re-write the rest in Rust. Generally try to keep it to major crates, like
nalgebra,anyhow,bytemuck, although if there's a well-maintained implementation of something in pure Rust we should use it (i.e. earcut). - Build Python wheels for every platform using cibuildwheel.
Implementation Notes
- Caching
trimeshuses hashes of numpy arrays and dirty flags on anndarraysubclass to save expensive values, likeface_normals(very often the slowest thing in a profile).rmeshobjects are immutable and mutations produce new objects with a new cache. Cached values are saved to aRwLockcache which can be accessed through convenience macros. TODO: does every value have to also be anArc?
- Soft Dependencies
trimeshtries to package high-quality preferably header-only upstream C codebases usingcibuildwheelinto their own Python package.rmeshtries to use a small number of compile-time dependencies. Most mesh algorithms should be done inside the codebase to avoid chasing mysterious upstream dependencies if at all possible. The Python install should havenumpyas the only Python dependency, as all heavy lifting will be implemented in Rust.
- Basic Data Types
trimeshuses anp.ndarrayobject for vertices (float64) and faces (int64)rmeshhas the choice betweenndarrayvsnalgebra:ndarray::Array2<f64>orVec<nalgebra::Point3<f64>>rmeshstarted with nalgebra, converted tondarrayon a branch, and then reverted back to nalgebra. Forum posts indicate ndarray doesn't win on performance like I kind of expected it to. The nalgebra objects have some nice properties: specifically you can constrain the number of columns (i.eVector3vsVector4) which I couldn't make work in ndarray although it's probably possible. I also generally preferred the nalgebra API but this is certainly a matter of taste.
- Vertex Attributes
trimeshkind of squirrels these away in multiple places:mesh.visual.TextureVisuals.uvwhich puts them inmesh.visual.vertex_attributesbut notmesh.vertex_attributes. Which is a little weird. And what if you had multiple sets of UV's and colors?rmeshintends to be more attribute-forward. Faces and vertices each get a flatVecofAttribute, similar to the GLTF format. For instance if a function wanted vertex color they'd go through the vec and then take the first (or n-th)Colorattribute. We could have a lookup helper if we really wanted to but most meshes as loaded rarely have more than ~3 attributes (with a median of 0).
Project Layout: Crates
rmesh is set up as a Cargo workspace which is a common choice for a complex project. The workspace crates (in ./crates) are:
rmesh- the basic crate where algorithms are implemented.
rmesh_macro- all
proc macrosmust be their own crate for Reasons. This as of writing only contains thecache_accessproc macro which handles some of the boilerplate for dealing with theRwLockcache.
- all
rmesh_python- The crate that builds to
pip install rmesh, and includes a dependency onPyO3and other Python plumbing. This should be 100% boilerplate for accessingrmesh.
- The crate that builds to
rmesh_wasm- The crate that builds to a WASM blob for use in Node and browsers.
rmesh_external(proposed but not implemented)- For things that really have to be in C/C++, like accessing OpenCASCADE for STEP loading. This doesn't work with
wasm-packwithout a lot of plumbing work.
- For things that really have to be in C/C++, like accessing OpenCASCADE for STEP loading. This doesn't work with
Packaging
rmesh is written in Rust and is designed for use from Rust, Python, or wasm. It has not been released as it is still undergoing substantial design revision, and may never be released if it doesn't work as well as hoped. It is currently building and tests are passing for all target platforms.
Python
The project currently builds wheels for a shocking number of platforms using CIBuildWheel, and the rmesh PyPi project is currently reserved.
Rust
There is no way to reserve a crate name on crates.io, so this may be anything.
NPM
The @rmesh "organization" has been reserved, and the WASM package is intended to be released to npm add @rmesh/rmesh