Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions include/gz/math/Line2.hh
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ namespace ignition
/// \param[in] _y2 Y coordinate of the end point.
public: void Set(double _x1, double _y1, double _x2, double _y2)
{
this->pts[0].Set(_x1, _y1);
this->pts[1].Set(_x2, _y2);
this->pts[0].Set(static_cast<T>(_x1), static_cast<T>(_y1));
this->pts[1].Set(static_cast<T>(_x2), static_cast<T>(_y2));
}

/// \brief Return the cross product of this line and the given line.
Expand Down Expand Up @@ -255,10 +255,7 @@ namespace ignition
/// \return The length of the line.
public: T Length() const
{
return sqrt((this->pts[0].X() - this->pts[1].X()) *
(this->pts[0].X() - this->pts[1].X()) +
(this->pts[0].Y() - this->pts[1].Y()) *
(this->pts[0].Y() - this->pts[1].Y()));
return static_cast<T>(this->pts[0].Distance(this->pts[1]));
}

/// \brief Get the slope of the line
Expand Down
87 changes: 87 additions & 0 deletions src/Line2_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,49 @@ TEST(Line2Test, Constructor)
EXPECT_NO_THROW(lineB[2].X());
EXPECT_DOUBLE_EQ(lineB[2].X(), lineB[1].X());
EXPECT_NO_THROW(lineA[0].X());

// Test Set method with Vector2
lineA.Set(math::Vector2d(5, 6), math::Vector2d(7, 8));
EXPECT_EQ(lineA[0], math::Vector2d(5, 6));
EXPECT_EQ(lineA[1], math::Vector2d(7, 8));
}

/////////////////////////////////////////////////
TEST(Line2Test, CrossProduct)
{
math::Line2d lineA(0, 0, 10, 0);
math::Line2d lineB(0, 0, 0, 10);
EXPECT_DOUBLE_EQ(lineA.CrossProduct(lineB), 100.0);

math::Vector2d pt(5, 5);
EXPECT_DOUBLE_EQ(lineA.CrossProduct(pt), 50.0);
}

/////////////////////////////////////////////////
TEST(Line2Test, OnSegmentAndWithin)
{
math::Line2d line(0, 0, 10, 10);

// Point on segment
math::Vector2d pt1(5, 5);
EXPECT_TRUE(line.Within(pt1));
EXPECT_TRUE(line.OnSegment(pt1));

// Point collinear with line, but outside segment bounds
math::Vector2d pt2(15, 15);
EXPECT_FALSE(line.Within(pt2));
EXPECT_FALSE(line.OnSegment(pt2));

// Point within bounding box of segment, but not collinear
math::Vector2d pt3(5, 6);
EXPECT_TRUE(line.Within(pt3));
EXPECT_FALSE(line.OnSegment(pt3));

// Endpoints
EXPECT_TRUE(line.Within(line[0]));
EXPECT_TRUE(line.OnSegment(line[0]));
EXPECT_TRUE(line.Within(line[1]));
EXPECT_TRUE(line.OnSegment(line[1]));
}

/////////////////////////////////////////////////
Expand Down Expand Up @@ -83,29 +126,34 @@ TEST(Line2Test, ParallelLine)
// Line is always parallel with itself
math::Line2d line(0, 0, 10, 0);
EXPECT_TRUE(line.Parallel(line, 1e-10));
EXPECT_DOUBLE_EQ(line.CrossProduct(line), 0.0);
}

{
// Degenerate line segment
// Still expect Line is parallel with itself
math::Line2d line(0, 0, 0, 0);
EXPECT_TRUE(line.Parallel(line, 1e-10));
EXPECT_DOUBLE_EQ(line.CrossProduct(line), 0.0);
}

math::Line2d lineA(0, 0, 10, 0);
math::Line2d lineB(0, 0, 10, 0);
EXPECT_TRUE(lineA.Parallel(lineB, 1e-10));
EXPECT_DOUBLE_EQ(lineA.CrossProduct(lineB), 0.0);

lineB.Set(0, 0, 0, 10);
EXPECT_FALSE(lineA.Parallel(lineB));

lineB.Set(0, 10, 10, 10);
EXPECT_TRUE(lineA.Parallel(lineB));
EXPECT_DOUBLE_EQ(lineA.CrossProduct(lineB), 0.0);

lineB.Set(0, 10, 10, 10.00001);
EXPECT_FALSE(lineA.Parallel(lineB, 1e-10));
EXPECT_FALSE(lineA.Parallel(lineB));
EXPECT_TRUE(lineA.Parallel(lineB, 1e-3));
EXPECT_NEAR(lineA.CrossProduct(lineB), 0.0, 1e-3);
}

/////////////////////////////////////////////////
Expand Down Expand Up @@ -234,6 +282,28 @@ TEST(Line2Test, Intersect)
lineB.Set(0, 10, 10, 0);
EXPECT_TRUE(lineA.Intersect(lineB, pt));
EXPECT_EQ(pt, math::Vector2d(5, 5));

// Collinear parallel non-overlapping lines
lineA.Set(0, 0, 10, 0);
lineB.Set(20, 0, 30, 0);
EXPECT_FALSE(lineA.Intersect(lineB, pt));
EXPECT_FALSE(lineA.Intersect(lineB));

// Collinear lines where lineB end point is within lineA but start point isn't
lineA.Set(0, 0, 10, 0);
lineB.Set(-5, 0, 5, 0);
EXPECT_TRUE(lineA.Intersect(lineB, pt));
EXPECT_EQ(pt, math::Vector2d(5, 0));

// Lines whose extensions intersect, but intersection Y is outside bounds
lineA.Set(-10, 0, 10, 0);
lineB.Set(0, 2, 0, 10);
EXPECT_FALSE(lineA.Intersect(lineB, pt));

// Lines whose extensions intersect, but intersection X is outside bounds
lineA.Set(0, -10, 0, 10);
lineB.Set(2, 0, 10, 0);
EXPECT_FALSE(lineA.Intersect(lineB, pt));
}

/////////////////////////////////////////////////
Expand All @@ -243,7 +313,9 @@ TEST(Line2Test, Equality)
math::Line2d lineB(1, 2, 2, 2);

EXPECT_TRUE(lineA != lineB);
EXPECT_FALSE(lineA == lineB);
EXPECT_TRUE(lineA == lineA);
EXPECT_FALSE(lineA != lineA);

lineB.Set(1, 1, 2, 1.1);
EXPECT_FALSE(lineA == lineB);
Expand All @@ -266,3 +338,18 @@ TEST(Line2Test, OperatorStreamOut)
stream << line;
EXPECT_EQ(stream.str(), "0 1 2 3");
}

/////////////////////////////////////////////////
TEST(Line2Test, TemplateTypes)
{
math::Line2i lineI(0, 0, 10, 10);
EXPECT_EQ(lineI[0], math::Vector2i(0, 0));
EXPECT_EQ(lineI[1], math::Vector2i(10, 10));
EXPECT_EQ(lineI.Length(), 14);

math::Line2f lineF(0.0f, 0.0f, 10.0f, 10.0f);
EXPECT_FLOAT_EQ(lineF[0].X(), 0.0f);
EXPECT_FLOAT_EQ(lineF[1].Y(), 10.0f);
EXPECT_NEAR(lineF.Length(), 14.1421356f, 1e-4f);
}

82 changes: 80 additions & 2 deletions src/python_pybind11/test/Line2_TEST.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

import math
import unittest
from ignition.math import Line2d
from ignition.math import Vector2d
from ignition.math import Line2d, Line2f, Line2i
from ignition.math import Vector2d, Vector2i


class TestLine2d(unittest.TestCase):
Expand All @@ -35,6 +35,43 @@ def test_construction(self):

self.assertAlmostEqual(line_b[2].x(), line_b[1].x())

# Test set method with Vector2d
line_a.set(Vector2d(5, 6), Vector2d(7, 8))
self.assertEqual(line_a[0], Vector2d(5, 6))
self.assertEqual(line_a[1], Vector2d(7, 8))

def test_cross_product(self):
line_a = Line2d(0, 0, 10, 0)
line_b = Line2d(0, 0, 0, 10)
self.assertAlmostEqual(line_a.cross_product(line_b), 100.0)

pt = Vector2d(5, 5)
self.assertAlmostEqual(line_a.cross_product(pt), 50.0)

def test_on_segment_and_within(self):
line = Line2d(0, 0, 10, 10)

# Point on segment
pt1 = Vector2d(5, 5)
self.assertTrue(line.within(pt1, 1e-6))
self.assertTrue(line.on_segment(pt1, 1e-6))

# Point collinear with line, but outside segment bounds
pt2 = Vector2d(15, 15)
self.assertFalse(line.within(pt2, 1e-6))
self.assertFalse(line.on_segment(pt2, 1e-6))

# Point within bounding box of segment, but not collinear
pt3 = Vector2d(5, 6)
self.assertTrue(line.within(pt3, 1e-6))
self.assertFalse(line.on_segment(pt3, 1e-6))

# Endpoints
self.assertTrue(line.within(line[0], 1e-6))
self.assertTrue(line.on_segment(line[0], 1e-6))
self.assertTrue(line.within(line[1], 1e-6))
self.assertTrue(line.on_segment(line[1], 1e-6))

def test_length(self):
line_a = Line2d(0, 0, 10, 10)
self.assertAlmostEqual(line_a.length(), math.sqrt(200), delta=1e-10)
Expand All @@ -53,26 +90,31 @@ def test_parallel_line(self):
# Line is always parallel with itself
line = Line2d(0, 0, 10, 0)
self.assertTrue(line.parallel(line, 1e-10))
self.assertAlmostEqual(line.cross_product(line), 0.0)

# Degenerate line segment
# Still expect Line is parallel with itself
line = Line2d(0, 0, 0, 0)
self.assertTrue(line.parallel(line, 1e-10))
self.assertAlmostEqual(line.cross_product(line), 0.0)

line_a = Line2d(0, 0, 10, 0)
line_b = Line2d(0, 0, 10, 0)
self.assertTrue(line_a.parallel(line_b, 1e-10))
self.assertAlmostEqual(line_a.cross_product(line_b), 0.0)

line_b.set(0, 0, 0, 10)
self.assertFalse(line_a.parallel(line_b))

line_b.set(0, 10, 10, 10)
self.assertTrue(line_a.parallel(line_b))
self.assertAlmostEqual(line_a.cross_product(line_b), 0.0)

line_b.set(0, 10, 10, 10.00001)
self.assertFalse(line_a.parallel(line_b, 1e-10))
self.assertFalse(line_a.parallel(line_b))
self.assertTrue(line_a.parallel(line_b, 1e-3))
self.assertAlmostEqual(line_a.cross_product(line_b), 0.0, delta=1e-3)

def test_collinear_line(self):
# Line is always collinear with itself
Expand Down Expand Up @@ -187,12 +229,36 @@ def test_intersect(self):
self.assertTrue(line_a.intersect(line_b, pt))
self.assertEqual(pt, Vector2d(5, 5))

# Collinear parallel non-overlapping lines
line_a.set(0, 0, 10, 0)
line_b.set(20, 0, 30, 0)
self.assertFalse(line_a.intersect(line_b, pt))
self.assertFalse(line_a.intersect(line_b))

# Collinear lines where line_b end point is within line_a but start point is not
line_a.set(0, 0, 10, 0)
line_b.set(-5, 0, 5, 0)
self.assertTrue(line_a.intersect(line_b, pt))
self.assertEqual(pt, Vector2d(5, 0))

# Lines whose extensions intersect, but intersection Y is outside bounds
line_a.set(-10, 0, 10, 0)
line_b.set(0, 2, 0, 10)
self.assertFalse(line_a.intersect(line_b, pt))

# Lines whose extensions intersect, but intersection X is outside bounds
line_a.set(0, -10, 0, 10)
line_b.set(2, 0, 10, 0)
self.assertFalse(line_a.intersect(line_b, pt))

def test_equality(self):
line_a = Line2d(1, 1, 2, 1)
line_b = Line2d(1, 2, 2, 2)

self.assertTrue(line_a != line_b)
self.assertFalse(line_a == line_b)
self.assertTrue(line_a == line_a)
self.assertFalse(line_a != line_a)

line_b.set(1, 1, 2, 1.1)
self.assertFalse(line_a == line_b)
Expand All @@ -210,6 +276,18 @@ def test_serialization(self):
line = Line2d(0, 1, 2, 3)
self.assertEqual(str(line), "0 1 2 3")

def test_template_types(self):
line_i = Line2i(0, 0, 10, 10)
self.assertEqual(line_i[0], Vector2i(0, 0))
self.assertEqual(line_i[1], Vector2i(10, 10))
self.assertEqual(line_i.length(), 14)

line_f = Line2f(0.0, 0.0, 10.0, 10.0)
self.assertAlmostEqual(line_f[0].x(), 0.0)
self.assertAlmostEqual(line_f[1].y(), 10.0)
self.assertAlmostEqual(line_f.length(), 14.1421356, delta=1e-4)


if __name__ == '__main__':
unittest.main()

Loading