Skip to content

Commit aec479d

Browse files
committed
[trajoptlib] Add Python bindings
TODO - [ ] most of the bindings - [ ] docstrings - [ ] ci - [ ] tests Signed-off-by: Jade Turner <spacey-sooty@proton.me>
1 parent d699cfd commit aec479d

4 files changed

Lines changed: 193 additions & 0 deletions

File tree

trajoptlib/CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
6363
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS FALSE)
6464

6565
option(BUILD_EXAMPLES "Build examples" OFF)
66+
option(BUILD_PYTHON "Build Python" OFF)
6667

6768
include(CompilerFlags)
6869

@@ -233,3 +234,30 @@ if(BUILD_EXAMPLES)
233234
endif()
234235
endforeach()
235236
endif()
237+
238+
if(BUILD_PYTHON)
239+
set(DEV_MODULE Development.Module)
240+
find_package(Python 3.8 COMPONENTS Interpreter ${DEV_MODULE} REQUIRED)
241+
242+
# nanobind dependency
243+
fetchcontent_declare(
244+
nanobind
245+
GIT_REPOSITORY https://github.com/wjakob/nanobind.git
246+
GIT_TAG v2.6.1
247+
)
248+
fetchcontent_makeavailable(nanobind)
249+
250+
# pybind11_mkdoc dependency
251+
fetchcontent_declare(
252+
pybind11_mkdoc
253+
GIT_REPOSITORY https://github.com/pybind/pybind11_mkdoc.git
254+
# master on 2023-02-08
255+
GIT_TAG 42fbf377824185e255b06d68fa70f4efcd569e2d
256+
GIT_SUBMODULES ""
257+
)
258+
fetchcontent_makeavailable(pybind11_mkdoc)
259+
include(cmake/modules/Pybind11Mkdoc.cmake)
260+
261+
nanobind_add_module(trajoptlib_py py/main.cpp)
262+
target_link_libraries(trajoptlib_py PUBLIC TrajoptLib)
263+
endif()
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
function(pybind11_mkdoc target headers)
2+
find_package(Python3 REQUIRED COMPONENTS Interpreter)
3+
4+
if(UNIX AND NOT APPLE)
5+
if(EXISTS /usr/lib/libclang.so)
6+
set(env_vars
7+
LLVM_DIR_PATH=/usr/lib
8+
LIBCLANG_PATH=/usr/lib/libclang.so
9+
)
10+
else()
11+
# Get default clang version
12+
execute_process(
13+
COMMAND
14+
bash -c
15+
"clang++ --version | grep -E -o \'[0-9]+\' | head -1"
16+
OUTPUT_VARIABLE CLANG_VERSION
17+
OUTPUT_STRIP_TRAILING_WHITESPACE
18+
COMMAND_ERROR_IS_FATAL ANY
19+
)
20+
21+
set(env_vars
22+
LLVM_DIR_PATH=/usr/lib/llvm-${CLANG_VERSION}
23+
LIBCLANG_PATH=/usr/lib/llvm-${CLANG_VERSION}/lib/libclang.so
24+
)
25+
endif()
26+
endif()
27+
28+
get_target_property(target_dirs ${target} INCLUDE_DIRECTORIES)
29+
list(TRANSFORM target_dirs PREPEND "-I")
30+
31+
get_target_property(eigen_dirs Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES)
32+
list(FILTER eigen_dirs INCLUDE REGEX "\\$<BUILD_INTERFACE:.*>")
33+
list(TRANSFORM eigen_dirs PREPEND "-I")
34+
35+
get_target_property(
36+
small_vector_dirs
37+
small_vector
38+
INTERFACE_INCLUDE_DIRECTORIES
39+
)
40+
list(FILTER small_vector_dirs INCLUDE REGEX "\\$<BUILD_INTERFACE:.*>")
41+
list(TRANSFORM small_vector_dirs PREPEND "-I")
42+
43+
add_custom_command(
44+
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/py/docstrings.hpp
45+
COMMAND
46+
${env_vars} ${Python3_EXECUTABLE} -m pybind11_mkdoc ${headers} -o
47+
${CMAKE_CURRENT_SOURCE_DIR}/py/docstrings.hpp
48+
-I/usr/lib/clang/`clang++ --version | grep -E -o '[0-9]+' | head
49+
-1`/include ${target_dirs} -std=c++23
50+
DEPENDS ${headers}
51+
USES_TERMINAL
52+
)
53+
add_custom_target(
54+
${target}_docstrings
55+
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/py/docstrings.hpp
56+
)
57+
endfunction()

trajoptlib/py/docstrings.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Copyright (c) TrajoptLib contributors
2+
3+
#pragma once

trajoptlib/py/main.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) TrajoptLib contributors
2+
3+
#include <nanobind/nanobind.h>
4+
#include <nanobind/operators.h>
5+
#include <nanobind/stl/string.h>
6+
7+
#include <trajopt/geometry/pose2.hpp>
8+
#include <trajopt/geometry/rotation2.hpp>
9+
#include <trajopt/geometry/translation2.hpp>
10+
#include <trajopt/swerve_trajectory_generator.hpp>
11+
12+
namespace nb = nanobind;
13+
using namespace nb::literals;
14+
15+
NB_MODULE(trajoptlib_py, m) {
16+
nb::class_<trajopt::Rotation2d>(m, "Rotation2d")
17+
.def(nb::init<>())
18+
.def(nb::init<double>())
19+
.def(nb::init<double, double>())
20+
.def(nb::self + nb::self)
21+
.def(nb::self - nb::self)
22+
.def(nb::self == nb::self)
23+
.def(-nb::self)
24+
.def("rotate_by",
25+
[](nb::handle_t<trajopt::Rotation2d> self,
26+
nb::handle_t<trajopt::Rotation2d> other) {
27+
trajopt::Rotation2d& self_cpp =
28+
nb::cast<trajopt::Rotation2d&>(self);
29+
trajopt::Rotation2d& other_cpp =
30+
nb::cast<trajopt::Rotation2d&>(other);
31+
return self_cpp.rotate_by(other_cpp);
32+
})
33+
.def("radians", &trajopt::Rotation2d::radians)
34+
.def("degrees", &trajopt::Rotation2d::degrees)
35+
.def("cos", &trajopt::Rotation2d::cos)
36+
.def("sin", &trajopt::Rotation2d::sin);
37+
38+
nb::class_<trajopt::Translation2d>(m, "Translation2d")
39+
.def(nb::init<>())
40+
.def(nb::init<double, double>())
41+
.def(nb::init<double, trajopt::Rotation2d>())
42+
.def("x", &trajopt::Translation2d::x)
43+
.def("y", &trajopt::Translation2d::y)
44+
.def(nb::self + nb::self)
45+
.def(nb::self - nb::self)
46+
.def(-nb::self)
47+
.def(nb::self * double())
48+
.def(nb::self / double())
49+
.def(nb::self == nb::self)
50+
.def("rotate_by",
51+
[](nb::handle_t<trajopt::Translation2d> self,
52+
nb::handle_t<trajopt::Rotation2d> rotation) {
53+
trajopt::Translation2d& self_cpp =
54+
nb::cast<trajopt::Translation2d&>(self);
55+
trajopt::Rotation2d& rotation_cpp =
56+
nb::cast<trajopt::Rotation2d&>(rotation);
57+
return self_cpp.rotate_by(rotation_cpp);
58+
})
59+
.def("angle", &trajopt::Translation2d::angle)
60+
.def("dot",
61+
[](nb::handle_t<trajopt::Translation2d> self,
62+
nb::handle_t<trajopt::Translation2d> other) {
63+
trajopt::Translation2d& self_cpp =
64+
nb::cast<trajopt::Translation2d&>(self);
65+
trajopt::Translation2d& other_cpp =
66+
nb::cast<trajopt::Translation2d&>(other);
67+
return self_cpp.dot(other_cpp);
68+
})
69+
.def("cross",
70+
[](nb::handle_t<trajopt::Translation2d> self,
71+
nb::handle_t<trajopt::Translation2d> other) {
72+
trajopt::Translation2d& self_cpp =
73+
nb::cast<trajopt::Translation2d&>(self);
74+
trajopt::Translation2d& other_cpp =
75+
nb::cast<trajopt::Translation2d&>(other);
76+
return self_cpp.cross(other_cpp);
77+
})
78+
.def("norm", &trajopt::Translation2d::norm)
79+
.def("squared_norm", &trajopt::Translation2d::squared_norm)
80+
.def("distance", [](nb::handle_t<trajopt::Translation2d> self,
81+
nb::handle_t<trajopt::Translation2d> other) {
82+
trajopt::Translation2d& self_cpp =
83+
nb::cast<trajopt::Translation2d&>(self);
84+
trajopt::Translation2d& other_cpp =
85+
nb::cast<trajopt::Translation2d&>(other);
86+
return self_cpp.distance(other_cpp);
87+
});
88+
89+
nb::class_<trajopt::Pose2d>(m, "Pose2d")
90+
.def(nb::init<>())
91+
.def(nb::init<trajopt::Translation2d, trajopt::Rotation2d>())
92+
.def(nb::init<double, double, trajopt::Rotation2d>())
93+
.def("translation", &trajopt::Pose2d::translation)
94+
.def("x", &trajopt::Pose2d::x)
95+
.def("y", &trajopt::Pose2d::y)
96+
.def("rotation", &trajopt::Pose2d::rotation)
97+
.def("rotate_by", [](nb::handle_t<trajopt::Translation2d> self,
98+
nb::handle_t<trajopt::Rotation2d> rotation) {
99+
trajopt::Translation2d& self_cpp =
100+
nb::cast<trajopt::Translation2d&>(self);
101+
trajopt::Rotation2d& rotation_cpp =
102+
nb::cast<trajopt::Rotation2d&>(rotation);
103+
return self_cpp.rotate_by(rotation_cpp);
104+
});
105+
}

0 commit comments

Comments
 (0)