[Arc / Ellipse / Circle] gracefully handle large arclen

This commit is contained in:
Jan Petykiewicz 2026-03-31 21:42:16 -07:00
commit 2952e6ef8f
4 changed files with 16 additions and 3 deletions

View file

@ -268,7 +268,7 @@ class Arc(PositionableImpl, Shape):
""" Figure out the parameter values at which we should place vertices to meet the arclength constraint""" """ Figure out the parameter values at which we should place vertices to meet the arclength constraint"""
dr = -wh if inner else wh dr = -wh if inner else wh
n_pts = numpy.ceil(2 * pi * max(self.radii + dr) / max_arclen).astype(int) n_pts = max(2, int(numpy.ceil(2 * pi * max(self.radii + dr) / max_arclen)))
arc_lengths, thetas = get_arclens(n_pts, *a_ranges[0 if inner else 1], dr=dr) arc_lengths, thetas = get_arclens(n_pts, *a_ranges[0 if inner else 1], dr=dr)
keep = [0] keep = [0]

View file

@ -108,7 +108,7 @@ class Circle(PositionableImpl, Shape):
n += [num_vertices] n += [num_vertices]
if max_arclen is not None: if max_arclen is not None:
n += [2 * pi * self.radius / max_arclen] n += [2 * pi * self.radius / max_arclen]
num_vertices = int(round(max(n))) num_vertices = max(3, int(round(max(n))))
thetas = numpy.linspace(2 * pi, 0, num_vertices, endpoint=False) thetas = numpy.linspace(2 * pi, 0, num_vertices, endpoint=False)
xs = numpy.cos(thetas) * self.radius xs = numpy.cos(thetas) * self.radius
ys = numpy.sin(thetas) * self.radius ys = numpy.sin(thetas) * self.radius

View file

@ -168,7 +168,7 @@ class Ellipse(PositionableImpl, Shape):
n += [num_vertices] n += [num_vertices]
if max_arclen is not None: if max_arclen is not None:
n += [perimeter / max_arclen] n += [perimeter / max_arclen]
num_vertices = int(round(max(n))) num_vertices = max(3, int(round(max(n))))
thetas = numpy.linspace(2 * pi, 0, num_vertices, endpoint=False) thetas = numpy.linspace(2 * pi, 0, num_vertices, endpoint=False)
sin_th, cos_th = (numpy.sin(thetas), numpy.cos(thetas)) sin_th, cos_th = (numpy.sin(thetas), numpy.cos(thetas))

View file

@ -109,6 +109,19 @@ def test_rotated_arc_bounds_match_polygonized_geometry() -> None:
bounds = arc.get_bounds_single() bounds = arc.get_bounds_single()
poly_bounds = arc.to_polygons(num_vertices=8192)[0].get_bounds_single() poly_bounds = arc.to_polygons(num_vertices=8192)[0].get_bounds_single()
assert_allclose(bounds, poly_bounds, atol=1e-3) assert_allclose(bounds, poly_bounds, atol=1e-3)
def test_curve_polygonizers_clamp_large_max_arclen() -> None:
for shape in (
Circle(radius=10),
Ellipse(radii=(10, 20)),
Arc(radii=(10, 20), angles=(0, 1), width=2),
):
polys = shape.to_polygons(num_vertices=None, max_arclen=1e9)
assert len(polys) == 1
assert len(polys[0].vertices) >= 3
def test_path_edge_cases() -> None: def test_path_edge_cases() -> None:
# Zero-length segments # Zero-length segments
p = MPath(vertices=[[0, 0], [0, 0], [10, 0]], width=2) p = MPath(vertices=[[0, 0], [0, 0], [10, 0]], width=2)