diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 45a03366..e89a99d2 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -4787,7 +4787,12 @@ def _real_minmax(self, v): if 0 < r2 < 1: local_extremizers.append(r2) else: - local_extremizers.append(0.5) + c = a[1] - a[0] + b = 2 * (a[0] - 2*a[1] + a[2]) + if b != 0: + r0 = -c/b + if 0 < r0 < 1: + local_extremizers.append(r0) local_extrema = [self.point(t)[v] for t in local_extremizers] return min(local_extrema), max(local_extrema) diff --git a/test/test_cubic_bezier.py b/test/test_cubic_bezier.py index e63893a1..37ef0382 100644 --- a/test/test_cubic_bezier.py +++ b/test/test_cubic_bezier.py @@ -1,3 +1,4 @@ +import random import unittest from random import * @@ -5,12 +6,15 @@ def get_random_cubic_bezier(): - return CubicBezier((random() * 50, random() * 50), (random() * 50, random() * 50), - (random() * 50, random() * 50), (random() * 50, random() * 50)) + return CubicBezier( + (random() * 50, random() * 50), + (random() * 50, random() * 50), + (random() * 50, random() * 50), + (random() * 50, random() * 50), + ) class TestElementCubicBezierLength(unittest.TestCase): - def test_cubic_bezier_length(self): n = 100 error = 0 @@ -25,18 +29,20 @@ def test_cubic_bezier_length(self): class TestElementCubicBezierPoint(unittest.TestCase): - def test_cubic_bezier_point_start_stop(self): import numpy as np + for _ in range(1000): b = get_random_cubic_bezier() self.assertEqual(b.start, b.point(0)) self.assertEqual(b.end, b.point(1)) - self.assertTrue(np.all(np.array([list(b.start), list(b.end)]) - == b.npoint([0, 1]))) + self.assertTrue( + np.all(np.array([list(b.start), list(b.end)]) == b.npoint([0, 1])) + ) def test_cubic_bezier_point_implementations_match(self): import numpy as np + for _ in range(1000): b = get_random_cubic_bezier() @@ -50,3 +56,21 @@ def test_cubic_bezier_point_implementations_match(self): for p, p1, p2 in zip(pos, v1, v2): self.assertEqual(b.point(p), Point(p1)) self.assertEqual(Point(p1), Point(p2)) + + def test_cubic_bounds_issue_214(self): + cubic = CubicBezier(0, -2 - 3j, -1 - 4j, -3j) + bbox = cubic.bbox() + self.assertLess(bbox[1], -3) + + def test_cubic_bounds_issue_214_random(self): + for i in range(100): + a = random() * 5 + b = random() * 5 + c = random() * 5 + d = a - 3 * b + 3 * c + cubic1 = CubicBezier(a, b, c, d) + bbox1 = cubic1.bbox() + cubic2 = CubicBezier(a, b, c, d + 1e-11) + bbox2 = cubic2.bbox() + for a, b in zip(bbox1, bbox2): + self.assertAlmostEqual(a, b, delta=1e-5)