use crate::int::shape::IntContour;
use alloc::vec::Vec;
use i_float::int::number::int::IntNumber;
use i_float::int::number::wide_int::WideIntNumber;
use i_float::int::point::IntPoint;
use i_float::triangle::Triangle;
pub type IntPath<I> = Vec<IntPoint<I>>;
pub type IntPaths<I> = Vec<IntPath<I>>;
pub trait ContourExtension<I: IntNumber> {
fn unsafe_area(&self) -> I::Wide;
fn is_convex(&self) -> bool;
fn is_clockwise_ordered(&self) -> bool;
fn contains_point(&self, point: IntPoint<I>) -> bool;
fn to_reversed(&self) -> IntContour<I>;
}
impl<I: IntNumber> ContourExtension<I> for [IntPoint<I>] {
fn unsafe_area(&self) -> I::Wide {
let n = self.len();
let mut p0 = self[n - 1];
let mut area = I::Wide::ZERO;
for &p1 in self.iter() {
let a = p0.x.to_wide().wrapping_mul(p1.y.to_wide());
let b = p0.y.to_wide().wrapping_mul(p1.x.to_wide());
area = area.wrapping_add(a).wrapping_sub(b);
p0 = p1;
}
area
}
fn is_convex(&self) -> bool {
let n = self.len();
if n <= 2 {
return true;
}
let p0 = self[n - 2];
let mut p1 = self[n - 1];
let mut e0 = p1 - p0;
let mut sign = I::Wide::ZERO;
for &p2 in self.iter() {
let e1 = p2 - p1;
let cross = e1.cross_product(e0).signum();
if cross == I::Wide::ZERO {
let dot = e1.dot_product(e0);
if dot < I::Wide::ZERO {
return false;
}
} else if sign == I::Wide::ZERO {
sign = cross
} else if sign != cross {
return false;
}
e0 = e1;
p1 = p2;
}
true
}
#[inline(always)]
fn is_clockwise_ordered(&self) -> bool {
self.unsafe_area() <= I::Wide::ZERO
}
fn contains_point(&self, point: IntPoint<I>) -> bool {
let Some(&last) = self.last() else {
return false;
};
let mut is_contain = false;
let mut b = last;
for &a in self.iter() {
let is_in_range = (a.y > point.y) != (b.y > point.y);
if is_in_range {
let is_intersection_right = if a.y < b.y {
Triangle::is_clockwise(a, point, b)
} else {
Triangle::is_clockwise(b, point, a)
};
if is_intersection_right {
is_contain = !is_contain;
}
}
b = a;
}
is_contain
}
#[inline]
fn to_reversed(&self) -> IntContour<I> {
let mut contour = self.to_vec();
contour.reverse();
contour
}
}
#[cfg(test)]
mod tests {
use crate::int::IntPoint;
use crate::int::path::ContourExtension;
use crate::int_path;
use alloc::vec::Vec;
#[test]
fn test_0() {
let contour = int_path![
[-314572800, 209715200],
[-314572800, -209715200],
[-209715200, -314572800],
[209715200, -314572800],
[314572800, -209715200],
[314572800, 209715200],
[209715200, 314572800],
[-209715200, 314572800],
];
let area: i64 = contour.unsafe_area();
let abs_area = area.unsigned_abs() as usize >> 1;
assert!(area > 0);
assert!(abs_area > 1);
}
#[test]
fn empty_contour_contains_nothing() {
let contour = Vec::<IntPoint<i32>>::new();
assert!(!contour.contains_point(IntPoint::new(0, 0)));
}
#[test]
fn contains_point_uses_wide_intermediates() {
let contour = int_path![[0, 0], [100_000, 100_000], [200_000, 0]];
assert!(contour.contains_point(IntPoint::new(100_000, 50_000)));
assert!(!contour.contains_point(IntPoint::new(100_000, 150_000)));
}
#[test]
fn contains_point_does_not_round_edge_intersection() {
let contour = int_path![[0, 0], [2, 0], [1, 2]];
let reversed = contour.to_reversed();
assert!(!contour.contains_point(IntPoint::new(0, 1)));
assert!(!reversed.contains_point(IntPoint::new(0, 1)));
}
}