1use crate::basics::{VertexSource, PATH_CMD_LINE_TO, PATH_CMD_MOVE_TO, PATH_CMD_STOP};
8use crate::math::{calc_intersection, dilate_triangle};
9
10#[derive(Clone)]
18pub struct CoordType<C: Clone> {
19 pub x: f64,
20 pub y: f64,
21 pub color: C,
22}
23
24impl<C: Clone + Default> Default for CoordType<C> {
25 fn default() -> Self {
26 Self {
27 x: 0.0,
28 y: 0.0,
29 color: C::default(),
30 }
31 }
32}
33
34pub struct SpanGouraud<C: Clone> {
49 coord: [CoordType<C>; 3],
50 x: [f64; 8],
51 y: [f64; 8],
52 cmd: [u32; 8],
53 vertex: usize,
54}
55
56impl<C: Clone + Default> SpanGouraud<C> {
57 pub fn new() -> Self {
58 let mut s = Self {
59 coord: [
60 CoordType::default(),
61 CoordType::default(),
62 CoordType::default(),
63 ],
64 x: [0.0; 8],
65 y: [0.0; 8],
66 cmd: [PATH_CMD_STOP; 8],
67 vertex: 0,
68 };
69 s.cmd[0] = PATH_CMD_STOP;
70 s
71 }
72
73 #[allow(clippy::too_many_arguments)]
75 pub fn new_with_triangle(
76 c1: C,
77 c2: C,
78 c3: C,
79 x1: f64,
80 y1: f64,
81 x2: f64,
82 y2: f64,
83 x3: f64,
84 y3: f64,
85 d: f64,
86 ) -> Self {
87 let mut s = Self::new();
88 s.colors(c1, c2, c3);
89 s.triangle(x1, y1, x2, y2, x3, y3, d);
90 s
91 }
92
93 pub fn colors(&mut self, c1: C, c2: C, c3: C) {
95 self.coord[0].color = c1;
96 self.coord[1].color = c2;
97 self.coord[2].color = c3;
98 }
99
100 #[allow(clippy::too_many_arguments)]
106 pub fn triangle(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, d: f64) {
107 self.coord[0].x = x1;
108 self.coord[0].y = y1;
109 self.coord[1].x = x2;
110 self.coord[1].y = y2;
111 self.coord[2].x = x3;
112 self.coord[2].y = y3;
113
114 self.x[0] = x1;
115 self.y[0] = y1;
116 self.x[1] = x2;
117 self.y[1] = y2;
118 self.x[2] = x3;
119 self.y[2] = y3;
120
121 self.cmd[0] = PATH_CMD_MOVE_TO;
122 self.cmd[1] = PATH_CMD_LINE_TO;
123 self.cmd[2] = PATH_CMD_LINE_TO;
124 self.cmd[3] = PATH_CMD_STOP;
125
126 if d != 0.0 {
127 let (dx, dy) = dilate_triangle(
128 self.coord[0].x,
129 self.coord[0].y,
130 self.coord[1].x,
131 self.coord[1].y,
132 self.coord[2].x,
133 self.coord[2].y,
134 d,
135 );
136 self.x[..6].copy_from_slice(&dx);
137 self.y[..6].copy_from_slice(&dy);
138
139 if let Some((ix, iy)) = calc_intersection(
141 self.x[4], self.y[4], self.x[5], self.y[5], self.x[0], self.y[0], self.x[1],
142 self.y[1],
143 ) {
144 self.coord[0].x = ix;
145 self.coord[0].y = iy;
146 }
147
148 if let Some((ix, iy)) = calc_intersection(
149 self.x[0], self.y[0], self.x[1], self.y[1], self.x[2], self.y[2], self.x[3],
150 self.y[3],
151 ) {
152 self.coord[1].x = ix;
153 self.coord[1].y = iy;
154 }
155
156 if let Some((ix, iy)) = calc_intersection(
157 self.x[2], self.y[2], self.x[3], self.y[3], self.x[4], self.y[4], self.x[5],
158 self.y[5],
159 ) {
160 self.coord[2].x = ix;
161 self.coord[2].y = iy;
162 }
163
164 self.cmd[3] = PATH_CMD_LINE_TO;
165 self.cmd[4] = PATH_CMD_LINE_TO;
166 self.cmd[5] = PATH_CMD_LINE_TO;
167 self.cmd[6] = PATH_CMD_STOP;
168 }
169 }
170
171 pub fn arrange_vertices(&self) -> [CoordType<C>; 3] {
176 let mut coord = [
177 self.coord[0].clone(),
178 self.coord[1].clone(),
179 self.coord[2].clone(),
180 ];
181
182 if self.coord[0].y > self.coord[2].y {
183 coord[0] = self.coord[2].clone();
184 coord[2] = self.coord[0].clone();
185 }
186
187 if coord[0].y > coord[1].y {
188 let tmp = coord[0].clone();
189 coord[0] = coord[1].clone();
190 coord[1] = tmp;
191 }
192
193 if coord[1].y > coord[2].y {
194 let tmp = coord[1].clone();
195 coord[1] = coord[2].clone();
196 coord[2] = tmp;
197 }
198
199 coord
200 }
201}
202
203impl<C: Clone + Default> Default for SpanGouraud<C> {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209impl<C: Clone + Default> VertexSource for SpanGouraud<C> {
210 fn rewind(&mut self, _path_id: u32) {
211 self.vertex = 0;
212 }
213
214 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
215 let idx = self.vertex;
216 *x = self.x[idx];
217 *y = self.y[idx];
218 let cmd = self.cmd[idx];
219 self.vertex += 1;
220 cmd
221 }
222}
223
224#[cfg(test)]
229mod tests {
230 use super::*;
231 use crate::color::Rgba8;
232
233 #[test]
234 fn test_new_default() {
235 let sg = SpanGouraud::<Rgba8>::new();
236 assert_eq!(sg.cmd[0], PATH_CMD_STOP);
237 }
238
239 #[test]
240 fn test_colors() {
241 let mut sg = SpanGouraud::<Rgba8>::new();
242 let red = Rgba8::new(255, 0, 0, 255);
243 let green = Rgba8::new(0, 255, 0, 255);
244 let blue = Rgba8::new(0, 0, 255, 255);
245 sg.colors(red, green, blue);
246 assert_eq!(sg.coord[0].color.r, 255);
247 assert_eq!(sg.coord[1].color.g, 255);
248 assert_eq!(sg.coord[2].color.b, 255);
249 }
250
251 #[test]
252 fn test_triangle_no_dilation() {
253 let mut sg = SpanGouraud::<Rgba8>::new();
254 sg.triangle(0.0, 0.0, 100.0, 0.0, 50.0, 100.0, 0.0);
255
256 assert_eq!(sg.cmd[0], PATH_CMD_MOVE_TO);
257 assert_eq!(sg.cmd[1], PATH_CMD_LINE_TO);
258 assert_eq!(sg.cmd[2], PATH_CMD_LINE_TO);
259 assert_eq!(sg.cmd[3], PATH_CMD_STOP);
260
261 assert_eq!(sg.x[0], 0.0);
262 assert_eq!(sg.y[0], 0.0);
263 assert_eq!(sg.x[1], 100.0);
264 assert_eq!(sg.y[1], 0.0);
265 assert_eq!(sg.x[2], 50.0);
266 assert_eq!(sg.y[2], 100.0);
267 }
268
269 #[test]
270 fn test_triangle_with_dilation() {
271 let mut sg = SpanGouraud::<Rgba8>::new();
272 sg.triangle(0.0, 0.0, 100.0, 0.0, 50.0, 100.0, 1.0);
273
274 assert_eq!(sg.cmd[0], PATH_CMD_MOVE_TO);
276 assert_eq!(sg.cmd[5], PATH_CMD_LINE_TO);
277 assert_eq!(sg.cmd[6], PATH_CMD_STOP);
278 }
279
280 #[test]
281 fn test_vertex_source_no_dilation() {
282 let mut sg = SpanGouraud::<Rgba8>::new();
283 sg.triangle(10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 0.0);
284 sg.rewind(0);
285
286 let mut x = 0.0;
287 let mut y = 0.0;
288
289 assert_eq!(sg.vertex(&mut x, &mut y), PATH_CMD_MOVE_TO);
290 assert_eq!(x, 10.0);
291 assert_eq!(y, 20.0);
292
293 assert_eq!(sg.vertex(&mut x, &mut y), PATH_CMD_LINE_TO);
294 assert_eq!(x, 30.0);
295 assert_eq!(y, 40.0);
296
297 assert_eq!(sg.vertex(&mut x, &mut y), PATH_CMD_LINE_TO);
298 assert_eq!(x, 50.0);
299 assert_eq!(y, 60.0);
300
301 assert_eq!(sg.vertex(&mut x, &mut y), PATH_CMD_STOP);
302 }
303
304 #[test]
305 fn test_arrange_vertices() {
306 let mut sg = SpanGouraud::<Rgba8>::new();
307 let red = Rgba8::new(255, 0, 0, 255);
308 let green = Rgba8::new(0, 255, 0, 255);
309 let blue = Rgba8::new(0, 0, 255, 255);
310 sg.colors(red, green, blue);
311 sg.triangle(50.0, 100.0, 0.0, 0.0, 100.0, 50.0, 0.0);
313
314 let sorted = sg.arrange_vertices();
315 assert!(sorted[0].y <= sorted[1].y);
316 assert!(sorted[1].y <= sorted[2].y);
317 assert_eq!(sorted[0].y, 0.0);
319 assert_eq!(sorted[0].color.g, 255); }
321
322 #[test]
323 fn test_new_with_triangle() {
324 let red = Rgba8::new(255, 0, 0, 255);
325 let green = Rgba8::new(0, 255, 0, 255);
326 let blue = Rgba8::new(0, 0, 255, 255);
327 let sg = SpanGouraud::new_with_triangle(
328 red, green, blue, 0.0, 0.0, 100.0, 0.0, 50.0, 100.0, 0.0,
329 );
330 assert_eq!(sg.coord[0].color.r, 255);
331 assert_eq!(sg.coord[1].color.g, 255);
332 assert_eq!(sg.x[2], 50.0);
333 }
334
335 #[test]
336 fn test_arrange_already_sorted() {
337 let mut sg = SpanGouraud::<Rgba8>::new();
338 let c = Rgba8::new(128, 128, 128, 255);
339 sg.colors(c, c, c);
340 sg.triangle(0.0, 0.0, 50.0, 50.0, 100.0, 100.0, 0.0);
341
342 let sorted = sg.arrange_vertices();
343 assert_eq!(sorted[0].y, 0.0);
344 assert_eq!(sorted[1].y, 50.0);
345 assert_eq!(sorted[2].y, 100.0);
346 }
347
348 #[test]
349 fn test_rewind_resets_vertex() {
350 let mut sg = SpanGouraud::<Rgba8>::new();
351 sg.triangle(0.0, 0.0, 10.0, 10.0, 20.0, 0.0, 0.0);
352
353 let mut x = 0.0;
354 let mut y = 0.0;
355 sg.rewind(0);
356 sg.vertex(&mut x, &mut y);
357 sg.vertex(&mut x, &mut y);
358
359 sg.rewind(0);
361 let cmd = sg.vertex(&mut x, &mut y);
362 assert_eq!(cmd, PATH_CMD_MOVE_TO);
363 assert_eq!(x, 0.0);
364 }
365}