vector-editor-core 0.4.0

Core structures for editing point based structures
Documentation
#![deny(missing_docs)]

/*!
Core structures for editing point-based structures.

This crate provides the fundamental building blocks for creating vector graphics editors.
It defines the core data structures such as `PointObject` and `Layer`, and provides traits
for configuring selection settings and behaviors.

## Features

- `PointObject`: A generic struct representing a collection of points with associated options.
- `Layer`: A struct representing a layer of `PointObject`s.
- `SelectSettings`: A trait for configuring selection settings for point objects.
- `Selectable`: A trait for selecting points within a `PointObject`.
*/

use data_stream::{
    FromStream, ToStream, collections::SizeSettings, default_settings::PortableSettings,
    from_stream, to_stream,
};
use inner_space::{InnerSpace, VectorSpace, distance};
use touch_selection::Selectable;

use std::{
    fs::File,
    io::{Read, Result, Write},
    ops::Sub,
    path::Path,
};

/// A struct representing a point object with associated points and options.
///
/// The `PointObject` struct is generic over the point type `P` and options type `O`.
#[derive(Clone, Debug)]
pub struct PointObject<P, O> {
    /// A vector of points of type `P`.
    pub points: Vec<P>,
    /// Options of type `O` associated with the point object.
    pub options: O,
}

impl<P, O> PointObject<P, O> {
    /// Creates a new `PointObject` with the given points and options.
    ///
    /// # Parameters
    /// - `points`: A vector of points of type `P`.
    /// - `options`: Options of type `O` for the point object.
    pub fn new(points: Vec<P>, options: O) -> Self {
        Self { points, options }
    }
}

impl<P, O> PointObject<P, O> {
    /// Saves the `PointObject` to a file at the specified path.
    ///
    /// # Parameters
    /// - `path`: The path to save the `PointObject` to.
    ///
    /// # Returns
    /// A `Result` indicating the success or failure of the save operation.
    pub fn save(&self, path: &Path) -> Result<()>
    where
        P: ToStream<PortableSettings>,
        O: ToStream<PortableSettings>,
    {
        let mut file = File::create(path)?;
        to_stream::<PortableSettings, _, _>(self, &mut file)
    }

    /// Loads a `PointObject` from a file at the specified path.
    ///
    /// # Parameters
    /// - `path`: The path to load the `PointObject` from.
    ///
    /// # Returns
    ////A `Result` containing the loaded `PointObject` or an error.
    pub fn load(path: &Path) -> Result<Self>
    where
        P: FromStream<PortableSettings>,
        O: FromStream<PortableSettings>,
    {
        let mut file = File::open(path)?;
        from_stream::<PortableSettings, _, _>(&mut file)
    }
}

impl<S: SizeSettings, P: ToStream<S>, O: ToStream<S>> ToStream<S> for PointObject<P, O> {
    fn to_stream<W: Write>(&self, stream: &mut W) -> Result<()> {
        to_stream::<S, _, _>(&self.points, stream)?;
        to_stream::<S, _, _>(&self.options, stream)
    }
}

impl<S: SizeSettings, P: FromStream<S>, O: FromStream<S>> FromStream<S> for PointObject<P, O> {
    fn from_stream<R: Read>(stream: &mut R) -> Result<Self> {
        Ok(Self {
            points: from_stream::<S, _, _>(stream)?,
            options: from_stream::<S, _, _>(stream)?,
        })
    }
}

/// A trait for configuring selection settings for point objects.
///
/// The `SelectSettings` trait is generic over the point type `P`.
pub trait SelectSettings<P: Sub>
where
    P::Output: InnerSpace,
{
    /// Returns the grab distance for selecting points.
    ///
    /// # Returns
    /// The grab distance as the scalar type of the point's vector space.
    fn grab_distance(&self) -> <P::Output as VectorSpace>::Scalar;

    /// Connects points during selection.
    ///
    /// # Parameters
    /// - `points`: A slice of points of type `P`.
    /// - `index`: The index of the currently selected point.
    /// - `selected`: A slice of selected point indices.
    fn connect(&mut self, _points: &[P], _index: usize, _selected: &[usize]) {}

    /// Performs special selection behavior.
    ///
    /// # Parameters
    /// - `points`: A slice of points of type `P`.
    /// - `index`: The index of the currently selected point.
    /// - `selected`: A slice of selected point indices.
    fn special(&mut self, _points: &[P], _index: usize, _selected: &[usize]) {}
}

impl<P: Copy + Sub, O: SelectSettings<P>> Selectable for PointObject<P, O>
where
    P::Output: InnerSpace,
{
    type Position = P;
    type Index = usize;

    fn add(&mut self, pos: P, index: Option<usize>) -> usize {
        let index = index.unwrap_or(self.points.len());
        self.points.push(pos);
        index
    }

    fn get(&self, pos: P) -> Option<usize> {
        let grab_distance = self.options.grab_distance();
        let mut found = None;
        for (point_index, point) in self.points.iter().enumerate() {
            let current_distance = distance(*point, pos);
            if current_distance < grab_distance
                && found.is_none_or(|(_, last_distance)| current_distance < last_distance)
            {
                found = Some((point_index, current_distance));
            }
        }

        found.map(|(index, _)| index)
    }

    fn connect(&mut self, index: usize, selected: &[usize]) {
        self.options.connect(&self.points, index, selected);
    }

    fn special(&mut self, index: usize, selected: &[usize]) {
        self.options.special(&self.points, index, selected);
    }
}

/// A struct representing a layer of point objects.
///
/// The `Layer` struct is generic over the point type `P` and options type `O`.
pub struct Layer<P, O> {
    /// A vector of `PointObject`s in the layer.
    pub objects: Vec<PointObject<P, O>>,
}

impl<P, O> Default for Layer<P, O> {
    fn default() -> Self {
        Self {
            objects: Vec::new(),
        }
    }
}

impl<P, O> Layer<P, O> {
    /// Creates a new empty `Layer`.
    pub fn new() -> Self {
        Self {
            objects: Vec::new(),
        }
    }
}

impl<S: SizeSettings, P: ToStream<S>, O: ToStream<S>> ToStream<S> for Layer<P, O> {
    fn to_stream<W: Write>(&self, stream: &mut W) -> Result<()> {
        to_stream::<S, _, _>(&self.objects, stream)
    }
}

impl<S: SizeSettings, P: FromStream<S>, O: FromStream<S>> FromStream<S> for Layer<P, O> {
    fn from_stream<R: Read>(stream: &mut R) -> Result<Self> {
        Ok(Self {
            objects: from_stream::<S, _, _>(stream)?,
        })
    }
}