From 4c6abc582052a551c0a7e40332917b43a9cf9b4f Mon Sep 17 00:00:00 2001 From: Tatarize Date: Sun, 4 Dec 2022 00:59:15 -0800 Subject: [PATCH 1/3] Add quick fails to paths --- svgpathtools/path.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/svgpathtools/path.py b/svgpathtools/path.py index 7418615..5903efe 100644 --- a/svgpathtools/path.py +++ b/svgpathtools/path.py @@ -711,6 +711,19 @@ class Line(object): Note: This will fail if the two segments coincide for more than a finite collection of points. tol is not used.""" + if isinstance(other_seg, (Line, QuadraticBezier, CubicBezier)): + ob = [e.real for e in other_seg.bpoints()] + sb = [e.real for e in self.bpoints()] + if min(ob) > max(sb): + return [] + if max(ob) < min(sb): + return [] + ob = [e.imag for e in other_seg.bpoints()] + sb = [e.imag for e in self.bpoints()] + if min(ob) > max(sb): + return [] + if max(ob) < min(sb): + return [] if isinstance(other_seg, Line): assert other_seg.end != other_seg.start and self.end != self.start assert self != other_seg @@ -1038,6 +1051,19 @@ class QuadraticBezier(object): self.point(t1) == other_seg.point(t2). Note: This will fail if the two segments coincide for more than a finite collection of points.""" + if isinstance(other_seg, (Line, QuadraticBezier, CubicBezier)): + ob = [e.real for e in other_seg.bpoints()] + sb = [e.real for e in self.bpoints()] + if min(ob) > max(sb): + return [] + if max(ob) < min(sb): + return [] + ob = [e.imag for e in other_seg.bpoints()] + sb = [e.imag for e in self.bpoints()] + if min(ob) > max(sb): + return [] + if max(ob) < min(sb): + return [] if isinstance(other_seg, Line): return bezier_by_line_intersections(self, other_seg) elif isinstance(other_seg, QuadraticBezier): @@ -1298,6 +1324,19 @@ class CubicBezier(object): This will fail if the two segments coincide for more than a finite collection of points. """ + if isinstance(other_seg, (Line, QuadraticBezier, CubicBezier)): + ob = [e.real for e in other_seg.bpoints()] + sb = [e.real for e in self.bpoints()] + if min(ob) > max(sb): + return [] + if max(ob) < min(sb): + return [] + ob = [e.imag for e in other_seg.bpoints()] + sb = [e.imag for e in self.bpoints()] + if min(ob) > max(sb): + return [] + if max(ob) < min(sb): + return [] if isinstance(other_seg, Line): return bezier_by_line_intersections(self, other_seg) elif (isinstance(other_seg, QuadraticBezier) or From b6e5a623ea84f65f0fb36f49933d1467f45c781c Mon Sep 17 00:00:00 2001 From: Tatarize Date: Sun, 4 Dec 2022 00:59:40 -0800 Subject: [PATCH 2/3] Add random intersections test --- test/test_path.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/test_path.py b/test/test_path.py index db77dc9..6053eec 100644 --- a/test/test_path.py +++ b/test/test_path.py @@ -1,6 +1,7 @@ # External dependencies from __future__ import division, absolute_import, print_function import os +import time from sys import version_info import unittest from math import sqrt, pi @@ -1491,6 +1492,49 @@ class Test_intersect(unittest.TestCase): self.assertTrue(len(yix) == 1) ################################################################### + def test_random_intersections(self): + from random import Random + r = Random() + distance = 100 + distribution = 10000 + count = 500 + + def random_complex(offset_x=0.0, offset_y=0.0): + return complex(r.random() * distance + offset_x, r.random() * distance + offset_y) + + def random_line(): + offset_x = r.random() * distribution + offset_y = r.random() * distribution + return Line(random_complex(offset_x, offset_y), random_complex(offset_x, offset_y)) + + def random_quad(): + offset_x = r.random() * distribution + offset_y = r.random() * distribution + return QuadraticBezier(random_complex(offset_x, offset_y), random_complex(offset_x, offset_y), random_complex(offset_x, offset_y)) + + def random_cubic(): + offset_x = r.random() * distribution + offset_y = r.random() * distribution + return CubicBezier(random_complex(offset_x, offset_y), random_complex(offset_x, offset_y), random_complex(offset_x, offset_y), random_complex(offset_x, offset_y)) + + def random_path(): + path = Path() + for i in range(count): + type_segment = random.randint(0, 3) + if type_segment == 0: + path.append(random_line()) + if type_segment == 1: + path.append(random_quad()) + if type_segment == 2: + path.append(random_cubic()) + return path + + path1 = random_path() + path2 = random_path() + t = time.time() + path1.intersect(path2) + print(f"\nIntersection calculation took {time.time() - t} seconds.\n") + def test_line_line_0(self): l0 = Line(start=(25.389999999999997+99.989999999999995j), end=(25.389999999999997+90.484999999999999j)) From 3eb21161cf2f7be4e31c4040c1a59746b9b42949 Mon Sep 17 00:00:00 2001 From: Andrew Port Date: Fri, 3 Feb 2023 21:00:57 -0500 Subject: [PATCH 3/3] add report of intersection count --- test/test_path.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_path.py b/test/test_path.py index 15a198b..4190f70 100644 --- a/test/test_path.py +++ b/test/test_path.py @@ -1536,8 +1536,9 @@ class Test_intersect(unittest.TestCase): path1 = random_path() path2 = random_path() t = time.time() - path1.intersect(path2) - print(f"\nIntersection calculation took {time.time() - t} seconds.\n") + intersections = path1.intersect(path2) + print("\nFound {} intersections in {} seconds.\n" + "".format(len(intersections), time.time() - t)) def test_line_line_0(self): l0 = Line(start=(25.389999999999997+99.989999999999995j),