use crate::error::{EvidenceKind, GridError, GridTask, Result};
use crate::feature::{CoordinateHypothesis, OrientedFeature, PointFeature};
use crate::lattice::{GridDimensions, LatticeKind};
use crate::result::{GridSolution, RejectedFeature};
pub use crate::shared::recovery_schedule::{RecoveryParams, RecoverySchedule};
pub use crate::shared::validate::ValidationParams as ValidateParams;
pub use crate::topological::TopologicalParams;
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum Evidence<'a> {
Positions(&'a [PointFeature]),
Oriented1(&'a [OrientedFeature<1>]),
Oriented2(&'a [OrientedFeature<2>]),
Oriented3(&'a [OrientedFeature<3>]),
CoordinateHypotheses {
features: &'a [PointFeature],
hypotheses: &'a [CoordinateHypothesis],
},
}
impl Evidence<'_> {
pub fn kind(&self) -> EvidenceKind {
match self {
Self::Positions(_) => EvidenceKind::Positions,
Self::Oriented1(_) => EvidenceKind::Oriented1,
Self::Oriented2(_) => EvidenceKind::Oriented2,
Self::Oriented3(_) => EvidenceKind::Oriented3,
Self::CoordinateHypotheses { .. } => EvidenceKind::CoordinateHypotheses,
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct DetectionParams {
pub max_residual_px: f32,
pub topological: TopologicalParams,
pub validate: ValidateParams,
pub recovery: RecoverySchedule,
}
impl Default for DetectionParams {
fn default() -> Self {
Self {
max_residual_px: 2.0,
topological: TopologicalParams::default(),
validate: ValidateParams::default(),
recovery: RecoverySchedule::default(),
}
}
}
impl DetectionParams {
pub fn new(max_residual_px: f32) -> Self {
Self {
max_residual_px,
..Self::default()
}
}
pub fn with_topological(mut self, topological: TopologicalParams) -> Self {
self.topological = topological;
self
}
pub fn with_validate(mut self, validate: ValidateParams) -> Self {
self.validate = validate;
self
}
pub fn with_max_residual_px(mut self, max_residual_px: f32) -> Self {
self.max_residual_px = max_residual_px;
self
}
pub fn with_recovery(mut self, recovery: RecoverySchedule) -> Self {
self.recovery = recovery;
self
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct DetectionRequest<'a> {
pub lattice: LatticeKind,
pub evidence: Evidence<'a>,
pub dimensions: Option<GridDimensions>,
pub params: DetectionParams,
}
impl<'a> DetectionRequest<'a> {
pub fn new(
lattice: LatticeKind,
evidence: Evidence<'a>,
dimensions: Option<GridDimensions>,
params: DetectionParams,
) -> Self {
Self {
lattice,
evidence,
dimensions,
params,
}
}
}
pub fn detect_grid(request: DetectionRequest<'_>) -> Result<GridSolution> {
let mut report = detect_grid_all(request)?;
if report.solutions.is_empty() {
Err(GridError::InsufficientEvidence)
} else {
Ok(report.solutions.remove(0))
}
}
fn run_square_oriented2(
features: &[OrientedFeature<2>],
request: &DetectionRequest<'_>,
synthesized_axes: bool,
) -> Result<Vec<GridSolution>> {
crate::topological::detect_square_oriented2_topological_all(
features,
request.dimensions,
&request.params,
synthesized_axes,
)
}
fn run_hex_oriented3(
features: &[OrientedFeature<3>],
request: &DetectionRequest<'_>,
) -> Result<Vec<GridSolution>> {
crate::topological::detect_hex_oriented3_topological_all(
features,
request.dimensions,
&request.params,
)
}
pub fn detect_grid_all(request: DetectionRequest<'_>) -> Result<DetectionReport> {
let solutions = match (request.lattice, request.evidence) {
(LatticeKind::Square, Evidence::Oriented2(features)) => {
run_square_oriented2(features, &request, false)?
}
(LatticeKind::Square, Evidence::Positions(features)) => {
let oriented = crate::orient::synthesize_oriented2(features);
run_square_oriented2(&oriented, &request, true)?
}
(LatticeKind::Square, Evidence::Oriented1(features)) => {
let oriented = crate::orient::synthesize_oriented2_from_oriented1(features);
run_square_oriented2(&oriented, &request, true)?
}
(LatticeKind::Hex, Evidence::Oriented3(features)) => {
run_hex_oriented3(features, &request)?
}
(LatticeKind::Hex, Evidence::Positions(features)) => {
let oriented = crate::orient::synthesize_oriented3(features);
run_hex_oriented3(&oriented, &request)?
}
_ => {
return Err(GridError::UnsupportedCombination {
task: GridTask::Detection,
lattice: request.lattice,
evidence: request.evidence.kind(),
})
}
};
Ok(DetectionReport::new(solutions, Vec::new()))
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct DetectionReport {
pub solutions: Vec<GridSolution>,
pub rejected: Vec<RejectedFeature>,
}
impl DetectionReport {
pub fn new(solutions: Vec<GridSolution>, rejected: Vec<RejectedFeature>) -> Self {
Self {
solutions,
rejected,
}
}
}