use crate::bounds::Bounds;
use crate::indexable::Indexable;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Predicate {
Intersects(Bounds),
Within(Bounds),
Contains(Bounds),
CoveredBy(Bounds),
Covers(Bounds),
Disjoint(Bounds),
Overlaps(Bounds),
}
impl Predicate {
#[must_use]
#[inline]
pub fn matches(&self, value: &Bounds) -> bool {
match self {
Predicate::Intersects(q) => q.intersects(value),
Predicate::Within(q) => value.within(q),
Predicate::Contains(q) => q.within(value),
Predicate::CoveredBy(q) => value.covered_by(q),
Predicate::Covers(q) => q.covered_by(value),
Predicate::Disjoint(q) => q.disjoint(value),
Predicate::Overlaps(q) => q.overlaps(value),
}
}
#[must_use]
#[inline]
pub fn could_match(&self, node: &Bounds) -> bool {
match self {
Predicate::Intersects(q) => q.intersects(node),
Predicate::Within(q) | Predicate::CoveredBy(q) | Predicate::Overlaps(q) => {
q.intersects(node)
}
Predicate::Contains(q) | Predicate::Covers(q) => node.contains(q),
Predicate::Disjoint(q) => !node.covered_by(q),
}
}
#[must_use]
#[inline]
pub fn covers_all(&self, node: &Bounds) -> bool {
match self {
Predicate::Intersects(q) | Predicate::CoveredBy(q) => q.contains(node),
Predicate::Disjoint(q) => q.disjoint(node),
Predicate::Within(_)
| Predicate::Contains(_)
| Predicate::Covers(_)
| Predicate::Overlaps(_) => false,
}
}
#[must_use]
#[inline]
pub fn and<P>(self, other: P) -> AndPredicate<Self, P> {
and(self, other)
}
}
impl core::ops::Not for Predicate {
type Output = NotPredicate<Self>;
#[inline]
fn not(self) -> Self::Output {
not(self)
}
}
pub trait QueryPredicate<T: Indexable> {
fn matches(&self, value: &T) -> bool;
fn could_match(&self, node: &Bounds) -> bool;
fn covers_all(&self, node: &Bounds) -> bool;
}
impl<T: Indexable> QueryPredicate<T> for Predicate {
#[inline]
fn matches(&self, value: &T) -> bool {
self.matches(&value.bounds())
}
#[inline]
fn could_match(&self, node: &Bounds) -> bool {
self.could_match(node)
}
#[inline]
fn covers_all(&self, node: &Bounds) -> bool {
self.covers_all(node)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AndPredicate<Left, Right> {
left: Left,
right: Right,
}
#[must_use]
#[inline]
pub fn and<Left, Right>(left: Left, right: Right) -> AndPredicate<Left, Right> {
AndPredicate { left, right }
}
impl<T, Left, Right> QueryPredicate<T> for AndPredicate<Left, Right>
where
T: Indexable,
Left: QueryPredicate<T>,
Right: QueryPredicate<T>,
{
#[inline]
fn matches(&self, value: &T) -> bool {
self.left.matches(value) && self.right.matches(value)
}
#[inline]
fn could_match(&self, node: &Bounds) -> bool {
self.left.could_match(node) && self.right.could_match(node)
}
#[inline]
fn covers_all(&self, node: &Bounds) -> bool {
self.left.covers_all(node) && self.right.covers_all(node)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NotPredicate<Inner> {
inner: Inner,
}
#[must_use]
#[inline]
pub fn not<Inner>(inner: Inner) -> NotPredicate<Inner> {
NotPredicate { inner }
}
impl<T, Inner> QueryPredicate<T> for NotPredicate<Inner>
where
T: Indexable,
Inner: QueryPredicate<T>,
{
#[inline]
fn matches(&self, value: &T) -> bool {
!self.inner.matches(value)
}
#[inline]
fn could_match(&self, node: &Bounds) -> bool {
!self.inner.covers_all(node)
}
#[inline]
fn covers_all(&self, node: &Bounds) -> bool {
!self.inner.could_match(node)
}
}
pub struct Satisfies<F> {
condition: F,
}
#[must_use]
#[inline]
pub fn satisfies<F>(condition: F) -> Satisfies<F> {
Satisfies { condition }
}
impl<T, F> QueryPredicate<T> for Satisfies<F>
where
T: Indexable,
F: Fn(&T) -> bool,
{
#[inline]
fn matches(&self, value: &T) -> bool {
(self.condition)(value)
}
#[inline]
fn could_match(&self, _node: &Bounds) -> bool {
true
}
#[inline]
fn covers_all(&self, _node: &Bounds) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::Predicate;
use crate::bounds::Bounds;
#[test]
fn intersects_matches_overlap() {
let p = Predicate::Intersects(Bounds::new([0.0, 0.0], [2.0, 2.0]));
assert!(p.matches(&Bounds::new([1.0, 1.0], [3.0, 3.0])));
assert!(!p.matches(&Bounds::new([5.0, 5.0], [6.0, 6.0])));
}
#[test]
fn within_matches_contained() {
let p = Predicate::Within(Bounds::new([0.0, 0.0], [10.0, 10.0]));
assert!(p.matches(&Bounds::new([2.0, 2.0], [3.0, 3.0])));
assert!(!p.matches(&Bounds::new([2.0, 2.0], [12.0, 3.0])));
}
#[test]
fn contains_matches_covering() {
let p = Predicate::Contains(Bounds::new([4.0, 4.0], [5.0, 5.0]));
assert!(p.matches(&Bounds::new([0.0, 0.0], [10.0, 10.0])));
assert!(!p.matches(&Bounds::new([0.0, 0.0], [4.5, 4.5])));
}
#[test]
fn pruning_skips_disjoint_subtrees() {
let p = Predicate::Intersects(Bounds::new([0.0, 0.0], [1.0, 1.0]));
assert!(p.could_match(&Bounds::new([0.5, 0.5], [9.0, 9.0])));
assert!(!p.could_match(&Bounds::new([5.0, 5.0], [9.0, 9.0])));
}
const QUERY: Bounds = Bounds::new([0.0, 0.0], [10.0, 10.0]);
const CONTAINED: Bounds = Bounds::new([2.0, 2.0], [3.0, 3.0]);
const OVERLAPPING: Bounds = Bounds::new([5.0, 5.0], [15.0, 15.0]);
const DISJOINT: Bounds = Bounds::new([20.0, 20.0], [30.0, 30.0]);
#[test]
fn covers_all_intersects() {
let p = Predicate::Intersects(QUERY);
assert!(p.covers_all(&CONTAINED));
assert!(p.covers_all(&QUERY));
assert!(!p.covers_all(&OVERLAPPING));
assert!(!p.covers_all(&DISJOINT));
}
#[test]
fn covers_all_within_never_assumes_value_dimension() {
let p = Predicate::Within(QUERY);
assert!(!p.covers_all(&CONTAINED));
assert!(!p.covers_all(&QUERY));
assert!(!p.covers_all(&OVERLAPPING));
assert!(!p.covers_all(&DISJOINT));
}
#[test]
fn covers_all_contains_never() {
let p = Predicate::Contains(QUERY);
assert!(!p.covers_all(&CONTAINED));
assert!(!p.covers_all(&QUERY));
assert!(!p.covers_all(&OVERLAPPING));
assert!(!p.covers_all(&DISJOINT));
}
}