diff --git a/common/autoware_pyplot/CMakeLists.txt b/common/autoware_pyplot/CMakeLists.txt new file mode 100644 index 0000000000000..6922d5d9306f7 --- /dev/null +++ b/common/autoware_pyplot/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.14) + +project(autoware_pyplot) + +find_package(autoware_cmake REQUIRED) + +find_package( + Python3 + COMPONENTS Interpreter Development + REQUIRED) +find_package(pybind11 REQUIRED) + +autoware_package() + +ament_auto_add_library(${PROJECT_NAME} STATIC + DIRECTORY src +) +target_link_libraries(${PROJECT_NAME} ${Python3_LIBRARIES} pybind11::embed) + +if(BUILD_TESTING) + find_package(ament_cmake_ros REQUIRED) + + file(GLOB_RECURSE test_files test/*.cpp) + + ament_add_ros_isolated_gtest(test_${PROJECT_NAME} ${test_files}) + + target_link_libraries(test_${PROJECT_NAME} + ${PROJECT_NAME} + ) +endif() + +ament_auto_package() diff --git a/common/autoware_pyplot/include/autoware/pyplot/axes.hpp b/common/autoware_pyplot/include/autoware/pyplot/axes.hpp new file mode 100644 index 0000000000000..53eb2daf21b8f --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/axes.hpp @@ -0,0 +1,141 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__AXES_HPP_ +#define AUTOWARE__PYPLOT__AXES_HPP_ + +#include +#include +#include + +#include + +namespace autoware::pyplot +{ +inline namespace axes +{ +class DECL_VISIBILITY Axes : public PyObjectWrapper +{ +public: + explicit Axes(const pybind11::object & object); + explicit Axes(pybind11::object && object); + + PyObjectWrapper add_patch( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper bar_label( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper cla( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper contour( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper errorbar( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper fill( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper fill_between( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + std::tuple get_xlim() const; + + std::tuple get_ylim() const; + + PyObjectWrapper grid( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper imshow( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + legend::Legend legend( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper plot( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + quiver::Quiver quiver( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper set_aspect( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper set_title( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper set_xlabel( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper set_xlim( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper set_ylabel( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper set_ylim( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper text( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + +private: + void load_attrs(); + + pybind11::object add_patch_attr; + pybind11::object cla_attr; + pybind11::object contour_attr; + pybind11::object contourf_attr; + pybind11::object fill_attr; + pybind11::object fill_between_attr; + pybind11::object get_xlim_attr; + pybind11::object get_ylim_attr; + pybind11::object grid_attr; + pybind11::object imshow_attr; + pybind11::object legend_attr; + pybind11::object quiver_attr; + pybind11::object plot_attr; + pybind11::object scatter_attr; + pybind11::object set_aspect_attr; + pybind11::object set_title_attr; + pybind11::object set_xlabel_attr; + pybind11::object set_xlim_attr; + pybind11::object set_ylabel_attr; + pybind11::object set_ylim_attr; + pybind11::object text_attr; +}; +} // namespace axes +} // namespace autoware::pyplot +#endif // AUTOWARE__PYPLOT__AXES_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/common.hpp b/common/autoware_pyplot/include/autoware/pyplot/common.hpp new file mode 100644 index 0000000000000..123bd82029e08 --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/common.hpp @@ -0,0 +1,44 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__COMMON_HPP_ +#define AUTOWARE__PYPLOT__COMMON_HPP_ + +#include + +namespace autoware::pyplot +{ + +#ifdef _WIN32 +#define DECL_VISIBILITY __declspec(dllexport) +#else +#define DECL_VISIBILITY __attribute__((visibility("hidden"))) +#endif + +inline namespace common +{ +class DECL_VISIBILITY PyObjectWrapper +{ +public: + explicit PyObjectWrapper(const pybind11::object & object); + explicit PyObjectWrapper(pybind11::object && object); + pybind11::object unwrap() const { return self_; } + +protected: + pybind11::object self_; +}; +} // namespace common +} // namespace autoware::pyplot + +#endif // AUTOWARE__PYPLOT__COMMON_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/figure.hpp b/common/autoware_pyplot/include/autoware/pyplot/figure.hpp new file mode 100644 index 0000000000000..d438682d5b368 --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/figure.hpp @@ -0,0 +1,62 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__FIGURE_HPP_ +#define AUTOWARE__PYPLOT__FIGURE_HPP_ + +#include +#include + +namespace autoware::pyplot +{ +inline namespace figure +{ +class DECL_VISIBILITY Figure : public PyObjectWrapper +{ +public: + explicit Figure(const pybind11::object & object); + explicit Figure(pybind11::object && object); + + axes::Axes add_axes( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + axes::Axes add_subplot( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper colorbar( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper savefig( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + + PyObjectWrapper tight_layout( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + +private: + void load_attrs(); + + pybind11::object add_axes_attr; + pybind11::object add_subplot_attr; + pybind11::object colorbar_attr; + pybind11::object savefig_attr; + pybind11::object tight_layout_attr; +}; +} // namespace figure +} // namespace autoware::pyplot +#endif // AUTOWARE__PYPLOT__FIGURE_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/legend.hpp b/common/autoware_pyplot/include/autoware/pyplot/legend.hpp new file mode 100644 index 0000000000000..853f1e288407c --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/legend.hpp @@ -0,0 +1,32 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__LEGEND_HPP_ +#define AUTOWARE__PYPLOT__LEGEND_HPP_ + +#include + +namespace autoware::pyplot +{ +inline namespace legend +{ +class DECL_VISIBILITY Legend : public PyObjectWrapper +{ +public: + explicit Legend(const pybind11::object & object); + explicit Legend(pybind11::object && object); +}; +} // namespace legend +} // namespace autoware::pyplot +#endif // AUTOWARE__PYPLOT__LEGEND_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/loader.hpp b/common/autoware_pyplot/include/autoware/pyplot/loader.hpp new file mode 100644 index 0000000000000..df44c257d5925 --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/loader.hpp @@ -0,0 +1,28 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__LOADER_HPP_ +#define AUTOWARE__PYPLOT__LOADER_HPP_ + +namespace autoware::pyplot +{ + +#define LOAD_FUNC_ATTR(obj, mod) \ + do { \ + obj##_attr = mod.attr(#obj); \ + } while (0) + +} // namespace autoware::pyplot + +#endif // AUTOWARE__PYPLOT__LOADER_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/patches.hpp b/common/autoware_pyplot/include/autoware/pyplot/patches.hpp new file mode 100644 index 0000000000000..0c6c545942c74 --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/patches.hpp @@ -0,0 +1,57 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__PATCHES_HPP_ +#define AUTOWARE__PYPLOT__PATCHES_HPP_ + +#include + +namespace autoware::pyplot +{ +inline namespace patches +{ +class DECL_VISIBILITY Circle : public PyObjectWrapper +{ +public: + explicit Circle( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); +}; + +class DECL_VISIBILITY Ellipse : public PyObjectWrapper +{ +public: + explicit Ellipse( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); +}; + +class DECL_VISIBILITY Rectangle : public PyObjectWrapper +{ +public: + explicit Rectangle( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); +}; + +class DECL_VISIBILITY Polygon : public PyObjectWrapper +{ +public: + explicit Polygon( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); +}; +} // namespace patches +} // namespace autoware::pyplot +#endif // AUTOWARE__PYPLOT__PATCHES_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/pyplot.hpp b/common/autoware_pyplot/include/autoware/pyplot/pyplot.hpp new file mode 100644 index 0000000000000..83e0febeb237f --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/pyplot.hpp @@ -0,0 +1,164 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__PYPLOT_HPP_ +#define AUTOWARE__PYPLOT__PYPLOT_HPP_ + +#include +#include +#include + +#include +#include +#include + +namespace autoware::pyplot +{ +struct DECL_VISIBILITY PyPlot +{ +public: + explicit PyPlot(const pybind11::module & mod_); + + axes::Axes axes(const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper axis( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper cla( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper clf( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + figure::Figure figure( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + axes::Axes gca( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + figure::Figure gcf( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper gci( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper grid( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper imshow( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper legend( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper plot( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper quiver( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper savefig( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper scatter( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper show( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + axes::Axes subplot(const pybind11::dict & kwargs = pybind11::dict()); + axes::Axes subplot(int cri); + + std::tuple subplots(const pybind11::dict & kwargs = pybind11::dict()); + std::tuple> subplots( + int r, int c, const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper title( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper xlabel( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper xlim( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper ylabel( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + + PyObjectWrapper ylim( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()); + +private: + void load_attrs(); + pybind11::module mod; + pybind11::object axes_attr; + pybind11::object cla_attr; + pybind11::object clf_attr; + pybind11::object figure_attr; + pybind11::object gca_attr; + pybind11::object gcf_attr; + pybind11::object gci_attr; + pybind11::object grid_attr; + pybind11::object imshow_attr; + pybind11::object legend_attr; + pybind11::object plot_attr; + pybind11::object quiver_attr; + pybind11::object savefig_attr; + pybind11::object scatter_attr; + pybind11::object show_attr; + pybind11::object subplot_attr; + pybind11::object subplots_attr; + pybind11::object title_attr; + pybind11::object xlabel_attr; + pybind11::object xlim_attr; + pybind11::object ylabel_attr; + pybind11::object ylim_attr; +}; + +PyPlot import(); + +} // namespace autoware::pyplot + +template +pybind11::tuple Args(ArgsT &&... args) +{ + return pybind11::make_tuple(std::forward(args)...); +} + +using Kwargs = pybind11::dict; + +namespace py = pybind11; +using namespace py::literals; + +#endif // AUTOWARE__PYPLOT__PYPLOT_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/quiver.hpp b/common/autoware_pyplot/include/autoware/pyplot/quiver.hpp new file mode 100644 index 0000000000000..443c0540d9308 --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/quiver.hpp @@ -0,0 +1,32 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__QUIVER_HPP_ +#define AUTOWARE__PYPLOT__QUIVER_HPP_ + +#include + +namespace autoware::pyplot +{ +inline namespace quiver +{ +class DECL_VISIBILITY Quiver : public PyObjectWrapper +{ +public: + explicit Quiver(const pybind11::object & object); + explicit Quiver(pybind11::object && object); +}; +} // namespace quiver +} // namespace autoware::pyplot +#endif // AUTOWARE__PYPLOT__QUIVER_HPP_ diff --git a/common/autoware_pyplot/include/autoware/pyplot/text.hpp b/common/autoware_pyplot/include/autoware/pyplot/text.hpp new file mode 100644 index 0000000000000..27f892e90488f --- /dev/null +++ b/common/autoware_pyplot/include/autoware/pyplot/text.hpp @@ -0,0 +1,40 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef AUTOWARE__PYPLOT__TEXT_HPP_ +#define AUTOWARE__PYPLOT__TEXT_HPP_ + +#include + +namespace autoware::pyplot +{ +inline namespace text +{ +class DECL_VISIBILITY Text : public PyObjectWrapper +{ +public: + explicit Text(const pybind11::object & object); + explicit Text(pybind11::object && object); + + PyObjectWrapper set_rotation( + const pybind11::tuple & args = pybind11::tuple(), + const pybind11::dict & kwargs = pybind11::dict()) const; + +private: + void load_attrs(); + pybind11::object set_rotation_attr; +}; +} // namespace text +} // namespace autoware::pyplot +#endif // AUTOWARE__PYPLOT__TEXT_HPP_ diff --git a/common/autoware_pyplot/package.xml b/common/autoware_pyplot/package.xml new file mode 100644 index 0000000000000..1391db88cddff --- /dev/null +++ b/common/autoware_pyplot/package.xml @@ -0,0 +1,25 @@ + + + + autoware_pyplot + 0.1.0 + C++ interface for matplotlib based on pybind11 + Mamoru Sobue + Yukinari Hisaki + Apache License 2.0 + + Mamoru Sobue + + ament_cmake_auto + autoware_cmake + + pybind11-dev + + ament_cmake_ros + ament_lint_auto + autoware_lint_common + + + ament_cmake + + diff --git a/common/autoware_pyplot/src/axes.cpp b/common/autoware_pyplot/src/axes.cpp new file mode 100644 index 0000000000000..0f08ffe5773f6 --- /dev/null +++ b/common/autoware_pyplot/src/axes.cpp @@ -0,0 +1,154 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +namespace autoware::pyplot +{ +inline namespace axes +{ +Axes::Axes(const pybind11::object & object) : PyObjectWrapper(object) +{ + load_attrs(); +} +Axes::Axes(pybind11::object && object) : PyObjectWrapper(object) +{ + load_attrs(); +} + +PyObjectWrapper Axes::add_patch(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{add_patch_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::cla(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{cla_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::contour(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{contour_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::fill(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{fill_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::fill_between( + const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{fill_between_attr(*args, **kwargs)}; +} + +std::tuple Axes::get_xlim() const +{ + const pybind11::list ret = get_xlim_attr(); + return {ret[0].cast(), ret[1].cast()}; +} + +std::tuple Axes::get_ylim() const +{ + const pybind11::list ret = get_ylim_attr(); + return {ret[0].cast(), ret[1].cast()}; +} + +PyObjectWrapper Axes::grid(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{grid_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::imshow(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{imshow_attr(*args, **kwargs)}; +} + +legend::Legend Axes::legend(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return legend::Legend{legend_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::plot(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{plot_attr(*args, **kwargs)}; +} + +quiver::Quiver Axes::quiver(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return Quiver{quiver_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::set_aspect(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{set_aspect_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::set_title(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{set_title_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::set_xlabel(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{set_xlabel_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::set_xlim(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{set_xlim_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::set_ylabel(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{set_ylabel_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::set_ylim(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{set_ylim_attr(*args, **kwargs)}; +} + +PyObjectWrapper Axes::text(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{text_attr(*args, **kwargs)}; +} + +void Axes::load_attrs() +{ + LOAD_FUNC_ATTR(add_patch, self_); + LOAD_FUNC_ATTR(cla, self_); + LOAD_FUNC_ATTR(contour, self_); + LOAD_FUNC_ATTR(contourf, self_); + LOAD_FUNC_ATTR(fill, self_); + LOAD_FUNC_ATTR(fill_between, self_); + LOAD_FUNC_ATTR(get_xlim, self_); + LOAD_FUNC_ATTR(get_ylim, self_); + LOAD_FUNC_ATTR(grid, self_); + LOAD_FUNC_ATTR(imshow, self_); + LOAD_FUNC_ATTR(legend, self_); + LOAD_FUNC_ATTR(quiver, self_); + LOAD_FUNC_ATTR(plot, self_); + LOAD_FUNC_ATTR(scatter, self_); + LOAD_FUNC_ATTR(set_aspect, self_); + LOAD_FUNC_ATTR(set_title, self_); + LOAD_FUNC_ATTR(set_xlabel, self_); + LOAD_FUNC_ATTR(set_xlim, self_); + LOAD_FUNC_ATTR(set_ylabel, self_); + LOAD_FUNC_ATTR(set_ylim, self_); + LOAD_FUNC_ATTR(text, self_); +} +} // namespace axes +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/src/common.cpp b/common/autoware_pyplot/src/common.cpp new file mode 100644 index 0000000000000..5b3fffa37f30e --- /dev/null +++ b/common/autoware_pyplot/src/common.cpp @@ -0,0 +1,30 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +namespace autoware::pyplot +{ +inline namespace common +{ +PyObjectWrapper::PyObjectWrapper(const pybind11::object & object) +{ + self_ = object; +} +PyObjectWrapper::PyObjectWrapper(pybind11::object && object) +{ + self_ = std::move(object); +} +} // namespace common +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/src/figure.cpp b/common/autoware_pyplot/src/figure.cpp new file mode 100644 index 0000000000000..07aacf8dd516a --- /dev/null +++ b/common/autoware_pyplot/src/figure.cpp @@ -0,0 +1,66 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +namespace autoware::pyplot +{ +inline namespace figure +{ +Figure::Figure(const pybind11::object & object) : PyObjectWrapper(object) +{ + load_attrs(); +} +Figure::Figure(pybind11::object && object) : PyObjectWrapper(object) +{ + load_attrs(); +} + +void Figure::load_attrs() +{ + LOAD_FUNC_ATTR(add_axes, self_); + LOAD_FUNC_ATTR(add_subplot, self_); + LOAD_FUNC_ATTR(colorbar, self_); + LOAD_FUNC_ATTR(savefig, self_); + LOAD_FUNC_ATTR(tight_layout, self_); +} + +axes::Axes Figure::add_axes(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return axes::Axes{add_axes_attr(*args, **kwargs)}; +} + +axes::Axes Figure::add_subplot(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return axes::Axes{add_subplot_attr(*args, **kwargs)}; +} + +PyObjectWrapper Figure::colorbar(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{colorbar_attr(*args, **kwargs)}; +} + +PyObjectWrapper Figure::savefig(const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{savefig_attr(*args, **kwargs)}; +} + +PyObjectWrapper Figure::tight_layout( + const pybind11::tuple & args, const pybind11::dict & kwargs) const +{ + return PyObjectWrapper{tight_layout_attr(*args, **kwargs)}; +} +} // namespace figure +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/src/legend.cpp b/common/autoware_pyplot/src/legend.cpp new file mode 100644 index 0000000000000..e5a128725fec7 --- /dev/null +++ b/common/autoware_pyplot/src/legend.cpp @@ -0,0 +1,28 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +namespace autoware::pyplot +{ +inline namespace legend +{ +Legend::Legend(const pybind11::object & object) : PyObjectWrapper(object) +{ +} +Legend::Legend(pybind11::object && object) : PyObjectWrapper(object) +{ +} +} // namespace legend +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/src/patches.cpp b/common/autoware_pyplot/src/patches.cpp new file mode 100644 index 0000000000000..4df4de27e81e3 --- /dev/null +++ b/common/autoware_pyplot/src/patches.cpp @@ -0,0 +1,45 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +namespace autoware::pyplot +{ +inline namespace patches +{ +Circle::Circle(const pybind11::tuple & args, const pybind11::dict & kwargs) +: PyObjectWrapper(pybind11::module::import("matplotlib.patches").attr("Circle")) +{ + self_ = self_(*args, **kwargs); +} + +Ellipse::Ellipse(const pybind11::tuple & args, const pybind11::dict & kwargs) +: PyObjectWrapper(pybind11::module::import("matplotlib.patches").attr("Ellipse")) +{ + self_ = self_(*args, **kwargs); +} + +Rectangle::Rectangle(const pybind11::tuple & args, const pybind11::dict & kwargs) +: PyObjectWrapper(pybind11::module::import("matplotlib.patches").attr("Rectangle")) +{ + self_ = self_(*args, **kwargs); +} + +Polygon::Polygon(const pybind11::tuple & args, const pybind11::dict & kwargs) +: PyObjectWrapper(pybind11::module::import("matplotlib.patches").attr("Polygon")) +{ + self_ = self_(*args, **kwargs); +} +} // namespace patches +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/src/pyplot.cpp b/common/autoware_pyplot/src/pyplot.cpp new file mode 100644 index 0000000000000..d45d34a35d767 --- /dev/null +++ b/common/autoware_pyplot/src/pyplot.cpp @@ -0,0 +1,205 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +namespace autoware::pyplot +{ +PyPlot::PyPlot(const pybind11::module & mod_) : mod(mod_) +{ + load_attrs(); +} + +void PyPlot::load_attrs() +{ + LOAD_FUNC_ATTR(axes, mod); + LOAD_FUNC_ATTR(cla, mod); + LOAD_FUNC_ATTR(clf, mod); + LOAD_FUNC_ATTR(figure, mod); + LOAD_FUNC_ATTR(gca, mod); + LOAD_FUNC_ATTR(gcf, mod); + LOAD_FUNC_ATTR(gci, mod); + LOAD_FUNC_ATTR(grid, mod); + LOAD_FUNC_ATTR(imshow, mod); + LOAD_FUNC_ATTR(legend, mod); + LOAD_FUNC_ATTR(plot, mod); + LOAD_FUNC_ATTR(quiver, mod); + LOAD_FUNC_ATTR(savefig, mod); + LOAD_FUNC_ATTR(scatter, mod); + LOAD_FUNC_ATTR(show, mod); + LOAD_FUNC_ATTR(subplot, mod); + LOAD_FUNC_ATTR(subplots, mod); + LOAD_FUNC_ATTR(title, mod); + LOAD_FUNC_ATTR(xlabel, mod); + LOAD_FUNC_ATTR(xlim, mod); + LOAD_FUNC_ATTR(ylabel, mod); + LOAD_FUNC_ATTR(ylim, mod); +} + +axes::Axes PyPlot::axes(const pybind11::dict & kwargs) +{ + return axes::Axes{axes_attr(**kwargs)}; +} + +PyObjectWrapper PyPlot::cla(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{cla_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::clf(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{clf_attr(*args, **kwargs)}; +} + +figure::Figure PyPlot::figure(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return figure::Figure{figure_attr(*args, **kwargs)}; +} + +axes::Axes PyPlot::gca(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return axes::Axes{gca_attr(*args, **kwargs)}; +} + +figure::Figure PyPlot::gcf(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return figure::Figure{gcf_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::gci(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{gci_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::grid(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{grid_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::imshow(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{imshow_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::legend(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{legend_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::plot(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{plot_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::quiver(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{quiver_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::scatter(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{scatter_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::savefig(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{savefig_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::show(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{show_attr(*args, **kwargs)}; +} + +axes::Axes PyPlot::subplot(const pybind11::dict & kwargs) +{ + return axes::Axes(subplot_attr(**kwargs)); +} + +axes::Axes PyPlot::subplot(int cri) +{ + return axes::Axes(subplot_attr(cri)); +} + +std::tuple PyPlot::subplots(const pybind11::dict & kwargs) +{ + pybind11::list ret = subplots_attr(**kwargs); + pybind11::object fig = ret[0]; + pybind11::object ax = ret[1]; + return {figure::Figure(fig), axes::Axes(ax)}; +} + +std::tuple> PyPlot::subplots( + int r, int c, const pybind11::dict & kwargs) +{ + // subplots() returns [][] (if r > 1 && c > 1) else [] + // return []axes in row-major + // NOTE: equal to Axes.flat + pybind11::tuple args = pybind11::make_tuple(r, c); + pybind11::list ret = subplots_attr(*args, **kwargs); + std::vector axes; + pybind11::object fig = ret[0]; + figure::Figure figure(fig); + if (r == 1 && c == 1) { + // python returns Axes + axes.emplace_back(ret[1]); + } else if (r == 1 || c == 1) { + // python returns []Axes + pybind11::list axs = ret[1]; + for (int i = 0; i < r * c; ++i) axes.emplace_back(axs[i]); + } else { + // python returns [][]Axes + pybind11::list axs = ret[1]; + for (pybind11::size_t i = 0; i < axs.size(); ++i) { + pybind11::list ax_i = axs[i]; + for (unsigned j = 0; j < ax_i.size(); ++j) axes.emplace_back(ax_i[j]); + } + } + return {figure, axes}; +} + +PyObjectWrapper PyPlot::title(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{title_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::xlabel(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{xlabel_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::xlim(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{xlim_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::ylabel(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{ylabel_attr(*args, **kwargs)}; +} + +PyObjectWrapper PyPlot::ylim(const pybind11::tuple & args, const pybind11::dict & kwargs) +{ + return PyObjectWrapper{ylim_attr(*args, **kwargs)}; +} + +PyPlot import() +{ + auto mod = pybind11::module::import("matplotlib.pyplot"); + auto g_pyplot = PyPlot(mod); + return g_pyplot; +} + +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/src/quiver.cpp b/common/autoware_pyplot/src/quiver.cpp new file mode 100644 index 0000000000000..72614fb6227a7 --- /dev/null +++ b/common/autoware_pyplot/src/quiver.cpp @@ -0,0 +1,28 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +namespace autoware::pyplot +{ +inline namespace quiver +{ +Quiver::Quiver(const pybind11::object & object) : PyObjectWrapper(object) +{ +} +Quiver::Quiver(pybind11::object && object) : PyObjectWrapper(object) +{ +} +} // namespace quiver +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/src/text.cpp b/common/autoware_pyplot/src/text.cpp new file mode 100644 index 0000000000000..024c7c2daf946 --- /dev/null +++ b/common/autoware_pyplot/src/text.cpp @@ -0,0 +1,35 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +namespace autoware::pyplot +{ +inline namespace text +{ +Text::Text(const pybind11::object & object) : PyObjectWrapper(object) +{ +} + +Text::Text(pybind11::object && object) : PyObjectWrapper(object) +{ +} + +void Text::load_attrs() +{ + LOAD_FUNC_ATTR(set_rotation, self_); +} +} // namespace text +} // namespace autoware::pyplot diff --git a/common/autoware_pyplot/test/test_pyplot.cpp b/common/autoware_pyplot/test/test_pyplot.cpp new file mode 100644 index 0000000000000..2055709c61f52 --- /dev/null +++ b/common/autoware_pyplot/test/test_pyplot.cpp @@ -0,0 +1,64 @@ +// Copyright 2024 TIER IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +/* + very weirdly, you must include to pass vector. Although the code + compiles without it, the executable crashes at runtime + */ +#include + +#include + +TEST(PyPlot, single_plot) +{ + // NOTE: somehow, running multiple tests simultaneously causes the python interpreter to crash + py::scoped_interpreter guard{}; + auto plt = autoware::pyplot::import(); + { + plt.plot(Args(std::vector({1, 3, 2, 4})), Kwargs("color"_a = "blue", "linewidth"_a = 1.0)); + plt.xlabel(Args("x-title")); + plt.ylabel(Args("y-title")); + plt.title(Args("title")); + plt.xlim(Args(0, 5)); + plt.ylim(Args(0, 5)); + plt.grid(Args(true)); + plt.savefig(Args("test_single_plot.png")); + } + { + auto [fig, axes] = plt.subplots(1, 2); + auto & ax1 = axes[0]; + auto & ax2 = axes[1]; + + auto c = + autoware::pyplot::Circle(Args(py::make_tuple(0, 0), 0.5), Kwargs("fc"_a = "g", "ec"_a = "r")); + ax1.add_patch(Args(c.unwrap())); + + auto e = autoware::pyplot::Ellipse( + Args(py::make_tuple(-0.25, 0), 0.5, 0.25), Kwargs("fc"_a = "b", "ec"_a = "y")); + ax1.add_patch(Args(e.unwrap())); + + auto r = autoware::pyplot::Rectangle( + Args(py::make_tuple(0, 0), 0.25, 0.5), Kwargs("ec"_a = "#000000", "fill"_a = false)); + ax2.add_patch(Args(r.unwrap())); + + ax1.set_aspect(Args("equal")); + ax2.set_aspect(Args("equal")); + plt.savefig(Args("test_double_plot.svg")); + } +}