///|
/// Represents a generated synthetic test frame with ground truth 3D pose, quad corners, and rendered image.
pub(all) struct SyntheticEvaluationFrame {
  frame_index : Int
  marker_id : Int
  ground_truth_pose : Pose3D
  ground_truth_quad : Quad
  image : Image
} derive(Debug)

///|
/// Batch generator for synthetic fiducial marker dataset benchmarks.
pub(all) struct SyntheticDatasetGenerator {
  camera : CameraIntrinsics
  dictionary : MarkerDictionary
  marker_length : Double
  image_size : Size2D
} derive(Debug, Eq)

///|
/// Creates a new SyntheticDatasetGenerator.
pub fn SyntheticDatasetGenerator::new(
  camera : CameraIntrinsics,
  dictionary : MarkerDictionary,
  marker_length? : Double = 0.1,
  image_size? : Size2D = Size2D::new(640, 480),
) -> SyntheticDatasetGenerator {
  { camera, dictionary, marker_length, image_size }
}

///|
/// Generates a suite of evaluation frames across distances from min_dist to max_dist with pitch and roll variations.
pub fn SyntheticDatasetGenerator::generate_distance_sweep(
  self : SyntheticDatasetGenerator,
  marker_id : Int,
  min_dist : Double,
  max_dist : Double,
  num_steps : Int,
) -> Array[SyntheticEvaluationFrame] {
  let frames : Array[SyntheticEvaluationFrame] = []
  if num_steps <= 0 {
    return frames
  }

  let dist_step = (max_dist - min_dist) / num_steps.to_double()

  for i in 0..