1use super::util::*;
14use std::fmt;
15
16#[derive(Clone, Copy, PartialEq, Debug, Default)]
17pub struct QuadraticBezier {
18 pub x1: f32,
19 pub y1: f32,
20 pub x2: f32,
21 pub y2: f32,
22}
23impl fmt::Display for QuadraticBezier {
24 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25 let m = format!("{} {}", format_float(self.x1), format_float(self.y1));
26 let x = (self.x1 + self.x1) / 2.0;
27 let y = self.y1 + (self.y2 - self.y1) / 2.0;
28 let q = format!("{} {}", format_float(x), format_float(y));
29 let end = format!("{} {}", format_float(self.x2), format_float(self.y2));
30 write!(f, "M{m} Q{q}, {end}")
31 }
32}
33
34#[derive(Clone, PartialEq, Debug, Default)]
35struct ControlPoint {
36 left: Option<Point>,
37 right: Option<Point>,
38}
39
40fn get_control_points(
42 p: &Point,
43 left: Option<&Point>,
44 right: Option<&Point>,
45 t: f32,
46) -> ControlPoint {
47 let x0 = left.unwrap_or(p).x;
48 let y0 = left.unwrap_or(p).y;
49 let x1 = p.x;
50 let y1 = p.y;
51 let x2 = right.unwrap_or(p).x;
52 let y2 = right.unwrap_or(p).y;
53
54 let d01 = ((x1 - x0).powf(2.0) + (y1 - y0).powf(2.0)).sqrt();
55 let d12 = ((x2 - x1).powf(2.0) + (y2 - y1).powf(2.0)).sqrt();
56 let fa = t * d01 / (d01 + d12);
58 let fb = t * d12 / (d01 + d12);
60 let p1x = x1 - fa * (x2 - x0);
62 let p1y = y1 - fa * (y2 - y0);
64 let p2x = x1 + fb * (x2 - x0);
65 let p2y = y1 + fb * (y2 - y0);
66
67 let mut cpl = None;
68 let mut cpr = None;
69 if left.is_some() {
70 cpl = Some((p1x, p1y).into());
71 }
72 if right.is_some() {
73 cpr = Some((p2x, p2y).into());
74 }
75 ControlPoint {
76 left: cpl,
77 right: cpr,
78 }
79}
80
81#[derive(Clone, PartialEq, Debug, Default)]
82pub struct SmoothCurve {
83 pub points: Vec<Point>,
84 pub close: bool,
85}
86impl fmt::Display for SmoothCurve {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 let tension = 0.25;
89
90 let close = self.close;
91 let count = self.points.len();
92 let mut control_points = vec![];
93 for (index, point) in self.points.iter().enumerate() {
94 let mut left = None;
95 let mut right = None;
96 if index >= 1 {
97 left = Some(&self.points[index - 1]);
98 } else if close {
99 left = self.points.last();
101 }
102 if index + 1 < count {
103 right = Some(&self.points[index + 1]);
104 } else if close {
105 right = self.points.first()
107 }
108 control_points.push(get_control_points(point, left, right, tension));
109 }
110
111 let mut arr = vec![];
112 for (index, point) in self.points.iter().enumerate() {
113 if index == 0 {
114 arr.push(format!(
115 "M{},{}",
116 format_float(point.x),
117 format_float(point.y)
118 ));
119 }
120 let cp1 = control_points[index].right;
121 let mut cp2 = None;
122 if let Some(value) = control_points.get(index + 1) {
123 cp2 = value.left;
124 } else if close {
125 cp2 = control_points[0].left;
127 }
128 let mut next_point = self.points.get(index + 1);
129 if close && index == count - 1 {
132 next_point = self.points.first();
133 }
134 if let Some(next_point_value) = next_point {
135 let next_point = format!(
136 "{} {}",
137 format_float(next_point_value.x),
138 format_float(next_point_value.y)
139 );
140 if let Some(cp1_value) = cp1 {
141 if let Some(cp2_value) = cp2 {
142 let c1 = format!(
143 "{} {}",
144 format_float(cp1_value.x),
145 format_float(cp1_value.y)
146 );
147 let c2 = format!(
148 "{} {}",
149 format_float(cp2_value.x),
150 format_float(cp2_value.y)
151 );
152 arr.push(format!("C{}, {}, {}", c1, c2, next_point));
153 continue;
154 }
155 }
156 let p = cp1.unwrap_or(cp2.unwrap_or_default());
157
158 let q = format!("{} {}", format_float(p.x), format_float(p.y));
159 arr.push(format!("Q{}, {}", q, next_point));
160 }
161 }
162 write!(f, "{}", arr.join(" "))
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::{QuadraticBezier, SmoothCurve};
169 use pretty_assertions::assert_eq;
170 #[test]
171 fn quadratic_bezier() {
172 let str = QuadraticBezier {
173 x1: 10.0,
174 y1: 30.0,
175 x2: 30.0,
176 y2: 10.0,
177 }
178 .to_string();
179 assert_eq!("M10 30 Q10 20, 30 10", str);
180 }
181
182 #[test]
183 fn smooth_curve() {
184 let str = SmoothCurve {
185 points: vec![
186 (10.0, 10.0).into(),
187 (20.0, 50.0).into(),
188 (30.0, 80.0).into(),
189 (40.0, 30.0).into(),
190 (50.0, 10.0).into(),
191 ],
192 close: false,
193 }
194 .to_string();
195 assert_eq!("M10,10 C12.5 20, 17.2 40.1, 20 50 C22.2 57.6, 28.1 81.9, 30 80 C33.1 76.9, 36.5 42.2, 40 30 C41.5 24.7, 47.5 15, 50 10", str);
196
197 let str = SmoothCurve {
198 points: vec![
199 (10.0, 10.0).into(),
200 (20.0, 50.0).into(),
201 (30.0, 80.0).into(),
202 (40.0, 30.0).into(),
203 (50.0, 10.0).into(),
204 ],
205 close: true,
206 }
207 .to_string();
208 assert_eq!("M10,10 C6.2 15.1, 17.2 40.1, 20 50 C22.2 57.6, 28.1 81.9, 30 80 C33.1 76.9, 36.5 42.2, 40 30 C41.5 24.7, 52.7 11.8, 50 10 C45.2 6.8, 13.7 5.1, 10 10", str);
209 }
210}