devela 0.28.0

A development substrate of coherence.
Documentation
// devela::geom::metric::distance
//
//! Defines [`Region`][S][1|2|3], [`RegionStrided`].
//
// TOC
// - struct Region, type aliases
// - struct RegionStrided
// - implementations

use crate::{Extent, Position, Stride};

#[doc = crate::_tags!(geom)]
/// A [`Position`]ed [`Extent`].
#[doc = crate::_doc_meta!{location("geom/metric")}]
///
/// See also: [`Region1`], [`Region2`], [`Region3`],
/// [`RegionS`], [`RegionS1`], [`RegionS2`], [`RegionS3`].
#[must_use]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Region<P, E, const D: usize> {
    ///
    pub pos: Position<P, D>,
    ///
    pub ext: Extent<E, D>,
}

#[doc = crate::_tags!(geom)]
/// A 1-dimensional [`Region`].
#[doc = crate::_doc_meta!{location("geom/metric")}]
pub type Region1<P, E> = Region<P, E, 1>;

#[doc = crate::_tags!(geom)]
/// A 2-dimensional [`Region`].
#[doc = crate::_doc_meta!{location("geom/metric")}]
pub type Region2<P, E> = Region<P, E, 2>;

#[doc = crate::_tags!(geom)]
/// A 3-dimensional [`Region`].
#[doc = crate::_doc_meta!{location("geom/metric")}]
pub type Region3<P, E> = Region<P, E, 3>;

#[doc = crate::_tags!(geom)]
/// A [`Position`]ed [`Extent`] sharing the **S**ame type.
#[doc = crate::_doc_meta!{location("geom/metric")}]
pub type RegionS<T, const D: usize> = Region<T, T, D>;

#[doc = crate::_tags!(geom)]
/// A 1-dimensional [`RegionS`].
#[doc = crate::_doc_meta!{location("geom/metric")}]
pub type RegionS1<T> = RegionS<T, 1>;

#[doc = crate::_tags!(geom)]
/// A 2-dimensional [`RegionS`].
#[doc = crate::_doc_meta!{location("geom/metric")}]
pub type RegionS2<T> = RegionS<T, 2>;

#[doc = crate::_tags!(geom)]
/// A 3-dimensional [`RegionS`].
#[doc = crate::_doc_meta!{location("geom/metric")}]
pub type RegionS3<T> = RegionS<T, 3>;

#[doc = crate::_tags!(geom)]
/// A [`Stride`]d [`Region`] defining structured traversal.
#[doc = crate::_doc_meta!{location("geom/metric")}]
///
/// `RegionStrided` extends `Region` by adding a stride, allowing
/// structured access to subregions or non-contiguous patterns.
///
/// - Used in **grids, datasets, and memory layouts**.
/// - Supports **efficient structured stepping** (e.g. row-major iteration).
#[must_use]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RegionStrided<P, E, const D: usize> {
    /// The positioned extent.
    pub region: Region<P, E, D>,
    /// The step size per dimension.
    pub stride: Stride<E, D>,
}

/* impls: constructors */

#[rustfmt::skip]
impl<P, E, const D: usize> Region<P, E, D> {
    /// Returns a new `Region` from a `pos`ition and an `ext`ent.
    pub const fn new(pos: Position<P, D>, ext: Extent<E, D>) -> Self { Self { pos, ext } }

    /// Returns a new `Region` from values convertible into a position and an extent.
    pub fn from_parts<PI, EI>(pos: PI, ext: EI) -> Self
    where
        PI: Into<Position<P, D>>,
        EI: Into<Extent<E, D>>,
    {
        Self::new(pos.into(), ext.into())
    }
}
impl<P, E> From<((P, P), (E, E))> for Region<P, E, 2> {
    fn from(((x, y), (w, h)): ((P, P), (E, E))) -> Self {
        Self {
            pos: Position::from((x, y)),
            ext: Extent::from((w, h)),
        }
    }
}
impl<P, E> From<((P, P, P), (E, E, E))> for Region<P, E, 3> {
    fn from(((x, y, z), (w, h, d)): ((P, P, P), (E, E, E))) -> Self {
        Self {
            pos: Position::from((x, y, z)),
            ext: Extent::from((w, h, d)),
        }
    }
}
impl<P, E, const D: usize> From<(Position<P, D>, Extent<E, D>)> for Region<P, E, D> {
    fn from(from: (Position<P, D>, Extent<E, D>)) -> Self {
        Self::new(from.0, from.1)
    }
}
impl<P, E, const D: usize> From<(Extent<E, D>, Position<P, D>)> for Region<P, E, D> {
    fn from(from: (Extent<E, D>, Position<P, D>)) -> Self {
        Self::new(from.1, from.0)
    }
}

/* impls: accessors */

#[rustfmt::skip]
impl<P: Copy, E: Copy> Region2<P, E> {
    #[must_use]
    /// Returns a copy of the first position dimension `x`.
    pub const fn x(self) -> P { self.pos.x() }
    #[must_use]
    /// Returns a copy of the second position dimension `y`.
    pub const fn y(self) -> P { self.pos.y() }

    #[must_use]
    /// Returns a copy of the first extent dimension `w`idth.
    pub const fn w(self) -> E { self.ext.w() }
    #[must_use]
    /// Returns a copy of the second extent dimension `h`eight.
    pub const fn h(self) -> E { self.ext.h() }
}

#[rustfmt::skip]
impl<P: Copy, E: Copy> Region3<P, E> {
    #[must_use]
    /// Returns a copy of the first position dimension `x`.
    pub const fn x(self) -> P { self.pos.x() }
    #[must_use]
    /// Returns a copy of the second position dimension `y`.
    pub const fn y(self) -> P { self.pos.y() }
    #[must_use]
    /// Returns a copy of the third position dimension `z`.
    pub const fn z(self) -> P { self.pos.z() }

    #[must_use]
    /// Returns a copy of the first extent dimension `w`idth.
    pub const fn w(self) -> E { self.ext.w() }
    #[must_use]
    /// Returns a copy of the second extent dimension `h`eight.
    pub const fn h(self) -> E { self.ext.h() }
    #[must_use]
    /// Returns a copy of the third extent dimension `d`epth.
    pub const fn d(self) -> E { self.ext.d() }
}

/* impls: mapping */

impl<P, E, const D: usize> Region<P, E, D> {
    /// Returns a new region by applying `f` to each position component.
    pub fn map_pos<P2>(self, f: impl FnMut(P) -> P2) -> Region<P2, E, D> {
        Region::new(self.pos.map(f), self.ext)
    }

    /// Returns a new region by applying `f` to each extent component.
    pub fn map_ext<E2>(self, f: impl FnMut(E) -> E2) -> Region<P, E2, D> {
        Region::new(self.pos, self.ext.map(f))
    }

    /// Returns a new region by applying `fpos` to the position and `fext` to the extent.
    pub fn map<P2, E2>(
        self,
        fpos: impl FnMut(P) -> P2,
        fext: impl FnMut(E) -> E2,
    ) -> Region<P2, E2, D> {
        Region::new(self.pos.map(fpos), self.ext.map(fext))
    }

    /// Returns a new region by fallibly applying `f` to each position component.
    pub fn try_map_pos<P2, X>(
        self,
        f: impl FnMut(P) -> Result<P2, X>,
    ) -> Result<Region<P2, E, D>, X> {
        Ok(Region::new(self.pos.try_map(f)?, self.ext))
    }

    /// Returns a new region by fallibly applying `f` to each extent component.
    pub fn try_map_ext<E2, X>(
        self,
        f: impl FnMut(E) -> Result<E2, X>,
    ) -> Result<Region<P, E2, D>, X> {
        Ok(Region::new(self.pos, self.ext.try_map(f)?))
    }

    /// Returns a new region by fallibly applying `fpos` to the position and
    /// `fext` to the extent.
    ///
    /// Both conversions must use the same error type.
    pub fn try_map<P2, E2, X>(
        self,
        fpos: impl FnMut(P) -> Result<P2, X>,
        fext: impl FnMut(E) -> Result<E2, X>,
    ) -> Result<Region<P2, E2, D>, X> {
        Ok(Region::new(self.pos.try_map(fpos)?, self.ext.try_map(fext)?))
    }

    /// Converts the position and extent using `From`.
    pub fn map_into<P2, E2>(self) -> Region<P2, E2, D>
    where
        P2: From<P>,
        E2: From<E>,
    {
        self.map(P2::from, E2::from)
    }

    /// Converts the position and extent using `TryFrom`.
    ///
    /// Both conversions must use the same error type.
    pub fn try_map_into<P2, E2, X>(self) -> Result<Region<P2, E2, D>, X>
    where
        P2: TryFrom<P, Error = X>,
        E2: TryFrom<E, Error = X>,
    {
        self.try_map(P2::try_from, E2::try_from)
    }
}