Skip to content

Commit

Permalink
Adds python interface to triangle. (#231)
Browse files Browse the repository at this point in the history
Signed-off-by: Marcos Wagner <[email protected]>
Signed-off-by: Franco Cipollone <[email protected]>

Co-authored-by: Marcos Wagner <[email protected]>
Co-authored-by: Franco Cipollone <[email protected]>
  • Loading branch information
3 people authored Aug 31, 2021
1 parent 3ea6229 commit 3eae090
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ if (PYTHONLIBS_FOUND)
Vector3_TEST
Vector4_TEST
Temperature_TEST
Triangle_TEST
)

foreach (test ${python_tests})
Expand Down
67 changes: 67 additions & 0 deletions src/python/Triangle.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.
*
*/

%module triangle
%{
#include <ignition/math/config.hh>
#include <ignition/math/Helpers.hh>
#include <ignition/math/Line2.hh>
#include <set>
#include <ignition/math/Triangle.hh>
#include <ignition/math/Vector2.hh>
%}

namespace ignition
{
namespace math
{
template<typename T>
class Triangle
{
%rename("%(undercase)s", %$isfunction, %$ismember, %$not %$isconstructor) "";
public: Triangle() = default;
public: Triangle(const math::Vector2<T> &_pt1,
const math::Vector2<T> &_pt2,
const math::Vector2<T> &_pt3);
public: void Set(const unsigned int _index, const math::Vector2<T> &_pt);
public: void Set(const math::Vector2<T> &_pt1,
const math::Vector2<T> &_pt2,
const math::Vector2<T> &_pt3);
public: bool Valid() const;
public: Line2<T> Side(const unsigned int _index) const;
public: bool Contains(const Line2<T> &_line) const;
public: bool Contains(const math::Vector2<T> &_pt) const;
public: bool Intersects(const Line2<T> &_line,
math::Vector2<T> &_ipt1,
math::Vector2<T> &_ipt2) const;
public: T Perimeter() const;
public: double Area() const;
};

%extend Triangle
{
ignition::math::Vector2<T> __getitem__(const unsigned int i) const
{
return (*$self)[i];
}
}

%template(Trianglei) Triangle<int>;
%template(Triangled) Triangle<double>;
%template(Trianglef) Triangle<float>;
}
}
162 changes: 162 additions & 0 deletions src/python/Triangle_TEST.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# 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.

import math
import unittest
from ignition.math import Line2d
from ignition.math import Triangled
from ignition.math import Vector2d


class TestTriangle(unittest.TestCase):

def test_constructor(self):
# Constructor
tri = Triangled()
self.assertAlmostEqual(tri[0], Vector2d(0, 0))
self.assertAlmostEqual(tri[1], Vector2d(0, 0))
self.assertAlmostEqual(tri[2], Vector2d(0, 0))

# Construct from three points
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(1, 0))

self.assertTrue(tri.valid())

self.assertAlmostEqual(tri[0], Vector2d(0, 0))
self.assertAlmostEqual(tri[1], Vector2d(0, 1))
self.assertAlmostEqual(tri[2], Vector2d(1, 0))
self.assertAlmostEqual(tri[3], tri[2])

# Construct degenerate from 3 collinear points
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(0, 2))

# Expect not valid
self.assertFalse(tri.valid())

def test_set(self):
tri = Triangled()

tri.set(0, Vector2d(3, 4))
tri.set(1, Vector2d(5, 6))
tri.set(2, Vector2d(7, 8))
self.assertAlmostEqual(tri[0], Vector2d(3, 4))
self.assertAlmostEqual(tri[1], Vector2d(5, 6))
self.assertAlmostEqual(tri[2], Vector2d(7, 8))

tri.set(Vector2d(0.1, 0.2),
Vector2d(0.3, 0.4),
Vector2d(1.5, 2.6))
self.assertAlmostEqual(tri[0], Vector2d(0.1, 0.2))
self.assertAlmostEqual(tri[1], Vector2d(0.3, 0.4))
self.assertAlmostEqual(tri[2], Vector2d(1.5, 2.6))

def test_side(self):
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(1, 0))

self.assertTrue(tri.side(0) == Line2d(0, 0, 0, 1))
self.assertTrue(tri.side(1) == Line2d(0, 1, 1, 0))
self.assertTrue(tri.side(2) == Line2d(1, 0, 0, 0))

def test_contains_line(self):
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(1, 0))

self.assertTrue(tri.contains(tri.side(0)))
self.assertTrue(tri.contains(tri.side(1)))
self.assertTrue(tri.contains(tri.side(2)))

self.assertTrue(tri.contains(Line2d(0.1, 0.1, 0.5, 0.5)))

self.assertFalse(tri.contains(Line2d(0.1, 0.1, 0.6, 0.6)))
self.assertFalse(tri.contains(Line2d(-0.1, -0.1, 0.5, 0.5)))

def test_intersects(self):
pt1 = Vector2d()
pt2 = Vector2d()
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(1, 0))

self.assertTrue(tri.intersects(tri.side(0), pt1, pt2))
self.assertAlmostEqual(pt1, Vector2d(0, 0))
self.assertAlmostEqual(pt2, Vector2d(0, 1))

self.assertTrue(tri.intersects(tri.side(1), pt1, pt2))
self.assertAlmostEqual(pt1, Vector2d(0, 1))
self.assertAlmostEqual(pt2, Vector2d(1, 0))

self.assertTrue(tri.intersects(tri.side(2), pt1, pt2))
self.assertAlmostEqual(pt1, Vector2d(1, 0))
self.assertAlmostEqual(pt2, Vector2d(0, 0))

self.assertTrue(tri.intersects(Line2d(0.1, 0.1, 0.5, 0.5), pt1, pt2))
self.assertAlmostEqual(pt1, Vector2d(0.1, 0.1))
self.assertAlmostEqual(pt2, Vector2d(0.5, 0.5))

self.assertTrue(tri.intersects(Line2d(0.1, 0.1, 0.6, 0.6), pt1, pt2))
self.assertAlmostEqual(pt1, Vector2d(0.5, 0.5))
self.assertAlmostEqual(pt2, Vector2d(0.1, 0.1))

self.assertTrue(tri.intersects(Line2d(-0.1, -0.1, 0.5, 0.5), pt1, pt2))
self.assertAlmostEqual(pt1, Vector2d(0.0, 0.0))
self.assertAlmostEqual(pt2, Vector2d(0.5, 0.5))

self.assertTrue(tri.intersects(Line2d(-2, -2, 0.2, 0.2), pt1, pt2))
self.assertAlmostEqual(pt1, Vector2d(0.0, 0.0))
self.assertAlmostEqual(pt2, Vector2d(0.2, 0.2))

self.assertFalse(tri.intersects(Line2d(-0.1, 0, -0.1, 1), pt1, pt2))

def test_contains_pt(self):
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(1, 0))

self.assertTrue(tri.contains(tri[0]))
self.assertTrue(tri.contains(tri[1]))
self.assertTrue(tri.contains(tri[2]))

self.assertTrue(tri.contains(Vector2d(0.1, 0.1)))
self.assertTrue(tri.contains(Vector2d(0, 0.5)))
self.assertTrue(tri.contains(Vector2d(0.5, 0)))
self.assertTrue(tri.contains(Vector2d(0.5, 0.5)))

self.assertFalse(tri.contains(Vector2d(-0.01, -0.01)))
self.assertFalse(tri.contains(Vector2d(1.01, 0)))
self.assertFalse(tri.contains(Vector2d(0, 1.01)))

def test_perimeter(self):
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(1, 0))

self.assertAlmostEqual(tri.perimeter(), 2.0 + math.sqrt(2.0))

def test_area(self):
tri = Triangled(Vector2d(0, 0),
Vector2d(0, 1),
Vector2d(1, 0))

self.assertAlmostEqual(tri.area(), 0.499999, delta=1e-6)


if __name__ == '__main__':
unittest.main()
1 change: 1 addition & 0 deletions src/python/python.i
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
%include Line3.i
%include SignalStats.i
%include Temperature.i
%include Triangle.i

0 comments on commit 3eae090

Please sign in to comment.