use calib_targets_puzzleboard::{MASTER_COLS, MASTER_ROWS};
use serde::{Deserialize, Serialize};
use super::chessboard::validate_square_size;
use super::error::PrintableTargetError;
pub(super) fn default_puzzleboard_dot_diameter_rel() -> f64 {
1.0 / 3.0
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PuzzleBoardTargetSpec {
pub rows: u32,
pub cols: u32,
pub square_size_mm: f64,
#[serde(default)]
pub origin_row: u32,
#[serde(default)]
pub origin_col: u32,
#[serde(default = "default_puzzleboard_dot_diameter_rel")]
pub dot_diameter_rel: f64,
}
impl PuzzleBoardTargetSpec {
pub fn new(rows: u32, cols: u32, square_size_mm: f64) -> Self {
Self {
rows,
cols,
square_size_mm,
origin_row: 0,
origin_col: 0,
dot_diameter_rel: default_puzzleboard_dot_diameter_rel(),
}
}
#[must_use]
pub fn with_origin(mut self, origin_row: u32, origin_col: u32) -> Self {
self.origin_row = origin_row;
self.origin_col = origin_col;
self
}
#[must_use]
pub fn with_dot_diameter_rel(mut self, dot_diameter_rel: f64) -> Self {
self.dot_diameter_rel = dot_diameter_rel;
self
}
}
pub(crate) fn validate_puzzleboard_spec(
spec: &PuzzleBoardTargetSpec,
) -> Result<(), PrintableTargetError> {
if spec.rows < 4 || spec.cols < 4 || spec.rows > MASTER_ROWS || spec.cols > MASTER_COLS {
return Err(PrintableTargetError::InvalidPuzzleBoardSize);
}
if spec.origin_row + spec.rows > MASTER_ROWS || spec.origin_col + spec.cols > MASTER_COLS {
return Err(PrintableTargetError::InvalidPuzzleBoardOrigin);
}
validate_square_size(spec.square_size_mm)?;
if !spec.dot_diameter_rel.is_finite()
|| spec.dot_diameter_rel <= 0.0
|| spec.dot_diameter_rel > 1.0
{
return Err(PrintableTargetError::InvalidPuzzleBoardDotDiameter);
}
Ok(())
}