autocal: 序列页阵列预览工具、2D 图点可见度,标签顺序与送到标定页
- 标签顺序:几何与安装 放到 测试序列 之前 - 序列点增加可选 uv_px(向后兼容),点表新增 U/V 列;加入所选点、删除所选、 送到标定页(按 U/V 经同一套求解加入标定队列并切换页面) - 已选点结果改显示在选点区状态行,不再写入全局顶部状态条 - 序列页左上区改紧凑:名称与备注同一行,分“阵列”“运动参数”两页; 运动参数每行三组,输入框改用 compactField - 新增阵列工具(array_plan.py、ui/array_tool.py):UV 规则网格(可蛇形遍历)与 阶段预设,逐点检查是否在曲面内、是否留有边距,候选点实时预览在 2D 图上, 只有可用点才能加入,已在序列里的点不重复加入 - 2D 平面图:标记改为缩放后再绘制并加深色描边,修复缩小后点不清; 底图可选 原图/灰度/变暗/仅轮廓,点色可选 黄/白/青/橙/灰(默认灰度+黄,写入偏好); 序列点带编号并连线显示遍历顺序,选中行高亮 - 新增测试 test_sequence_uv、test_sequence_to_capture、test_array_plan、test_array_tool
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
"""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 == ""
|
||||
Reference in New Issue
Block a user