[utils.transform] better input validation in normalize_mirror and apply_transform

This commit is contained in:
Jan Petykiewicz 2026-04-01 21:59:27 -07:00
commit d366db5a62
2 changed files with 32 additions and 2 deletions

View file

@ -5,7 +5,7 @@ from numpy.testing import assert_equal, assert_allclose
from numpy import pi
import pytest
from ..utils import remove_duplicate_vertices, remove_colinear_vertices, poly_contains_points, rotation_matrix_2d, apply_transforms, DeferredDict
from ..utils import remove_duplicate_vertices, remove_colinear_vertices, poly_contains_points, rotation_matrix_2d, apply_transforms, normalize_mirror, DeferredDict
from ..file.utils import tmpfile
from ..utils.curves import bezier
from ..error import PatternError
@ -94,6 +94,28 @@ def test_apply_transforms_advanced() -> None:
assert_allclose(combined[0], [0, 10, pi / 2, 1, 1], atol=1e-10)
def test_apply_transforms_empty_inputs() -> None:
empty_outer = apply_transforms(numpy.empty((0, 5)), [[1, 2, 0, 0, 1]])
assert empty_outer.shape == (0, 5)
empty_inner = apply_transforms([[1, 2, 0, 0, 1]], numpy.empty((0, 5)))
assert empty_inner.shape == (0, 5)
both_empty_tensor = apply_transforms(numpy.empty((0, 5)), numpy.empty((0, 5)), tensor=True)
assert both_empty_tensor.shape == (0, 0, 5)
def test_normalize_mirror_validates_length() -> None:
with pytest.raises(ValueError, match='2-item sequence'):
normalize_mirror(())
with pytest.raises(ValueError, match='2-item sequence'):
normalize_mirror((True,))
with pytest.raises(ValueError, match='2-item sequence'):
normalize_mirror((True, False, True))
def test_bezier_validates_weight_length() -> None:
with pytest.raises(PatternError, match='one entry per control point'):
bezier([[0, 0], [1, 1]], [0, 0.5, 1], weights=[1])

View file

@ -50,7 +50,10 @@ def normalize_mirror(mirrored: Sequence[bool]) -> tuple[bool, float]:
`angle_to_rotate` in radians
"""
mirrored_x, mirrored_y = mirrored
if len(mirrored) != 2:
raise ValueError(f'mirrored must be a 2-item sequence, got length {len(mirrored)}')
mirrored_x, mirrored_y = (bool(value) for value in mirrored)
mirror_x = (mirrored_x != mirrored_y) # XOR
angle = numpy.pi if mirrored_y else 0
return mirror_x, angle
@ -111,6 +114,11 @@ def apply_transforms(
if inner.shape[1] == 4:
inner = numpy.pad(inner, ((0, 0), (0, 1)), constant_values=1.0)
if outer.shape[0] == 0 or inner.shape[0] == 0:
if tensor:
return numpy.empty((outer.shape[0], inner.shape[0], 5))
return numpy.empty((0, 5))
# If mirrored, flip y's
xy_mir = numpy.tile(inner[:, :2], (outer.shape[0], 1, 1)) # dims are outer, inner, xyrm
xy_mir[outer[:, 3].astype(bool), :, 1] *= -1