[utils] remove_duplicate_vertices now takes a tolerance (default exact)

This commit is contained in:
Jan Petykiewicz 2026-06-16 00:12:16 -07:00
commit f4df8e0553
2 changed files with 40 additions and 3 deletions

View file

@ -5,7 +5,15 @@ from numpy.testing import assert_equal, assert_allclose
from numpy import pi from numpy import pi
import pytest import pytest
from ..utils import remove_duplicate_vertices, remove_colinear_vertices, poly_contains_points, rotation_matrix_2d, apply_transforms, normalize_mirror, DeferredDict from ..utils import (
DeferredDict,
apply_transforms,
normalize_mirror,
poly_contains_points,
remove_colinear_vertices,
remove_duplicate_vertices,
rotation_matrix_2d,
)
from ..file.utils import tmpfile from ..file.utils import tmpfile
from ..utils.curves import bezier from ..utils.curves import bezier
from ..error import PatternError from ..error import PatternError
@ -23,6 +31,23 @@ def test_remove_duplicate_vertices() -> None:
assert_equal(v_clean_open, [[0, 0], [1, 1], [2, 2], [0, 0]]) assert_equal(v_clean_open, [[0, 0], [1, 1], [2, 2], [0, 0]])
def test_remove_duplicate_vertices_tolerance_defaults_to_exact_match() -> None:
v = [[0, 0], [1, 1], [1 + 1e-13, 1], [2, 2], [1e-13, 0]]
assert_allclose(remove_duplicate_vertices(v, closed_path=True), v, atol=0, rtol=0)
assert_allclose(
remove_duplicate_vertices(v, closed_path=True, tolerance=1e-12),
[[0, 0], [1 + 1e-13, 1], [2, 2]],
atol=0,
rtol=0,
)
def test_remove_duplicate_vertices_rejects_negative_tolerance() -> None:
with pytest.raises(ValueError, match='non-negative'):
remove_duplicate_vertices([[0, 0]], tolerance=-1)
def test_remove_colinear_vertices() -> None: def test_remove_colinear_vertices() -> None:
v = [[0, 0], [1, 0], [2, 0], [2, 1], [2, 2], [1, 1], [0, 0]] v = [[0, 0], [1, 0], [2, 0], [2, 1], [2, 2], [1, 1], [0, 0]]
v_clean = remove_colinear_vertices(v, closed_path=True) v_clean = remove_colinear_vertices(v, closed_path=True)

View file

@ -5,7 +5,11 @@ import numpy
from numpy.typing import NDArray, ArrayLike from numpy.typing import NDArray, ArrayLike
def remove_duplicate_vertices(vertices: ArrayLike, closed_path: bool = True) -> NDArray[numpy.float64]: def remove_duplicate_vertices(
vertices: ArrayLike,
closed_path: bool = True,
tolerance: float = 0.0,
) -> NDArray[numpy.float64]:
""" """
Given a list of vertices, remove any consecutive duplicates. Given a list of vertices, remove any consecutive duplicates.
@ -13,14 +17,22 @@ def remove_duplicate_vertices(vertices: ArrayLike, closed_path: bool = True) ->
vertices: `[[x0, y0], [x1, y1], ...]` vertices: `[[x0, y0], [x1, y1], ...]`
closed_path: If True, `vertices` is interpreted as an implicity-closed path closed_path: If True, `vertices` is interpreted as an implicity-closed path
(i.e. the last vertex will be removed if it is the same as the first) (i.e. the last vertex will be removed if it is the same as the first)
tolerance: Maximum coordinate-wise absolute difference for two vertices to
be considered duplicates. Default `0` requires exact equality.
Returns: Returns:
`vertices` with no consecutive duplicates. This may be a view into the original array. `vertices` with no consecutive duplicates. This may be a view into the original array.
""" """
if tolerance < 0:
raise ValueError(f'tolerance must be non-negative, got {tolerance}')
vertices = numpy.asarray(vertices) vertices = numpy.asarray(vertices)
if vertices.shape[0] <= 1: if vertices.shape[0] <= 1:
return vertices return vertices
if tolerance == 0:
duplicates = (vertices == numpy.roll(vertices, -1, axis=0)).all(axis=1) duplicates = (vertices == numpy.roll(vertices, -1, axis=0)).all(axis=1)
else:
duplicates = (numpy.abs(vertices - numpy.roll(vertices, -1, axis=0)) <= tolerance).all(axis=1)
if not closed_path: if not closed_path:
duplicates[-1] = False duplicates[-1] = False