use crate::core::fill_rule::FillRule;
use crate::core::overlay::ShapeType;
use crate::core::relate::PredicateOverlay;
use crate::core::solver::Solver;
use i_float::adapter::FloatPointAdapter;
use i_float::float::compatible::FloatPointCompatible;
use i_shape::source::resource::ShapeResource;
pub struct FloatPredicateOverlay<P: FloatPointCompatible> {
pub(crate) overlay: PredicateOverlay,
pub(crate) adapter: FloatPointAdapter<P>,
}
impl<P: FloatPointCompatible> FloatPredicateOverlay<P> {
#[inline]
pub fn with_adapter(adapter: FloatPointAdapter<P>, capacity: usize) -> Self {
Self {
overlay: PredicateOverlay::new(capacity),
adapter,
}
}
#[inline]
pub fn with_adapter_custom(
adapter: FloatPointAdapter<P>,
fill_rule: FillRule,
solver: Solver,
capacity: usize,
) -> Self {
let mut overlay = PredicateOverlay::new(capacity);
overlay.fill_rule = fill_rule;
overlay.solver = solver;
Self { overlay, adapter }
}
pub fn with_subj_and_clip<R0, R1>(subj: &R0, clip: &R1) -> Self
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
{
let iter = subj.iter_paths().chain(clip.iter_paths()).flatten();
let adapter = FloatPointAdapter::with_iter(iter);
let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len());
let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len());
let mut result = Self {
overlay: PredicateOverlay::new(subj_capacity + clip_capacity),
adapter,
};
result.add_source(subj, ShapeType::Subject);
result.add_source(clip, ShapeType::Clip);
result
}
pub fn with_subj_and_clip_custom<R0, R1>(
subj: &R0,
clip: &R1,
fill_rule: FillRule,
solver: Solver,
) -> Self
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
{
let iter = subj.iter_paths().chain(clip.iter_paths()).flatten();
let adapter = FloatPointAdapter::with_iter(iter);
let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len());
let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len());
let mut overlay = PredicateOverlay::new(subj_capacity + clip_capacity);
overlay.fill_rule = fill_rule;
overlay.solver = solver;
let mut result = Self { overlay, adapter };
result.add_source(subj, ShapeType::Subject);
result.add_source(clip, ShapeType::Clip);
result
}
pub fn add_source<R: ShapeResource<P> + ?Sized>(&mut self, resource: &R, shape_type: ShapeType) {
for contour in resource.iter_paths() {
self.overlay
.add_path_iter(contour.iter().map(|p| self.adapter.float_to_int(p)), shape_type);
}
}
#[inline]
pub fn clear(&mut self) {
self.overlay.clear();
}
#[inline]
pub fn intersects(&mut self) -> bool {
self.overlay.intersects()
}
#[inline]
pub fn interiors_intersect(&mut self) -> bool {
self.overlay.interiors_intersect()
}
#[inline]
pub fn touches(&mut self) -> bool {
self.overlay.touches()
}
#[inline]
pub fn point_intersects(&mut self) -> bool {
self.overlay.point_intersects()
}
#[inline]
pub fn within(&mut self) -> bool {
self.overlay.within()
}
}
pub trait FloatRelate<R1, P>
where
R1: ShapeResource<P> + ?Sized,
P: FloatPointCompatible,
{
fn intersects(&self, other: &R1) -> bool;
fn interiors_intersect(&self, other: &R1) -> bool;
fn touches(&self, other: &R1) -> bool;
fn point_intersects(&self, other: &R1) -> bool;
fn within(&self, other: &R1) -> bool;
fn disjoint(&self, other: &R1) -> bool;
fn covers(&self, other: &R1) -> bool;
}
impl<R0, R1, P> FloatRelate<R1, P> for R0
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
P: FloatPointCompatible,
{
#[inline]
fn intersects(&self, other: &R1) -> bool {
FloatPredicateOverlay::with_subj_and_clip(self, other).intersects()
}
#[inline]
fn interiors_intersect(&self, other: &R1) -> bool {
FloatPredicateOverlay::with_subj_and_clip(self, other).interiors_intersect()
}
#[inline]
fn touches(&self, other: &R1) -> bool {
FloatPredicateOverlay::with_subj_and_clip(self, other).touches()
}
#[inline]
fn point_intersects(&self, other: &R1) -> bool {
FloatPredicateOverlay::with_subj_and_clip(self, other).point_intersects()
}
#[inline]
fn within(&self, other: &R1) -> bool {
FloatPredicateOverlay::with_subj_and_clip(self, other).within()
}
#[inline]
fn disjoint(&self, other: &R1) -> bool {
!self.intersects(other)
}
#[inline]
fn covers(&self, other: &R1) -> bool {
other.within(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
use alloc::vec::Vec;
#[test]
fn test_intersects_overlapping() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(square.intersects(&other));
assert!(other.intersects(&square));
}
#[test]
fn test_intersects_disjoint() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[20.0, 20.0], [20.0, 30.0], [30.0, 30.0], [30.0, 20.0]];
assert!(!square.intersects(&other));
assert!(!other.intersects(&square));
}
#[test]
fn test_intersects_contained() {
let outer = vec![[0.0, 0.0], [0.0, 20.0], [20.0, 20.0], [20.0, 0.0]];
let inner = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(outer.intersects(&inner));
assert!(inner.intersects(&outer));
}
#[test]
fn test_intersects_touching_edge() {
let left = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let right = vec![[10.0, 0.0], [10.0, 10.0], [20.0, 10.0], [20.0, 0.0]];
assert!(left.intersects(&right));
assert!(right.intersects(&left));
}
#[test]
fn test_intersects_empty() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let empty: Vec<[f64; 2]> = vec![];
assert!(!square.intersects(&empty));
assert!(!empty.intersects(&square));
}
#[test]
fn test_disjoint() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[20.0, 20.0], [20.0, 30.0], [30.0, 30.0], [30.0, 20.0]];
assert!(square.disjoint(&other));
assert!(other.disjoint(&square));
let overlapping = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(!square.disjoint(&overlapping));
}
#[test]
fn test_interiors_intersect_overlapping() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(square.interiors_intersect(&other));
assert!(other.interiors_intersect(&square));
}
#[test]
fn test_interiors_intersect_touching_edge() {
let left = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let right = vec![[10.0, 0.0], [10.0, 10.0], [20.0, 10.0], [20.0, 0.0]];
assert!(!left.interiors_intersect(&right));
assert!(!right.interiors_intersect(&left));
}
#[test]
fn test_interiors_intersect_disjoint() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[20.0, 20.0], [20.0, 30.0], [30.0, 30.0], [30.0, 20.0]];
assert!(!square.interiors_intersect(&other));
}
#[test]
fn test_touches_edge_sharing() {
let left = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let right = vec![[10.0, 0.0], [10.0, 10.0], [20.0, 10.0], [20.0, 0.0]];
assert!(left.touches(&right));
assert!(right.touches(&left));
}
#[test]
fn test_touches_overlapping() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(!square.touches(&other));
assert!(!other.touches(&square));
}
#[test]
fn test_touches_disjoint() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[20.0, 20.0], [20.0, 30.0], [30.0, 30.0], [30.0, 20.0]];
assert!(!square.touches(&other));
}
#[test]
fn test_within_contained() {
let outer = vec![[0.0, 0.0], [0.0, 20.0], [20.0, 20.0], [20.0, 0.0]];
let inner = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(inner.within(&outer));
assert!(!outer.within(&inner));
}
#[test]
fn test_within_overlapping() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(!square.within(&other));
assert!(!other.within(&square));
}
#[test]
fn test_within_disjoint() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[20.0, 20.0], [20.0, 30.0], [30.0, 30.0], [30.0, 20.0]];
assert!(!square.within(&other));
assert!(!other.within(&square));
}
#[test]
fn test_within_empty() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let empty: Vec<[f64; 2]> = vec![];
assert!(!empty.within(&square));
assert!(!square.within(&empty));
}
#[test]
fn test_covers_contained() {
let outer = vec![[0.0, 0.0], [0.0, 20.0], [20.0, 20.0], [20.0, 0.0]];
let inner = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(outer.covers(&inner));
assert!(!inner.covers(&outer));
}
#[test]
fn test_covers_overlapping() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(!square.covers(&other));
assert!(!other.covers(&square));
}
#[test]
fn test_covers_empty() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let empty: Vec<[f64; 2]> = vec![];
assert!(!square.covers(&empty));
}
#[test]
fn test_predicate_consistency() {
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(square.intersects(&other));
assert!(!square.disjoint(&other));
assert!(square.interiors_intersect(&other));
assert!(!square.touches(&other));
assert!(!square.within(&other));
assert!(!square.covers(&other));
let distant = vec![[100.0, 100.0], [100.0, 110.0], [110.0, 110.0], [110.0, 100.0]];
assert!(!square.intersects(&distant));
assert!(square.disjoint(&distant));
assert!(!square.interiors_intersect(&distant));
assert!(!square.touches(&distant));
assert!(!square.within(&distant));
assert!(!square.covers(&distant));
let outer = vec![[0.0, 0.0], [0.0, 20.0], [20.0, 20.0], [20.0, 0.0]];
let inner = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(outer.intersects(&inner));
assert!(!outer.disjoint(&inner));
assert!(outer.interiors_intersect(&inner));
assert!(!outer.touches(&inner));
assert!(!outer.within(&inner));
assert!(outer.covers(&inner));
assert!(inner.intersects(&outer));
assert!(inner.interiors_intersect(&outer));
assert!(inner.within(&outer));
assert!(!inner.covers(&outer));
let left = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let right = vec![[10.0, 0.0], [10.0, 10.0], [20.0, 10.0], [20.0, 0.0]];
assert!(left.intersects(&right));
assert!(!left.disjoint(&right));
assert!(!left.interiors_intersect(&right));
assert!(left.touches(&right));
assert!(!left.within(&right));
assert!(!left.covers(&right));
}
#[test]
fn test_predicate_overlay_with_adapter() {
use crate::core::overlay::ShapeType;
use i_float::adapter::FloatPointAdapter;
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
let iter = square.iter().chain(other.iter());
let adapter = FloatPointAdapter::with_iter(iter);
let mut overlay = FloatPredicateOverlay::with_adapter(adapter, 16);
overlay.add_source(&square, ShapeType::Subject);
overlay.add_source(&other, ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_predicate_overlay_with_adapter_custom() {
use crate::core::fill_rule::FillRule;
use crate::core::overlay::ShapeType;
use crate::core::solver::Solver;
use i_float::adapter::FloatPointAdapter;
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
let iter = square.iter().chain(other.iter());
let adapter = FloatPointAdapter::with_iter(iter);
let mut overlay =
FloatPredicateOverlay::with_adapter_custom(adapter, FillRule::NonZero, Solver::default(), 16);
overlay.add_source(&square, ShapeType::Subject);
overlay.add_source(&other, ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_predicate_overlay_with_subj_and_clip_custom() {
use crate::core::fill_rule::FillRule;
use crate::core::solver::Solver;
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
let mut overlay = FloatPredicateOverlay::with_subj_and_clip_custom(
&square,
&other,
FillRule::NonZero,
Solver::default(),
);
assert!(overlay.intersects());
}
#[test]
fn test_predicate_overlay_clear() {
use crate::core::overlay::ShapeType;
let square = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
let mut overlay = FloatPredicateOverlay::with_subj_and_clip(&square, &other);
assert!(overlay.intersects());
overlay.clear();
let touching = vec![[10.0, 0.0], [10.0, 10.0], [15.0, 10.0], [15.0, 0.0]];
overlay.add_source(&square, ShapeType::Subject);
overlay.add_source(&touching, ShapeType::Clip);
assert!(overlay.intersects());
}
#[test]
fn test_predicate_overlay_all_predicates() {
let outer = vec![[0.0, 0.0], [0.0, 20.0], [20.0, 20.0], [20.0, 0.0]];
let inner = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
let mut overlay = FloatPredicateOverlay::with_subj_and_clip(&inner, &outer);
assert!(overlay.intersects());
overlay.clear();
overlay.add_source(&inner, crate::core::overlay::ShapeType::Subject);
overlay.add_source(&outer, crate::core::overlay::ShapeType::Clip);
assert!(overlay.interiors_intersect());
overlay.clear();
overlay.add_source(&inner, crate::core::overlay::ShapeType::Subject);
overlay.add_source(&outer, crate::core::overlay::ShapeType::Clip);
assert!(!overlay.touches());
overlay.clear();
overlay.add_source(&inner, crate::core::overlay::ShapeType::Subject);
overlay.add_source(&outer, crate::core::overlay::ShapeType::Clip);
assert!(overlay.within());
}
#[test]
fn test_point_intersects_trait() {
let square1 = vec![[0.0, 0.0], [0.0, 10.0], [10.0, 10.0], [10.0, 0.0]];
let square2 = vec![[10.0, 10.0], [10.0, 20.0], [20.0, 20.0], [20.0, 10.0]];
assert!(square1.point_intersects(&square2));
assert!(square2.point_intersects(&square1));
let square3 = vec![[10.0, 0.0], [10.0, 10.0], [20.0, 10.0], [20.0, 0.0]];
assert!(
!square1.point_intersects(&square3),
"edge sharing is not point-only"
);
assert!(square1.touches(&square3));
let square4 = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
assert!(
!square1.point_intersects(&square4),
"overlapping is not point-only"
);
let square5 = vec![[100.0, 100.0], [100.0, 110.0], [110.0, 110.0], [110.0, 100.0]];
assert!(
!square1.point_intersects(&square5),
"disjoint has no point intersection"
);
}
}