"""Candidate arrays on the flat map: the grid, the presets and the on-surface check.""" from __future__ import annotations from types import SimpleNamespace import numpy as np import pytest from orisys_autocal import array_plan from orisys_autocal.array_plan import ( NEAR_EDGE, OFF_SURFACE, PRESETS, check_candidates, grid_uv, preset_uv, ) def _flat(width: int = 200, height: int = 200, hole: tuple[int, int, int, int] | None = None): """A fully covered raster, optionally with a rectangular hole (x0, y0, x1, y1).""" covered = np.ones((height, width), dtype=np.int32) if hole: x0, y0, x1, y1 = hole covered[y0:y1, x0:x1] = -1 return SimpleNamespace(face_index=covered, width=width, height=height) def test_a_grid_is_centred_and_bottom_row_first(): points = grid_uv(2, 3, 10, 20, centre=(0, 100), serpentine=False) assert points == [(-10, 90), (0, 90), (10, 90), (-10, 110), (0, 110), (10, 110)] def test_a_serpentine_walks_every_second_row_backwards(): points = grid_uv(2, 3, 10, 20, centre=(0, 100), serpentine=True) assert points[3:] == [(10, 110), (0, 110), (-10, 110)] def test_an_empty_or_oversized_grid(): assert grid_uv(0, 3, 10, 10) == [] with pytest.raises(ValueError, match="more than"): grid_uv(30, 30, 1, 1) def test_presets_overlap_only_at_the_hardware_check_point(): labels = [label for key in PRESETS for label, _u, _v in PRESETS[key]] assert sorted(label for label in set(labels) if labels.count(label) > 1) == ["C13"] # The first-model grid is centred on the hardware-check point. assert ("C13", 0, 470) in PRESETS["stage1"] assert ("C13", 0, 470) in PRESETS["stage0"] assert preset_uv("stage0") == [("C13", 0, 470)] with pytest.raises(ValueError): preset_uv("nope") assert array_plan.PRESET_KEYS[0] == "stage0" def test_a_point_on_the_surface_is_valid_and_off_it_is_not(): flat = _flat(hole=(40, 40, 60, 60)) origin = (100, 150) # raster (x, y) = (100 + u, 150 - v) result = check_candidates(flat, [(0, 100), (-50, 100)], origin=origin) assert [c.valid for c in result] == [True, False] assert result[1].reason == OFF_SURFACE # raster (50, 50) is in the hole def test_outside_the_raster_is_off_surface_not_an_error(): result = check_candidates(_flat(), [(900, 900)], origin=(100, 150)) assert result[0].valid is False and result[0].reason == OFF_SURFACE def test_a_margin_rejects_points_too_close_to_a_hole_or_the_border(): flat = _flat(hole=(100, 60, 110, 70)) origin = (100, 150) # raster (100, 75): five pixels below the hole's bottom edge. near = check_candidates(flat, [(0, 75)], margin_px=8, origin=origin)[0] far = check_candidates(flat, [(0, 75)], margin_px=3, origin=origin)[0] assert (near.valid, near.reason) == (False, NEAR_EDGE) assert far.valid # The raster border counts as an edge. assert check_candidates(flat, [(-98, 100)], margin_px=5, origin=origin)[0].reason == NEAR_EDGE def test_labels_survive_the_check_and_no_surface_vouches_for_nothing(): flat = _flat() kept = check_candidates(flat, [("C13", 0, 100)], origin=(100, 150))[0] assert kept.label == "C13" and kept.valid none = check_candidates(None, [("C13", 0, 100), (1, 2)]) assert [c.valid for c in none] == [False, False] assert none[0].label == "C13" and none[1].label == ""