masque/masque/test/test_shapes.py
2026-02-15 12:36:13 -08:00

142 lines
5 KiB
Python

import numpy
from numpy.testing import assert_equal, assert_allclose
from numpy import pi
from ..shapes import Arc, Ellipse, Circle, Polygon, PolyCollection
def test_poly_collection_init() -> None:
# Two squares: [[0,0], [1,0], [1,1], [0,1]] and [[10,10], [11,10], [11,11], [10,11]]
verts = [[0, 0], [1, 0], [1, 1], [0, 1], [10, 10], [11, 10], [11, 11], [10, 11]]
offsets = [0, 4]
pc = PolyCollection(vertex_lists=verts, vertex_offsets=offsets)
assert len(list(pc.polygon_vertices)) == 2
assert_equal(pc.get_bounds_single(), [[0, 0], [11, 11]])
def test_poly_collection_to_polygons() -> None:
verts = [[0, 0], [1, 0], [1, 1], [0, 1], [10, 10], [11, 10], [11, 11], [10, 11]]
offsets = [0, 4]
pc = PolyCollection(vertex_lists=verts, vertex_offsets=offsets)
polys = pc.to_polygons()
assert len(polys) == 2
assert_equal(polys[0].vertices, [[0, 0], [1, 0], [1, 1], [0, 1]])
assert_equal(polys[1].vertices, [[10, 10], [11, 10], [11, 11], [10, 11]])
def test_circle_init() -> None:
c = Circle(radius=10, offset=(5, 5))
assert c.radius == 10
assert_equal(c.offset, [5, 5])
def test_circle_to_polygons() -> None:
c = Circle(radius=10)
polys = c.to_polygons(num_vertices=32)
assert len(polys) == 1
assert isinstance(polys[0], Polygon)
# A circle with 32 vertices should have vertices distributed around (0,0)
bounds = polys[0].get_bounds_single()
assert_allclose(bounds, [[-10, -10], [10, 10]], atol=1e-10)
def test_ellipse_init() -> None:
e = Ellipse(radii=(10, 5), offset=(1, 2), rotation=pi / 4)
assert_equal(e.radii, [10, 5])
assert_equal(e.offset, [1, 2])
assert e.rotation == pi / 4
def test_ellipse_to_polygons() -> None:
e = Ellipse(radii=(10, 5))
polys = e.to_polygons(num_vertices=64)
assert len(polys) == 1
bounds = polys[0].get_bounds_single()
assert_allclose(bounds, [[-10, -5], [10, 5]], atol=1e-10)
def test_arc_init() -> None:
a = Arc(radii=(10, 10), angles=(0, pi / 2), width=2, offset=(0, 0))
assert_equal(a.radii, [10, 10])
assert_equal(a.angles, [0, pi / 2])
assert a.width == 2
def test_arc_to_polygons() -> None:
# Quarter circle arc
a = Arc(radii=(10, 10), angles=(0, pi / 2), width=2)
polys = a.to_polygons(num_vertices=32)
assert len(polys) == 1
# Outer radius 11, inner radius 9
# Quarter circle from 0 to 90 deg
bounds = polys[0].get_bounds_single()
# Min x should be 0 (inner edge start/stop or center if width is large)
# But wait, the arc is centered at 0,0.
# Outer edge goes from (11, 0) to (0, 11)
# Inner edge goes from (9, 0) to (0, 9)
# So x ranges from 0 to 11, y ranges from 0 to 11.
assert_allclose(bounds, [[0, 0], [11, 11]], atol=1e-10)
def test_shape_mirror() -> None:
e = Ellipse(radii=(10, 5), offset=(10, 20), rotation=pi / 4)
e.mirror(0) # Mirror across x axis (axis 0): in-place relative to offset
assert_equal(e.offset, [10, 20])
# rotation was pi/4, mirrored(0) -> -pi/4 == 3pi/4 (mod pi)
assert_allclose(e.rotation, 3 * pi / 4, atol=1e-10)
a = Arc(radii=(10, 10), angles=(0, pi / 4), width=2, offset=(10, 20))
a.mirror(0)
assert_equal(a.offset, [10, 20])
# For Arc, mirror(0) negates rotation and angles
assert_allclose(a.angles, [0, -pi / 4], atol=1e-10)
def test_shape_flip_across() -> None:
e = Ellipse(radii=(10, 5), offset=(10, 20), rotation=pi / 4)
e.flip_across(axis=0) # Mirror across y=0: flips y-offset
assert_equal(e.offset, [10, -20])
# rotation also flips: -pi/4 == 3pi/4 (mod pi)
assert_allclose(e.rotation, 3 * pi / 4, atol=1e-10)
# Mirror across specific y
e = Ellipse(radii=(10, 5), offset=(10, 20))
e.flip_across(y=10) # Mirror across y=10
# y=20 mirrored across y=10 -> y=0
assert_equal(e.offset, [10, 0])
def test_shape_scale() -> None:
e = Ellipse(radii=(10, 5))
e.scale_by(2)
assert_equal(e.radii, [20, 10])
a = Arc(radii=(10, 5), angles=(0, pi), width=2)
a.scale_by(0.5)
assert_equal(a.radii, [5, 2.5])
assert a.width == 1
def test_shape_arclen() -> None:
# Test that max_arclen correctly limits segment lengths
# Ellipse
e = Ellipse(radii=(10, 5))
# Approximate perimeter is ~48.4
# With max_arclen=5, should have > 10 segments
polys = e.to_polygons(max_arclen=5)
v = polys[0].vertices
dist = numpy.sqrt(numpy.sum(numpy.diff(v, axis=0, append=v[:1]) ** 2, axis=1))
assert numpy.all(dist <= 5.000001)
assert len(v) > 10
# Arc
a = Arc(radii=(10, 10), angles=(0, pi / 2), width=2)
# Outer perimeter is 11 * pi/2 ~ 17.27
# Inner perimeter is 9 * pi/2 ~ 14.14
# With max_arclen=2, should have > 8 segments on outer edge
polys = a.to_polygons(max_arclen=2)
v = polys[0].vertices
# Arc polygons are closed, but contain both inner and outer edges and caps
# Let's just check that all segment lengths are within limit
dist = numpy.sqrt(numpy.sum(numpy.diff(v, axis=0, append=v[:1]) ** 2, axis=1))
assert numpy.all(dist <= 2.000001)