use crate::core::fill_rule::FillRule;
use crate::core::overlay::ShapeType;
use crate::core::overlay_rule::OverlayRule;
use crate::core::solver::Solver;
use crate::float::overlay::{FloatOverlay, OverlayOptions};
use crate::float::relate::FloatPredicateOverlay;
use i_float::adapter::FloatPointAdapter;
use i_float::float::compatible::FloatPointCompatible;
use i_float::float::number::FloatNumber;
use i_shape::base::data::Shapes;
use i_shape::source::resource::ShapeResource;
#[derive(Debug, Clone, Copy)]
pub enum FixedScaleOverlayError {
ScaleTooLarge,
ScaleNonPositive,
ScaleNotFinite,
}
impl FixedScaleOverlayError {
#[inline]
pub fn validate_scale<T: FloatNumber>(scale: T) -> Result<f64, Self> {
let s = scale.to_f64();
if !s.is_finite() {
return Err(Self::ScaleNotFinite);
}
if s <= 0.0 {
return Err(Self::ScaleNonPositive);
}
Ok(s)
}
}
pub trait FixedScaleFloatOverlay<R0, R1, P>
where
R0: ShapeResource<P>,
R1: ShapeResource<P>,
P: FloatPointCompatible,
{
fn overlay_with_fixed_scale(
&self,
source: &R1,
overlay_rule: OverlayRule,
fill_rule: FillRule,
scale: P::Scalar,
) -> Result<Shapes<P>, FixedScaleOverlayError>;
}
impl<R0, R1, P> FixedScaleFloatOverlay<R0, R1, P> for R0
where
R0: ShapeResource<P>,
R1: ShapeResource<P>,
P: FloatPointCompatible,
{
#[inline]
fn overlay_with_fixed_scale(
&self,
source: &R1,
overlay_rule: OverlayRule,
fill_rule: FillRule,
scale: P::Scalar,
) -> Result<Shapes<P>, FixedScaleOverlayError> {
Ok(FloatOverlay::with_subj_and_clip_fixed_scale(self, source, scale)?
.overlay(overlay_rule, fill_rule))
}
}
impl<P: FloatPointCompatible> FloatOverlay<P> {
pub fn with_subj_and_clip_fixed_scale<R0, R1>(
subj: &R0,
clip: &R1,
scale: P::Scalar,
) -> Result<Self, FixedScaleOverlayError>
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
{
let s = FixedScaleOverlayError::validate_scale(scale)?;
let iter = subj.iter_paths().chain(clip.iter_paths()).flatten();
let mut adapter = FloatPointAdapter::with_iter(iter);
if adapter.dir_scale < scale {
return Err(FixedScaleOverlayError::ScaleTooLarge);
}
adapter.dir_scale = scale;
adapter.inv_scale = P::Scalar::from_float(1.0 / s);
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());
Ok(Self::with_adapter(adapter, subj_capacity + clip_capacity)
.unsafe_add_source(subj, ShapeType::Subject)
.unsafe_add_source(clip, ShapeType::Clip))
}
pub fn with_subj_and_clip_fixed_scale_custom<R0, R1>(
subj: &R0,
clip: &R1,
options: OverlayOptions<P::Scalar>,
solver: Solver,
scale: P::Scalar,
) -> Result<Self, FixedScaleOverlayError>
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
{
let s = FixedScaleOverlayError::validate_scale(scale)?;
let iter = subj.iter_paths().chain(clip.iter_paths()).flatten();
let mut adapter = FloatPointAdapter::with_iter(iter);
if adapter.dir_scale < scale {
return Err(FixedScaleOverlayError::ScaleTooLarge);
}
adapter.dir_scale = scale;
adapter.inv_scale = P::Scalar::from_float(1.0 / s);
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());
Ok(
Self::new_custom(adapter, options, solver, subj_capacity + clip_capacity)
.unsafe_add_source(subj, ShapeType::Subject)
.unsafe_add_source(clip, ShapeType::Clip),
)
}
}
impl<P: FloatPointCompatible> FloatPredicateOverlay<P> {
pub fn with_subj_and_clip_fixed_scale<R0, R1>(
subj: &R0,
clip: &R1,
scale: P::Scalar,
) -> Result<Self, FixedScaleOverlayError>
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
{
let s = FixedScaleOverlayError::validate_scale(scale)?;
let iter = subj.iter_paths().chain(clip.iter_paths()).flatten();
let mut adapter = FloatPointAdapter::with_iter(iter);
if adapter.dir_scale < scale {
return Err(FixedScaleOverlayError::ScaleTooLarge);
}
adapter.dir_scale = scale;
adapter.inv_scale = P::Scalar::from_float(1.0 / s);
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::with_adapter(adapter, subj_capacity + clip_capacity);
result.add_source(subj, ShapeType::Subject);
result.add_source(clip, ShapeType::Clip);
Ok(result)
}
pub fn with_subj_and_clip_fixed_scale_custom<R0, R1>(
subj: &R0,
clip: &R1,
fill_rule: FillRule,
solver: Solver,
scale: P::Scalar,
) -> Result<Self, FixedScaleOverlayError>
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
{
let s = FixedScaleOverlayError::validate_scale(scale)?;
let iter = subj.iter_paths().chain(clip.iter_paths()).flatten();
let mut adapter = FloatPointAdapter::with_iter(iter);
if adapter.dir_scale < scale {
return Err(FixedScaleOverlayError::ScaleTooLarge);
}
adapter.dir_scale = scale;
adapter.inv_scale = P::Scalar::from_float(1.0 / s);
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::with_adapter_custom(adapter, fill_rule, solver, subj_capacity + clip_capacity);
result.add_source(subj, ShapeType::Subject);
result.add_source(clip, ShapeType::Clip);
Ok(result)
}
}
pub trait FixedScaleFloatRelate<R1, P>
where
R1: ShapeResource<P> + ?Sized,
P: FloatPointCompatible,
{
fn intersects_with_fixed_scale(
&self,
other: &R1,
scale: P::Scalar,
) -> Result<bool, FixedScaleOverlayError>;
fn interiors_intersect_with_fixed_scale(
&self,
other: &R1,
scale: P::Scalar,
) -> Result<bool, FixedScaleOverlayError>;
fn touches_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result<bool, FixedScaleOverlayError>;
fn within_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result<bool, FixedScaleOverlayError>;
fn disjoint_with_fixed_scale(&self, other: &R1, scale: P::Scalar)
-> Result<bool, FixedScaleOverlayError>;
fn covers_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result<bool, FixedScaleOverlayError>;
}
impl<R0, R1, P> FixedScaleFloatRelate<R1, P> for R0
where
R0: ShapeResource<P> + ?Sized,
R1: ShapeResource<P> + ?Sized,
P: FloatPointCompatible,
{
#[inline]
fn intersects_with_fixed_scale(
&self,
other: &R1,
scale: P::Scalar,
) -> Result<bool, FixedScaleOverlayError> {
Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects())
}
#[inline]
fn interiors_intersect_with_fixed_scale(
&self,
other: &R1,
scale: P::Scalar,
) -> Result<bool, FixedScaleOverlayError> {
Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.interiors_intersect())
}
#[inline]
fn touches_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result<bool, FixedScaleOverlayError> {
Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.touches())
}
#[inline]
fn within_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result<bool, FixedScaleOverlayError> {
Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.within())
}
#[inline]
fn disjoint_with_fixed_scale(
&self,
other: &R1,
scale: P::Scalar,
) -> Result<bool, FixedScaleOverlayError> {
Ok(!FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects())
}
#[inline]
fn covers_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result<bool, FixedScaleOverlayError> {
Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(other, self, scale)?.within())
}
}
#[cfg(test)]
mod tests {
use crate::core::fill_rule::FillRule;
use crate::core::overlay_rule::OverlayRule;
use crate::float::overlay::FloatOverlay;
use crate::float::relate::FloatPredicateOverlay;
use crate::float::scale::{FixedScaleFloatOverlay, FixedScaleFloatRelate};
use alloc::vec;
#[test]
fn test_contour() {
let left_rect = vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
let right_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
let shapes = left_rect
.overlay_with_fixed_scale(&right_rect, OverlayRule::Union, FillRule::EvenOdd, 10.0)
.unwrap();
assert_eq!(shapes.len(), 1);
assert_eq!(shapes[0].len(), 1);
assert_eq!(shapes[0][0].len(), 4);
}
#[test]
fn test_contours() {
let r3 = vec![
vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]],
vec![[0.0, 1.0], [0.0, 2.0], [1.0, 2.0], [1.0, 1.0]],
vec![[1.0, 1.0], [1.0, 2.0], [2.0, 2.0], [2.0, 1.0]],
];
let right_bottom_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
let shapes = FloatOverlay::with_subj_and_clip_fixed_scale(&r3, &right_bottom_rect, 10.0)
.unwrap()
.overlay(OverlayRule::Union, FillRule::EvenOdd);
assert_eq!(shapes.len(), 1);
assert_eq!(shapes[0].len(), 1);
assert_eq!(shapes[0][0].len(), 4);
}
#[test]
fn test_shapes() {
let shapes = vec![vec![
vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]],
vec![[0.0, 1.0], [0.0, 2.0], [1.0, 2.0], [1.0, 1.0]],
vec![[1.0, 1.0], [1.0, 2.0], [2.0, 2.0], [2.0, 1.0]],
]];
let right_bottom_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
let shapes = FloatOverlay::with_subj_and_clip_fixed_scale(&shapes, &right_bottom_rect, 10.0)
.unwrap()
.overlay(OverlayRule::Union, FillRule::EvenOdd);
assert_eq!(shapes.len(), 1);
assert_eq!(shapes[0].len(), 1);
assert_eq!(shapes[0][0].len(), 4);
}
#[test]
fn test_fail() {
let shapes = vec![vec![
vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]],
vec![[0.0, 1.0], [0.0, 2.0], [1.0, 2.0], [1.0, 1.0]],
vec![[1.0, 1.0], [1.0, 2.0], [2.0, 2.0], [2.0, 1.0]],
]];
let right_bottom_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
let scale = (1u64 << 32) as f64;
let result = FloatOverlay::with_subj_and_clip_fixed_scale(&shapes, &right_bottom_rect, scale);
assert!(!result.is_ok());
}
#[test]
fn test_invalid_scale() {
let left_rect = vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
let right_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
assert!(FloatOverlay::with_subj_and_clip_fixed_scale(&left_rect, &right_rect, -1.0).is_err());
assert!(FloatOverlay::with_subj_and_clip_fixed_scale(&left_rect, &right_rect, 0.0).is_err());
assert!(FloatOverlay::with_subj_and_clip_fixed_scale(&left_rect, &right_rect, f64::NAN).is_err());
assert!(
FloatOverlay::with_subj_and_clip_fixed_scale(&left_rect, &right_rect, f64::INFINITY).is_err()
);
}
#[test]
fn test_intersects_with_fixed_scale() {
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 result = square.intersects_with_fixed_scale(&other, 1000.0);
assert!(result.unwrap());
}
#[test]
fn test_intersects_with_fixed_scale_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]];
let result = square.intersects_with_fixed_scale(&other, 1000.0);
assert!(!result.unwrap());
}
#[test]
fn test_interiors_intersect_with_fixed_scale() {
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 result = square.interiors_intersect_with_fixed_scale(&other, 1000.0);
assert!(result.unwrap());
}
#[test]
fn test_touches_with_fixed_scale() {
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]];
let result = left.touches_with_fixed_scale(&right, 1000.0);
assert!(result.unwrap());
}
#[test]
fn test_within_with_fixed_scale() {
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 result = inner.within_with_fixed_scale(&outer, 1000.0);
assert!(result.unwrap());
}
#[test]
fn test_predicate_overlay_with_fixed_scale() {
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_fixed_scale(&square, &other, 1000.0).unwrap();
assert!(overlay.intersects());
}
#[test]
fn test_predicate_fixed_scale_invalid() {
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!(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(&square, &other, -1.0).is_err());
assert!(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(&square, &other, 0.0).is_err());
assert!(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(&square, &other, f64::NAN).is_err());
assert!(
FloatPredicateOverlay::with_subj_and_clip_fixed_scale(&square, &other, f64::INFINITY).is_err()
);
}
#[test]
fn test_disjoint_with_fixed_scale() {
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]];
let result = square.disjoint_with_fixed_scale(&other, 1000.0);
assert!(result.unwrap());
let overlapping = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]];
let result = square.disjoint_with_fixed_scale(&overlapping, 1000.0);
assert!(!result.unwrap());
}
#[test]
fn test_covers_with_fixed_scale() {
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 result = outer.covers_with_fixed_scale(&inner, 1000.0);
assert!(result.unwrap());
let result = inner.covers_with_fixed_scale(&outer, 1000.0);
assert!(!result.unwrap());
}
#[test]
fn test_fixed_scale_custom_overlay() {
use crate::core::solver::Solver;
let left_rect = vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
let right_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
let shapes = FloatOverlay::with_subj_and_clip_fixed_scale_custom(
&left_rect,
&right_rect,
Default::default(),
Solver::default(),
10.0,
)
.unwrap()
.overlay(OverlayRule::Union, FillRule::EvenOdd);
assert_eq!(shapes.len(), 1);
assert_eq!(shapes[0].len(), 1);
assert_eq!(shapes[0][0].len(), 4);
}
#[test]
fn test_fixed_scale_custom_overlay_invalid() {
use crate::core::solver::Solver;
let left_rect = vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
let right_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
let result = FloatOverlay::with_subj_and_clip_fixed_scale_custom(
&left_rect,
&right_rect,
Default::default(),
Solver::default(),
-1.0,
);
assert!(result.is_err());
}
#[test]
fn test_fixed_scale_custom_overlay_scale_too_large() {
use crate::core::solver::Solver;
let left_rect = vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
let right_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]];
let scale = (1u64 << 32) as f64;
let result = FloatOverlay::with_subj_and_clip_fixed_scale_custom(
&left_rect,
&right_rect,
Default::default(),
Solver::default(),
scale,
);
assert!(result.is_err());
}
#[test]
fn test_predicate_overlay_with_fixed_scale_custom() {
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_fixed_scale_custom(
&square,
&other,
FillRule::NonZero,
Solver::default(),
1000.0,
)
.unwrap();
assert!(overlay.intersects());
}
#[test]
fn test_predicate_overlay_with_fixed_scale_custom_invalid() {
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 result = FloatPredicateOverlay::with_subj_and_clip_fixed_scale_custom(
&square,
&other,
FillRule::NonZero,
Solver::default(),
-1.0,
);
assert!(result.is_err());
let scale = (1u64 << 32) as f64;
let result = FloatPredicateOverlay::with_subj_and_clip_fixed_scale_custom(
&square,
&other,
FillRule::NonZero,
Solver::default(),
scale,
);
assert!(result.is_err());
}
#[test]
fn test_fixed_scale_relate_invalid_scale() {
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_with_fixed_scale(&other, -1.0).is_err());
assert!(square.interiors_intersect_with_fixed_scale(&other, -1.0).is_err());
assert!(square.touches_with_fixed_scale(&other, -1.0).is_err());
assert!(square.within_with_fixed_scale(&other, -1.0).is_err());
assert!(square.disjoint_with_fixed_scale(&other, -1.0).is_err());
assert!(square.covers_with_fixed_scale(&other, -1.0).is_err());
}
}