#geometry #rbf #morphing #graphics #ffd

gmac_rs

Blazingly fast geometry manipulation and creation library

7 releases

0.2.0 Jul 7, 2025
0.1.8 Jul 5, 2025
0.1.5 Jun 26, 2025
0.0.1 Jun 26, 2025

#241 in Math

MIT license

170KB
3.5K SLoC

GMAC

Build & Test License: MIT

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:

  1. Clone the repository:
    git clone https://github.com/alexlovric/gmac.git
    cd gmac/gmac_py
    
  2. 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
    
  3. Build the release binary:
    maturin develop --release
    
  4. Build the python wheel:
    maturin build --release
    
  5. 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