142 lines
5 KiB
Python
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)
|