1use core::ops::Range;
2
3use crate::range_conv::Scalable;
4use itertools::{Itertools, MinMaxResult, MinMaxResult::MinMax};
5
6use embedded_graphics::{draw_target::DrawTarget, geometry::Point, Drawable};
7
8use embedded_graphics::primitives::Primitive;
9use embedded_graphics::{primitives::Line, primitives::PrimitiveStyle};
10use embedded_graphics::pixelcolor::PixelColor;
11
12pub struct PlotPoint {
14 pub x: i32,
15 pub y: i32,
16}
17
18pub struct Curve<'a> {
20 points: &'a [PlotPoint],
22 pub x_range: Range<i32>,
23 pub y_range: Range<i32>,
24}
25
26impl<'a> Curve<'a> {
27 pub fn new(points: &'a [PlotPoint], x_range: Range<i32>, y_range: Range<i32>) -> Curve {
29 Curve {
30 points,
31 x_range,
32 y_range,
33 }
34 }
35
36 pub fn from_data(points: &'a [PlotPoint]) -> Curve {
38 let x_range = match points.iter().map(|p| (p.x)).minmax() {
39 MinMaxResult::NoElements => 0..0,
40 MinMaxResult::OneElement(v) => v..v,
41 MinMax(min, max) => min..max,
42 };
43
44 let y_range = match points.iter().map(|p| (p.y)).minmax() {
45 MinMaxResult::NoElements => 0..0,
46 MinMaxResult::OneElement(v) => v..v,
47 MinMax(min, max) => min..max,
48 };
49
50 Curve {
51 points,
52 x_range,
53 y_range,
54 }
55 }
56
57 pub fn into_drawable_curve<C>(
59 &self,
60 top_left: &'a Point,
61 bottom_right: &'a Point,
62 ) -> DrawableCurve<C, impl Iterator<Item = Point> + Clone + '_>
63 where
64 C: PixelColor,
65 {
66 assert!(top_left.x < bottom_right.x);
67 assert!(top_left.y < bottom_right.y);
68 assert!(!self.x_range.is_empty());
69 assert!(!self.y_range.is_empty());
70
71 let it = self.points.iter().map(move |p| Point {
72 x: p.x.scale_between_ranges(
73 &self.x_range,
74 &Range {
75 start: top_left.x,
76 end: bottom_right.x,
77 },
78 ),
79 y: p.y.scale_between_ranges(
80 &self.y_range,
81 &Range {
82 start: bottom_right.y,
83 end: top_left.y,
84 },
85 ),
86 });
87 DrawableCurve {
88 scaled_data: it,
89 color: None,
90 thickness: None,
91 }
92 }
93}
94
95pub struct DrawableCurve<C, I> {
97 scaled_data: I,
98 color: Option<C>,
99 thickness: Option<usize>,
100}
101
102impl<C, I> DrawableCurve<C, I>
104where
105 C: PixelColor,
106 I: Iterator<Item = Point> + Clone,
107{
108 pub fn set_color(mut self, color: C) -> DrawableCurve<C, I> {
110 self.color = Some(color);
111 self
112 }
113
114 pub fn set_thickness(mut self, thickness: usize) -> DrawableCurve<C, I> {
116 self.thickness = Some(thickness);
117 self
118 }
119}
120
121impl<C, I> Drawable for DrawableCurve<C, I>
122where
123 C: PixelColor + Default,
124 I: Iterator<Item = Point> + Clone,
125{
126 type Color = C;
127 type Output = ();
128
129 fn draw<D: DrawTarget<Color = C>>(
131 &self,
132 display: &mut D,
133 ) -> Result<(), <D as DrawTarget>::Error> {
134 let color = match &self.color {
135 None => C::default(),
136 Some(c) => *c,
137 };
138 let thickness = match &self.thickness {
139 None => 2,
140 Some(t) => *t,
141 };
142 let style = PrimitiveStyle::with_stroke(color, thickness as u32);
143 self.scaled_data.clone().tuple_windows().try_for_each(
144 |(prev, point)| -> Result<(), D::Error> {
145 Line::new(prev, point).into_styled(style).draw(display)
146 },
147 )
148 }
149}