1use crate::basics::{is_stop, VertexSource, PATH_CMD_MOVE_TO, PATH_CMD_STOP};
7use crate::trans_affine::TransAffine;
8
9#[derive(Clone, Copy, PartialEq)]
14#[allow(dead_code)]
15enum Status {
16 Initial,
17 Markers,
18 Polygon,
19 Stop,
20}
21
22pub struct ConvMarker<'a, ML: VertexSource, MS: VertexSource> {
30 marker_locator: &'a mut ML,
31 marker_shapes: &'a mut MS,
32 transform: TransAffine,
33 mtx: TransAffine,
34 status: Status,
35 marker: u32,
36 num_markers: u32,
37}
38
39impl<'a, ML: VertexSource, MS: VertexSource> ConvMarker<'a, ML, MS> {
40 pub fn new(marker_locator: &'a mut ML, marker_shapes: &'a mut MS) -> Self {
41 Self {
42 marker_locator,
43 marker_shapes,
44 transform: TransAffine::new(),
45 mtx: TransAffine::new(),
46 status: Status::Initial,
47 marker: 0,
48 num_markers: 1,
49 }
50 }
51
52 pub fn transform(&self) -> &TransAffine {
53 &self.transform
54 }
55
56 pub fn transform_mut(&mut self) -> &mut TransAffine {
57 &mut self.transform
58 }
59}
60
61impl<ML: VertexSource, MS: VertexSource> VertexSource for ConvMarker<'_, ML, MS> {
62 fn rewind(&mut self, _path_id: u32) {
63 self.status = Status::Initial;
64 self.marker = 0;
65 self.num_markers = 1;
66 }
67
68 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
69 let mut cmd = PATH_CMD_MOVE_TO;
70 let mut x1: f64 = 0.0;
71 let mut y1: f64 = 0.0;
72 let mut x2: f64 = 0.0;
73 let mut y2: f64 = 0.0;
74
75 loop {
78 if is_stop(cmd) {
79 return cmd;
80 }
81 match self.status {
82 Status::Initial => {
83 if self.num_markers == 0 {
84 cmd = PATH_CMD_STOP;
85 continue;
86 }
87 self.marker_locator.rewind(self.marker);
88 self.marker += 1;
89 self.num_markers = 0;
90 self.status = Status::Markers;
91 continue;
93 }
94 Status::Markers => {
95 if is_stop(self.marker_locator.vertex(&mut x1, &mut y1)) {
96 self.status = Status::Initial;
97 continue;
98 }
99 if is_stop(self.marker_locator.vertex(&mut x2, &mut y2)) {
100 self.status = Status::Initial;
101 continue;
102 }
103 self.num_markers += 1;
104 self.mtx = self.transform;
105 self.mtx
106 .multiply(&TransAffine::new_rotation((y2 - y1).atan2(x2 - x1)));
107 self.mtx.multiply(&TransAffine::new_translation(x1, y1));
108 self.marker_shapes.rewind(self.marker - 1);
109 self.status = Status::Polygon;
110 continue;
112 }
113 Status::Polygon => {
114 cmd = self.marker_shapes.vertex(x, y);
115 if is_stop(cmd) {
116 cmd = PATH_CMD_MOVE_TO;
117 self.status = Status::Markers;
118 continue;
119 }
120 self.mtx.transform(x, y);
121 return cmd;
122 }
123 Status::Stop => {
124 cmd = PATH_CMD_STOP;
125 continue;
126 }
127 }
128 }
129 }
130}
131
132#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::basics::{is_move_to, is_vertex, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP};
140
141 struct SimpleLocator {
143 vertices: Vec<(f64, f64, u32)>,
144 pos: usize,
145 active: bool,
146 }
147
148 impl SimpleLocator {
149 fn new(vertices: Vec<(f64, f64, u32)>) -> Self {
150 Self {
151 vertices,
152 pos: 0,
153 active: false,
154 }
155 }
156 }
157
158 impl VertexSource for SimpleLocator {
159 fn rewind(&mut self, path_id: u32) {
160 if path_id == 0 {
161 self.pos = 0;
162 self.active = true;
163 } else {
164 self.active = false;
165 }
166 }
167 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
168 if !self.active || self.pos >= self.vertices.len() {
169 return PATH_CMD_STOP;
170 }
171 let (vx, vy, cmd) = self.vertices[self.pos];
172 *x = vx;
173 *y = vy;
174 self.pos += 1;
175 cmd
176 }
177 }
178
179 struct TriangleMarker {
181 vertices: [(f64, f64, u32); 4],
182 pos: usize,
183 }
184
185 impl TriangleMarker {
186 fn new() -> Self {
187 Self {
188 vertices: [
189 (0.0, -5.0, PATH_CMD_MOVE_TO),
190 (5.0, 5.0, PATH_CMD_LINE_TO),
191 (-5.0, 5.0, PATH_CMD_LINE_TO),
192 (0.0, 0.0, PATH_CMD_STOP),
193 ],
194 pos: 0,
195 }
196 }
197 }
198
199 impl VertexSource for TriangleMarker {
200 fn rewind(&mut self, _path_id: u32) {
201 self.pos = 0;
202 }
203 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
204 if self.pos >= self.vertices.len() {
205 return PATH_CMD_STOP;
206 }
207 let (vx, vy, cmd) = self.vertices[self.pos];
208 *x = vx;
209 *y = vy;
210 self.pos += 1;
211 cmd
212 }
213 }
214
215 #[test]
216 fn test_single_marker_horizontal() {
217 let mut locator = SimpleLocator::new(vec![
219 (0.0, 0.0, PATH_CMD_MOVE_TO),
220 (10.0, 0.0, PATH_CMD_LINE_TO),
221 ]);
222 let mut shape = TriangleMarker::new();
223 let mut marker = ConvMarker::new(&mut locator, &mut shape);
224 marker.rewind(0);
225
226 let mut x = 0.0;
228 let mut y = 0.0;
229 let cmd = marker.vertex(&mut x, &mut y);
230 assert!(is_move_to(cmd));
231
232 let cmd = marker.vertex(&mut x, &mut y);
234 assert!(is_vertex(cmd));
235 let cmd = marker.vertex(&mut x, &mut y);
236 assert!(is_vertex(cmd));
237 }
238
239 #[test]
240 fn test_marker_at_origin_no_rotation() {
241 let mut locator = SimpleLocator::new(vec![
243 (0.0, 0.0, PATH_CMD_MOVE_TO),
244 (10.0, 0.0, PATH_CMD_LINE_TO),
245 ]);
246 let mut shape = TriangleMarker::new();
247 let mut marker = ConvMarker::new(&mut locator, &mut shape);
248 marker.rewind(0);
249
250 let mut x = 0.0;
251 let mut y = 0.0;
252 let cmd = marker.vertex(&mut x, &mut y);
253 assert!(is_move_to(cmd));
254 assert!((x - 0.0).abs() < 1e-8);
256 assert!((y - (-5.0)).abs() < 1e-8);
257 }
258
259 #[test]
260 fn test_marker_terminates() {
261 let mut locator = SimpleLocator::new(vec![
263 (0.0, 0.0, PATH_CMD_MOVE_TO),
264 (10.0, 0.0, PATH_CMD_LINE_TO),
265 ]);
266 let mut shape = TriangleMarker::new();
267 let mut marker = ConvMarker::new(&mut locator, &mut shape);
268 marker.rewind(0);
269
270 let mut x = 0.0;
271 let mut y = 0.0;
272 marker.vertex(&mut x, &mut y);
274 marker.vertex(&mut x, &mut y);
275 marker.vertex(&mut x, &mut y);
276 let cmd = marker.vertex(&mut x, &mut y);
278 assert!(is_stop(cmd));
279 }
280
281 #[test]
282 fn test_user_transform() {
283 let mut locator = SimpleLocator::new(vec![
284 (0.0, 0.0, PATH_CMD_MOVE_TO),
285 (10.0, 0.0, PATH_CMD_LINE_TO),
286 ]);
287 let mut shape = TriangleMarker::new();
288 let mut marker = ConvMarker::new(&mut locator, &mut shape);
289 *marker.transform_mut() = TransAffine::new_scaling_uniform(2.0);
291 marker.rewind(0);
292
293 let mut x = 0.0;
294 let mut y = 0.0;
295 marker.vertex(&mut x, &mut y);
296 assert!((x - 0.0).abs() < 1e-8);
298 assert!((y - (-10.0)).abs() < 1e-8);
299 }
300
301 #[test]
302 fn test_empty_locator() {
303 let mut locator = SimpleLocator::new(vec![]);
304 let mut shape = TriangleMarker::new();
305 let mut marker = ConvMarker::new(&mut locator, &mut shape);
306 marker.rewind(0);
307
308 let mut x = 0.0;
309 let mut y = 0.0;
310 let cmd = marker.vertex(&mut x, &mut y);
311 assert!(is_stop(cmd));
312 }
313
314 #[test]
315 fn test_single_vertex_locator() {
316 let mut locator = SimpleLocator::new(vec![(0.0, 0.0, PATH_CMD_MOVE_TO)]);
318 let mut shape = TriangleMarker::new();
319 let mut marker = ConvMarker::new(&mut locator, &mut shape);
320 marker.rewind(0);
321
322 let mut x = 0.0;
323 let mut y = 0.0;
324 let cmd = marker.vertex(&mut x, &mut y);
325 assert!(is_stop(cmd));
326 }
327
328 #[test]
329 fn test_marker_vertical_edge() {
330 let mut locator = SimpleLocator::new(vec![
332 (0.0, 0.0, PATH_CMD_MOVE_TO),
333 (0.0, 10.0, PATH_CMD_LINE_TO),
334 ]);
335 let mut shape = TriangleMarker::new();
336 let mut marker = ConvMarker::new(&mut locator, &mut shape);
337 marker.rewind(0);
338
339 let mut x = 0.0;
340 let mut y = 0.0;
341 marker.vertex(&mut x, &mut y);
342 assert!((x - 5.0).abs() < 1e-8);
344 assert!((y - 0.0).abs() < 1e-8);
345 }
346
347 #[test]
348 fn test_rewind_resets() {
349 let mut locator = SimpleLocator::new(vec![
350 (0.0, 0.0, PATH_CMD_MOVE_TO),
351 (10.0, 0.0, PATH_CMD_LINE_TO),
352 ]);
353 let mut shape = TriangleMarker::new();
354 let mut marker = ConvMarker::new(&mut locator, &mut shape);
355
356 marker.rewind(0);
357 let mut x = 0.0;
358 let mut y = 0.0;
359 marker.vertex(&mut x, &mut y);
360
361 marker.rewind(0);
363 let mut x2 = 0.0;
364 let mut y2 = 0.0;
365 marker.vertex(&mut x2, &mut y2);
366 assert_eq!(x, x2);
367 assert_eq!(y, y2);
368 }
369}