Skip to main content

A fast geometry manipulation and creation library written in Rust, wrapped in python

Project description

GMAC

Build & Test License: MIT

A fast geometry manipulation and creation library made in rust, with a convenient python interface, and very few dependencies. Primary features include:

  • Create primitives
  • Deform geometries using RBF and FFD
  • Transform geometries (or selection just a selection of nodes)
  • Large range of selection and transformation tools
  • Convenient python interface (gmac_py)
  • Import/export vtk-type files and stl files

Here's a demonstration of a plane tail deformed using the Free Form deformer (FFD):

Variation 1 Variation 2

Both plane tail variations were created using the Gmac Free Form deformer (FFD).

Add to your rust project

Add the following to your Cargo.toml:

[dependencies]
gmac = "0.1.7" # includes rayon by default

If thats not beefy enough, try the openblas or intel-mkl feature:

[dependencies]
gmac = { version = "0.1.7", features = ["openblas"] } # or intel-mkl

Make sure you have the required dependencies installed for the features you choose. For openblas openssl is required.

For those who want a lightweight dependency free version:

[dependencies]
gmac = { version = "0.1.7", default-features = false }

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_node},
    },
    io::{stl::StlFormat, vtk::write_vtu},
    morph::{ffd::FreeFormDeformer, design_block::DesignBlock},
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a simple box geometry or import one
    let mut geometry = generate_box(
        [1.0, 1.0, 1.0],  // Dimensions (length, width, height)
        [0.0, 0.0, 0.0],  // Center coordinates
        [0.0, 0.0, 0.0],  // Rotation angles (degrees)
        [5, 5, 5],        // Number of elements in each direction
    );
    
    // Alternative: Load geometry from an STL file
    // let mut geometry = Mesh::from_stl("<path_to_stl>")?;

    // Create a design block (control lattice) for FFD
    let design_block = DesignBlock::new(
        [0.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)
        [2, 2, 2],        // Control points in 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(&geometry, Some(2))?;

    // Create a transformation matrix
    let transform_matrix = build_transformation_matrix(
        [0.25, 0.0, 0.0],   // Translation vector (x, y, z)
        [45.0, 0.0, 0.0],   // Rotation angles (degrees)
        [1.0, 1.5, 1.5],    // Scaling factors (x, y, z)
    );

    // Create a copy of the original control points to modify
    let mut deformed_design_nodes = design_block.nodes.clone();

    // Apply the transformation to each free control point
    free_design_ids.iter().for_each(|&id| {
        transform_node(
            &mut deformed_design_nodes[id], // Control points
            &transform_matrix,              // Transformation
            &[0.2, 0., 0.],                 // Origin or pivot point
        )
    });

    // Create a Free-Form Deformer with the original design block
    let ffd = FreeFormDeformer::new(design_block);

    // Apply the deformation to the original geometry
    geometry.nodes = ffd.deform(&geometry.nodes, &deformed_design_nodes)?;

    // Save the deformed geometry as a VTK file for visualization
    write_vtu(&geometry.nodes, &geometry.cells, Some("deformed.vtu"))?;

    // Save the final deformed geometry as an STL file
    geometry.write_stl(Some("deformed.stl"), Some(StlFormat::Binary))?;

    Ok(())
}
Original control points Deformed control points

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, transformation::transform_node,
    primitives::generate_box, transformation::build_transformation_matrix,
    selection::select_nodes_in_plane_direction,
};
use gmac::io::{stl::StlFormat, vtk::write_vtp};
use gmac::morph::rbf::RbfDeformer;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create or import geometry as before ... 
    
    // Create a set of control points
    let original_control_points = generate_block_cluster(
        [1.2, 1.2, 1.2], // Lengths of block
        [0.0, 0.0, 0.0], // Center of block
        [0.0, 0.0, 0.0], // No rotation
        [2, 2, 2],       // 2x2x2 grid of control points
    );

    // Select control points that lie in a plane defined 
    // by an origin point and normal vector
    let target_control_point_ids = select_nodes_in_plane_direction(
        &original_control_points,
        [0.3, 0.0, 0.0], // A point in the plane
        [1.0, 0.0, 0.0], // Normal vector (x-axis here)
    );

    // Apply transformation to the selected control points
    let transform_matrix = build_transformation_matrix(
        [1.0, 0.0, 0.0],   // Move 1 unit in x-direction
        [45.0, 0.0, 0.0],  // Rotate 45 degrees around x-axis
        [1.0, 0.75, 0.75], // Scale y and z dimensions to 75%
    );

    // Apply the transformation to each selected control point
    let mut deformed_control_points = original_control_points.clone();
    for &id in &target_control_point_ids {
        let mut point = deformed_control_points[id];
        transform_node(
            &mut point,
            &transform_matrix,
            &[0.0, 0.0, 0.0], // Origin point
        );
        deformed_control_points[id] = point;
    }

    // Create an RBF deformer with the control point configurations
    let rbf = RbfDeformer::new(
        original_control_points,
        deformed_control_points,
        Some("gaussian"), // Type of RBF kernel
        Some(1.0),        // Shape parameter for the RBF kernel
    )?;

    // Apply the RBF deformation to the original box geometry
    geometry.nodes = rbf.deform(&geometry.nodes)?;

    // Save the final deformed geometry as an STL file
    geometry.write_stl(Some("deformed.stl"), None)?; // binary default

    Ok(())
}

Here you can see the original control points and mesh, as well as the deformed control points and mesh:

Original control points Deformed 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
import gmac.morph as morph
import gmac.io as io
import numpy as np

# Create a simple box geometry
geometry = gmac.generate_box(
    [1.0, 1.0, 1.0],  # Dimensions (length, width, height)
    [0.0, 0.0, 0.0],  # Center coordinates
    [0.0, 0.0, 0.0],  # Rotation angles (degrees)
    [5, 5, 5]         # Number of divisions in each direction
)

# Or import one from stl
# geometry = gmac.Mesh.from_stl("path_to_stl")

# Create a design block (control lattice) for FFD
design_block = morph.DesignBlock(
    [0.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)
    [2, 2, 2]         # Number of control points in each direction
)

# Select which control points will be free to move
free_design_ids = design_block.select_free_design_nodes(geometry, 2)

# Create a transformation matrix
transformation_matrix = gmac.build_transformation_matrix(
    [0.25, 0.0, 0.0],   # Translation vector (x, y, z)
    [45.0, 0.0, 0.0],   # Rotation angles (degrees)
    [1.0, 1.5, 1.5]     # Scaling factors (x, y, z)
)

# Transform only the free control points
deformed_design_nodes = np.array(design_block.nodes)
deformed_design_nodes[free_design_ids] = gmac.transform_nodes(
    deformed_design_nodes[free_design_ids],
    transformation_matrix,
    [0.2, 0., 0.],
)

# Save the deformed control points for visualization
io.write_vtp(deformed_design_nodes, "deformed_design_nodes.vtp")

# Create a Free-Form Deformer with the original design block
ffd = morph.FreeFormDeformer(design_block)

# Apply the deformation to the original geometry
geometry.nodes = ffd.deform(geometry.nodes, deformed_design_nodes)

# Save the final deformed geometry as an STL file (default binary)
geometry.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_block_deformation_ffd*.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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

gmac-0.1.8-cp38-abi3-win_amd64.whl (291.1 kB view details)

Uploaded CPython 3.8+Windows x86-64

gmac-0.1.8-cp38-abi3-manylinux_2_34_x86_64.whl (393.7 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.34+ x86-64

gmac-0.1.8-cp38-abi3-macosx_11_0_arm64.whl (341.3 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

Details for the file gmac-0.1.8-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: gmac-0.1.8-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 291.1 kB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.9.0

File hashes

Hashes for gmac-0.1.8-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 d768823e22c4c50d371021b928789d4c221e90185c258dde440400f01501e872
MD5 fa7057265d496ced85a37c15e100110d
BLAKE2b-256 eddb164b6128807eeb1dc3ff60416d6c570fb5a892ab7796ca34ac88d7bcbde7

See more details on using hashes here.

File details

Details for the file gmac-0.1.8-cp38-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for gmac-0.1.8-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 f8082410c321c479191b828cc41345e9aa16aac0545a2c378758b9991448f30b
MD5 e6c2c685a7eaef8177fe5494e93000a4
BLAKE2b-256 2a799f6bac1759bf7038cf588894f2b8ef2250fd40c746ccf7e661b9b945a6d3

See more details on using hashes here.

File details

Details for the file gmac-0.1.8-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gmac-0.1.8-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 fae2a480ec8eeffc3a80e945077ef6dcc128d644cba9cdc37c6b4fb327959c13
MD5 af887d461c126b7a846efea1df7c9728
BLAKE2b-256 ec119d94fc82bbbc045efec95a2dd177a487110d8d52e3aa95a4bf45d696cf37

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page