use super::*;
fn request() -> PlaneCalibrationRequest {
PlaneCalibrationRequest {
raster_to_source: [0.5, 0., 0., -0.5, 10., 120.],
raster_size: [400, 200], source_points: [[10., 20.], [110., 20.]],
distance_metres: 10., world_anchor: [1000., 2000., 3.],
world_direction: [2., 0., 0.], plane_normal: [0., 0., 4.],
}
}
fn close(actual: [f64; 3], expected: [f64; 3]) {
for (a, b) in actual.into_iter().zip(expected) { assert!((a - b).abs() < 1e-9, "{actual:?} != {expected:?}"); }
}
#[test]
fn issue_4260_measured_span_sets_one_world_plane_independent_of_paper_dpi() {
let r = request();
let plane = calibrate_appearance_plane(&r).unwrap();
close(plane.raster_corners[0], [1000., 2010., 3.]);
close(plane.raster_corners[1], [1020., 2010., 3.]);
close(plane.raster_corners[2], [1020., 2000., 3.]);
close(plane.raster_corners[3], [1000., 2000., 3.]);
assert_eq!(plane.metres_per_source_unit, 0.1);
let Mapping::Planar { origin, axis_u, axis_v, metres_per_tile, frame } = plane.mapping else { panic!("Expected planar mapping") };
assert!(matches!(frame, MappingFrame::World));
assert_eq!(origin, [1000., 2000., 3.]);
assert_eq!(axis_u, [1., 0., 0.]);
assert_eq!(axis_v, [0., 1., 0.]);
assert_eq!(metres_per_tile, [20., 10.]);
let mut higher_dpi = r;
higher_dpi.raster_size = [800, 400];
higher_dpi.raster_to_source[..4].iter_mut().for_each(|v| *v *= 0.5);
assert_eq!(calibrate_appearance_plane(&higher_dpi).unwrap().raster_corners, plane.raster_corners);
}
#[test]
fn issue_4260_crop_rotation_and_user_unit_do_not_recalibrate_native_landmarks() {
let mut r = request();
r.raster_to_source = [0., 0.5, 0.5, 0., 10., 20.];
r.raster_size = [200, 400];
let rotated = calibrate_appearance_plane(&r).unwrap();
close(rotated.raster_corners[0], [1000., 2000., 3.]);
close(rotated.raster_corners[1], [1000., 2010., 3.]);
close(rotated.raster_corners[2], [1020., 2010., 3.]);
r.raster_to_source = [0.25, 0., 0., -0.25, 60., 95.];
r.raster_size = [200, 200];
let cropped = calibrate_appearance_plane(&r).unwrap();
close(cropped.raster_corners[0], [1005., 2007.5, 3.]);
close(cropped.raster_corners[2], [1010., 2002.5, 3.]);
r.raster_size = [400, 400];
r.raster_to_source[..4].iter_mut().for_each(|v| *v *= 0.5);
assert_eq!(calibrate_appearance_plane(&r).unwrap().raster_corners, cropped.raster_corners);
}
#[test]
fn issue_4260_diagonal_landmarks_and_vertical_plane_preserve_known_distance() {
let mut r = request();
r.source_points = [[0., 0.], [3., 4.]];
r.distance_metres = 10.;
r.world_anchor = [0., 0., 5.];
r.world_direction = [0., 0., 1.];
r.plane_normal = [0., -1., 0.];
r.raster_to_source = [0.003, 0.004, 0.004, -0.003, 0., 0.];
r.raster_size = [1000, 1000];
let plane = calibrate_appearance_plane(&r).unwrap();
close(plane.raster_corners[0], [0., 0., 5.]);
close(plane.raster_corners[1], [0., 0., 15.]);
close(plane.raster_corners[2], [10., 0., 15.]);
}
#[test]
fn issue_4260_partial_coordinate_collapse_rejects_distorted_diagonal_planes() {
let mut r = request();
r.world_anchor = [1e16, 0., 0.];
r.world_direction = [1., 1., 0.];
r.plane_normal = [0., 0., 1.];
r.raster_to_source = [1., 0., 0., -1., 0., 1.];
r.raster_size = [1, 1];
r.source_points = [[0., 0.], [1., 0.]];
r.distance_metres = 1.;
assert!(calibrate_appearance_plane(&r).unwrap_err().contains("coordinate precision"));
r.world_anchor = [5_000_000., 4_000_000., 100.];
let plane = calibrate_appearance_plane(&r).unwrap();
for edge in 0..4 {
let delta = std::array::from_fn(|i| plane.raster_corners[(edge + 1) % 4][i]
- plane.raster_corners[edge][i]);
assert!((length(delta) - 1.).abs() < 1e-8);
}
r.world_direction = [1., 0., 0.];
r.world_anchor = [0., 1e16, 0.];
r.raster_to_source = [1e10, 0., 0., -1., 0., 0.];
assert!(calibrate_appearance_plane(&r).is_err());
}
#[test]
fn issue_4260_invalid_or_unrepresentable_calibration_is_never_a_default_scale() {
let cases: [fn(&mut PlaneCalibrationRequest); 12] = [
|r| r.source_points[1] = r.source_points[0],
|r| r.distance_metres = 0.,
|r| r.distance_metres = f64::INFINITY,
|r| r.raster_to_source[0] = f64::NAN,
|r| r.raster_to_source[2] = 0.25, |r| r.raster_to_source[3] = 0., |r| r.world_direction = [0., 0., 1.], |r| r.plane_normal = [0., 0., 0.],
|r| r.raster_size = [8192, 8192],
|r| r.raster_size = [0, 10],
|r| r.world_anchor = [1e30, 1e30, 1e30],
|r| r.raster_to_source[4] = f64::MAX,
];
for (index, change) in cases.into_iter().enumerate() {
let mut r = request(); change(&mut r);
assert!(calibrate_appearance_plane(&r).is_err(), "invalid case {index} was accepted");
}
}