|
use super::*; |
|
use crate::consts::MAX_ABSOLUTE_DIFFERENCE; |
|
use crate::utils::f64_compare; |
|
use crate::{SubpathTValue, TValue}; |
|
|
|
impl<PointId: crate::Identifier> Subpath<PointId> { |
|
|
|
pub fn closed(&self) -> bool { |
|
self.closed |
|
} |
|
|
|
|
|
pub fn set_closed(&mut self, new_closed: bool) { |
|
self.closed = new_closed; |
|
} |
|
|
|
|
|
pub fn manipulator_from_id(&self, id: PointId) -> Option<&ManipulatorGroup<PointId>> { |
|
self.manipulator_groups.iter().find(|manipulator_group| manipulator_group.id == id) |
|
} |
|
|
|
|
|
pub fn manipulator_mut_from_id(&mut self, id: PointId) -> Option<&mut ManipulatorGroup<PointId>> { |
|
self.manipulator_groups.iter_mut().find(|manipulator_group| manipulator_group.id == id) |
|
} |
|
|
|
|
|
pub fn manipulator_index_from_id(&self, id: PointId) -> Option<usize> { |
|
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id) |
|
} |
|
|
|
|
|
pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup<PointId>) { |
|
assert!(group.is_finite(), "Inserting non finite manipulator group"); |
|
self.manipulator_groups.insert(index, group) |
|
} |
|
|
|
|
|
pub fn push_manipulator_group(&mut self, group: ManipulatorGroup<PointId>) { |
|
assert!(group.is_finite(), "Pushing non finite manipulator group"); |
|
self.manipulator_groups.push(group) |
|
} |
|
|
|
|
|
pub fn last_manipulator_group_mut(&mut self) -> Option<&mut ManipulatorGroup<PointId>> { |
|
self.manipulator_groups.last_mut() |
|
} |
|
|
|
|
|
pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup<PointId> { |
|
self.manipulator_groups.remove(index) |
|
} |
|
|
|
|
|
|
|
pub fn insert(&mut self, t: SubpathTValue) { |
|
let (segment_index, t) = self.t_value_to_parametric(t); |
|
|
|
if f64_compare(t, 0., MAX_ABSOLUTE_DIFFERENCE) || f64_compare(t, 1., MAX_ABSOLUTE_DIFFERENCE) { |
|
return; |
|
} |
|
|
|
|
|
|
|
let curve = self.iter().nth(segment_index).unwrap(); |
|
|
|
let [first, second] = curve.split(TValue::Parametric(t)); |
|
let new_group = ManipulatorGroup { |
|
anchor: first.end(), |
|
in_handle: first.handle_end(), |
|
out_handle: second.handle_start(), |
|
id: PointId::new(), |
|
}; |
|
let number_of_groups = self.manipulator_groups.len() + 1; |
|
self.manipulator_groups.insert((segment_index) + 1, new_group); |
|
self.manipulator_groups[segment_index % number_of_groups].out_handle = first.handle_start(); |
|
self.manipulator_groups[(segment_index + 2) % number_of_groups].in_handle = second.handle_end(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn append_bezier(&mut self, bezier: &Bezier, append_type: AppendType) { |
|
if self.manipulator_groups.is_empty() { |
|
self.manipulator_groups = vec![ManipulatorGroup { |
|
anchor: bezier.start(), |
|
in_handle: None, |
|
out_handle: None, |
|
id: PointId::new(), |
|
}]; |
|
} |
|
let mut last_index = self.manipulator_groups.len() - 1; |
|
let last_anchor = self.manipulator_groups[last_index].anchor; |
|
|
|
if let AppendType::SmoothJoin(max_absolute_difference) = append_type { |
|
|
|
|
|
if !last_anchor.abs_diff_eq(bezier.start(), max_absolute_difference) { |
|
let last_bezier = if self.manipulator_groups.len() > 1 { |
|
self.manipulator_groups[last_index - 1].to_bezier(&self.manipulator_groups[last_index]) |
|
} else { |
|
Bezier::from_linear_dvec2(last_anchor, last_anchor) |
|
}; |
|
let join_bezier = last_bezier.join(bezier); |
|
self.append_bezier(&join_bezier, AppendType::IgnoreStart); |
|
last_index = self.manipulator_groups.len() - 1; |
|
} |
|
} |
|
self.manipulator_groups[last_index].out_handle = bezier.handle_start(); |
|
self.manipulator_groups.push(ManipulatorGroup { |
|
anchor: bezier.end(), |
|
in_handle: bezier.handle_end(), |
|
out_handle: None, |
|
id: PointId::new(), |
|
}); |
|
} |
|
} |
|
|
|
#[cfg(test)] |
|
mod tests { |
|
use super::*; |
|
use crate::utils::SubpathTValue; |
|
use glam::DVec2; |
|
|
|
fn set_up_open_subpath() -> Subpath<EmptyId> { |
|
let start = DVec2::new(20., 30.); |
|
let middle1 = DVec2::new(80., 90.); |
|
let middle2 = DVec2::new(100., 100.); |
|
let end = DVec2::new(60., 45.); |
|
|
|
let handle1 = DVec2::new(75., 85.); |
|
let handle2 = DVec2::new(40., 30.); |
|
let handle3 = DVec2::new(10., 10.); |
|
|
|
Subpath::new( |
|
vec![ |
|
ManipulatorGroup { |
|
anchor: start, |
|
in_handle: None, |
|
out_handle: Some(handle1), |
|
id: EmptyId, |
|
}, |
|
ManipulatorGroup { |
|
anchor: middle1, |
|
in_handle: None, |
|
out_handle: Some(handle2), |
|
id: EmptyId, |
|
}, |
|
ManipulatorGroup { |
|
anchor: middle2, |
|
in_handle: None, |
|
out_handle: None, |
|
id: EmptyId, |
|
}, |
|
ManipulatorGroup { |
|
anchor: end, |
|
in_handle: None, |
|
out_handle: Some(handle3), |
|
id: EmptyId, |
|
}, |
|
], |
|
false, |
|
) |
|
} |
|
|
|
fn set_up_closed_subpath() -> Subpath<EmptyId> { |
|
let mut subpath = set_up_open_subpath(); |
|
subpath.closed = true; |
|
subpath |
|
} |
|
|
|
#[test] |
|
fn insert_in_first_segment_of_open_subpath() { |
|
let mut subpath = set_up_open_subpath(); |
|
let location = subpath.evaluate(SubpathTValue::GlobalParametric(0.2)); |
|
let split_pair = subpath.iter().next().unwrap().split(TValue::Parametric((0.2 * 3.) % 1.)); |
|
subpath.insert(SubpathTValue::GlobalParametric(0.2)); |
|
assert_eq!(subpath.manipulator_groups[1].anchor, location); |
|
assert_eq!(split_pair[0], subpath.iter().next().unwrap()); |
|
assert_eq!(split_pair[1], subpath.iter().nth(1).unwrap()); |
|
} |
|
|
|
#[test] |
|
fn insert_in_last_segment_of_open_subpath() { |
|
let mut subpath = set_up_open_subpath(); |
|
let location = subpath.evaluate(SubpathTValue::GlobalParametric(0.9)); |
|
let split_pair = subpath.iter().nth(2).unwrap().split(TValue::Parametric((0.9 * 3.) % 1.)); |
|
subpath.insert(SubpathTValue::GlobalParametric(0.9)); |
|
assert_eq!(subpath.manipulator_groups[3].anchor, location); |
|
assert_eq!(split_pair[0], subpath.iter().nth(2).unwrap()); |
|
assert_eq!(split_pair[1], subpath.iter().nth(3).unwrap()); |
|
} |
|
|
|
#[test] |
|
fn insert_at_existing_manipulator_group_of_open_subpath() { |
|
|
|
let mut subpath = set_up_open_subpath(); |
|
let location = subpath.evaluate(SubpathTValue::GlobalParametric(0.75)); |
|
subpath.insert(SubpathTValue::GlobalParametric(0.75)); |
|
assert_eq!(subpath.manipulator_groups[3].anchor, location); |
|
assert_eq!(subpath.manipulator_groups.len(), 5); |
|
assert_eq!(subpath.len_segments(), 4); |
|
} |
|
|
|
#[test] |
|
fn insert_at_last_segment_of_closed_subpath() { |
|
let mut subpath = set_up_closed_subpath(); |
|
let location = subpath.evaluate(SubpathTValue::GlobalParametric(0.9)); |
|
let split_pair = subpath.iter().nth(3).unwrap().split(TValue::Parametric((0.9 * 4.) % 1.)); |
|
subpath.insert(SubpathTValue::GlobalParametric(0.9)); |
|
assert_eq!(subpath.manipulator_groups[4].anchor, location); |
|
assert_eq!(split_pair[0], subpath.iter().nth(3).unwrap()); |
|
assert_eq!(split_pair[1], subpath.iter().nth(4).unwrap()); |
|
assert!(subpath.closed); |
|
} |
|
|
|
#[test] |
|
fn insert_at_last_manipulator_group_of_closed_subpath() { |
|
|
|
let mut subpath = set_up_closed_subpath(); |
|
let location = subpath.evaluate(SubpathTValue::GlobalParametric(1.)); |
|
subpath.insert(SubpathTValue::GlobalParametric(1.)); |
|
assert_eq!(subpath.manipulator_groups[0].anchor, location); |
|
assert_eq!(subpath.manipulator_groups.len(), 4); |
|
assert!(subpath.closed); |
|
} |
|
} |
|
|