use crate::tools::{GridPoint, IsolineSegment, MarchingSquaresResult, OrderedF64};
use alloc::{collections::BTreeMap, rc::Rc};
use core::cell::RefCell;
use s2json::{MValue, VectorFeature, VectorGeometry, VectorLineString, VectorMultiLineString};
use serde::{Deserialize, Serialize};
pub type SegmentLookup = BTreeMap<GridPoint, Vec<Rc<RefCell<IsolineSegment>>>>;
#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize, MValue)]
pub struct ContourProperties {
pub elev: f64,
#[serde(rename = "elevFt")]
pub elev_ft: f64,
}
pub fn stitch_marching_square_segments(
marching_squares: MarchingSquaresResult,
) -> Vec<VectorFeature> {
let mut result: Vec<VectorFeature> = vec![];
for (OrderedF64(threshold), mut segments) in marching_squares {
let mut point_to_segments: SegmentLookup = BTreeMap::new();
for seg in &segments {
let IsolineSegment { from: p1, to: p2, .. } = *seg.borrow();
let p1_set = point_to_segments.entry(p1).or_insert(vec![]);
p1_set.push(seg.clone());
let p2_set = point_to_segments.entry(p2).or_insert(vec![]);
p2_set.push(seg.clone());
}
let mut coordinates: VectorMultiLineString = vec![];
for start_seg in &mut segments {
if start_seg.borrow().visited {
continue;
}
let mut forward_points: VectorLineString =
vec![start_seg.borrow().from.into(), start_seg.borrow().to.into()];
start_seg.borrow_mut().visited = true;
grow_line(&mut forward_points, start_seg.borrow().to, &mut point_to_segments);
let mut backward_points: VectorLineString = vec![];
grow_line(&mut backward_points, start_seg.borrow().from, &mut point_to_segments);
let mut line: VectorLineString = vec![];
line.extend(backward_points.iter().rev().cloned());
line.extend(forward_points.drain(..));
if line.len() <= 2 {
continue;
}
coordinates.push(line);
}
if coordinates.is_empty() {
continue;
}
result.push(VectorFeature::new_wm(
None,
(ContourProperties { elev: threshold, elev_ft: threshold * 3.28084 }).into(),
VectorGeometry::new_multilinestring(coordinates, None),
None,
));
}
result
}
fn grow_line(line: &mut VectorLineString, mut curr_pt: GridPoint, graph: &mut SegmentLookup) {
loop {
let Some(neighbors) = graph.get_mut(&curr_pt) else {
break;
};
let Some(next_seg) = neighbors.iter_mut().find(|s| !s.borrow().visited) else {
break;
};
let next_seg = &mut next_seg.borrow_mut();
next_seg.visited = true;
let next_pt = if next_seg.from == curr_pt { next_seg.to } else { next_seg.from };
line.push(next_pt.into());
curr_pt = next_pt;
}
}