Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Plane pybind11 interface #346

Merged
merged 12 commits into from
Dec 29, 2021
1 change: 0 additions & 1 deletion src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ if (PYTHONLIBS_FOUND)
Frustum_TEST
Inertial_TEST
OrientedBox_TEST
Plane_TEST
python_TEST
SignalStats_TEST
Sphere_TEST
Expand Down
1 change: 1 addition & 0 deletions src/python_pybind11/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ if (${pybind11_FOUND})
Matrix4_TEST
MassMatrix3_TEST
MovingWindowFilter_TEST
Plane_TEST
PID_TEST
Pose3_TEST
Quaternion_TEST
Expand Down
124 changes: 124 additions & 0 deletions src/python_pybind11/src/Plane.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2021 Open Source Robotics Foundation
*
* 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 IGNITION_MATH_PYTHON__PLANE_HH_
#define IGNITION_MATH_PYTHON__PLANE_HH_

#include <string>

#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <pybind11/stl.h>

#include <ignition/math/Plane.hh>

namespace py = pybind11;
using namespace pybind11::literals;

namespace ignition
{
namespace math
{
namespace python
{
/// Define a pybind11 wrapper for an ignition::math::Plane
/**
* \param[in] module a pybind11 module to add the definition to
* \param[in] typestr name of the type used by Python
*/
template<typename T>
void defineMathPlane(py::module &m, const std::string &typestr)
{
using Class = ignition::math::Plane<T>;
std::string pyclass_name = typestr;
py::class_<Class> plane(m,
pyclass_name.c_str(),
py::buffer_protocol(),
py::dynamic_attr());
plane.def(py::init<>())
.def(py::init<const ignition::math::Vector3<T>&, T>(),
py::arg("_normal") = ignition::math::Vector3<T>::Zero,
py::arg("_offset") = 0.0)
.def(py::init<const ignition::math::Vector3<T>&,
const ignition::math::Vector2<T>&, T>())
.def(py::init<const Class&>())
.def("set",
py::overload_cast<const ignition::math::Vector3<T>&, T>
(&Class::Set),
"Set the plane")
.def("set",
py::overload_cast<const ignition::math::Vector3<T>&,
const ignition::math::Vector2<T>&, T>
(&Class::Set),
"Set the plane")
.def("distance",
py::overload_cast<const ignition::math::Vector3<T>&>
(&Class::Distance, py::const_),
"The distance to the plane from the given point. The "
"distance can be negative, which indicates the point is on the "
"negative side of the plane.")
.def("distance",
py::overload_cast<const ignition::math::Vector3<T>&,
const ignition::math::Vector3<T>&>
(&Class::Distance, py::const_),
"Get distance to the plane give an origin and direction.")
.def("intersection",
&Class::Intersection,
py::arg("_point") = ignition::math::Vector3<T>::Zero,
py::arg("_gradient") = ignition::math::Vector3<T>::Zero,
py::arg("_tolerance") = 1e-6,
"Get the intersection of an infinite line with the plane, "
"given the line's gradient and a point in parametrized space.")
.def("side",
py::overload_cast<const ignition::math::Vector3<T>&>
(&Class::Side, py::const_),
"The side of the plane a point is on.")
.def("side",
py::overload_cast<const ignition::math::AxisAlignedBox&>
(&Class::Side, py::const_),
"The side of the plane a point is on.")
.def("size",
py::overload_cast<>(&Class::Size),
py::return_value_policy::reference,
"Get the plane size")
.def("normal",
py::overload_cast<>(&Class::Normal),
py::return_value_policy::reference,
"Get the plane size")
.def("offset",
&Class::Offset,
"Get the plane offset")
.def("__copy__", [](const Class &self) {
return Class(self);
})
.def("__deepcopy__", [](const Class &self, py::dict) {
return Class(self);
}, "memo"_a);

py::enum_<ignition::math::Planed::PlaneSide>(m, "PlaneSide")
.value("NEGATIVE_SIDE", ignition::math::Planed::PlaneSide::NEGATIVE_SIDE)
.value("POSITIVE_SIDE", ignition::math::Planed::PlaneSide::POSITIVE_SIDE)
.value("NO_SIDE", ignition::math::Planed::PlaneSide::NO_SIDE)
.value("BOTH_SIDE", ignition::math::Planed::PlaneSide::BOTH_SIDE)
.export_values();
}

} // namespace python
} // namespace math
} // namespace ignition

#endif // IGNITION_MATH_PYTHON__PLANE_HH_
3 changes: 3 additions & 0 deletions src/python_pybind11/src/_ignition_math_pybind11.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "Matrix3.hh"
#include "Matrix4.hh"
#include "MovingWindowFilter.hh"
#include "Plane.hh"
chapulina marked this conversation as resolved.
Show resolved Hide resolved
#include "PID.hh"
#include "Pose3.hh"
#include "Quaternion.hh"
Expand Down Expand Up @@ -139,6 +140,8 @@ PYBIND11_MODULE(math, m)
ignition::math::python::defineMathMassMatrix3<double>(m, "MassMatrix3d");
ignition::math::python::defineMathMassMatrix3<float>(m, "MassMatrix3f");

ignition::math::python::defineMathPlane<double>(m, "Planed");
chapulina marked this conversation as resolved.
Show resolved Hide resolved

ignition::math::python::defineMathFilter<int>(m, "Filteri");
ignition::math::python::defineMathFilter<float>(m, "Filterf");
ignition::math::python::defineMathFilter<double>(m, "Filterd");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import unittest

from ignition.math import AxisAlignedBox, Planed, Vector2d, Vector3d
from ignition.math import AxisAlignedBox, Planed, PlaneSide, Vector2d, Vector3d


class TestPlane(unittest.TestCase):
Expand Down Expand Up @@ -76,46 +76,46 @@ def test_side_point(self):

# On the negative side of the plane (below the plane)
point = Vector3d(0, 0, 0)
self.assertEqual(plane.side(point), Planed.NEGATIVE_SIDE)
self.assertEqual(plane.side(point), PlaneSide.NEGATIVE_SIDE)

# Still on the negative side of the plane (below the plane)
point.set(1, 1, 0)
self.assertEqual(plane.side(point), Planed.NEGATIVE_SIDE)
self.assertEqual(plane.side(point), PlaneSide.NEGATIVE_SIDE)

# Above the plane (positive side)
point.set(1, 1, 2)
self.assertEqual(plane.side(point), Planed.POSITIVE_SIDE)
self.assertEqual(plane.side(point), PlaneSide.POSITIVE_SIDE)

# On the plane
point.set(0, 0, 1)
self.assertEqual(plane.side(point), Planed.NO_SIDE)
self.assertEqual(plane.side(point), PlaneSide.NO_SIDE)

# Change the plane, but the point is still on the negative side
plane.set(Vector3d(1, 0, 0), 4)
self.assertEqual(plane.side(point), Planed.NEGATIVE_SIDE)
self.assertEqual(plane.side(point), PlaneSide.NEGATIVE_SIDE)

# Point is now on the positive side
point.set(4.1, 0, 1)
self.assertEqual(plane.side(point), Planed.POSITIVE_SIDE)
self.assertEqual(plane.side(point), PlaneSide.POSITIVE_SIDE)

def test_side__axis_aligned_box(self):
plane = Planed(Vector3d(0, 0, 1), 1)

# On the negative side of the plane (below the plane)
box = AxisAlignedBox(Vector3d(-.5, -.5, -.5), Vector3d(.5, .5, .5))
self.assertEqual(plane.side(box), Planed.NEGATIVE_SIDE)
self.assertEqual(plane.side(box), PlaneSide.NEGATIVE_SIDE)

# Still on the negative side of the plane (below the plane)
box = AxisAlignedBox(Vector3d(-10, -10, -10), Vector3d(.9, .9, .9))
self.assertEqual(plane.side(box), Planed.NEGATIVE_SIDE)
self.assertEqual(plane.side(box), PlaneSide.NEGATIVE_SIDE)

# Above the plane (positive side)
box = AxisAlignedBox(Vector3d(2, 2, 2), Vector3d(3, 3, 3))
self.assertEqual(plane.side(box), Planed.POSITIVE_SIDE)
self.assertEqual(plane.side(box), PlaneSide.POSITIVE_SIDE)

# On both sides the plane
box = AxisAlignedBox(Vector3d(0, 0, 0), Vector3d(3, 3, 3))
self.assertEqual(plane.side(box), Planed.BOTH_SIDE)
self.assertEqual(plane.side(box), PlaneSide.BOTH_SIDE)

def test_intersection(self):
plane = Planed(Vector3d(0.5, 0, 1), 1)
Expand Down