From 847b270bc2af10bd499e9520ef33b8b9de24cc55 Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Sat, 6 Jan 2018 22:58:10 -0700 Subject: [PATCH 1/6] add a failing Line.intersect(Line) test These two lines are parallel but do not intersect. Line.intersect() finds an incorrect intersection. --- test/test_path.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_path.py b/test/test_path.py index 3456e82..b027501 100644 --- a/test/test_path.py +++ b/test/test_path.py @@ -991,6 +991,12 @@ class Test_intersect(unittest.TestCase): self.assertTrue(len(yix), 1) ################################################################### + def test_line_line(self): + l0 = Line(start=(25.389999999999997+99.989999999999995j), end=(25.389999999999997+90.484999999999999j)) + l1 = Line(start=(25.390000000000001+84.114999999999995j), end=(25.389999999999997+74.604202137430320j)) + i = l0.intersect(l1) + assert(len(i)) == 0 + class TestPathTools(unittest.TestCase): # moved from test_pathtools.py From fc34d2c4cfc1de43243fd3d83120502c29ee745c Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Sat, 6 Jan 2018 23:15:43 -0700 Subject: [PATCH 2/6] deal with float rounding error in Line.intersect(Line) This commit fixes #41. In the test case added in the previous commit, two non-intersecting lines are very nearly collinear, but float rounding errors lead to incorrect intersections reported. This commit makes Line.intersect() treat denominators below 1e-9 as 0, to make it more accepting of float rounding. --- svgpathtools/path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svgpathtools/path.py b/svgpathtools/path.py index 51378fd..f5fc806 100644 --- a/svgpathtools/path.py +++ b/svgpathtools/path.py @@ -572,7 +572,7 @@ class Line(object): d = (other_seg.start.imag, other_seg.end.imag) denom = ((a[1] - a[0])*(d[0] - d[1]) - (b[1] - b[0])*(c[0] - c[1])) - if denom == 0: + if denom < 1e-9: return [] t1 = (c[0]*(b[0] - d[1]) - c[1]*(b[0] - d[0]) - From de600f9b91bf92e654fe128123f26b520ee19361 Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Sat, 20 Jan 2018 20:36:06 -0700 Subject: [PATCH 3/6] rename test to make room for more --- test/test_path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_path.py b/test/test_path.py index b027501..57b2daa 100644 --- a/test/test_path.py +++ b/test/test_path.py @@ -991,7 +991,7 @@ class Test_intersect(unittest.TestCase): self.assertTrue(len(yix), 1) ################################################################### - def test_line_line(self): + def test_line_line_0(self): l0 = Line(start=(25.389999999999997+99.989999999999995j), end=(25.389999999999997+90.484999999999999j)) l1 = Line(start=(25.390000000000001+84.114999999999995j), end=(25.389999999999997+74.604202137430320j)) i = l0.intersect(l1) From cc4573ffc75b0646011fe2399cd614687e3b58e6 Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Sat, 20 Jan 2018 20:36:25 -0700 Subject: [PATCH 4/6] add a failing Line.intersect(Line) test I introduced this bug recently, sorry! The bug is fixed in the following commit. --- test/test_path.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_path.py b/test/test_path.py index 57b2daa..8474b26 100644 --- a/test/test_path.py +++ b/test/test_path.py @@ -997,6 +997,13 @@ class Test_intersect(unittest.TestCase): i = l0.intersect(l1) assert(len(i)) == 0 + def test_line_line_1(self): + l0 = Line(start=(-124.705378549+327.696674827j), end=(12.4926214511+121.261674827j)) + l1 = Line(start=(-12.4926214511+121.261674827j), end=(124.705378549+327.696674827j)) + i = l0.intersect(l1) + assert(len(i)) == 1 + assert(abs(l0.point(i[0][0])-l1.point(i[0][1])) < 1e-9) + class TestPathTools(unittest.TestCase): # moved from test_pathtools.py From 89d9acf06eacd0f9d810d076e6a06ef9610a7454 Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Sat, 20 Jan 2018 20:37:08 -0700 Subject: [PATCH 5/6] Line.intersect(Line): fix a "miss some intersections" bug Negative numbers are allowed in the denominator, what we really want to avoid is near-zero denominators. --- svgpathtools/path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svgpathtools/path.py b/svgpathtools/path.py index f5fc806..b2c4fec 100644 --- a/svgpathtools/path.py +++ b/svgpathtools/path.py @@ -572,7 +572,7 @@ class Line(object): d = (other_seg.start.imag, other_seg.end.imag) denom = ((a[1] - a[0])*(d[0] - d[1]) - (b[1] - b[0])*(c[0] - c[1])) - if denom < 1e-9: + if abs(denom) < 1e-9: return [] t1 = (c[0]*(b[0] - d[1]) - c[1]*(b[0] - d[0]) - From a50c522f869467df6915fb53e2f5aa5886347776 Mon Sep 17 00:00:00 2001 From: Andy Port Date: Tue, 27 Feb 2018 22:38:01 -0800 Subject: [PATCH 6/6] use np.close to check to check for vanishing denom Just to offer users some amount of control over the tolerance. --- svgpathtools/path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svgpathtools/path.py b/svgpathtools/path.py index b2c4fec..0b5bb49 100644 --- a/svgpathtools/path.py +++ b/svgpathtools/path.py @@ -572,7 +572,7 @@ class Line(object): d = (other_seg.start.imag, other_seg.end.imag) denom = ((a[1] - a[0])*(d[0] - d[1]) - (b[1] - b[0])*(c[0] - c[1])) - if abs(denom) < 1e-9: + if np.isclose(denom, 0): return [] t1 = (c[0]*(b[0] - d[1]) - c[1]*(b[0] - d[0]) -