File size: 4,894 Bytes
2409829
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use super::Bezier;
use glam::{DAffine2, DVec2};
use std::fmt::{Debug, Formatter, Result};
use std::hash::Hash;

/// An id type used for each [ManipulatorGroup].
pub trait Identifier: Sized + Clone + PartialEq + Hash + 'static {
	fn new() -> Self;
}

/// An empty id type for use in tests
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[cfg(test)]
pub(crate) struct EmptyId;

#[cfg(test)]
impl Identifier for EmptyId {
	fn new() -> Self {
		Self
	}
}

/// Structure used to represent a single anchor with up to two optional associated handles along a `Subpath`
#[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,
}

// TODO: Remove once we no longer need to hash floats in Graphite
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> {
	/// Construct a new manipulator group from an anchor, in handle and out handle
	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 }
	}

	/// Construct a new manipulator point with just an anchor position
	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)
	}

	/// Construct a new manipulator group from an anchor, in handle, out handle and an id
	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 }
	}

	/// Construct a new manipulator point with just an anchor position and an id
	pub fn new_anchor_with_id(anchor: DVec2, id: PointId) -> Self {
		Self::new_with_id(anchor, Some(anchor), Some(anchor), id)
	}

	/// Create a bezier curve that starts at the current manipulator group and finishes in the `end_group` manipulator group.
	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),
		}
	}

	/// Apply a transformation to all of the [ManipulatorGroup] points
	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));
	}

	/// Are all handles at finite positions
	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())
	}

	/// Reverse directions of handles
	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,
}