"""SrasFile.angles_share_raw_grid(): the no-alignment-needed gating path for Export Fused ROI. A plain multi-angle scan gives each angle its own bounding box and stage x_start (scan_format.md's whole reason v6 geometry is per-angle), so it must read as "not shareable" without a live alignment. A file the viewer's own Alignment Wizard exported repeats one Per-Angle Geometry record and one Row Table span for every angle (scan_format.md, "Files written by the viewer's Alignment Wizard"), so it must read as "shareable" with no alignment needed at all. No Qt: this exercises sras_format/sras_compute/sras_align_export directly, mirroring tests/test_align_export.py. """ import sras_align_export as export import sras_compute as compute from sras_format import SrasFile import tools.make_test_sras as gen _THRESHOLD_MV = 80.0 def test_single_angle_file_always_shares_its_grid(tmp_path): path = tmp_path / "one_angle.sras" gen.write(path, n_angles=1) sras = SrasFile(str(path)) assert sras.angles_share_raw_grid() def test_plain_multi_angle_file_does_not_share_its_grid(tmp_path): """tools.make_test_sras.write gives every angle its own geometry and stage x_start (build()'s `x_start = -0.5 + 0.1 * a`), matching how real v6 scans vary per angle — so this must read as "not shareable".""" path = tmp_path / "plain.sras" gen.write(path, n_angles=3) sras = SrasFile(str(path)) assert not sras.angles_share_raw_grid() def test_wizard_exported_file_shares_its_grid(tmp_path): src_path = tmp_path / "rotating.sras" meta = gen.write_rotating(src_path, n_angles=4) sras = SrasFile(str(src_path)) params = {a: compute.ManualAngleParams(rot, shift) for a, (rot, shift) in meta["truth"].items()} result = compute.build_manual_alignment(sras, 0, _THRESHOLD_MV, params) out_path = tmp_path / "rotating_aligned.sras" export.write_aligned_sras(sras, result, out_path) out = SrasFile(str(out_path)) assert not sras.angles_share_raw_grid(), ( "sanity check: the *source* rotating scan must NOT already share a " "grid, or this test would not actually exercise the wizard export") assert out.angles_share_raw_grid() def test_mutated_angle_breaks_the_shared_grid(tmp_path): """A loaded SrasFile's x_start_mm is a public per-angle array (as tests/test_gui.py::test_alignment_geometry_is_stage_independent also relies on) -- mutating one angle's start must be visible here too.""" src_path = tmp_path / "rotating.sras" meta = gen.write_rotating(src_path, n_angles=3) sras = SrasFile(str(src_path)) params = {a: compute.ManualAngleParams(rot, shift) for a, (rot, shift) in meta["truth"].items()} result = compute.build_manual_alignment(sras, 0, _THRESHOLD_MV, params) out_path = tmp_path / "rotating_aligned.sras" export.write_aligned_sras(sras, result, out_path) out = SrasFile(str(out_path)) assert out.angles_share_raw_grid() out.x_start_mm[1] += 1.0 assert not out.angles_share_raw_grid()