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.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},
    },
    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.

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

Uploaded CPython 3.8+Windows x86-64

gmac-0.1.7-cp38-abi3-manylinux_2_34_x86_64.whl (348.7 kB view details)

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

gmac-0.1.7-cp38-abi3-macosx_11_0_arm64.whl (304.8 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: gmac-0.1.7-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 254.3 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.7-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 fc02ef48f171eb8cb16f4a11cf3e0e7a96a3957542482cc96aac96575c5b1e86
MD5 ca43a60063367ec654149d5c0184d5d3
BLAKE2b-256 bbc0f71736fa3e720c4a11dc826f1ab4faeb5011f8162ef337d285e28420810c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gmac-0.1.7-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 462c13c184bec4a376d5416a5bab0e6fde0d99bedecdea6ea9d2d1a70df397b1
MD5 3071b8a4e4383bedaff4d636c7fd2210
BLAKE2b-256 a575e72d7da77a569c65eb72c93a652e8553c5890d8c6908a4dec047dc7b6784

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for gmac-0.1.7-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 832f5763ee120d22350b52a358891ee21bfa831d26cfeb136870cb2411659362
MD5 31aae139037822a24d4d1e3f6872108e
BLAKE2b-256 f3ba281a9a36358363b8960f60f204bbf94d7158454a7d0990b836262b683c96

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