use crate::float::curve::builder::CurveError;
use crate::float::curve::path::CurvePath;
use crate::float::curve::path::finite_rect;
use alloc::vec::Vec;
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::i_float::float::rect::FloatRect;
#[derive(Clone, PartialEq)]
pub struct CurveShape<P: FloatPointCompatible> {
pub(crate) contours: Vec<CurvePath<P>>,
}
impl<P> core::fmt::Debug for CurveShape<P>
where
P: FloatPointCompatible + core::fmt::Debug,
P::Scalar: core::fmt::Debug,
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("CurveShape")
.field("contours", &self.contours)
.finish()
}
}
impl<P: FloatPointCompatible> CurveShape<P> {
pub fn try_new(contours: Vec<CurvePath<P>>) -> Result<Self, CurveError> {
Self::validate_contours(&contours)?;
Ok(Self { contours })
}
pub(crate) fn from_validated_contours(contours: Vec<CurvePath<P>>) -> Self {
debug_assert!(Self::validate_contours(&contours).is_ok());
Self { contours }
}
#[inline]
pub fn from_path(path: CurvePath<P>) -> Self {
Self {
contours: alloc::vec![path],
}
}
#[inline]
pub fn contours(&self) -> &[CurvePath<P>] {
&self.contours
}
#[inline]
pub fn iter(&self) -> core::slice::Iter<'_, CurvePath<P>> {
self.contours.iter()
}
#[inline]
#[allow(
clippy::len_without_is_empty,
reason = "a validated curve shape is never empty"
)]
pub fn len(&self) -> usize {
self.contours.len()
}
#[inline]
pub fn into_contours(self) -> Vec<CurvePath<P>> {
self.contours
}
pub fn segment_count(&self) -> usize {
self.contours.iter().map(|path| path.segments.len()).sum()
}
pub(crate) fn validate_contours(contours: &[CurvePath<P>]) -> Result<(), CurveError> {
if contours.is_empty() {
return Err(CurveError::NoContours);
}
for contour in contours {
contour.validate()?;
}
let bounds = contours
.iter()
.map(CurvePath::bounds)
.reduce(FloatRect::with_rects)
.unwrap_or_else(FloatRect::zero);
if !finite_rect(&bounds) {
return Err(CurveError::NonFiniteBounds);
}
Ok(())
}
}
impl<P: FloatPointCompatible> From<CurvePath<P>> for CurveShape<P> {
#[inline]
fn from(path: CurvePath<P>) -> Self {
Self::from_path(path)
}
}
impl<P: FloatPointCompatible> TryFrom<Vec<CurvePath<P>>> for CurveShape<P> {
type Error = CurveError;
#[inline]
fn try_from(contours: Vec<CurvePath<P>>) -> Result<Self, Self::Error> {
Self::try_new(contours)
}
}
impl<P: FloatPointCompatible> AsRef<[CurvePath<P>]> for CurveShape<P> {
#[inline]
fn as_ref(&self) -> &[CurvePath<P>] {
&self.contours
}
}
impl<P: FloatPointCompatible> IntoIterator for CurveShape<P> {
type Item = CurvePath<P>;
type IntoIter = alloc::vec::IntoIter<CurvePath<P>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.contours.into_iter()
}
}
impl<'a, P: FloatPointCompatible> IntoIterator for &'a CurveShape<P> {
type Item = &'a CurvePath<P>;
type IntoIter = core::slice::Iter<'a, CurvePath<P>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.contours.iter()
}
}