7 releases
| 0.2.0 | Jul 7, 2025 |
|---|---|
| 0.1.8 | Jul 5, 2025 |
| 0.1.5 | Jun 26, 2025 |
| 0.0.1 |
|
#241 in Math
170KB
3.5K
SLoC
GMAC
A fast mesh manipulation and creation library made in rust, with a convenient python interface, and very few dependencies. Primary features include:
- Deform meshes using RBF and FFD
- Transform meshes (or selection just a selection of nodes)
- Large range of selection and transformation tools
- Import/export stl, obj and vtk-type files
- Convenient python interface
- Create primitives
- Great performance
Here's a demonstration of a plane tail deformed using the Free Form deformer (FFD):
| Variation 1 | Variation 2 |
|---|---|
![]() |
![]() |
Add to your rust project
Add the following to your Cargo.toml:
[dependencies]
gmac = "^0.2.0"
This includes rayon by default, for those who want a lightweight dependency free version use default-features = false
For Rbf specifically, if you still need more performance try adding features openblas or intel-mkl.
Make sure you have the required dependencies installed for the features you choose. For openblas openssl is required.
Examples in Rust
Deforming with Free Form Deformer (FFD)
Heres a demonstration of deformation using the FreeFormDeformer:
use gmac::{
core::{
primitives::generate_box,
transformation::{build_transformation_matrix, transform_selected_nodes},
},
io::stl::StlFormat,
morph::{design_block::DesignBlock, ffd::FreeFormDeformer},
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a simple primitive or import something else
let mut mesh = generate_box(
[2.0, 1.0, 1.0], // Lengths (x, y, z)
[0.0, 0.0, 0.0], // Center coordinates
[0.0, 0.0, 0.0], // Rotation angles (degrees)
[12, 12, 12], // Elements in each direction
)?;
// Alternative: mesh = Mesh::from_stl("path_to_stl")? or from_obj
// Create a design block (control lattice) for FFD
// This should contain the region you wish to deform
let design_block = DesignBlock::new(
[1.8, 1.2, 1.2], // Lengths (x, y, z)
[0.2, 0.0, 0.0], // Center offset
[0.0, 0.0, 0.0], // Rotation angles (degrees)
[3, 2, 2], // Control points each direction
)?;
// Select which control points will be free to move during
// deformation, the second parameter specifies the number of
// fixed layers of control points at the boundaries
let free_design_ids = design_block
.select_free_design_nodes(&mesh, Some(2))?;
// Apply the transformation to each free control point
let transform_matrix = build_transformation_matrix(
[0.25, 0.0, 0.0], // Translation vector (x, y, z)
[125.0, 0.0, 0.0], // Rotation angles (degrees)
[1.0, 1.25, 1.25], // Scaling factors (x, y, z)
);
let deformed_design_nodes = design_block.transform_subset(
&free_design_ids,
&transform_matrix,
&[0.2, 0.0, 0.0], // Pivot point
);
// Create a Free-Form Deformer with original design block
let ffd = FreeFormDeformer::new(design_block)?;
// Apply the deformation to the original geometry
mesh.nodes = ffd.deform(&mesh.nodes, &deformed_design_nodes)?;
// Save the final deformed geometry as an STL/OBJ/VTK file
mesh.write_stl(Some("deformed.stl"), Some(StlFormat::Binary))?;
Ok(())
}
| Original mesh & control points | Deformed mesh & control points |
|---|---|
![]() |
![]() |
Note: There are many ways to achieve the same result with all the tools provided, this is just one of them.
Deforming with Radial Basis Functions (RBF)
Using the RbfDeformer is very similar to using the FFD, but instead of using a design block (control lattice), you use a set of control points:
use gmac::core::{
clusters::generate_block_cluster,
primitives::generate_torus,
selection::select_nodes_in_plane_direction,
transformation::{build_transformation_matrix, transform_selected_nodes},
};
use gmac::morph::rbf::RbfDeformer;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a simple geometry or import one
let mut mesh = generate_torus(1.0, 0.3, [0.0, 0.0, 0.0], 64, 64)?;
// Create a set of control points
let original_control_points = generate_block_cluster(
[2.8, 1.0, 2.8], // Slightly larger than the original box
[0.0, 0.0, 0.0], // Centered at the origin
[0.0, 0.0, 0.0], // No rotation
[3, 3, 3], // Control points in each direction
)?;
// Select control points that lie in a plane defined
let target_control_point_ids = select_nodes_in_plane_direction(
&original_control_points,
[0.0, 0.0, 0.0], // A point in the plane
[1.0, 0.0, 0.0], // Normal vector (x-axis in this case)
);
// Apply transformation to the selected control points
let transform_matrix = build_transformation_matrix(
[0.0, 0.0, 0.0], // Translation
[45.0, 20.0, 0.0], // Rotation
[1.0, 1.0, 1.0], // Scale
);
let deformed_control_points = transform_selected_nodes(
&original_control_points,
&target_control_point_ids,
&transform_matrix,
&[0.0, 0.0, 0.0], // Origin/pivot point
);
// Create an RBF deformer with the control point configurations
let rbf = RbfDeformer::new(
original_control_points,
deformed_control_points,
Some("thin_plate_spline"), // Type of RBF kernel
Some(1.0), // Shape parameter
)?;
// Apply the RBF deformation to the original geometry
mesh.nodes = rbf.deform(&mesh.nodes)?;
// Save the final deformed geometry as an STL/OBJ/VTK file
mesh.write_stl(Some("deformed.stl"), None)?;
Ok(())
}
Here you can see the original control points and mesh, as well as the deformed control points and mesh:
| Original mesh & control points | Deformed mesh & control points |
|---|---|
![]() |
![]() |
Examples in Python
Using GMAC to deform a box
Heres a simple demonstration of using the Free Form Deformer (FFD) to deform a generated box (or an imported STL file).
import gmac as gm
# Create a simple primitive geometry or import something
mesh = gm.generate_box([2.0, 1.0, 1.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [12, 12, 12])
# Alternative: mesh = gm.Mesh.from_stl("path_to_stl") or obj
# Create a design block (control lattice) for FFD
design_block = gm.morph.DesignBlock(
[1.8, 1.2, 1.2], # Dimensions of the control lattice
[0.2, 0.0, 0.0], # Center offset
[0.0, 0.0, 0.0], # Rotation angles (degrees)
[3, 2, 2], # Control points in each direction
)
# Select which control points will be free to move
free_design_ids = design_block.select_free_design_nodes(mesh, 2)
# Transform only the free control points using transformation matrix
transformation_matrix = gm.build_transformation_matrix(
[0.25, 0.0, 0.0], # Translation vector
[125.0, 0.0, 0.0], # Rotation angles
[1.0, 1.25, 1.25], # Scaling factors
)
deformed_design_nodes = gm.transform_selected_nodes(
design_block.nodes,
free_design_ids,
transformation_matrix,
[0.2, 0.0, 0.0],
)
# Save the deformed control points for visualisation
gm.io.write_vtp(deformed_design_nodes, "deformed_design_nodes.vtp")
# Create a Free-Form Deformer with the original design block
ffd = gm.morph.FreeFormDeformer(design_block)
# Apply the deformation to the original geometry
mesh.nodes = ffd.deform(mesh.nodes, deformed_design_nodes)
# Save the deformed geometry as STL/OBJ/VTK file
mesh.write_stl("deformed_geometry.stl") # binary default
For Radial Basis Function (RBF) deformation, see the RbfDeformer example in examples.
Build python from source
These instructions assume that Python3 and Cargo are installed on your system. To set up this project, follow these steps:
- Clone the repository:
git clone https://github.com/alexlovric/gmac.git cd gmac/gmac_py - Create a virtual environment and install build system:
python3 -m venv .venv source .venv/bin/activate # In windows /Scripts/activate python3 -m pip install -r requirements.txt - Build the release binary:
maturin develop --release - Build the python wheel:
maturin build --release - Running examples:
python3 -m pip install <path to wheel (target/wheels/*.whl)> cd examples python3 -m pip install -r requirements.txt python3 *simple_ffd_deformation*.py
References
The gmac_morph is heavily influenced by PyGEM (https://github.com/mathLab/PyGeM), and the following
Sieger, Menzel, Botsch. On Shape Deformation Techniques for Simulation-based Design Optimization. SEMA SIMAI Springer Series, 2015.
Lombardi, Parolini, Quarteroni, Rozza. Numerical Simulation of Sailing Boats: Dynamics, FSI, and Shape Optimization. Springer Optimization and Its Applications, 2012.
License
MIT License - See LICENSE for details.
Support
If you'd like to support the project consider:
- Identifying the features you'd like to see implemented or bugs you'd like to fix and open an issue.
- Contributing to the code by resolving existing issues, I'm happy to have you.
- Donating to help me continue development, Buy Me a Coffee
Dependencies
~2–13MB
~165K SLoC





