Spaces:
Running
Running
File size: 3,043 Bytes
37e3d5a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #!/usr/bin/env python3
"""Tests for Plan 1.3 §4 Blum-medial-axis lathe-profile derivation (Phase 2)."""
from __future__ import annotations
import sys
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "stage2_spec"))
from derive_geometry import derive_lathe_profile # noqa: E402
def circle_mask(size: int, r: float):
c = (size - 1) / 2.0
return [((x - c) ** 2 + (y - c) ** 2) <= r * r for y in range(size) for x in range(size)]
def rect_mask(w: int, h: int, mx: int):
# a vertical bar of constant half-width mx, centered
cx = w // 2
return [abs(x - cx) <= mx for y in range(h) for x in range(w)]
def triangle_mask(w: int, h: int):
# point at top (y=0), widening to full at bottom (y=h-1)
cx = w // 2
mask = []
for y in range(h):
half = int((y / (h - 1)) * (cx - 1))
for x in range(w):
mask.append(abs(x - cx) <= half)
return mask
class LatheProfileTest(unittest.TestCase):
def test_circle_radius_peaks_in_middle(self):
size = 64
mask = circle_mask(size, 28)
prof = derive_lathe_profile(mask, size, size, samples=15)
radii = [p[0] for p in prof["points"]]
mid = radii[len(radii) // 2]
self.assertGreater(mid, radii[0])
self.assertGreater(mid, radii[-1])
self.assertLess(radii[0], 0.15) # near the poles the radius is ~0
self.assertLess(radii[-1], 0.15)
def test_rectangle_radius_roughly_constant(self):
w, h = 40, 80
mask = rect_mask(w, h, 8)
prof = derive_lathe_profile(mask, w, h, samples=15)
# drop first/last (endpoints can clip) and check the middle band is stable
mids = [p[0] for p in prof["points"][3:-3]]
self.assertTrue(mids)
self.assertLess(max(mids) - min(mids), 0.03, mids)
def test_triangle_radius_increases_top_to_bottom(self):
w, h = 60, 80
mask = triangle_mask(w, h)
prof = derive_lathe_profile(mask, w, h, samples=15)
radii = [p[0] for p in prof["points"]]
# top (axisPos -0.5) narrow → bottom (+0.5) wide
self.assertLess(radii[0], radii[-1])
self.assertEqual(prof["axis"], "vertical")
def test_axis_picks_longer_dimension(self):
# a subject whose bbox is WIDER than tall → horizontal revolve axis.
w, h = 80, 40
cy = h // 2
mask = [abs(y - cy) <= 6 for y in range(h) for x in range(w)] # wide horizontal bar
prof = derive_lathe_profile(mask, w, h, samples=10)
self.assertEqual(prof["axis"], "horizontal")
def test_axis_positions_span_normalized_range(self):
size = 40
mask = circle_mask(size, 16)
prof = derive_lathe_profile(mask, size, size, samples=9)
positions = [p[1] for p in prof["points"]]
self.assertAlmostEqual(positions[0], -0.5, delta=0.05)
self.assertAlmostEqual(positions[-1], 0.5, delta=0.05)
if __name__ == "__main__":
unittest.main(verbosity=2)
|