File size: 5,231 Bytes
9f084b2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbbfba8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9f084b2
 
 
 
 
 
 
 
 
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""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)]