use geo_types::{private_utils::get_bounding_rect, Line};
use crate::{
coordinate_position::CoordPos, sweep::SweepPoint, BoundingRect, Coord, CoordinatePosition,
GeoNum, HasKernel, Intersects, Kernel, LineString, Orientation, Polygon, Rect,
};
#[derive(Clone, PartialEq)]
pub struct MonoPoly<T: GeoNum> {
top: LineString<T>,
bot: LineString<T>,
bounds: Rect<T>,
}
impl<T: GeoNum> BoundingRect<T> for MonoPoly<T> {
type Output = Rect<T>;
fn bounding_rect(&self) -> Self::Output {
self.bounds
}
}
impl<T: GeoNum> std::fmt::Debug for MonoPoly<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let top: Vec<SweepPoint<T>> = self.top.0.iter().map(|c| (*c).into()).collect();
let bot: Vec<SweepPoint<T>> = self.bot.0.iter().map(|c| (*c).into()).collect();
f.debug_struct("MonoPoly")
.field("top", &top)
.field("bot", &bot)
.finish()
}
}
impl<T: GeoNum> MonoPoly<T> {
pub(super) fn new(top: LineString<T>, bot: LineString<T>) -> Self {
debug_assert_eq!(top.0.first(), bot.0.first());
debug_assert_eq!(top.0.last(), bot.0.last());
debug_assert_ne!(top.0.first(), top.0.last());
let bounds = get_bounding_rect(top.0.iter().chain(bot.0.iter()).cloned()).unwrap();
Self { top, bot, bounds }
}
#[must_use]
pub fn top(&self) -> &LineString<T> {
&self.top
}
#[must_use]
pub fn bot(&self) -> &LineString<T> {
&self.bot
}
pub fn into_ls_pair(self) -> (LineString<T>, LineString<T>) {
(self.top, self.bot)
}
pub fn bounding_segment(&self, x: T) -> Option<(Line<T>, Line<T>)> {
let tl_idx = self.top.0.partition_point(|c| c.x < x);
if tl_idx == 0 && self.top.0[0].x != x {
return None;
}
let bl_idx = self.bot.0.partition_point(|c| c.x < x);
if bl_idx == 0 {
debug_assert_eq!(tl_idx, 0);
debug_assert_eq!(self.bot.0[0].x, x);
return Some((
Line::new(self.top.0[0], self.top.0[1]),
Line::new(self.bot.0[0], self.bot.0[1]),
));
} else {
debug_assert_ne!(tl_idx, 0);
}
Some((
Line::new(self.top.0[tl_idx - 1], self.top.0[tl_idx]),
Line::new(self.bot.0[bl_idx - 1], self.bot.0[bl_idx]),
))
}
pub fn into_polygon(self) -> Polygon<T> {
let mut down = self.bot.0;
let mut top = self.top.0;
down.reverse();
assert_eq!(down.first(), top.last());
top.extend(down.drain(1..));
let geom = LineString(top);
debug_assert!(geom.is_closed());
Polygon::new(geom, vec![])
}
}
impl<T: GeoNum> CoordinatePosition for MonoPoly<T> {
type Scalar = T;
fn calculate_coordinate_position(
&self,
coord: &Coord<Self::Scalar>,
is_inside: &mut bool,
boundary_count: &mut usize,
) {
if !self.bounds.intersects(coord) {
return;
}
let (top, bot) = if let Some(t) = self.bounding_segment(coord.x) {
t
} else {
return;
};
match <T as HasKernel>::Ker::orient2d(top.start, *coord, top.end) {
Orientation::Clockwise => return,
Orientation::Collinear => {
*is_inside = true;
*boundary_count += 1;
return;
}
_ => {}
}
match <T as HasKernel>::Ker::orient2d(bot.start, *coord, bot.end) {
Orientation::CounterClockwise => (),
Orientation::Collinear => {
*is_inside = true;
*boundary_count += 1;
}
_ => {
*is_inside = true;
}
}
}
}
impl<T: GeoNum> Intersects<Coord<T>> for MonoPoly<T> {
fn intersects(&self, other: &Coord<T>) -> bool {
self.coordinate_position(other) != CoordPos::Outside
}
}