use crate::homography::homography_from_4pt;
use nalgebra::Point2;
pub use crate::square::grow::Seed;
#[derive(Clone, Copy, Debug)]
pub struct SeedOutput {
pub seed: Seed,
pub cell_size: f32,
}
pub const SEED_QUAD_GRID: [(i32, i32); 4] = [(0, 0), (1, 0), (0, 1), (1, 1)];
pub fn seed_has_midpoint_violation(
positions: &[Point2<f32>],
seed_quad: [usize; 4],
cell_size: f32,
midpoint_tol_rel: f32,
on_edge_midpoint: &[usize],
on_parallelogram_center: &[usize],
) -> bool {
let tol = midpoint_tol_rel * cell_size;
let tol_sq = tol * tol;
let [a, b, c, d] = seed_quad;
let pa = positions[a];
let pb = positions[b];
let pc = positions[c];
let pd = positions[d];
let midpoints = [
Point2::from((pa.coords + pb.coords) * 0.5),
Point2::from((pa.coords + pc.coords) * 0.5),
Point2::from((pb.coords + pd.coords) * 0.5),
Point2::from((pc.coords + pd.coords) * 0.5),
];
for mp in midpoints {
if any_within(positions, on_edge_midpoint, mp, tol_sq, &seed_quad) {
return true;
}
}
let center = Point2::from((pa.coords + pd.coords) * 0.5);
if any_within(
positions,
on_parallelogram_center,
center,
tol_sq,
&seed_quad,
) {
return true;
}
false
}
fn any_within(
positions: &[Point2<f32>],
candidates: &[usize],
target: Point2<f32>,
tol_sq: f32,
exclude: &[usize],
) -> bool {
for &idx in candidates {
if exclude.contains(&idx) {
continue;
}
let p = positions[idx];
let dx = p.x - target.x;
let dy = p.y - target.y;
if dx * dx + dy * dy <= tol_sq {
return true;
}
}
false
}
pub fn seed_cell_size(positions: &[Point2<f32>], seed: Seed) -> Option<f32> {
let p = |i: usize| positions[i];
let edges = [
(p(seed.a) - p(seed.b)).norm(),
(p(seed.a) - p(seed.c)).norm(),
(p(seed.b) - p(seed.d)).norm(),
(p(seed.c) - p(seed.d)).norm(),
];
if edges.iter().any(|&e| e <= 0.0) {
return None;
}
Some(edges.iter().sum::<f32>() * 0.25)
}
pub fn seed_homography(
positions: &[Point2<f32>],
seed: Seed,
) -> Option<crate::homography::Homography> {
let img_pts = [
positions[seed.a],
positions[seed.b],
positions[seed.d],
positions[seed.c],
];
let grid_pts = [
Point2::new(0.0, 0.0),
Point2::new(1.0, 0.0),
Point2::new(1.0, 1.0),
Point2::new(0.0, 1.0),
];
homography_from_4pt(&grid_pts, &img_pts)
}
#[cfg(test)]
mod tests {
use super::*;
fn positions_4(a: (f32, f32), b: (f32, f32), c: (f32, f32), d: (f32, f32)) -> Vec<Point2<f32>> {
vec![
Point2::new(a.0, a.1),
Point2::new(b.0, b.1),
Point2::new(c.0, c.1),
Point2::new(d.0, d.1),
]
}
#[test]
fn seed_cell_size_unit_square() {
let p = positions_4((0.0, 0.0), (10.0, 0.0), (0.0, 10.0), (10.0, 10.0));
let s = seed_cell_size(
&p,
Seed {
a: 0,
b: 1,
c: 2,
d: 3,
},
)
.unwrap();
assert!((s - 10.0).abs() < 1e-4);
}
#[test]
fn midpoint_violation_detects_2x_mislabel() {
let positions = vec![
Point2::new(0.0, 0.0), Point2::new(20.0, 0.0), Point2::new(0.0, 20.0), Point2::new(20.0, 20.0), Point2::new(10.0, 0.0), ];
let violation = seed_has_midpoint_violation(
&positions,
[0, 1, 2, 3],
20.0,
0.3,
&[4], &[], );
assert!(violation);
}
#[test]
fn midpoint_violation_absent_on_clean_seed() {
let positions = positions_4((0.0, 0.0), (10.0, 0.0), (0.0, 10.0), (10.0, 10.0));
let violation = seed_has_midpoint_violation(&positions, [0, 1, 2, 3], 10.0, 0.3, &[], &[]);
assert!(!violation);
}
}