1use core::fmt;
13use embedded_graphics::{
14 mono_font::{MonoFont, MonoTextStyle},
15 pixelcolor::PixelColor,
16 prelude::*,
17 primitives::{Circle, Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment},
18 text::{Alignment, Text},
19};
20
21#[derive(Copy, Clone, Debug)]
26pub struct RenderError;
27
28impl fmt::Display for RenderError {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.write_str("render error")
31 }
32}
33
34pub trait Renderer<C: PixelColor> {
38 fn fill_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError>;
40 fn stroke_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError>;
42 fn fill_circle(&mut self, center: Point, radius: u32, color: C) -> Result<(), RenderError>;
44 fn stroke_line(
46 &mut self,
47 start: Point,
48 end: Point,
49 color: C,
50 width: u32,
51 ) -> Result<(), RenderError>;
52 fn draw_text(
54 &mut self,
55 text: &str,
56 position: Point,
57 font: &MonoFont<'_>,
58 color: C,
59 alignment: Alignment,
60 ) -> Result<(), RenderError>;
61
62 fn draw_image(
64 &mut self,
65 _top_left: Point,
66 _size: Size,
67 _pixels: &[C],
68 ) -> Result<(), RenderError> {
69 Ok(())
70 }
71
72 fn stroke_arc(
86 &mut self,
87 center: Point,
88 radius: u32,
89 start_deg: i32,
90 sweep_deg: i32,
91 width: u32,
92 color: C,
93 ) -> Result<(), RenderError> {
94 if radius == 0 || width == 0 || sweep_deg == 0 {
95 return Ok(());
96 }
97 let total = sweep_deg.unsigned_abs().min(360);
101 let step: i32 = if sweep_deg >= 0 { 1 } else { -1 };
102 let r = radius as f32;
103
104 let point_at = |deg: i32| -> Point {
105 let (s, c) = arc_sin_cos(deg);
106 Point::new(center.x + (c * r) as i32, center.y - (s * r) as i32)
109 };
110
111 let mut prev = point_at(start_deg);
112 for i in 1..=total as i32 {
113 let next = point_at(start_deg + i * step);
114 self.stroke_line(prev, next, color, width)?;
115 prev = next;
116 }
117 Ok(())
118 }
119
120 fn fill_arc(
128 &mut self,
129 center: Point,
130 radius: u32,
131 start_deg: i32,
132 sweep_deg: i32,
133 color: C,
134 ) -> Result<(), RenderError> {
135 if radius == 0 || sweep_deg == 0 {
136 return Ok(());
137 }
138 let total = sweep_deg.unsigned_abs().min(360);
139 let step: i32 = if sweep_deg >= 0 { 1 } else { -1 };
140 let r = radius as f32;
141 for i in 0..=total as i32 {
144 let (s, c) = arc_sin_cos(start_deg + i * step);
145 let end = Point::new(center.x + (c * r) as i32, center.y - (s * r) as i32);
146 self.stroke_line(center, end, color, 2)?;
147 }
148 Ok(())
149 }
150
151 fn push_clip(&mut self, _rect: Rectangle) {}
159
160 fn pop_clip(&mut self) {}
163}
164
165pub struct DrawTargetRenderer<'d, D> {
167 target: &'d mut D,
168}
169
170impl<'d, D> DrawTargetRenderer<'d, D> {
171 pub fn new(target: &'d mut D) -> Self {
173 Self { target }
174 }
175}
176
177impl<'d, C, D> Renderer<C> for DrawTargetRenderer<'d, D>
178where
179 C: PixelColor,
180 D: DrawTarget<Color = C>,
181{
182 fn fill_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError> {
183 rect.into_styled(PrimitiveStyle::with_fill(color))
184 .draw(self.target)
185 .map_err(|_| RenderError)
186 }
187
188 fn stroke_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError> {
189 let style = PrimitiveStyleBuilder::new()
190 .stroke_color(color)
191 .stroke_width(1)
192 .stroke_alignment(StrokeAlignment::Inside)
193 .build();
194 rect.into_styled(style)
195 .draw(self.target)
196 .map_err(|_| RenderError)
197 }
198
199 fn fill_circle(&mut self, center: Point, radius: u32, color: C) -> Result<(), RenderError> {
200 Circle::with_center(center, radius * 2)
202 .into_styled(PrimitiveStyle::with_fill(color))
203 .draw(self.target)
204 .map_err(|_| RenderError)
205 }
206
207 fn stroke_line(
208 &mut self,
209 start: Point,
210 end: Point,
211 color: C,
212 width: u32,
213 ) -> Result<(), RenderError> {
214 Line::new(start, end)
215 .into_styled(PrimitiveStyle::with_stroke(color, width))
216 .draw(self.target)
217 .map_err(|_| RenderError)
218 }
219
220 fn draw_text(
221 &mut self,
222 text: &str,
223 position: Point,
224 font: &MonoFont<'_>,
225 color: C,
226 alignment: Alignment,
227 ) -> Result<(), RenderError> {
228 let style = MonoTextStyle::new(font, color);
229 Text::with_alignment(text, position, style, alignment)
230 .draw(self.target)
231 .map(|_| ())
232 .map_err(|_| RenderError)
233 }
234
235 fn draw_image(&mut self, top_left: Point, size: Size, pixels: &[C]) -> Result<(), RenderError> {
236 let area = Rectangle::new(top_left, size);
237 self.target
238 .fill_contiguous(&area, pixels.iter().copied())
239 .map_err(|_| RenderError)
240 }
241}
242
243static SIN_TABLE: [f32; 360] = [
249 0.0,
250 0.01745241,
251 0.0348995,
252 0.05233596,
253 0.06975647,
254 0.08715574,
255 0.1045285,
256 0.1218693,
257 0.1391731,
258 0.1564345,
259 0.1736482,
260 0.190809,
261 0.2079117,
262 0.2249511,
263 0.2419219,
264 0.258819,
265 0.2756374,
266 0.2923717,
267 0.309017,
268 0.3255682,
269 0.3420201,
270 0.3583679,
271 0.3746066,
272 0.3907311,
273 0.4067366,
274 0.4226183,
275 0.4383711,
276 0.4539905,
277 0.4694716,
278 0.4848096,
279 0.5,
280 0.5150381,
281 0.5299193,
282 0.544639,
283 0.5591929,
284 0.5735764,
285 0.5877853,
286 0.601815,
287 0.6156615,
288 0.6293204,
289 0.6427876,
290 0.656059,
291 0.6691306,
292 0.6819984,
293 0.6946584,
294 0.7071068,
295 0.7193398,
296 0.7313537,
297 0.7431448,
298 0.7547096,
299 0.7660444,
300 0.777146,
301 0.7880108,
302 0.7986355,
303 0.809017,
304 0.819152,
305 0.8290376,
306 0.8386706,
307 0.8480481,
308 0.8571673,
309 0.8660254,
310 0.8746197,
311 0.8829476,
312 0.8910065,
313 0.898794,
314 0.9063078,
315 0.9135455,
316 0.9205049,
317 0.9271839,
318 0.9335804,
319 0.9396926,
320 0.9455186,
321 0.9510565,
322 0.9563048,
323 0.9612617,
324 0.9659258,
325 0.9702957,
326 0.9743701,
327 0.9781476,
328 0.9816272,
329 0.9848078,
330 0.9876883,
331 0.9902681,
332 0.9925462,
333 0.9945219,
334 0.9961947,
335 0.9975641,
336 0.9986295,
337 0.9993908,
338 0.9998477,
339 1.0,
340 0.9998477,
341 0.9993908,
342 0.9986295,
343 0.9975641,
344 0.9961947,
345 0.9945219,
346 0.9925462,
347 0.9902681,
348 0.9876883,
349 0.9848078,
350 0.9816272,
351 0.9781476,
352 0.9743701,
353 0.9702957,
354 0.9659258,
355 0.9612617,
356 0.9563048,
357 0.9510565,
358 0.9455186,
359 0.9396926,
360 0.9335804,
361 0.9271839,
362 0.9205049,
363 0.9135455,
364 0.9063078,
365 0.898794,
366 0.8910065,
367 0.8829476,
368 0.8746197,
369 0.8660254,
370 0.8571673,
371 0.8480481,
372 0.8386706,
373 0.8290376,
374 0.819152,
375 0.809017,
376 0.7986355,
377 0.7880108,
378 0.777146,
379 0.7660444,
380 0.7547096,
381 0.7431448,
382 0.7313537,
383 0.7193398,
384 0.7071068,
385 0.6946584,
386 0.6819984,
387 0.6691306,
388 0.656059,
389 0.6427876,
390 0.6293204,
391 0.6156615,
392 0.601815,
393 0.5877853,
394 0.5735764,
395 0.5591929,
396 0.544639,
397 0.5299193,
398 0.5150381,
399 0.5,
400 0.4848096,
401 0.4694716,
402 0.4539905,
403 0.4383711,
404 0.4226183,
405 0.4067366,
406 0.3907311,
407 0.3746066,
408 0.3583679,
409 0.3420201,
410 0.3255682,
411 0.309017,
412 0.2923717,
413 0.2756374,
414 0.258819,
415 0.2419219,
416 0.2249511,
417 0.2079117,
418 0.190809,
419 0.1736482,
420 0.1564345,
421 0.1391731,
422 0.1218693,
423 0.1045285,
424 0.08715574,
425 0.06975647,
426 0.05233596,
427 0.0348995,
428 0.01745241,
429 0.0,
430 -0.01745241,
431 -0.0348995,
432 -0.05233596,
433 -0.06975647,
434 -0.08715574,
435 -0.1045285,
436 -0.1218693,
437 -0.1391731,
438 -0.1564345,
439 -0.1736482,
440 -0.190809,
441 -0.2079117,
442 -0.2249511,
443 -0.2419219,
444 -0.258819,
445 -0.2756374,
446 -0.2923717,
447 -0.309017,
448 -0.3255682,
449 -0.3420201,
450 -0.3583679,
451 -0.3746066,
452 -0.3907311,
453 -0.4067366,
454 -0.4226183,
455 -0.4383711,
456 -0.4539905,
457 -0.4694716,
458 -0.4848096,
459 -0.5,
460 -0.5150381,
461 -0.5299193,
462 -0.544639,
463 -0.5591929,
464 -0.5735764,
465 -0.5877853,
466 -0.601815,
467 -0.6156615,
468 -0.6293204,
469 -0.6427876,
470 -0.656059,
471 -0.6691306,
472 -0.6819984,
473 -0.6946584,
474 -0.7071068,
475 -0.7193398,
476 -0.7313537,
477 -0.7431448,
478 -0.7547096,
479 -0.7660444,
480 -0.777146,
481 -0.7880108,
482 -0.7986355,
483 -0.809017,
484 -0.819152,
485 -0.8290376,
486 -0.8386706,
487 -0.8480481,
488 -0.8571673,
489 -0.8660254,
490 -0.8746197,
491 -0.8829476,
492 -0.8910065,
493 -0.898794,
494 -0.9063078,
495 -0.9135455,
496 -0.9205049,
497 -0.9271839,
498 -0.9335804,
499 -0.9396926,
500 -0.9455186,
501 -0.9510565,
502 -0.9563048,
503 -0.9612617,
504 -0.9659258,
505 -0.9702957,
506 -0.9743701,
507 -0.9781476,
508 -0.9816272,
509 -0.9848078,
510 -0.9876883,
511 -0.9902681,
512 -0.9925462,
513 -0.9945219,
514 -0.9961947,
515 -0.9975641,
516 -0.9986295,
517 -0.9993908,
518 -0.9998477,
519 -1.0,
520 -0.9998477,
521 -0.9993908,
522 -0.9986295,
523 -0.9975641,
524 -0.9961947,
525 -0.9945219,
526 -0.9925462,
527 -0.9902681,
528 -0.9876883,
529 -0.9848078,
530 -0.9816272,
531 -0.9781476,
532 -0.9743701,
533 -0.9702957,
534 -0.9659258,
535 -0.9612617,
536 -0.9563048,
537 -0.9510565,
538 -0.9455186,
539 -0.9396926,
540 -0.9335804,
541 -0.9271839,
542 -0.9205049,
543 -0.9135455,
544 -0.9063078,
545 -0.898794,
546 -0.8910065,
547 -0.8829476,
548 -0.8746197,
549 -0.8660254,
550 -0.8571673,
551 -0.8480481,
552 -0.8386706,
553 -0.8290376,
554 -0.819152,
555 -0.809017,
556 -0.7986355,
557 -0.7880108,
558 -0.777146,
559 -0.7660444,
560 -0.7547096,
561 -0.7431448,
562 -0.7313537,
563 -0.7193398,
564 -0.7071068,
565 -0.6946584,
566 -0.6819984,
567 -0.6691306,
568 -0.656059,
569 -0.6427876,
570 -0.6293204,
571 -0.6156615,
572 -0.601815,
573 -0.5877853,
574 -0.5735764,
575 -0.5591929,
576 -0.544639,
577 -0.5299193,
578 -0.5150381,
579 -0.5,
580 -0.4848096,
581 -0.4694716,
582 -0.4539905,
583 -0.4383711,
584 -0.4226183,
585 -0.4067366,
586 -0.3907311,
587 -0.3746066,
588 -0.3583679,
589 -0.3420201,
590 -0.3255682,
591 -0.309017,
592 -0.2923717,
593 -0.2756374,
594 -0.258819,
595 -0.2419219,
596 -0.2249511,
597 -0.2079117,
598 -0.190809,
599 -0.1736482,
600 -0.1564345,
601 -0.1391731,
602 -0.1218693,
603 -0.1045285,
604 -0.08715574,
605 -0.06975647,
606 -0.05233596,
607 -0.0348995,
608 -0.01745241,
609];
610
611#[must_use]
617pub fn arc_sin_cos(deg: i32) -> (f32, f32) {
618 let d = deg.rem_euclid(360) as usize;
619 let c = (deg + 90).rem_euclid(360) as usize;
620 (SIN_TABLE[d], SIN_TABLE[c])
621}