auv-scan 0.0.22

Scroll scanning interaction implementation for AUV
use super::*;
use crate::frame::{SCAN_FRAME_SCHEMA_VERSION, ScanBounds, ScanImageDimensions};

fn frame(sequence_index: u32, x: i64, y: i64) -> ScanFrame {
  ScanFrame {
    schema_version: SCAN_FRAME_SCHEMA_VERSION.into(),
    frame_id: format!("frame-{sequence_index}"),
    sequence_index,
    captured_at_millis: 0,
    window_bounds: ScanBounds {
      x,
      y,
      width: 8,
      height: 8,
    },
    viewport_bounds: None,
    image_dimensions: ScanImageDimensions {
      width: 8,
      height: 8,
    },
  }
}

#[test]
fn estimates_signed_two_dimensional_delta() {
  let bundle = ScanFrameBundle {
    frames: vec![frame(0, 7, 20), frame(1, -3, 32)],
  };

  let result = estimate_viewport_motion(&bundle).expect("two frames");
  assert!(matches!(
    result,
    MotionResult::Estimated(MotionEstimate {
      delta_x: -10,
      delta_y: 12,
      confidence: 1.0,
    })
  ));
}

#[test]
fn rejects_a_bundle_without_an_adjacent_pair() {
  let bundle = ScanFrameBundle {
    frames: vec![frame(0, 0, 0)],
  };

  let error = estimate_viewport_motion(&bundle).expect_err("single frame");
  assert!(matches!(error, MotionError::InsufficientFrames { found: 1 }));
}

#[test]
fn non_monotonic_sequence_has_no_motion_estimate() {
  let result = estimate_viewport_motion_between(&frame(4, 0, 0), &frame(3, 4, 5));

  assert!(matches!(
    result,
    MotionResult::Unknown(MotionUnknown { code, .. }) if code == "motion_unknown"
  ));
}