File size: 2,952 Bytes
1a12965
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Unit tests for src.utils.geometry_utils."""

from __future__ import annotations

import math

from src.utils.geometry_utils import (
    box_center, box_to_polygon, clamp01, containment_pct, coverage_pct,
    distance_between, iou, margin_utilization, points_to_polygon, safe_area,
)


def test_box_to_polygon_area():
    poly = box_to_polygon((0, 0, 10, 20))
    assert safe_area(poly) == 200.0


def test_box_to_polygon_normalizes_reversed_coords():
    poly = box_to_polygon((10, 20, 0, 0))
    assert safe_area(poly) == 200.0


def test_coverage_pct_full_overlap():
    target = box_to_polygon((0, 0, 100, 100))
    sig = box_to_polygon((0, 0, 100, 100))
    assert coverage_pct(target, sig) == 100.0
    assert containment_pct(target, sig) == 100.0
    assert iou(target, sig) == 1.0


def test_coverage_pct_partial_overlap():
    target = box_to_polygon((0, 0, 100, 100))
    sig = box_to_polygon((50, 0, 150, 100))
    # Half of the target box is covered by the signature.
    assert math.isclose(coverage_pct(target, sig), 50.0, abs_tol=1e-6)
    # Half of the signature lies within the target box.
    assert math.isclose(containment_pct(target, sig), 50.0, abs_tol=1e-6)


def test_coverage_pct_no_overlap():
    target = box_to_polygon((0, 0, 10, 10))
    sig = box_to_polygon((100, 100, 110, 110))
    assert coverage_pct(target, sig) == 0.0
    assert containment_pct(target, sig) == 0.0
    assert iou(target, sig) == 0.0


def test_iou_partial():
    a = box_to_polygon((0, 0, 10, 10))
    b = box_to_polygon((5, 0, 15, 10))
    # Intersection = 50, union = 100+100-50 = 150
    assert math.isclose(iou(a, b), 50.0 / 150.0, rel_tol=1e-6)


def test_points_to_polygon_handles_degenerate_input():
    poly = points_to_polygon([])
    assert poly.is_empty
    poly_single = points_to_polygon([[5, 5]])
    assert safe_area(poly_single) > 0


def test_points_to_polygon_valid_shape():
    poly = points_to_polygon([[0, 0], [10, 0], [10, 10], [0, 10]])
    assert poly.is_valid
    assert safe_area(poly) == 100.0


def test_box_center_and_distance():
    assert box_center((0, 0, 10, 20)) == (5.0, 10.0)
    assert distance_between((0, 0), (3, 4)) == 5.0


def test_distance_between_nan_safe():
    assert math.isnan(distance_between((float("nan"), 0), (1, 1)))


def test_clamp01():
    assert clamp01(-1) == 0.0
    assert clamp01(2) == 1.0
    assert clamp01(0.5) == 0.5
    assert clamp01(float("nan")) == 0.0


def test_margin_utilization_centered_small_signature():
    box = (0, 0, 100, 100)
    sig = box_to_polygon((45, 45, 55, 55))
    # A small centered signature uses very little of the available margin.
    assert margin_utilization(box, sig) < 0.2


def test_margin_utilization_full_box_signature():
    box = (0, 0, 100, 100)
    sig = box_to_polygon((0, 0, 100, 100))
    # A signature filling the whole box reaches ~1.0 (corner at half-diagonal).
    assert math.isclose(margin_utilization(box, sig), 1.0, rel_tol=1e-6)