1use crate::basics::{is_closed, is_end_poly, is_move_to, is_stop, VertexSource};
8
9pub trait RendererPrimitivesLike {
17 type Color: Clone;
18
19 fn coord(c: f64) -> i32;
20 fn move_to(&mut self, x: i32, y: i32);
21 fn line_to(&mut self, x: i32, y: i32, last: bool);
22 fn set_line_color(&mut self, c: Self::Color);
23}
24
25pub struct RasterizerOutline<'a, Ren: RendererPrimitivesLike> {
37 ren: &'a mut Ren,
38 start_x: i32,
39 start_y: i32,
40 vertices: u32,
41}
42
43impl<'a, Ren: RendererPrimitivesLike> RasterizerOutline<'a, Ren> {
44 pub fn new(ren: &'a mut Ren) -> Self {
45 Self {
46 ren,
47 start_x: 0,
48 start_y: 0,
49 vertices: 0,
50 }
51 }
52
53 pub fn move_to(&mut self, x: i32, y: i32) {
55 self.vertices = 1;
56 self.start_x = x;
57 self.start_y = y;
58 self.ren.move_to(x, y);
59 }
60
61 pub fn line_to(&mut self, x: i32, y: i32) {
63 self.vertices += 1;
64 self.ren.line_to(x, y, false);
65 }
66
67 pub fn move_to_d(&mut self, x: f64, y: f64) {
69 self.move_to(Ren::coord(x), Ren::coord(y));
70 }
71
72 pub fn line_to_d(&mut self, x: f64, y: f64) {
74 self.line_to(Ren::coord(x), Ren::coord(y));
75 }
76
77 pub fn close(&mut self) {
79 if self.vertices > 2 {
80 self.line_to(self.start_x, self.start_y);
81 }
82 self.vertices = 0;
83 }
84
85 pub fn add_vertex(&mut self, x: f64, y: f64, cmd: u32) {
87 if is_move_to(cmd) {
88 self.move_to_d(x, y);
89 } else if is_end_poly(cmd) {
90 if is_closed(cmd) {
91 self.close();
92 }
93 } else {
94 self.line_to_d(x, y);
95 }
96 }
97
98 pub fn add_path<VS: VertexSource>(&mut self, vs: &mut VS, path_id: u32) {
100 vs.rewind(path_id);
101 let mut x = 0.0;
102 let mut y = 0.0;
103 loop {
104 let cmd = vs.vertex(&mut x, &mut y);
105 if is_stop(cmd) {
106 break;
107 }
108 self.add_vertex(x, y, cmd);
109 }
110 }
111
112 pub fn render_all_paths<VS: VertexSource>(
114 &mut self,
115 vs: &mut VS,
116 colors: &[Ren::Color],
117 path_ids: &[u32],
118 ) {
119 for i in 0..colors.len().min(path_ids.len()) {
120 self.ren.set_line_color(colors[i].clone());
121 self.add_path(vs, path_ids[i]);
122 }
123 }
124}
125
126#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::basics::{VertexSource, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP};
134
135 #[derive(Default)]
136 struct MockRenderer {
137 moves: Vec<(i32, i32)>,
138 lines: Vec<(i32, i32)>,
139 }
140
141 impl RendererPrimitivesLike for MockRenderer {
142 type Color = u32;
143
144 fn coord(c: f64) -> i32 {
145 (c * 256.0) as i32
146 }
147 fn move_to(&mut self, x: i32, y: i32) {
148 self.moves.push((x, y));
149 }
150 fn line_to(&mut self, x: i32, y: i32, _last: bool) {
151 self.lines.push((x, y));
152 }
153 fn set_line_color(&mut self, _c: u32) {}
154 }
155
156 #[test]
157 fn test_move_and_line_to() {
158 let mut ren = MockRenderer::default();
159 let mut ras = RasterizerOutline::new(&mut ren);
160 ras.move_to(10, 20);
161 ras.line_to(30, 40);
162
163 assert_eq!(ren.moves.len(), 1);
164 assert_eq!(ren.moves[0], (10, 20));
165 assert_eq!(ren.lines.len(), 1);
166 assert_eq!(ren.lines[0], (30, 40));
167 }
168
169 #[test]
170 fn test_close() {
171 let mut ren = MockRenderer::default();
172 let mut ras = RasterizerOutline::new(&mut ren);
173 ras.move_to(0, 0);
174 ras.line_to(100, 0);
175 ras.line_to(100, 100);
176 ras.close();
177
178 assert_eq!(ren.lines.len(), 3);
180 assert_eq!(ren.lines[2], (0, 0));
181 }
182
183 #[test]
184 fn test_close_with_fewer_than_3_vertices() {
185 let mut ren = MockRenderer::default();
186 let mut ras = RasterizerOutline::new(&mut ren);
187 ras.move_to(0, 0);
188 ras.line_to(100, 0);
189 ras.close();
190
191 assert_eq!(ren.lines.len(), 1);
193 }
194
195 #[test]
196 fn test_add_path() {
197 struct TrianglePath {
198 idx: usize,
199 }
200 impl VertexSource for TrianglePath {
201 fn rewind(&mut self, _path_id: u32) {
202 self.idx = 0;
203 }
204 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
205 let verts: [(f64, f64, u32); 4] = [
206 (0.0, 0.0, PATH_CMD_MOVE_TO),
207 (10.0, 0.0, PATH_CMD_LINE_TO),
208 (5.0, 10.0, PATH_CMD_LINE_TO),
209 (0.0, 0.0, PATH_CMD_STOP),
210 ];
211 if self.idx >= verts.len() {
212 return PATH_CMD_STOP;
213 }
214 let v = verts[self.idx];
215 *x = v.0;
216 *y = v.1;
217 self.idx += 1;
218 v.2
219 }
220 }
221
222 let mut ren = MockRenderer::default();
223 let mut ras = RasterizerOutline::new(&mut ren);
224 let mut path = TrianglePath { idx: 0 };
225 ras.add_path(&mut path, 0);
226
227 assert_eq!(ren.moves.len(), 1);
228 assert_eq!(ren.lines.len(), 2);
229 }
230
231 #[test]
232 fn test_move_to_d() {
233 let mut ren = MockRenderer::default();
234 let mut ras = RasterizerOutline::new(&mut ren);
235 ras.move_to_d(1.5, 2.5);
236 assert_eq!(ren.moves[0], (384, 640));
238 }
239}