"""Tests for geometry/transforms.py — coordinate transformations.""" from __future__ import annotations import pytest from src.app.geometry import transforms class TestRescaleBbox: def test_upscale(self) -> None: result = transforms.rescale_bbox((10, 20, 100, 50), 2.0) assert result == (20, 40, 200, 100) def test_downscale(self) -> None: result = transforms.rescale_bbox((10, 20, 100, 50), 0.5) assert result == (5, 10, 50, 25) def test_identity(self) -> None: b = (10, 20, 100, 50) assert transforms.rescale_bbox(b, 1.0) == b def test_zero_factor_rejected(self) -> None: with pytest.raises(ValueError, match="must be > 0"): transforms.rescale_bbox((0, 0, 10, 10), 0) def test_negative_factor_rejected(self) -> None: with pytest.raises(ValueError, match="must be > 0"): transforms.rescale_bbox((0, 0, 10, 10), -1) class TestRescalePolygon: def test_upscale(self) -> None: poly = [(10, 20), (30, 20), (30, 40), (10, 40)] result = transforms.rescale_polygon(poly, 2.0) assert result == [(20, 40), (60, 40), (60, 80), (20, 80)] def test_zero_rejected(self) -> None: with pytest.raises(ValueError): transforms.rescale_polygon([(0, 0)], 0) class TestClipBboxToPage: def test_already_inside(self) -> None: result = transforms.clip_bbox_to_page((10, 20, 50, 30), 100, 100) assert result == (10, 20, 50, 30) def test_overflow_right(self) -> None: result = transforms.clip_bbox_to_page((80, 10, 50, 20), 100, 100) # x=80, x2=130, clipped to x2=100, so w=20 assert result == (80, 10, 20, 20) def test_overflow_bottom(self) -> None: result = transforms.clip_bbox_to_page((10, 85, 20, 30), 100, 100) assert result == (10, 85, 20, 15) def test_negative_origin(self) -> None: result = transforms.clip_bbox_to_page((-5, -10, 50, 50), 100, 100) assert result[0] == 0 assert result[1] == 0 def test_min_dimension_1(self) -> None: # bbox entirely outside right edge result = transforms.clip_bbox_to_page((200, 50, 10, 10), 100, 100) assert result[2] >= 1 assert result[3] >= 1 class TestClipPolygonToPage: def test_clamp(self) -> None: poly = [(-5, -10), (150, -10), (150, 120), (-5, 120)] result = transforms.clip_polygon_to_page(poly, 100, 100) assert result == [(0, 0), (100, 0), (100, 100), (0, 100)] class TestRotateBbox90: def test_once(self) -> None: # Page 100x200, bbox at (10, 20, 30, 40) # After 90° CW: new_x = 200 - 20 - 40 = 140, new_y = 10 # w and h swap: w=40, h=30 result = transforms.rotate_bbox_90((10, 20, 30, 40), 100, 200, times=1) assert result == (140, 10, 40, 30) def test_twice(self) -> None: # 180°: (page_w - x - w, page_h - y - h, w, h) result = transforms.rotate_bbox_90((10, 20, 30, 40), 100, 200, times=2) assert result == (60, 140, 30, 40) def test_four_times_identity(self) -> None: b = (10, 20, 30, 40) result = transforms.rotate_bbox_90(b, 100, 200, times=4) assert result == pytest.approx(b) def test_zero_times_identity(self) -> None: b = (10, 20, 30, 40) result = transforms.rotate_bbox_90(b, 100, 200, times=0) assert result == b class TestRescalePoint: def test_upscale(self) -> None: assert transforms.rescale_point((10, 20), 2.0) == (20, 40) def test_zero_rejected(self) -> None: with pytest.raises(ValueError, match="must be > 0"): transforms.rescale_point((10, 20), 0) class TestRotatePoint90: def test_once(self) -> None: # Page 100x200, point (10, 20) → (200 - 20, 10) = (180, 10) assert transforms.rotate_point_90((10, 20), 100, 200, times=1) == (180, 10) def test_four_times_identity(self) -> None: p = (10, 20) result = transforms.rotate_point_90(p, 100, 200, times=4) assert result == pytest.approx(p) def test_zero_times_identity(self) -> None: p = (10, 20) assert transforms.rotate_point_90(p, 100, 200, times=0) == p class TestRotatePolygon90: def test_rotates_all_points(self) -> None: poly = [(0, 0), (10, 0), (10, 10), (0, 10)] result = transforms.rotate_polygon_90(poly, 100, 200, times=1) assert len(result) == 4 # Check first point: (0,0) → (200-0, 0) = (200, 0) assert result[0] == (200, 0) def test_four_times_identity(self) -> None: poly = [(10, 20), (30, 20), (30, 40), (10, 40)] result = transforms.rotate_polygon_90(poly, 100, 200, times=4) for orig, rotated in zip(poly, result, strict=True): assert rotated == pytest.approx(orig) class TestTranslate: def test_bbox(self) -> None: result = transforms.translate_bbox((10, 20, 30, 40), 5, -10) assert result == (15, 10, 30, 40) def test_polygon(self) -> None: poly = [(0, 0), (10, 0), (10, 10)] result = transforms.translate_polygon(poly, 100, 200) assert result == [(100, 200), (110, 200), (110, 210)]