1use crate::basics::{is_stop, VertexSource, PATH_CMD_CURVE3, PATH_CMD_CURVE4, PATH_CMD_LINE_TO};
8use crate::curves::{Curve3, Curve4};
9
10pub struct ConvCurve<VS: VertexSource> {
21 source: VS,
22 last_x: f64,
23 last_y: f64,
24 curve3: Curve3,
25 curve4: Curve4,
26}
27
28impl<VS: VertexSource> ConvCurve<VS> {
29 pub fn new(source: VS) -> Self {
30 Self {
31 source,
32 last_x: 0.0,
33 last_y: 0.0,
34 curve3: Curve3::new(),
35 curve4: Curve4::new(),
36 }
37 }
38
39 pub fn source(&self) -> &VS {
40 &self.source
41 }
42
43 pub fn source_mut(&mut self) -> &mut VS {
44 &mut self.source
45 }
46
47 pub fn set_approximation_scale(&mut self, s: f64) {
49 self.curve3.set_approximation_scale(s);
50 self.curve4.set_approximation_scale(s);
51 }
52
53 pub fn approximation_scale(&self) -> f64 {
54 self.curve4.approximation_scale()
55 }
56
57 pub fn set_angle_tolerance(&mut self, v: f64) {
59 self.curve3.set_angle_tolerance(v);
60 self.curve4.set_angle_tolerance(v);
61 }
62
63 pub fn angle_tolerance(&self) -> f64 {
64 self.curve4.angle_tolerance()
65 }
66
67 pub fn set_cusp_limit(&mut self, v: f64) {
69 self.curve4.set_cusp_limit(v);
70 }
71
72 pub fn cusp_limit(&self) -> f64 {
73 self.curve4.cusp_limit()
74 }
75}
76
77impl<VS: VertexSource> VertexSource for ConvCurve<VS> {
78 fn rewind(&mut self, path_id: u32) {
79 self.source.rewind(path_id);
80 self.last_x = 0.0;
81 self.last_y = 0.0;
82 self.curve3.reset();
83 self.curve4.reset();
84 }
85
86 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
87 if !is_stop(self.curve3.vertex(x, y)) {
89 self.last_x = *x;
90 self.last_y = *y;
91 return PATH_CMD_LINE_TO;
92 }
93
94 if !is_stop(self.curve4.vertex(x, y)) {
96 self.last_x = *x;
97 self.last_y = *y;
98 return PATH_CMD_LINE_TO;
99 }
100
101 let mut cmd = self.source.vertex(x, y);
103
104 match cmd {
105 PATH_CMD_CURVE3 => {
106 let (mut end_x, mut end_y) = (0.0, 0.0);
108 self.source.vertex(&mut end_x, &mut end_y);
109
110 self.curve3
111 .init(self.last_x, self.last_y, *x, *y, end_x, end_y);
112
113 self.curve3.vertex(x, y);
115 self.curve3.vertex(x, y);
117 cmd = PATH_CMD_LINE_TO;
118 }
119 PATH_CMD_CURVE4 => {
120 let (mut ct2_x, mut ct2_y) = (0.0, 0.0);
122 let (mut end_x, mut end_y) = (0.0, 0.0);
123 self.source.vertex(&mut ct2_x, &mut ct2_y);
124 self.source.vertex(&mut end_x, &mut end_y);
125
126 self.curve4
127 .init(self.last_x, self.last_y, *x, *y, ct2_x, ct2_y, end_x, end_y);
128
129 self.curve4.vertex(x, y);
131 self.curve4.vertex(x, y);
133 cmd = PATH_CMD_LINE_TO;
134 }
135 _ => {}
136 }
137
138 self.last_x = *x;
139 self.last_y = *y;
140 cmd
141 }
142}
143
144#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::basics::PATH_CMD_MOVE_TO;
152 use crate::path_storage::PathStorage;
153
154 fn collect_vertices<VS: VertexSource>(vs: &mut VS) -> Vec<(f64, f64, u32)> {
155 let mut result = Vec::new();
156 vs.rewind(0);
157 loop {
158 let (mut x, mut y) = (0.0, 0.0);
159 let cmd = vs.vertex(&mut x, &mut y);
160 if is_stop(cmd) {
161 break;
162 }
163 result.push((x, y, cmd));
164 }
165 result
166 }
167
168 #[test]
169 fn test_no_curves_passthrough() {
170 let mut path = PathStorage::new();
171 path.move_to(10.0, 20.0);
172 path.line_to(30.0, 40.0);
173 path.line_to(50.0, 60.0);
174
175 let mut cc = ConvCurve::new(path);
176 let verts = collect_vertices(&mut cc);
177 assert_eq!(verts.len(), 3);
178 assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
179 assert_eq!(verts[1].2, PATH_CMD_LINE_TO);
180 assert!((verts[0].0 - 10.0).abs() < 1e-10);
181 assert!((verts[2].0 - 50.0).abs() < 1e-10);
182 }
183
184 #[test]
185 fn test_curve3_flattening() {
186 let mut path = PathStorage::new();
187 path.move_to(0.0, 0.0);
188 path.curve3(50.0, 100.0, 100.0, 0.0);
189
190 let mut cc = ConvCurve::new(path);
191 let verts = collect_vertices(&mut cc);
192
193 assert!(
195 verts.len() > 2,
196 "Expected multiple vertices, got {}",
197 verts.len()
198 );
199 assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
200 for v in &verts[1..] {
202 assert_eq!(v.2, PATH_CMD_LINE_TO);
203 }
204 assert!((verts[0].0).abs() < 1e-10);
206 assert!((verts[0].1).abs() < 1e-10);
207 let last = verts.last().unwrap();
209 assert!((last.0 - 100.0).abs() < 1.0, "End x={}", last.0);
210 assert!((last.1).abs() < 1.0, "End y={}", last.1);
211 }
212
213 #[test]
214 fn test_curve4_flattening() {
215 let mut path = PathStorage::new();
216 path.move_to(0.0, 0.0);
217 path.curve4(33.0, 100.0, 66.0, 100.0, 100.0, 0.0);
218
219 let mut cc = ConvCurve::new(path);
220 let verts = collect_vertices(&mut cc);
221
222 assert!(
223 verts.len() > 2,
224 "Expected multiple vertices, got {}",
225 verts.len()
226 );
227 assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
228 for v in &verts[1..] {
229 assert_eq!(v.2, PATH_CMD_LINE_TO);
230 }
231 let last = verts.last().unwrap();
233 assert!((last.0 - 100.0).abs() < 1.0, "End x={}", last.0);
234 }
235
236 #[test]
237 fn test_mixed_lines_and_curves() {
238 let mut path = PathStorage::new();
239 path.move_to(0.0, 0.0);
240 path.line_to(50.0, 0.0);
241 path.curve3(75.0, 50.0, 100.0, 0.0);
242 path.line_to(150.0, 0.0);
243
244 let mut cc = ConvCurve::new(path);
245 let verts = collect_vertices(&mut cc);
246
247 assert!(
249 verts.len() > 4,
250 "Expected > 4 vertices, got {}",
251 verts.len()
252 );
253 assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
254 }
255
256 #[test]
257 fn test_approximation_scale() {
258 let path = PathStorage::new();
259 let mut cc = ConvCurve::new(path);
260 cc.set_approximation_scale(2.0);
261 assert!((cc.approximation_scale() - 2.0).abs() < 1e-10);
262 }
263
264 #[test]
265 fn test_rewind_resets() {
266 let mut path = PathStorage::new();
267 path.move_to(0.0, 0.0);
268 path.curve3(50.0, 100.0, 100.0, 0.0);
269
270 let mut cc = ConvCurve::new(path);
271 let verts1 = collect_vertices(&mut cc);
272 let verts2 = collect_vertices(&mut cc);
273 assert_eq!(verts1.len(), verts2.len());
274 }
275
276 #[test]
277 fn test_empty_path() {
278 let path = PathStorage::new();
279 let mut cc = ConvCurve::new(path);
280 let verts = collect_vertices(&mut cc);
281 assert_eq!(verts.len(), 0);
282 }
283
284 #[test]
285 fn test_source_access() {
286 let path = PathStorage::new();
287 let cc = ConvCurve::new(path);
288 let _ = cc.source();
289 }
290
291 #[test]
292 fn test_angle_tolerance_and_cusp_limit() {
293 let path = PathStorage::new();
294 let mut cc = ConvCurve::new(path);
295 cc.set_angle_tolerance(0.5);
296 assert!((cc.angle_tolerance() - 0.5).abs() < 1e-10);
297 cc.set_cusp_limit(2.0);
298 assert!((cc.cusp_limit() - 2.0).abs() < 1e-10);
299 }
300}