Overview
ShapeOp integration uses nanobind for Python bindings in COMPAS ShapeOp. This guide explains how to contribute to the project, implement new constraints or forces, and understand the overall architecture of the codebase.
File and Folder Structure
The compas_shapeop package is organized with a clear separation between C++ backend and Python frontend:
Source Code
src/shapeop/- Core ShapeOp C++ library files (Solver, Constraints, Forces)src/shapeop.cpp- Main C++ binding file that connects ShapeOp to Python via nanobindsrc/compas_shapeop/- Python frontend code that wraps the C++ bindingssrc/compas.h- Precompiled header with common includes for faster compilation
Build & Dependencies
build/- Build artifacts from CMakeCMakeLists.txt- C++ project configuration for ShapeOp and nanobind integrationexternal/- External C++ dependencies like Eigen (automatically downloaded if needed)pyproject.toml- Python project configuration (pip install -e .)requirements.txt- Runtime requirementsrequirements-dev.txt- Development requirements for building docs, testing, etc.tasks.py- Development tasks (invoke test, invoke docs, invoke format, invoke lint)
Examples and Documentation
examples/- Example scripts demonstrating various constraints and applicationsdata/- Sample mesh files for examplesdocs/- Documentation source files
Code Structure
The compas_shapeop package has three main components:
ShapeOp Core Library: The C++ implementation of the ShapeOp solver, constraints, and forces (in
src/shapeop/)C++ Binding Layer: The nanobind wrapper that exposes ShapeOp to Python (in
src/shapeop.cpp)Python Interface: The Python classes that provide a user-friendly API (in
src/compas_shapeop/)
The C++ binding layer is implemented as the SolverWrapper class in shapeop.cpp, which wraps the ShapeOp::Solver class and provides methods for adding constraints, applying forces, and solving the system.
Zero-Copy Integration
One of the key features of compas_shapeop is the zero-copy integration between Python and C++. This means that when the solver modifies point positions, the NumPy array is automatically updated without any data copying or conversion.
This is achieved through nanobind’s Eigen integration, which allows the C++ Eigen matrices to be directly exposed as NumPy arrays with shared memory. The Python Solver class maintains a direct view of the C++ solver’s internal points matrix, enabling high-performance simulations.
Constraints and Forces
ShapeOp provides several constraints and forces for geometry processing:
Constraints:
Closeness - Keeps vertices close to their original or target positions
EdgeStrain - Maintains edge lengths within a specified range
Circle - Makes vertices lie on a circle
Plane - Makes vertices lie on a plane
Similarity - Transforms vertices to match a target shape
Regular Polygon - Makes vertices form a regular polygon
Forces:
VertexForce - Applies a force vector to specific vertices
NormalForce - Applies forces along face normals for inflation/deflation