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 (ASCII only currently)

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.6"

If you want to use RBF with larger datasets, it is recommended to use the openblas or intel-mkl feature:

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

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_node},
};
use gmac::io::{stl::write_stl, vtk::write_vtu};
use gmac::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_ascii("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
    write_stl(&geometry.nodes, &geometry.cells, Some("deformed.stl"))?;

    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::write_stl, 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
    write_stl(&geometry.nodes, &geometry.cells, Some("deformed.stl"))?;

    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).

mport 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_ascii("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
io.write_stl(geometry.nodes, geometry.cells, "deformed_geometry.stl")

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
    

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.

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.6-cp38-abi3-win_amd64.whl (249.2 kB view details)

Uploaded CPython 3.8+Windows x86-64

gmac-0.1.6-cp38-abi3-manylinux_2_34_x86_64.whl (343.4 kB view details)

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

gmac-0.1.6-cp38-abi3-macosx_11_0_arm64.whl (300.6 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: gmac-0.1.6-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 249.2 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.6-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 86b0477567e6d17d66351806705ace4e4f88b83d1fe23e778343f2a5d400b0ea
MD5 f7a6131049b796937425f719734bd2d1
BLAKE2b-256 0f9de337d336170a8c8dc221291f67588edbe95a6ec19db9665a3bfa7a8e567e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gmac-0.1.6-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 447534ed5bc6420a0785dc1d6c0b6671f1ab1e5b8e9cddbdbab1fcc525c0504f
MD5 dd8845f440dfbdc62f5bebcad391c786
BLAKE2b-256 b40b7f91bb6e43059a54930255055372acc89e2766f90a072bed1ce15ddca839

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gmac-0.1.6-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c839bc53ebb04e8947aa26d7afc1ab2ded79e82ecc93a15ba131e6759d490bbc
MD5 2a3dea88b854ceb7b4a3a4e161daf921
BLAKE2b-256 97a1ecda5003cb2fe03688e9045636a95df3a96940debb929d92399e1baa0a82

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