Creation of Python bindings for a SAMSON Extensions#
This tutorial demonstrates how to create Python bindings for a SAMSON Extension, i.e. how to expose extension's functionality in Python. We will be using the PyBindTutorial sample from SAMSON-Developers-Tutorials as an example.
If you have any questions or experience any problems with creating Python bindings for your SAMSON Extension, please, check the SAMSON Connect Forum.
Sample code#
The source code for an Extension created in this tutorial can be found at SAMSON-Developer-Tutorials.
Prerequisites#
Download or clone SAMSON-Developers-Tutorials samples. We will be using the PyBindTutorial sample in this tutorial.
Used packages#
Python bindings are done thanks to pybind11, "a lightweight header-only library that exposes C++ types in Python".
You can find an extraction of the necessary files in the PyBindTutorial/external
folder or you can clone the pybind11 repository (we advice using pybind11 v.2.13.6+). Only the header files (pybind11/include/pybind11/...
) are necessary from the pybind11 repository.
Building a SAMSON Extension with Python#
Open the root CMakeLists.txt
from the SAMSON-Developers-Tutorials and check if the PyBindTutorial directory is added:
When generating a project with cmake
, you need to specify the Python_ROOT_DIR
:
cmake -G"Visual Studio 17" `
-DQT5_CMAKE_INCLUDE_DIR="C:\Qt\6.5.2\msvc2019_64\lib\cmake" `
-DSAMSON_SDK_PATH="YourSDKPath/SAMSON-Sdks/6.0.2" `
-DPython_ROOT_DIR="YourSAMSONPath/6.0.2/Binaries" ..
YourSDKPath
- is the path to your SAMSON SDK installation.YourSAMSONPath
- is the path to your SAMSON installation (the Binaries subfolder contains the shipped Python).
See also Building a SAMSON Extension on Windows.
In the Qt Creator project, add the Python_ROOT_DIR
and set it to the path to Python shipped with SAMSON, e.g.: $HOME/OneAngstrom/SAMSON/6.0.2/Binaries/
, replace $HOME
with your user's folder.
See also Building a SAMSON Extension on Linux.
In the Qt Creator project, add the Python_ROOT_DIR
and set it to the path to the Python framework shipped with SAMSON, e.g.: $HOME/Applications/SAMSON.app/6.0.2/Binaries/Python.framework/Versions/3.11
, replace $HOME
with your user's folder.
See also Building a SAMSON Extension on macOS.
On Linux and Mac, to build a SAMSON Extension with Python, you might need to provide a path to your Python installation in build environment variables of your project. The path to your Python executable should be the first in the PATH build environment variable of your project. See an image below (Linux, Qt Creator):
Now, we need to provide paths to Python header files and link against the Python library. For that, open the CMakeLists.txt
from the PyBindTutorial sample and add the following lines just before TARGET_LINK_LIBRARIES
:
####################################################
# Python bindings thanks to pybind11
####################################################
IF( DEBUG )
# Disable the creation of Python bindings in the Debug mode
# since Python is not supported in the Debug mode
SET (Python_LIBRARIES "")
ELSE( DEBUG )
INCLUDE ( PythonVersion )
MESSAGE("================================ PYTHON VERSION ====================================")
MESSAGE("PYTHON_MAJOR_VERSION_NUMBER = " ${PYTHON_MAJOR_VERSION_NUMBER})
MESSAGE("PYTHON_MINOR_VERSION_NUMBER = " ${PYTHON_MINOR_VERSION_NUMBER})
MESSAGE("PYTHON_PATCH_VERSION_NUMBER = " ${PYTHON_PATCH_VERSION_NUMBER})
MESSAGE("======================================================================================")
SET( PYTHON_VERSION ${PYTHON_MAJOR_VERSION_NUMBER}.${PYTHON_MINOR_VERSION_NUMBER} )
MESSAGE(" Searching for Python " ${PYTHON_VERSION})
MESSAGE("--------------------------------------------------------------------------------------")
IF( UNIX )
FIND_PACKAGE(Python3 ${PYTHON_VERSION} EXACT COMPONENTS Interpreter Development REQUIRED)
SET( Python_FOUND ${Python3_FOUND} )
SET( Python_Interpreter_FOUND ${Python3_Interpreter_FOUND} )
SET( Python_VERSION ${Python3_VERSION} )
SET( Python_EXECUTABLE ${Python3_EXECUTABLE} )
SET( Python_LIBRARIES ${Python3_LIBRARIES} )
SET( Python_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} )
SET( Python_LIBRARY_DIRS ${Python3_LIBRARY_DIRS} )
SET( Python_INTERPRETER_ID ${Python3_INTERPRETER_ID} )
SET( Python_VERSION ${Python3_VERSION} )
SET( Python_VERSION_MAJOR ${Python3_VERSION_MAJOR} )
SET( Python_VERSION_MINOR ${Python3_VERSION_MINOR} )
SET( Python_VERSION_PATCH ${Python3_VERSION_PATCH} )
ELSE( UNIX )
FIND_PACKAGE(Python ${PYTHON_VERSION} EXACT COMPONENTS Interpreter Development REQUIRED)
ENDIF( UNIX )
MESSAGE("Python_FOUND: " ${Python_FOUND} )
MESSAGE("Python Interpreter found: " ${Python_Interpreter_FOUND})
MESSAGE("Python version: " ${Python_VERSION})
MESSAGE("Python executable: " ${Python_EXECUTABLE})
MESSAGE("Python libraries: " ${Python_LIBRARIES} )
MESSAGE("Python include dirs: " ${Python_INCLUDE_DIRS} )
MESSAGE("Python libraries dirs: " ${Python_LIBRARY_DIRS} )
MESSAGE("Python_INTERPRETER_ID: " ${Python_INTERPRETER_ID} )
MESSAGE("Python_VERSION: " ${Python_VERSION} )
MESSAGE("Python_VERSION_MAJOR: " ${Python_VERSION_MAJOR} )
MESSAGE("Python_VERSION_MINOR: " ${Python_VERSION_MINOR} )
MESSAGE("Python_VERSION_PATCH: " ${Python_VERSION_PATCH} )
MESSAGE("======================================================================================")
MESSAGE("")
IF ( ${Python_Interpreter_FOUND} )
# This define is used in the code to determine
# whether Python bindings should be created or not
add_definitions(-DCREATE_PYTHON_BINDINGS)
# Set path to pybind11 headers
SET(Pybind11_INCLUDE_DIRS "./external/pybind11/include/")
# include paths to python and pybind11 headers
INCLUDE_DIRECTORIES(${Python_INCLUDE_DIRS} ${Pybind11_INCLUDE_DIRS})
# Set path to headers for SAMSON SDK SBQuantity and SBType* wrappers
SET(SAMSONSDKWRAPPERS_INCLUDE_DIRS "./external/SAMSON-SDK-wrappers/")
INCLUDE_DIRECTORIES(${SAMSONSDKWRAPPERS_INCLUDE_DIRS})
ENDIF ( ${Python_Interpreter_FOUND} )
ENDIF( DEBUG )
Note
Python bindings are available only in the release mode. We disable the creation of Python bindings in the debug mode since Python is not shipped with debug libraries.
In this CMake code, we add a definition of CREATE_PYTHON_BINDINGS
which later is used in the C++ code to determine whether Python bindings should be created or not.
And we include the Python_LIBRARIES
in the TARGET_LINK_LIBRARIES
:
TARGET_LINK_LIBRARIES( ${OUTPUT_NAME}
${QT_LIBRARIES}
${Python_LIBRARIES}
${OPENGL_LIBRARY}
${SAMSON_SDK_LIBRARIES} )
Creating Python bindings#
In this tutorial we will show you how to expose some basic functionality of a SAMSON Extension. For more information on how to create Python bindings with pybind11, we refer you to the pybind11 documentation. In particular, we suggest you to check the following:
- pybind11: First steps
- pybind11: Object-oriented code
- pybind11: Classes
- pybind11: Functions
- pybind11: Return value policies
- pybind11: FAQ
First, let us create an embedded Python module using pybind11. We use an embedded module since we have the Jupyter QtConsole embedded in the Python Scripting Extension.
Please, open the PythonBindings.hpp
file. An embedded module can be added using the PYBIND11_EMBEDDED_MODULE
macro. Note that the definition must be placed at global scope. An embedded module can be imported in the Python Scripting Extension like any other module. These modules are added to Python’s list of builtins, so they can also be imported in pure Python files loaded by the interpreter (the Jupyter QtConsole embedded into Python Scripting Extension). See pybind11: Adding embedded modules for more information.
You can add an unlimited number of embedded modules. But we recommend having a single embedded module for a SAMSON Extension named based on the UUID of the SAMSON Extension so that there will be no clashes between modules from different SAMSON Extensions named the same. For example, if a SAMSON Extension A has an embedded Python module named "mymodule" and a SAMSON Extension B has an embedded Python module named "mymodule", then there will be a clash and it will not be possible to use one of the modules. The UUID of a SAMSON Extension can be found in the CMakeLists.txt of your SAMSON Extension, check the line: SET(OUTPUT_UUID F2078F9E-F2CB-BA72-EE86-1E01A10B63D4)
. The UUID of the PyBindTutorial SAMSON Extension is F2078F9E-F2CB-BA72-EE86-1E01A10B63D4
. The name of the module will be: "SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4".
#pragma once
#ifdef CREATE_PYTHON_BINDINGS
#include "pybind11/pybind11.h"
#include "pybind11/embed.h"
namespace py = pybind11;
// Must be defined in the global scope.
// The first parameter is the name of the module (without quotes)
// The second parameter is the variable that will be used
// as the interface to add functions and classes to the module.
PYBIND11_EMBEDDED_MODULE(SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4, m) {
// module docstring
m.doc() = "SEPyBindTutorial from SAMSON Developers tutorial";
// the module name - preferably the SAMSON Extension name
m.def("moduleName", [](){ return "pybindtutorial"; });
}
#endif //CREATE_PYTHON_BINDINGS
In the code above, we define an embedded module named SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4, set its docstring, and expose a function named "moduleName" which returns the module's name.
You can call it in Python as follows:
Let us create Python bindings for some functionality of the PyBindTutorial App. Particularly, we would like to expose to Python a function from the SEPyBindTutorialApp
class called addCustomStructuralModel which adds a custom structural model with custom structural groups into the SAMSON data graph. We create the Python bindings for it in a separate file SEPyBindTutorialAppPythonBindings.cpp. We have two options described below.
1. Expose a function which implements the functionality of the addCustomStructuralModel
function without creating Python bindings for the SEPyBindTutorialApp
class.
void exposeSEPyBindTutorialApp(py::module& m) {
m.def("addCustomStructuralModel", [](){
SEPyBindTutorialApp* app = new SEPyBindTutorialApp();
app->addCustomStructuralModel();
},
"Add a custom structural model with a custom group");
}
Here, we expose a function in which an instance of the SEPyBindTutorialApp
is created and the addCustomStructuralModel
function is called. In this case, you can call it in Python as follows:
import SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4 as pybindtutorial
pybindtutorial.addCustomStructuralModel()
2. Create Python bindings for a class, its constructor, and the addCustomStructuralModel
function.
void exposeSEPyBindTutorialApp(py::module& m) {
py::class_ c(m, "SEPyBindTutorialApp", "The SEPyBindTutorialApp class");
// constructor
c.def(py::init<>(), "Constructs a custom structural group");
// functions
c.def("addCustomStructuralModel",
&SEPyBindTutorialApp::addCustomStructuralModel,
"Add a custom structural model with a custom group");
}
Here, we expose the SEPyBindTutorialApp
class, its constructor, and the addCustomStructuralModel
function itself. In this case, you can call it in Python as follows:
import SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4 as pybindtutorial
# create a class instance
tutorialApp = pybindtutorial.SEPyBindTutorialApp()
# call a function on this instance
tutorialApp.addCustomStructuralModel()
Now we can invoke the exposeSEPyBindTutorialApp
function inside the PYBIND11_EMBEDDED_MODULE
macro:
PYBIND11_EMBEDDED_MODULE(SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4, m) {
// ...
// call functions that expose functionality of the SAMSON Extension
exposeSEPyBindTutorialApp(m);
}
Let us now expose the CustomStructuralModel
class which is based on the SBStructuralModel class from the SAMSON SDK.
Note
The functionality of SAMSON classes already exposed by the Python Scripting Extension itself does not need to be re-exposed again. For example, in this case you do not need to create any bindings for the functionality from the SBStructuralModel class, since it is already exposed in the Python Scripting Extension.
We create Python bindings for the CustomStructuralModel
class in the file CustomStructuralModelPythonBindings.cpp as follows (see pybind11: Object-oriented code and pybind11: Classes for more information):
void exposeCustomStructuralModel(py::module& m) {
py::class_<
CustomStructuralModel, /* the class */
std::unique_ptr, /* the class type */
SBStructuralModel /* the base class */
>
c(m, /* pybind11::module */
"CustomStructuralModel", /* the class name in python*/
/* the docstring */
R"(This class describes a custom structural model from SEPyBindTutorial Extension)"
);
// constructors
c.def(py::init<>(), "Constructs a custom structural model");
// attributes
// read-only attributes
c.def_property_readonly("hasCustomComment",
&CustomStructuralModel::hasCustomComment,
"Returns true when the custom model's custom comment is set");
// read-and-write attributes
c.def_property("customComment",
&CustomStructuralModel::getCustomComment,
&CustomStructuralModel::setCustomComment,
"A custom comment");
// functions
c.def("clearCustomComment",
&CustomStructuralModel::clearCustomComment,
"Clears the custom comment");
}
As you can see, we can expose some functions in a more pythonic way - as attributes, read-only or with a read and write access. In the last case, we need to provide getter and setter functions.
Now we can invoke the exposeCustomStructuralModel
function inside the PYBIND11_EMBEDDED_MODULE macro:
PYBIND11_EMBEDDED_MODULE(SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4, m) {
// ...
// call functions that expose the functionality of the SAMSON Extension
// ...
exposeCustomStructuralModel(m);
}
Special case: Python bindings for functions that return/receive SAMSON physical quantities and types#
SAMSON physical quantities and types (i.e., SBQuantity
class and SBPhysicalVector3, SBPhysicalVector6, SBPhysicalInterval, SBDTypePhysicalIAVector3, SBPhysicalMatrix33, SBPhysicalMatrix66, SBSpatialTransform
classes) are not directly exposed to Python since they are template-based. Instead, special wrappers for these classes were created and exposed to be used in Python. These wrappers allow one to fully utilize the functionality of SAMSON physical quantities and types and significantly reduce the size of the Python Scripting Extension.
List of wrapped SAMSON physical quantities and types:
SBDQuantityWrapper
- wrapsSBDQuantity
, parameterized bySBUnitSystem
SBDTypePhysicalVector3Wrapper
- wrapsSBDTypePhysicalVector3
, parameterized bySBDQuantityWrapper
SBDTypePhysicalVector6Wrapper
- wrapsSBDTypePhysicalVector6
, parameterized bySBDQuantityWrapper
SBDTypePhysicalIntervalWrapper
- wrapsSBDTypePhysicalInterval
, parameterized bySBDQuantityWrapper
SBDTypePhysicalIAVector3Wrapper
- wrapsSBDTypePhysicalIAVector3
, parameterized bySBDQuantityWrapper
SBDTypePhysicalMatrix33Wrapper
- wrapsSBDTypePhysicalMatrix33
, parameterized bySBDQuantityWrapper
SBDTypePhysicalMatrix66Wrapper
- wrapsSBDTypePhysicalMatrix66
, parameterized bySBDQuantityWrapper
SBDTypeSpatialTransformWrapper
- wrapsSBDTypeSpatialTransform
These wrapped SAMSON physical quantities and types are exposed by Python Scripting Extension together with commonly used short names (see the tutorial on Python Scripting Units), so you do not need to create bindings for them.
Header files for these wrapper classes are provided together with the PyBindTutorial sample from SAMSON-Developers-Tutorials. You can find them in the folder PyBindTutorial/external/SAMSON-SDK-wrappers
. The wrapper classes are named the same as the classes that they wrap with an addition of a word Wrapper at the end. For simplicity, this path is included in the CMakeLists.txt of the PyBindTutorial sample:
# Set path to headers for wrappers for SAMSON physical quantities and types
SET(SAMSONSDKWRAPPERS_INCLUDE_DIRS "./external/SAMSON-SDK-wrappers/")
INCLUDE_DIRECTORIES(${SAMSONSDKWRAPPERS_INCLUDE_DIRS})
You can copy these wrapper files to your SAMSON Extension project if you want to create bindings for functions that return/receive SAMSON physical quantities and types.
To create Python bindings for functions which return or receive SAMSON physical quantities and types, it is necessary to create wrappers for such functions. The provided wrapper classes are simple to use: they have template constructors for the creation of a wrapped object (e.g., a SBQuantityWrapper
object from a SBQuantityWrapper
object) and template getter functions that return a SAMSON physical quantity or type from a wrapped object (e.g., a SBQuantity
object from a SBQuantityWrapper
object).
In the PyBindTutorial sample, you will find a special class with functions that return or receive different SAMSON physical quantities and types: UnitsExample.hpp, UnitsExample.cpp. These are just simple getter and setter functions to describe a technique of creating Python bindings for such functions. The Python bindings for the UnitsExample
class can be found in the UnitsExamplePythonBindings.cpp file. The Python bindings for the UnitsExample
class are created as follows:
/* File: source/UnitsExamplePythonBindings.cpp */
/* ... */
void exposeUnitsExample(py::module& m) {
// The py::class_ creates bindings for a C++ class
py::class_ c(m, "UnitsExample", R"(An example of a class which has functions that return/receive SAMSON Units and Types)");
// constructors
c.def(py::init<>(), "Constructor");
/* ... */
}
Python bindings for functions that return a SAMSON physical quantity#
The SBDQuantityWrapper
class is a class template with a type of the SBUnitSystem
(i.e., SBUnitSystemSI, SBUnitSystemAU, SBUnitSystemDalton, SBUnitSystemElectronvolt, SBUnitSystemKilocaloriePerMole
) as the template argument. This argument should coincide with SBUnitSystem
type of the SBQuantity
object that is being wrapped:
/* SI units */
SBQuantity::dimensionless dimensionless;
// SBQuantityWrapperSI is a shortname for SBDQuantityWrapper
SBDQuantityWrapper dimensionlessWrapped = SBQuantityWrapperSI(dimensionless);
SBQuantity::length length;
SBDQuantityWrapper lengthWrapped = SBQuantityWrapperSI(length);
/* AU units */
SBQuantity::auMass auMass;
// SBQuantityWrapperAU is a shortname for SBDQuantityWrapper
SBDQuantityWrapper auMassWrapped = SBQuantityWrapperAU(auMass);
/* Dalton units */
SBQuantity::dalton dalton;
// SBQuantityWrapperDalton is a shortname for SBDQuantityWrapper
SBDQuantityWrapper daltonWrapped = SBQuantityWrapperDalton(dalton);
/* electronvolt units */
SBQuantity::electronvolt energyEv;
// SBQuantityWrapperElectronvolt is a shortname for SBDQuantityWrapper
SBDQuantityWrapper energyEvWrapped = SBQuantityWrapperElectronvolt(energyEv);
/* kilocaloriePerMole units */
SBQuantity::kilocaloriePerMole kcalPerMol;
// SBQuantityWrapperKilocaloriePerMole is a shortname for SBDQuantityWrapper
SBDQuantityWrapper energyEvWrapped = SBQuantityWrapperKilocaloriePerMole(kcalPerMol);
Let's see how to create Python bindings for some functions from the UnitsExample
class that return SBQuantity
objects.
/* File: include/UnitsExample.hpp */
/* Class: UnitsExample */
/* ... */
class UnitsExample {
public:
/* ... */
SBQuantity::dimensionless const& getDimensionless();
SBQuantity::length const& getLength();
SBQuantity::auMass const& getAuMass();
/* ... */
};
In the source/UnitsExamplePythonBindings.cpp file you will find wrappers for these functions in which SBQuantity
objects are transformed to SBQuantityWrapper
objects. The getter functions can be wrapped as follows:
/* File: source/UnitsExamplePythonBindings.cpp */
// include headers with the wrapper for SAMSON physical quantities for use in Python
#include "SBDQuantityWrapper.hpp"
SBDQuantityWrapper getDimensionlessWrapper(UnitsExample& obj) {
// create a SBDQuantityWrapper object from SBQuantity and return it
return SBDQuantityWrapper(obj.getDimensionless());
}
SBQuantityWrapperSI getLengthWrapper(UnitsExample& obj) {
// create a SBDQuantityWrapper object from SBQuantity and return it
return SBQuantityWrapperSI(obj.getLength());
}
SBDQuantityWrapper getAuMassWrapper(UnitsExample& obj) {
// create a SBDQuantityWrapper object from SBQuantity and return it
return SBDQuantityWrapper(obj.getAuMass());
}
Now we can create Python bindings for these wrapper functions:
/* File: source/UnitsExamplePythonBindings.cpp */
/* ... */
void exposeUnitsExample(py::module& m) {
// The py::class_ creates bindings for a C++ class
py::class_ c(m, "UnitsExample", R"(An example of a class which has functions that return/receive SAMSON Units and Types)");
// constructors
c.def(py::init<>(), "Constructor");
/* ... */
c.def("getDimensionless", &getDimensionlessWrapper);
c.def("getLength", &getLengthWrapper);
c.def("getAuMass", &getAuMassWrapper);
/* ... */
}
You can use these functions in Python Scripting as follows:
import SE_F2078F9E_F2CB_BA72_EE86_1E01A10B63D4 as pybindtutorial
ex = pybindtutorial.UnitsExample()
ex.getDimensionless()
ex.getAuMass()
Note
Since there is no connection between a SBQuantityWrapper
object and a SBQuantity
object that it wraps (the same for SBPhysicalVector3Wrapper
, etc), getter and setter functions that return SBQuantityWrapper
, SBPhysicalVector3Wrapper
, etc, should not be exposed to Python as attributes via def_property()
, but as functions via def()
. You may also expose getter functions as read-only attributes via def_property_readonly()
.
Python bindings for functions that receive a SAMSON physical quantity#
The header file for the SBDQuantityWrapper
class provides a function template that returns a SBQuantity
object from a SBDQuantityWrapper
object:
where the template argument is a SBQuantity
type (e.g., SBQuantity::length
).
Let's see some examples:
/* SI units */
SBDQuantityWrapper dimensionlessWrapped;
SBQuantity::dimensionless dimensionless = getSBQuantity(dimensionlessWrapped);
SBQuantityWrapperSI lengthWrapped;
SBQuantity::length length = getSBQuantity(lengthWrapped);
/* AU units */
SBDQuantityWrapper auMassWrapped;
SBQuantity::auMass auMass = getSBQuantity(auMassWrapped);
/* Dalton units */
SBDQuantityWrapper daltonWrapped;
SBQuantity::dalton dalton = getSBQuantity(daltonWrapped);
/* electronvolt units */
SBDQuantityWrapper energyEvWrapped;
SBQuantity::electronvolt energyEv = getSBQuantity(energyEvWrapped);
/* kilocaloriePerMole units */
SBDQuantityWrapper energyEvWrapped;
SBQuantity::kilocaloriePerMole kcalPerMol = getSBQuantity(energyEvWrapped);
Let's see how to create Python bindings for some functions from the UnitsExample
class that receive SBQuantity
objects.
/* File: include/UnitsExample.hpp */
/* Class: UnitsExample */
/* ... */
class UnitsExample {
public:
/* ... */
void setDimensionless(const SBQuantity::dimensionless& v);
void setLength(const SBQuantity::length& v);
void setAuMass(const SBQuantity::auMass& v);
/* ... */
};
In the UnitsExamplePythonBindings.cpp file you will find wrappers for these functions in which SBQuantityWrapper
objects are transformed to SBQuantity
objects. The setter functions can be wrapped as follows:
/* File: source/UnitsExamplePythonBindings.cpp */
void setDimensionlessWrapper(UnitsExample& obj, SBDQuantityWrapper& u) {
// create a SBQuantity object from SBDQuantityWrapper and pass it into a specific function
obj.setDimensionless( getSBQuantity(u) );
}
void setLengthWrapper(UnitsExample& obj, SBQuantityWrapperSI& u) {
// create a SBQuantity object from SBDQuantityWrapper and pass it into a specific function
obj.setLength( getSBQuantity(u) );
}
void setAuMassWrapper(UnitsExample& obj, SBDQuantityWrapper& u) {
// create a SBQuantity object from SBDQuantityWrapper and pass it into a specific function
obj.setAuMass( getSBQuantity(u) );
}
Now we can create Python bindings for these wrapper functions:
/* File: source/UnitsExamplePythonBindings.cpp */
/* ... */
void exposeUnitsExample(py::module& m) {
// The py::class_ creates bindings for a C++ class
py::class_ c(m, "UnitsExample",
R"(An example of a class which has functions that return/receive SAMSON Units and Types)");
// constructors
c.def(py::init<>(), "Constructor");
/* ... */
c.def("setDimensionless", &setDimensionlessWrapper);
c.def("setLength", &setLengthWrapper);
c.def("setAuMass", &setAuMassWrapper);
/* ... */
}
Python bindings for functions that return a SAMSON physical type#
Wrapper classes for SAMSON physical types (except for SBDTypeSpatialTransformWrapper
) are template classes parametrized by a type of the SBQuantityWrapper
(i.e., SBQuantityWrapper, SBQuantityWrapper, SBQuantityWrapper, SBQuantityWrapper, SBQuantityWrapper
). This template argument should coincide with the SBQuantity
unit type of the SAMSON physical type that is being wrapped. Let's see some examples:
/* Example objects for SBVector3 */
SBVector3 dimensionlessVec3;
// SBPhysicalVector3WrapperSI is a shortname for SBDTypePhysicalVector3Wrapper
SBTypePhysicalVector3Wrapper> dimensionlessVec3Wrapped = SBPhysicalVector3WrapperSI(dimensionlessVec3);
SBPosition3 position3;
SBTypePhysicalVector3Wrapper position3Wrapped = SBPhysicalVector3WrapperSI(position3);
/* Example objects for SBVector6 */
SBVelocity6 velocity6;
// SBPhysicalVector6WrapperSI is a shortname for SBTypePhysicalVector6Wrapper
SBTypePhysicalVector6Wrapper velocity6Wrapped = SBPhysicalVector6WrapperSI(velocity6);
/* Example objects for SBPhysicalMatrix33 */
SBInverseMass33 inverseMass33;
// SBPhysicalMatrix33WrapperSI is a shortname for SBTypePhysicalMatrix33Wrapper
SBTypePhysicalMatrix33Wrapper inverseMass33Wrapped = SBPhysicalMatrix33WrapperSI(inverseMass33);
/* Example objects for SBPhysicalMatrix66 */
SBInertia66 inertia66;
// SBPhysicalMatrix66WrapperSI is a shortname for SBTypePhysicalMatrix66Wrapper
SBTypePhysicalMatrix66Wrapper inertia66Wrapped = SBPhysicalMatrix66WrapperSI(inertia66);
/* Example objects for SBSpatialTransform */
SBSpatialTransform spatialTransform;
SBSpatialTransformWrapper spatialTransformWrapped = SBSpatialTransformWrapper(spatialTransform);
Let's see how to create Python bindings for some functions from the UnitsExample
class that return SAMSON physical types.
/* File: include/UnitsExample.hpp */
/* Class: UnitsExample */
/* ... */
class UnitsExample {
public:
/* ... */
SBVector3 const& getVector3();
SBPosition3 const& getPosition3();
SBVelocity6 const& getVelocity6();
SBInverseMass33 const& getInverseMass33();
SBInertia66 const& getInertia66();
SBSpatialTransform const& getSpatialTransform();
/* ... */
};
In the UnitsExamplePythonBindings.cpp file you will find wrappers for these functions in which SAMSON physical types are transformed to their wrapped versions objects. The getter functions can be wrapped as follows:
/* File: source/UnitsExamplePythonBindings.cpp */
// include headers with wrappers for SAMSON physical types (SBDType*) for use in Python
#include "SBDTypePhysicalVector3Wrapper.hpp"
#include "SBDTypePhysicalVector6Wrapper.hpp"
#include "SBDTypePhysicalIntervalWrapper.hpp"
#include "SBDTypePhysicalIAVector3Wrapper.hpp"
#include "SBDTypePhysicalMatrix33Wrapper.hpp"
#include "SBDTypePhysicalMatrix66Wrapper.hpp"
#include "SBDTypeSpatialTransformWrapper.hpp"
SBPhysicalVector3WrapperSI getVector3Wrapper(UnitsExample& obj) {
// create a SBDTypePhysicalVector3Wrapper object from SBDTypePhysicalVector3 and return it
return SBPhysicalVector3WrapperSI(obj.getVector3());
}
SBPhysicalVector3WrapperSI getPosition3Wrapper(UnitsExample& obj) {
// create a SBDTypePhysicalVector3Wrapper object from SBDTypePhysicalVector3 and return it
return SBPhysicalVector3WrapperSI(obj.getPosition3());
}
SBPhysicalVector6WrapperSI getVelocity6Wrapper(UnitsExample& obj) {
// create a SBPhysicalVector6WrapperSI object from SBDTypePhysicalVector6 and return it
return SBPhysicalVector6WrapperSI(obj.getVelocity6());
}
SBPhysicalMatrix33WrapperSI getInverseMass33Wrapper(UnitsExample& obj) {
return SBPhysicalMatrix33WrapperSI(obj.getInverseMass33());
}
SBPhysicalMatrix66WrapperSI getInertia66Wrapper(UnitsExample& obj) {
return SBPhysicalMatrix66WrapperSI(obj.getInertia66());
}
SBSpatialTransformWrapper getSpatialTransformWrapper(UnitsExample& obj) {
return SBSpatialTransformWrapper(obj.getSpatialTransform());
}
Now we can create Python bindings for these wrapper functions:
/* File: source/UnitsExamplePythonBindings.cpp */
/* ... */
void exposeUnitsExample(py::module& m) {
// The py::class_ creates bindings for a C++ class
py::class_ c(m, "UnitsExample",
R"(An example of a class which has functions that return/receive SAMSON Units and Types)");
// constructors
c.def(py::init<>(), "Constructor");
/* ... */
c.def("getVector3", &getVector3Wrapper);
c.def("getPosition3", &getPosition3Wrapper);
c.def("getVelocity6", &getVelocity6Wrapper);
c.def("getInverseMass33", &getInverseMass33Wrapper);
c.def("getInertia66", &getInertia66Wrapper);
c.def("getSpatialTransform", &getSpatialTransformWrapper);
/* ... */
}
How to create Python bindings for functions that receive a SAMSON physical type#
Header files for wrapper classes for SAMSON physical types each, except for the SBDTypeSpatialTransformWrapper
class, provide a function template that returns a SAMSON physical type from its wrapped version:
getSBPhysicalVector3(...)
getSBPhysicalVector6(...)
getSBPhysicalInterval(...)
getSBPhysicalIAVector3(...)
getSBPhysicalMatrix33(...)
getSBPhysicalMatrix66(...)
where the template arguments are of SBQuantity
type.
Let's see some examples:
/* Example objects for SBVector3 */
SBTypePhysicalVector3Wrapper dimensionlessVec3Wrapped;
SBVector3 dimensionlessVec3 = getSBPhysicalVector3(dimensionlessVec3Wrapped);
SBPhysicalVector3WrapperSI position3Wrapped;
SBPosition3 position3 = getSBPhysicalVector3(position3Wrapped);
/* Example objects for SBVector6 */
SBPhysicalVector6WrapperSI velocity6Wrapped;
SBVelocity6 velocity6 = getSBPhysicalVector6(velocity6Wrapped);
/* Example objects for SBPhysicalMatrix33 */
SBPhysicalMatrix33WrapperSI inverseMass33Wrapped;
SBInverseMass33 inverseMass33 = getSBPhysicalMatrix33(inverseMass33Wrapped);
/* Example objects for SBPhysicalMatrix66 */
SBPhysicalMatrix66WrapperSI inertia66Wrapped;
SBInertia66 inertia66 = getSBPhysicalMatrix66(inertia66Wrapped);
/* Example objects for SBSpatialTransform */
SBSpatialTransformWrapper spatialTransformWrapped;
SBSpatialTransform spatialTransform = spatialTransformWrapped.toSBSpatialTransform();
Let's see how to create Python bindings for some functions from the UnitsExample
class that receive SAMSON physical types.
/* File: include/UnitsExample.hpp */
/* Class: UnitsExample */
/* ... */
class UnitsExample {
public:
/* ... */
void setVector3(const SBVector3& v);
void setPosition3(const SBPosition3& v);
void setVelocity6(const SBVelocity6& v);
void setInverseMass33(const SBInverseMass33& v);
void setInertia66(const SBInertia66& v);
void setSpatialTransform(const SBSpatialTransform& v);
/* ... */
};
In the UnitsExamplePythonBindings.cpp file you will find wrappers for these functions in which wrapped SAMSON physical types are transformed to SAMSON physical types. The setter functions can be wrapped as follows:
/* File: source/UnitsExamplePythonBindings.cpp */
void setVector3Wrapper(UnitsExample& obj, SBPhysicalVector3WrapperSI& u) {
// create a SBDTypePhysicalVector3 object from SBDTypePhysicalVector3Wrapper and pass it into a specific function
obj.setVector3( getSBPhysicalVector3(u) );
}
void setPosition3Wrapper(UnitsExample& obj, SBPhysicalVector3WrapperSI& u) {
// create a SBDTypePhysicalVector3 object from SBDTypePhysicalVector3Wrapper and pass it into a specific function
obj.setPosition3( getSBPhysicalVector3(u) );
}
void setVelocity6Wrapper(UnitsExample& obj, SBPhysicalVector6WrapperSI& u) {
// create a SBDTypePhysicalVector6 object from SBPhysicalVector6WrapperSI and pass it into a specific function
obj.setVelocity6( getSBPhysicalVector6(u) );
}
void setInverseMass33Wrapper(UnitsExample& obj, SBPhysicalMatrix33WrapperSI& u) {
obj.setInverseMass33( getSBPhysicalMatrix33(u) );
}
void setInertia66Wrapper(UnitsExample& obj, SBPhysicalMatrix66WrapperSI& u) {
obj.setInertia66( getSBPhysicalMatrix66(u) );
}
void setSpatialTransformWrapper(UnitsExample& obj, SBSpatialTransformWrapper& u) {
obj.setSpatialTransform( u.toSBSpatialTransform() );
}
Now we can create Python bindings for these wrapper functions:
/* File: source/UnitsExamplePythonBindings.cpp */
/* ... */
void exposeUnitsExample(py::module& m) {
// The py::class_ creates bindings for a C++ class
py::class_ c(m, "UnitsExample",
R"(An example of a class which has functions that return/receive SAMSON Units and Types)");
// constructors
c.def(py::init<>(), "Constructor");
/* ... */
c.def("setVector3", &setVector3Wrapper);
c.def("setPosition3", &setPosition3Wrapper);
c.def("setVelocity6", &setVelocity6Wrapper);
c.def("setInverseMass33", &setInverseMass33Wrapper);
c.def("setInertia66", &setInertia66Wrapper);
c.def("setSpatialTransform", &setSpatialTransformWrapper);
/* ... */
}
Running SAMSON with Python#
Please, check the following tutorials to learn how to run SAMSON from an IDE on your OS: