|
use super::Bezier; |
|
use glam::{DAffine2, DVec2}; |
|
use std::fmt::{Debug, Formatter, Result}; |
|
use std::hash::Hash; |
|
|
|
|
|
pub trait Identifier: Sized + Clone + PartialEq + Hash + 'static { |
|
fn new() -> Self; |
|
} |
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] |
|
#[cfg(test)] |
|
pub(crate) struct EmptyId; |
|
|
|
#[cfg(test)] |
|
impl Identifier for EmptyId { |
|
fn new() -> Self { |
|
Self |
|
} |
|
} |
|
|
|
|
|
#[derive(Copy, Clone, PartialEq)] |
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
|
pub struct ManipulatorGroup<PointId: crate::Identifier> { |
|
pub anchor: DVec2, |
|
pub in_handle: Option<DVec2>, |
|
pub out_handle: Option<DVec2>, |
|
pub id: PointId, |
|
} |
|
|
|
|
|
impl<PointId: crate::Identifier> Hash for ManipulatorGroup<PointId> { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
self.anchor.to_array().iter().for_each(|x| x.to_bits().hash(state)); |
|
self.in_handle.is_some().hash(state); |
|
if let Some(in_handle) = self.in_handle { |
|
in_handle.to_array().iter().for_each(|x| x.to_bits().hash(state)); |
|
} |
|
self.out_handle.is_some().hash(state); |
|
if let Some(out_handle) = self.out_handle { |
|
out_handle.to_array().iter().for_each(|x| x.to_bits().hash(state)); |
|
} |
|
self.id.hash(state); |
|
} |
|
} |
|
|
|
#[cfg(feature = "dyn-any")] |
|
unsafe impl<PointId: crate::Identifier> dyn_any::StaticType for ManipulatorGroup<PointId> { |
|
type Static = ManipulatorGroup<PointId>; |
|
} |
|
|
|
impl<PointId: crate::Identifier> Debug for ManipulatorGroup<PointId> { |
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
|
f.debug_struct("ManipulatorGroup") |
|
.field("anchor", &self.anchor) |
|
.field("in_handle", &self.in_handle) |
|
.field("out_handle", &self.out_handle) |
|
.finish() |
|
} |
|
} |
|
|
|
impl<PointId: crate::Identifier> ManipulatorGroup<PointId> { |
|
|
|
pub fn new(anchor: DVec2, in_handle: Option<DVec2>, out_handle: Option<DVec2>) -> Self { |
|
let id = PointId::new(); |
|
Self { anchor, in_handle, out_handle, id } |
|
} |
|
|
|
|
|
pub fn new_anchor(anchor: DVec2) -> Self { |
|
Self::new(anchor, Some(anchor), Some(anchor)) |
|
} |
|
|
|
pub fn new_anchor_linear(anchor: DVec2) -> Self { |
|
Self::new(anchor, None, None) |
|
} |
|
|
|
|
|
pub fn new_with_id(anchor: DVec2, in_handle: Option<DVec2>, out_handle: Option<DVec2>, id: PointId) -> Self { |
|
Self { anchor, in_handle, out_handle, id } |
|
} |
|
|
|
|
|
pub fn new_anchor_with_id(anchor: DVec2, id: PointId) -> Self { |
|
Self::new_with_id(anchor, Some(anchor), Some(anchor), id) |
|
} |
|
|
|
|
|
pub fn to_bezier(&self, end_group: &ManipulatorGroup<PointId>) -> Bezier { |
|
let start = self.anchor; |
|
let end = end_group.anchor; |
|
let out_handle = self.out_handle; |
|
let in_handle = end_group.in_handle; |
|
|
|
match (out_handle, in_handle) { |
|
(Some(handle1), Some(handle2)) => Bezier::from_cubic_dvec2(start, handle1, handle2, end), |
|
(Some(handle), None) | (None, Some(handle)) => Bezier::from_quadratic_dvec2(start, handle, end), |
|
(None, None) => Bezier::from_linear_dvec2(start, end), |
|
} |
|
} |
|
|
|
|
|
pub fn apply_transform(&mut self, affine_transform: DAffine2) { |
|
self.anchor = affine_transform.transform_point2(self.anchor); |
|
self.in_handle = self.in_handle.map(|in_handle| affine_transform.transform_point2(in_handle)); |
|
self.out_handle = self.out_handle.map(|out_handle| affine_transform.transform_point2(out_handle)); |
|
} |
|
|
|
|
|
pub fn is_finite(&self) -> bool { |
|
self.anchor.is_finite() && self.in_handle.is_none_or(|handle| handle.is_finite()) && self.out_handle.is_none_or(|handle| handle.is_finite()) |
|
} |
|
|
|
|
|
pub fn flip(mut self) -> Self { |
|
std::mem::swap(&mut self.in_handle, &mut self.out_handle); |
|
self |
|
} |
|
|
|
pub fn has_in_handle(&self) -> bool { |
|
self.in_handle.map(|handle| Self::has_handle(self.anchor, handle)).unwrap_or(false) |
|
} |
|
|
|
pub fn has_out_handle(&self) -> bool { |
|
self.out_handle.map(|handle| Self::has_handle(self.anchor, handle)).unwrap_or(false) |
|
} |
|
|
|
fn has_handle(anchor: DVec2, handle: DVec2) -> bool { |
|
!((handle.x - anchor.x).abs() < f64::EPSILON && (handle.y - anchor.y).abs() < f64::EPSILON) |
|
} |
|
} |
|
|
|
#[derive(Copy, Clone)] |
|
pub enum AppendType { |
|
IgnoreStart, |
|
SmoothJoin(f64), |
|
} |
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)] |
|
pub enum ArcType { |
|
Open, |
|
Closed, |
|
PieSlice, |
|
} |
|
|