1use crate::array::{shorten_path, VertexDist, VertexSequence};
7use crate::basics::{
8 get_close_flag, is_move_to, is_vertex, PointD, PATH_CMD_END_POLY, PATH_CMD_LINE_TO,
9 PATH_CMD_MOVE_TO, PATH_CMD_STOP, PATH_FLAGS_CCW, PATH_FLAGS_CLOSE, PATH_FLAGS_CW,
10};
11use crate::math_stroke::{InnerJoin, LineCap, LineJoin, MathStroke};
12
13#[derive(Debug, Clone, Copy, PartialEq)]
18enum Status {
19 Initial,
20 Ready,
21 Cap1,
22 Cap2,
23 Outline1,
24 CloseFirst,
25 Outline2,
26 OutVertices,
27 EndPoly1,
28 EndPoly2,
29 Stop,
30}
31
32pub struct VcgenStroke {
39 stroker: MathStroke,
40 src_vertices: VertexSequence,
41 out_vertices: Vec<PointD>,
42 shorten: f64,
43 closed: u32,
44 status: Status,
45 prev_status: Status,
46 src_vertex: usize,
47 out_vertex: usize,
48}
49
50impl VcgenStroke {
51 pub fn new() -> Self {
52 Self {
53 stroker: MathStroke::new(),
54 src_vertices: VertexSequence::new(),
55 out_vertices: Vec::new(),
56 shorten: 0.0,
57 closed: 0,
58 status: Status::Initial,
59 prev_status: Status::Initial,
60 src_vertex: 0,
61 out_vertex: 0,
62 }
63 }
64
65 pub fn set_line_cap(&mut self, lc: LineCap) {
67 self.stroker.set_line_cap(lc);
68 }
69 pub fn line_cap(&self) -> LineCap {
70 self.stroker.line_cap()
71 }
72
73 pub fn set_line_join(&mut self, lj: LineJoin) {
74 self.stroker.set_line_join(lj);
75 }
76 pub fn line_join(&self) -> LineJoin {
77 self.stroker.line_join()
78 }
79
80 pub fn set_inner_join(&mut self, ij: InnerJoin) {
81 self.stroker.set_inner_join(ij);
82 }
83 pub fn inner_join(&self) -> InnerJoin {
84 self.stroker.inner_join()
85 }
86
87 pub fn set_width(&mut self, w: f64) {
88 self.stroker.set_width(w);
89 }
90 pub fn width(&self) -> f64 {
91 self.stroker.width()
92 }
93
94 pub fn set_miter_limit(&mut self, ml: f64) {
95 self.stroker.set_miter_limit(ml);
96 }
97 pub fn miter_limit(&self) -> f64 {
98 self.stroker.miter_limit()
99 }
100
101 pub fn set_miter_limit_theta(&mut self, t: f64) {
102 self.stroker.set_miter_limit_theta(t);
103 }
104
105 pub fn set_inner_miter_limit(&mut self, ml: f64) {
106 self.stroker.set_inner_miter_limit(ml);
107 }
108 pub fn inner_miter_limit(&self) -> f64 {
109 self.stroker.inner_miter_limit()
110 }
111
112 pub fn set_approximation_scale(&mut self, s: f64) {
113 self.stroker.set_approximation_scale(s);
114 }
115 pub fn approximation_scale(&self) -> f64 {
116 self.stroker.approximation_scale()
117 }
118
119 pub fn set_shorten(&mut self, s: f64) {
120 self.shorten = s;
121 }
122 pub fn shorten(&self) -> f64 {
123 self.shorten
124 }
125
126 pub fn remove_all(&mut self) {
128 self.src_vertices.remove_all();
129 self.closed = 0;
130 self.status = Status::Initial;
131 }
132
133 pub fn add_vertex(&mut self, x: f64, y: f64, cmd: u32) {
134 self.status = Status::Initial;
135 if is_move_to(cmd) {
136 self.src_vertices.modify_last(VertexDist::new(x, y));
137 } else if is_vertex(cmd) {
138 self.src_vertices.add(VertexDist::new(x, y));
139 } else {
140 self.closed = get_close_flag(cmd);
141 }
142 }
143
144 pub fn rewind(&mut self, _path_id: u32) {
146 if self.status == Status::Initial {
147 self.src_vertices.close(self.closed != 0);
148 shorten_path(&mut self.src_vertices, self.shorten, self.closed);
149 if self.src_vertices.size() < 3 {
150 self.closed = 0;
151 }
152 }
153 self.status = Status::Ready;
154 self.src_vertex = 0;
155 self.out_vertex = 0;
156 }
157
158 pub fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
159 let mut cmd = PATH_CMD_LINE_TO;
164 loop {
165 match self.status {
166 Status::Initial => {
167 self.rewind(0);
168 }
170 Status::Ready => {
171 if self.src_vertices.size() < 2 + (self.closed != 0) as usize {
172 return PATH_CMD_STOP;
173 }
174 self.status = if self.closed != 0 {
175 Status::Outline1
176 } else {
177 Status::Cap1
178 };
179 cmd = PATH_CMD_MOVE_TO;
180 self.src_vertex = 0;
181 self.out_vertex = 0;
182 }
184 Status::Cap1 => {
185 let v0 = *self.src_vertices.curr(0);
186 let v1 = *self.src_vertices.curr(1);
187 self.stroker
188 .calc_cap(&mut self.out_vertices, &v0, &v1, v0.dist);
189 self.src_vertex = 1;
190 self.prev_status = Status::Outline1;
191 self.status = Status::OutVertices;
192 self.out_vertex = 0;
193 }
195 Status::Cap2 => {
196 let n = self.src_vertices.size();
197 let v0 = *self.src_vertices.curr(n - 1);
198 let v1 = *self.src_vertices.curr(n - 2);
199 self.stroker
200 .calc_cap(&mut self.out_vertices, &v0, &v1, v1.dist);
201 self.prev_status = Status::Outline2;
202 self.status = Status::OutVertices;
203 self.out_vertex = 0;
204 }
206 Status::Outline1 => {
207 if self.closed != 0 {
208 if self.src_vertex >= self.src_vertices.size() {
209 self.prev_status = Status::CloseFirst;
210 self.status = Status::EndPoly1;
211 continue; }
213 } else if self.src_vertex >= self.src_vertices.size() - 1 {
214 self.status = Status::Cap2;
215 continue; }
217 let v_prev = *self.src_vertices.prev(self.src_vertex);
218 let v_curr = *self.src_vertices.curr(self.src_vertex);
219 let v_next = *self.src_vertices.next(self.src_vertex);
220 self.stroker.calc_join(
221 &mut self.out_vertices,
222 &v_prev,
223 &v_curr,
224 &v_next,
225 v_prev.dist,
226 v_curr.dist,
227 );
228 self.src_vertex += 1;
229 self.prev_status = self.status;
230 self.status = Status::OutVertices;
231 self.out_vertex = 0;
232 }
234 Status::CloseFirst => {
235 self.status = Status::Outline2;
236 cmd = PATH_CMD_MOVE_TO;
237 }
239 Status::Outline2 => {
240 if self.src_vertex <= (self.closed == 0) as usize {
241 self.status = Status::EndPoly2;
242 self.prev_status = Status::Stop;
243 continue; }
245 self.src_vertex -= 1;
246 let v_next = *self.src_vertices.next(self.src_vertex);
247 let v_curr = *self.src_vertices.curr(self.src_vertex);
248 let v_prev = *self.src_vertices.prev(self.src_vertex);
249 self.stroker.calc_join(
250 &mut self.out_vertices,
251 &v_next,
252 &v_curr,
253 &v_prev,
254 v_curr.dist,
255 v_prev.dist,
256 );
257 self.prev_status = self.status;
258 self.status = Status::OutVertices;
259 self.out_vertex = 0;
260 }
262 Status::OutVertices => {
263 if self.out_vertex >= self.out_vertices.len() {
264 self.status = self.prev_status;
265 } else {
267 let c = self.out_vertices[self.out_vertex];
268 self.out_vertex += 1;
269 *x = c.x;
270 *y = c.y;
271 return cmd;
272 }
273 }
274 Status::EndPoly1 => {
275 self.status = self.prev_status;
276 return PATH_CMD_END_POLY | PATH_FLAGS_CLOSE | PATH_FLAGS_CCW;
277 }
278 Status::EndPoly2 => {
279 self.status = self.prev_status;
280 return PATH_CMD_END_POLY | PATH_FLAGS_CLOSE | PATH_FLAGS_CW;
281 }
282 Status::Stop => {
283 return PATH_CMD_STOP;
284 }
285 }
286 }
287 }
288}
289
290impl Default for VcgenStroke {
291 fn default() -> Self {
292 Self::new()
293 }
294}
295
296impl crate::conv_adaptor_vcgen::VcgenGenerator for VcgenStroke {
297 fn remove_all(&mut self) {
298 self.remove_all();
299 }
300 fn add_vertex(&mut self, x: f64, y: f64, cmd: u32) {
301 self.add_vertex(x, y, cmd);
302 }
303 fn rewind(&mut self, path_id: u32) {
304 self.rewind(path_id);
305 }
306 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
307 self.vertex(x, y)
308 }
309}
310
311#[cfg(test)]
316mod tests {
317 use super::*;
318 use crate::basics::is_stop;
319
320 fn collect_gen_vertices(gen: &mut VcgenStroke) -> Vec<(f64, f64, u32)> {
321 gen.rewind(0);
322 let mut result = Vec::new();
323 loop {
324 let (mut x, mut y) = (0.0, 0.0);
325 let cmd = gen.vertex(&mut x, &mut y);
326 if is_stop(cmd) {
327 break;
328 }
329 result.push((x, y, cmd));
330 }
331 result
332 }
333
334 #[test]
335 fn test_new_defaults() {
336 let gen = VcgenStroke::new();
337 assert!((gen.width() - 1.0).abs() < 1e-10);
338 assert_eq!(gen.line_cap(), LineCap::Butt);
339 assert_eq!(gen.line_join(), LineJoin::Miter);
340 }
341
342 #[test]
343 fn test_empty_produces_stop() {
344 let mut gen = VcgenStroke::new();
345 let verts = collect_gen_vertices(&mut gen);
346 assert!(verts.is_empty());
347 }
348
349 #[test]
350 fn test_single_segment_open() {
351 let mut gen = VcgenStroke::new();
352 gen.set_width(10.0);
353 gen.add_vertex(0.0, 0.0, PATH_CMD_MOVE_TO);
354 gen.add_vertex(100.0, 0.0, PATH_CMD_LINE_TO);
355
356 let verts = collect_gen_vertices(&mut gen);
357 assert!(
359 verts.len() >= 4,
360 "Expected at least 4 vertices, got {}",
361 verts.len()
362 );
363 assert_eq!(verts[0].2, PATH_CMD_MOVE_TO);
365 }
366
367 #[test]
368 fn test_closed_triangle() {
369 let mut gen = VcgenStroke::new();
370 gen.set_width(4.0);
371 gen.add_vertex(10.0, 10.0, PATH_CMD_MOVE_TO);
372 gen.add_vertex(50.0, 10.0, PATH_CMD_LINE_TO);
373 gen.add_vertex(30.0, 40.0, PATH_CMD_LINE_TO);
374 gen.add_vertex(0.0, 0.0, PATH_CMD_END_POLY | PATH_FLAGS_CLOSE);
375
376 let verts = collect_gen_vertices(&mut gen);
377 assert!(
379 verts.len() >= 6,
380 "Expected at least 6 vertices for closed triangle stroke, got {}",
381 verts.len()
382 );
383 }
384
385 #[test]
386 fn test_width_setter() {
387 let mut gen = VcgenStroke::new();
388 gen.set_width(5.0);
389 assert!((gen.width() - 5.0).abs() < 1e-10);
390 }
391
392 #[test]
393 fn test_line_cap_setter() {
394 let mut gen = VcgenStroke::new();
395 gen.set_line_cap(LineCap::Round);
396 assert_eq!(gen.line_cap(), LineCap::Round);
397 }
398
399 #[test]
400 fn test_line_join_setter() {
401 let mut gen = VcgenStroke::new();
402 gen.set_line_join(LineJoin::Round);
403 assert_eq!(gen.line_join(), LineJoin::Round);
404 }
405
406 #[test]
407 fn test_shorten() {
408 let mut gen = VcgenStroke::new();
409 gen.set_shorten(5.0);
410 assert!((gen.shorten() - 5.0).abs() < 1e-10);
411 }
412
413 #[test]
414 fn test_rewind_resets() {
415 let mut gen = VcgenStroke::new();
416 gen.set_width(10.0);
417 gen.add_vertex(0.0, 0.0, PATH_CMD_MOVE_TO);
418 gen.add_vertex(100.0, 0.0, PATH_CMD_LINE_TO);
419
420 let verts1 = collect_gen_vertices(&mut gen);
421 gen.rewind(0);
423 let mut verts2 = Vec::new();
424 loop {
425 let (mut x, mut y) = (0.0, 0.0);
426 let cmd = gen.vertex(&mut x, &mut y);
427 if is_stop(cmd) {
428 break;
429 }
430 verts2.push((x, y, cmd));
431 }
432 assert_eq!(verts1.len(), verts2.len());
433 }
434
435 #[test]
436 fn test_remove_all() {
437 let mut gen = VcgenStroke::new();
438 gen.add_vertex(0.0, 0.0, PATH_CMD_MOVE_TO);
439 gen.add_vertex(100.0, 0.0, PATH_CMD_LINE_TO);
440 gen.remove_all();
441 let verts = collect_gen_vertices(&mut gen);
442 assert!(verts.is_empty());
443 }
444
445 #[test]
446 fn test_round_cap_produces_more_vertices() {
447 let mut butt = VcgenStroke::new();
448 butt.set_width(20.0);
449 butt.set_line_cap(LineCap::Butt);
450 butt.add_vertex(0.0, 0.0, PATH_CMD_MOVE_TO);
451 butt.add_vertex(100.0, 0.0, PATH_CMD_LINE_TO);
452 let butt_verts = collect_gen_vertices(&mut butt);
453
454 let mut round = VcgenStroke::new();
455 round.set_width(20.0);
456 round.set_line_cap(LineCap::Round);
457 round.add_vertex(0.0, 0.0, PATH_CMD_MOVE_TO);
458 round.add_vertex(100.0, 0.0, PATH_CMD_LINE_TO);
459 let round_verts = collect_gen_vertices(&mut round);
460
461 assert!(
463 round_verts.len() > butt_verts.len(),
464 "Round ({}) should have more vertices than butt ({})",
465 round_verts.len(),
466 butt_verts.len()
467 );
468 }
469
470 #[test]
471 fn test_horizontal_line_stroke_y_extent() {
472 let mut gen = VcgenStroke::new();
473 gen.set_width(10.0); gen.add_vertex(0.0, 0.0, PATH_CMD_MOVE_TO);
475 gen.add_vertex(100.0, 0.0, PATH_CMD_LINE_TO);
476
477 let verts = collect_gen_vertices(&mut gen);
478 let max_y = verts.iter().map(|v| v.1).fold(f64::MIN, f64::max);
479 let min_y = verts.iter().map(|v| v.1).fold(f64::MAX, f64::min);
480
481 assert!(max_y >= 4.5, "Max y={} should be >= 4.5", max_y);
483 assert!(min_y <= -4.5, "Min y={} should be <= -4.5", min_y);
484 }
485}