leibniz 0.2.0

The package provides a differentiable vector graphics rasterization loss.
Documentation
//! Prediction layout.

use super::Command;

/// Layout for one predicted glyph.
#[derive(Clone, Debug)]
pub struct Layout {
    batch: usize,
    commands: Vec<Command>,
    contours: Vec<Range>,
}

/// Sequence range for one contour.
#[derive(Clone, Debug)]
pub struct Range {
    start: usize,
    end: usize,
}

impl Layout {
    /// Create a layout.
    ///
    /// # Panics
    ///
    /// Panics if any contour range is outside the command sequence.
    pub fn new(batch: usize, commands: Vec<Command>, contours: Vec<Range>) -> Self {
        assert!(
            contours.iter().all(|range| range.end() <= commands.len()),
            "contour ranges must be in bounds for the command sequence"
        );

        Self {
            batch,
            commands,
            contours,
        }
    }

    /// Batch item index.
    pub const fn batch(&self) -> usize {
        self.batch
    }

    /// Commands for a contour range.
    ///
    /// # Panics
    ///
    /// Panics if the range is outside the command sequence.
    pub fn commands(&self, range: &Range) -> &[Command] {
        &self.commands[range.start()..range.end()]
    }

    /// Contour ranges.
    pub fn contours(&self) -> &[Range] {
        &self.contours
    }
}

impl Range {
    /// Create a range.
    ///
    /// # Panics
    ///
    /// Panics if `start` is greater than `end`.
    pub const fn new(start: usize, end: usize) -> Self {
        assert!(start <= end, "range start must not exceed range end");

        Self { start, end }
    }

    /// End index.
    pub const fn end(&self) -> usize {
        self.end
    }

    /// Whether the range is empty.
    pub const fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Number of points in the range.
    pub const fn len(&self) -> usize {
        self.end - self.start
    }

    /// Start index.
    pub const fn start(&self) -> usize {
        self.start
    }
}

#[cfg(test)]
mod tests {
    use super::{Command, Layout, Range};

    #[test]
    #[should_panic]
    fn layout_rejects_out_of_bounds_commands() {
        let _ = Layout::new(0, vec![Command::Quadratic], vec![Range::new(0, 2)]);
    }

    #[test]
    fn layout_returns_contour_commands() {
        let layout = Layout::new(
            0,
            vec![Command::Linear, Command::Quadratic, Command::Linear],
            vec![Range::new(1, 3)],
        );

        assert_eq!(
            layout.commands(&Range::new(1, 3)),
            &[Command::Quadratic, Command::Linear]
        );
    }

    #[test]
    #[should_panic]
    fn range_rejects_reversed_bounds() {
        let _ = Range::new(5, 2);
    }

    #[test]
    fn range_returns_length() {
        assert_eq!(Range::new(2, 5).len(), 3);
    }
}