i_overlay 8.1.0

Boolean Operations for 2D Polygons: Supports intersection, union, difference, xor, and self-intersections for all polygon varieties.
Documentation
use crate::mesh::variable_stroke::style::StrokeVertex;
use alloc::vec::Vec;
use i_float::float::compatible::FloatPointCompatible;

pub trait VariableStrokeSource<P>
where
    P: FloatPointCompatible,
{
    type ResourceIter<'a>: Iterator<Item = &'a [StrokeVertex<P>]>
    where
        P: 'a,
        Self: 'a;

    fn iter_variable_paths(&self) -> Self::ResourceIter<'_>;
}

pub struct ContourResourceIterator<'a, P: FloatPointCompatible> {
    slice: &'a [StrokeVertex<P>],
    finished: bool,
}

impl<'a, P: FloatPointCompatible> ContourResourceIterator<'a, P> {
    #[inline]
    fn with_slice(slice: &'a [StrokeVertex<P>]) -> Self {
        Self {
            slice,
            finished: false,
        }
    }
}

impl<'a, P: FloatPointCompatible> Iterator for ContourResourceIterator<'a, P> {
    type Item = &'a [StrokeVertex<P>];

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.finished {
            return None;
        }
        self.finished = true;
        Some(self.slice)
    }

    #[inline]
    fn count(self) -> usize {
        1
    }
}

impl<P: FloatPointCompatible> VariableStrokeSource<P> for [StrokeVertex<P>] {
    type ResourceIter<'a>
        = ContourResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'_> {
        ContourResourceIterator::with_slice(self)
    }
}

impl<P: FloatPointCompatible, const N: usize> VariableStrokeSource<P> for [StrokeVertex<P>; N] {
    type ResourceIter<'a>
        = ContourResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'_> {
        ContourResourceIterator::with_slice(self)
    }
}

impl<P: FloatPointCompatible> VariableStrokeSource<P> for Vec<StrokeVertex<P>> {
    type ResourceIter<'a>
        = ContourResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'_> {
        ContourResourceIterator::with_slice(self.as_slice())
    }
}

impl<'b, P: FloatPointCompatible> VariableStrokeSource<P> for &'b [StrokeVertex<P>] {
    type ResourceIter<'a>
        = ContourResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'b> {
        ContourResourceIterator::with_slice(self)
    }
}

pub struct ShapeResourceIterator<'a, P: FloatPointCompatible> {
    slice: &'a [Vec<StrokeVertex<P>>],
    index: usize,
}

impl<'a, P: FloatPointCompatible> Iterator for ShapeResourceIterator<'a, P> {
    type Item = &'a [StrokeVertex<P>];

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        let path = self.slice.get(self.index)?;
        self.index += 1;
        Some(path.as_slice())
    }

    #[inline]
    fn count(self) -> usize {
        self.slice.len()
    }
}

impl<P: FloatPointCompatible> VariableStrokeSource<P> for [Vec<StrokeVertex<P>>] {
    type ResourceIter<'a>
        = ShapeResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'_> {
        ShapeResourceIterator {
            slice: self,
            index: 0,
        }
    }
}

impl<P: FloatPointCompatible, const N: usize> VariableStrokeSource<P> for [Vec<StrokeVertex<P>>; N] {
    type ResourceIter<'a>
        = ShapeResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'_> {
        ShapeResourceIterator {
            slice: self,
            index: 0,
        }
    }
}

impl<P: FloatPointCompatible> VariableStrokeSource<P> for Vec<Vec<StrokeVertex<P>>> {
    type ResourceIter<'a>
        = ShapeResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'_> {
        ShapeResourceIterator {
            slice: self.as_slice(),
            index: 0,
        }
    }
}

impl<'b, P: FloatPointCompatible> VariableStrokeSource<P> for &'b [Vec<StrokeVertex<P>>] {
    type ResourceIter<'a>
        = ShapeResourceIterator<'a, P>
    where
        P: 'a,
        Self: 'a;

    #[inline]
    fn iter_variable_paths(&self) -> Self::ResourceIter<'b> {
        ShapeResourceIterator {
            slice: self,
            index: 0,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::VariableStrokeSource;
    use crate::mesh::variable_stroke::StrokeVertex;
    use alloc::vec;
    use alloc::vec::Vec;

    type Vertex = StrokeVertex<[f64; 2]>;

    fn path(y: f64) -> Vec<Vertex> {
        vec![
            StrokeVertex::new([0.0, y], 2.0),
            StrokeVertex::new([10.0, y], 4.0),
        ]
    }

    #[test]
    fn contour_resource_forms_yield_exactly_one_path() {
        let array = [
            StrokeVertex::new([0.0, 0.0], 2.0),
            StrokeVertex::new([10.0, 0.0], 4.0),
        ];
        let slice: &[Vertex] = &array;
        let owned = array.to_vec();

        assert_eq!(
            <[Vertex; 2] as VariableStrokeSource<_>>::iter_variable_paths(&array).count(),
            1
        );
        assert_eq!(
            <Vec<Vertex> as VariableStrokeSource<_>>::iter_variable_paths(&owned).count(),
            1
        );

        let mut slice_iter = <[Vertex] as VariableStrokeSource<_>>::iter_variable_paths(slice);
        assert_eq!(slice_iter.next().unwrap().len(), 2);
        assert!(slice_iter.next().is_none());

        let mut reference_iter = <&[Vertex] as VariableStrokeSource<_>>::iter_variable_paths(&slice);
        assert_eq!(reference_iter.next().unwrap()[1].point, [10.0, 0.0]);
        assert!(reference_iter.next().is_none());
    }

    #[test]
    fn shape_resource_forms_preserve_path_order_and_count() {
        let array = [path(0.0), path(10.0)];
        let slice: &[Vec<Vertex>] = &array;
        let owned = array.to_vec();

        assert_eq!(
            <[Vec<Vertex>; 2] as VariableStrokeSource<_>>::iter_variable_paths(&array).count(),
            2
        );
        assert_eq!(
            <[Vec<Vertex>] as VariableStrokeSource<_>>::iter_variable_paths(slice).count(),
            2
        );
        assert_eq!(
            <Vec<Vec<Vertex>> as VariableStrokeSource<_>>::iter_variable_paths(&owned).count(),
            2
        );

        let mut reference_iter = <&[Vec<Vertex>] as VariableStrokeSource<_>>::iter_variable_paths(&slice);
        assert_eq!(reference_iter.next().unwrap()[0].point, [0.0, 0.0]);
        assert_eq!(reference_iter.next().unwrap()[0].point, [0.0, 10.0]);
        assert!(reference_iter.next().is_none());
    }
}