use crate::geometry::{LabelPx, SafeArea};
use image::{GrayImage, Luma};
pub const CALIBRATION_RING_STEP_PX: u32 = 4;
pub const CALIBRATION_RINGS: u32 = 6;
pub const CALIBRATION_RULER_MAJOR_PX: u32 = 26;
pub const CALIBRATION_RULER_MINOR_PX: u32 = 12;
pub fn calibration_pattern(
label: LabelPx,
safe: Option<SafeArea>,
pixels_per_mm: f64,
) -> GrayImage {
let w = label.width_px;
let h = label.height_px;
let mut img = GrayImage::from_pixel(w, h, Luma([255]));
if w == 0 || h == 0 {
return img;
}
for y in 0..h {
for x in 0..w {
let expect_down = (y as i64 * (w as i64 - 1)) / (h as i64 - 1).max(1);
let expect_up = ((h as i64 - 1 - y as i64) * (w as i64 - 1)) / (h as i64 - 1).max(1);
let on_diag = (x as i64 - expect_down).abs() <= 1 || (x as i64 - expect_up).abs() <= 1;
let on_cross =
(x as i64 - w as i64 / 2).abs() <= 1 || (y as i64 - h as i64 / 2).abs() <= 1;
if on_diag || on_cross {
img.put_pixel(x, y, Luma([0]));
}
}
}
let ring_step = (0.5 * pixels_per_mm).round().max(1.0) as u32;
for ring in 0..CALIBRATION_RINGS {
let inset = ring * ring_step;
if inset * 2 + 1 >= w.min(h) {
break;
}
let (x0, y0) = (inset, inset);
let (x1, y1) = (w - 1 - inset, h - 1 - inset);
for x in x0..=x1 {
img.put_pixel(x, y0, Luma([0]));
img.put_pixel(x, y1, Luma([0]));
}
for y in y0..=y1 {
img.put_pixel(x0, y, Luma([0]));
img.put_pixel(x1, y, Luma([0]));
}
}
let ruler_scale = pixels_per_mm / crate::geometry::PX_PER_MM;
let major_len = (f64::from(CALIBRATION_RULER_MAJOR_PX) * ruler_scale).round() as u32;
let minor_len = (f64::from(CALIBRATION_RULER_MINOR_PX) * ruler_scale).round() as u32;
let height_mm = (f64::from(h) / pixels_per_mm).floor() as u32;
for mm in 0..=height_mm {
let y = (f64::from(mm) * pixels_per_mm).round() as u32;
if y >= h {
break;
}
let major = mm % 5 == 0;
let len = if major { major_len } else { minor_len };
let thick = if major { 3 } else { 1 };
for t in 0..thick {
let yy = (y + t).min(h - 1);
for x in 0..len.min(w) {
img.put_pixel(x, yy, Luma([0]));
img.put_pixel(w - 1 - x, yy, Luma([0]));
}
}
}
if let Some(area) = safe.and_then(|s| s.content(label)) {
let t = 3i64;
let (x0, y0) = (area.x as i64, area.y as i64);
let (x1, y1) = (x0 + area.w as i64 - 1, y0 + area.h as i64 - 1);
for y in 0..h as i64 {
for x in 0..w as i64 {
let inside = x >= x0 && x <= x1 && y >= y0 && y <= y1;
let near_edge = (x - x0).abs() < t
|| (x - x1).abs() < t
|| (y - y0).abs() < t
|| (y - y1).abs() < t;
if inside && near_edge {
img.put_pixel(x as u32, y as u32, Luma([0]));
}
}
}
}
img
}