use crate::Layer;
use inner_space::{InnerSpace, VectorSpace};
use std::ops::Sub;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PointIndex {
pub object: usize,
pub point: usize,
}
#[derive(Default)]
pub struct LayerEditor {
pub snapping: bool,
editing: Option<PointIndex>,
}
impl LayerEditor {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub const fn editing(&self) -> Option<PointIndex> {
self.editing
}
pub const fn toggle_snapping(&mut self) {
self.snapping = !self.snapping;
}
pub const fn finish(&mut self) {
self.editing = None;
}
pub fn update<P: Copy, O>(&self, layer: &mut Layer<P, O>, position: P) {
let Some(index) = self.editing else {
return;
};
layer.objects[index.object].points[index.point] = position;
}
pub fn select<P, O>(
&mut self,
layer: &mut Layer<P, O>,
placement: P,
grab_position: P,
grab_distance: <P::Output as VectorSpace>::Scalar,
options: O,
) where
P: Copy + Sub,
P::Output: InnerSpace,
<P::Output as VectorSpace>::Scalar: PartialOrd + Copy,
{
if let Some(index) = &mut self.editing {
index.point += 1;
layer.objects[index.object]
.points
.insert(index.point, placement);
return;
}
let mut nearest: Option<(PointIndex, <P::Output as VectorSpace>::Scalar)> = None;
for (object, ground) in layer.objects.iter().enumerate() {
if let Some((point, current)) = ground.nearest(grab_position, grab_distance)
&& nearest.is_none_or(|(_, last)| current < last)
{
nearest = Some((PointIndex { object, point }, current));
}
}
if let Some((index, _)) = nearest {
self.editing = Some(index);
return;
}
let object = layer.objects.len();
layer
.objects
.push(crate::PointObject::new(vec![placement, placement], options));
self.editing = Some(PointIndex { object, point: 1 });
}
pub fn try_undo<P, O>(&mut self, layer: &mut Layer<P, O>) {
let Some(index) = &mut self.editing else {
layer.objects.pop();
return;
};
let ground = &mut layer.objects[index.object];
if index.point > 1 {
ground.points.remove(index.point);
index.point -= 1;
}
}
}
#[cfg(test)]
mod tests {
use super::{LayerEditor, PointIndex};
use crate::Layer;
#[test]
fn select_extend_grab_undo() {
let mut editor = LayerEditor::new();
let mut layer: Layer<f32, ()> = Layer::new();
editor.select(&mut layer, 0.0, 0.0, 0.5, ());
assert_eq!(layer.objects.len(), 1);
assert_eq!(layer.objects[0].points, vec![0.0, 0.0]);
assert_eq!(
editor.editing(),
Some(PointIndex {
object: 0,
point: 1
})
);
editor.update(&mut layer, 5.0);
assert_eq!(layer.objects[0].points[1], 5.0);
editor.select(&mut layer, 10.0, 10.0, 0.5, ());
assert_eq!(layer.objects[0].points, vec![0.0, 5.0, 10.0]);
assert_eq!(
editor.editing(),
Some(PointIndex {
object: 0,
point: 2
})
);
editor.try_undo(&mut layer);
assert_eq!(layer.objects[0].points, vec![0.0, 5.0]);
editor.finish();
editor.select(&mut layer, 100.0, 5.1, 0.5, ());
assert_eq!(layer.objects.len(), 1);
assert_eq!(
editor.editing(),
Some(PointIndex {
object: 0,
point: 1
})
);
}
}