From e2895dbc2642e892f693c3d9f231f0c6058b08fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Sun, 20 Dec 2020 12:11:06 +0100 Subject: [PATCH] Single- and Poly- Plot --- Cargo.toml | 2 +- examples/polyplot_mono.rs | 50 ++++++++++++++++++++++++++++++++ examples/polyplot_rgb.rs | 49 +++++++++++++++++++++++++++++++ examples/single_plot_mono.rs | 34 ++++++++++++++++++++++ examples/single_plot_rgb.rs | 37 ++++++++++++++++++++++++ src/curve.rs | 56 ++++++++++++++++++++++++------------ src/drawable_curve.rs | 13 +++++---- src/lib.rs | 4 ++- src/polyplot.rs | 39 +++++++++++++++++++++++++ src/range_conv.rs | 21 +++++++------- src/single_plot.rs | 39 +++++++++++++++++++++++++ 11 files changed, 307 insertions(+), 37 deletions(-) create mode 100644 examples/polyplot_mono.rs create mode 100644 examples/polyplot_rgb.rs create mode 100644 examples/single_plot_mono.rs create mode 100644 examples/single_plot_rgb.rs create mode 100644 src/polyplot.rs create mode 100644 src/single_plot.rs diff --git a/Cargo.toml b/Cargo.toml index 5ae3ceb..a4cb1d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] embedded-graphics = "0.6.0" - +itertools = "0.9.0" [dev-dependencies] embedded-graphics-simulator = "0.2.1" diff --git a/examples/polyplot_mono.rs b/examples/polyplot_mono.rs new file mode 100644 index 0000000..d767a12 --- /dev/null +++ b/examples/polyplot_mono.rs @@ -0,0 +1,50 @@ +use embedded_graphics::{ + pixelcolor::BinaryColor, + prelude::*, +}; + +use embedded_graphics_simulator::{ + SimulatorDisplay, + Window, + OutputSettingsBuilder, + BinaryColorTheme +}; + +use embedded_plots::{ + polyplot::{PolyPlot}, + curve::{PlotPoint, Curve}, +}; + +fn main() -> Result<(), core::convert::Infallible> { + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(128, 48)); + + let data1 = vec![ + PlotPoint { x: 0, y: 0 }, + PlotPoint { x: 1, y: 1 }, + PlotPoint { x: 2, y: 1 }, + PlotPoint { x: 3, y: 0 }, + ]; + + let data2 = vec![ + PlotPoint { x: 0, y: 1 }, + PlotPoint { x: 1, y: 0 }, + PlotPoint { x: 2, y: 3 }, + PlotPoint { x: 3, y: 2 }, + PlotPoint { x: 4, y: 2 }, + ]; + + let curves = vec![ + (Curve::from_data(data1.as_slice()), BinaryColor::On), + (Curve::from_data(data2.as_slice()), BinaryColor::On), + ]; + + let plot = PolyPlot::new(curves.as_slice(), Point { x: 10, y: 3 }, Point { x: 120, y: 45 }); + + plot.draw(&mut display)?; + let output_settings = OutputSettingsBuilder::new() + .theme(BinaryColorTheme::OledBlue) + .build(); + Window::new("Basic plot", &output_settings).show_static(&display); + + Ok(()) +} \ No newline at end of file diff --git a/examples/polyplot_rgb.rs b/examples/polyplot_rgb.rs new file mode 100644 index 0000000..056e3d5 --- /dev/null +++ b/examples/polyplot_rgb.rs @@ -0,0 +1,49 @@ +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::*, +}; + +use embedded_graphics_simulator::{ + SimulatorDisplay, + Window, + OutputSettingsBuilder, + +}; + +use embedded_plots::{ + polyplot::{PolyPlot}, + curve::{PlotPoint, Curve}, +}; + +fn main() -> Result<(), core::convert::Infallible> { + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); + + let data1 = vec![ + PlotPoint { x: 0, y: 0 }, + PlotPoint { x: 1, y: 1 }, + PlotPoint { x: 2, y: 1 }, + PlotPoint { x: 3, y: 0 }, + ]; + + let data2 = vec![ + PlotPoint { x: 0, y: 1 }, + PlotPoint { x: 1, y: 0 }, + PlotPoint { x: 2, y: 3 }, + PlotPoint { x: 3, y: 2 }, + PlotPoint { x: 4, y: 2 }, + ]; + + let curves = vec![ + (Curve::from_data(data1.as_slice()), RgbColor::YELLOW), + (Curve::from_data(data2.as_slice()), RgbColor::RED), + ]; + + let plot = PolyPlot::new(curves.as_slice(), Point { x: 10, y: 10 }, Point { x: 470, y: 260 }); + + plot.draw(&mut display)?; + let output_settings = OutputSettingsBuilder::new() + .build(); + Window::new("Basic plot", &output_settings).show_static(&display); + + Ok(()) +} \ No newline at end of file diff --git a/examples/single_plot_mono.rs b/examples/single_plot_mono.rs new file mode 100644 index 0000000..6796886 --- /dev/null +++ b/examples/single_plot_mono.rs @@ -0,0 +1,34 @@ +use embedded_graphics::{ + prelude::*, +}; + +use embedded_graphics_simulator::{SimulatorDisplay, Window, OutputSettingsBuilder, BinaryColorTheme}; + +use embedded_plots::{ + single_plot::{SinglePlot}, + curve::{PlotPoint, Curve}, +}; +use embedded_graphics::pixelcolor::BinaryColor; + +fn main() -> Result<(), core::convert::Infallible> { + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(128, 48)); + + let data = vec![ + PlotPoint { x: 0, y: 0 }, + PlotPoint { x: 1, y: 1 }, + PlotPoint { x: 2, y: 1 }, + PlotPoint { x: 3, y: 0 }, + ]; + let curve = Curve::from_data(data.as_slice()); + + let plot = SinglePlot::new(&curve, BinaryColor::On, Point { x: 9, y: 3 }, Point { x: 120, y: 45 }); + + plot.draw(&mut display)?; + let output_settings = OutputSettingsBuilder::new() + .theme(BinaryColorTheme::OledBlue) + .build(); + Window::new("Basic plot", &output_settings) + .show_static(&display); + + Ok(()) +} \ No newline at end of file diff --git a/examples/single_plot_rgb.rs b/examples/single_plot_rgb.rs new file mode 100644 index 0000000..3276bd2 --- /dev/null +++ b/examples/single_plot_rgb.rs @@ -0,0 +1,37 @@ +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::*, +}; + +use embedded_graphics_simulator::{ + SimulatorDisplay, + Window, + OutputSettingsBuilder, + +}; + +use embedded_plots::{ + single_plot::{SinglePlot}, + curve::{PlotPoint, Curve}, +}; + +fn main() -> Result<(), core::convert::Infallible> { + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); + + let data = vec![ + PlotPoint { x: 0, y: 0 }, + PlotPoint { x: 1, y: 1 }, + PlotPoint { x: 2, y: 1 }, + PlotPoint { x: 3, y: 0 }, + ]; + let curve = Curve::from_data(data.as_slice()); + + let plot = SinglePlot::new(&curve, RgbColor::YELLOW, Point { x: 10, y: 10 }, Point { x: 470, y: 260 }); + + plot.draw(&mut display)?; + let output_settings = OutputSettingsBuilder::new() + .build(); + Window::new("Basic plot", &output_settings).show_static(&display); + + Ok(()) +} \ No newline at end of file diff --git a/src/curve.rs b/src/curve.rs index 4915622..a05aba9 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -3,52 +3,72 @@ use core::ops::{Range}; use crate::range_conv::Scalable; use crate::drawable_curve::DrawableCurve; use embedded_graphics::prelude::*; +use itertools::MinMaxResult::MinMax; +use itertools::{Itertools, MinMaxResult}; pub struct PlotPoint { pub x: i32, pub y: i32, } -pub struct Curve<'a>{ +pub struct Curve<'a> { points: &'a [PlotPoint], + x_range: Range, + y_range: Range, } impl<'a> Curve<'a> { - pub fn new(points: &'a [PlotPoint]) -> Curve { - Curve {points} + pub fn new(points: &'a [PlotPoint], x_range: Range, y_range: Range) -> Curve { + Curve { points, x_range, y_range } } - pub fn into_drawable_curve(self, - x_range: &'a Range, - y_range: &'a Range, - top_left : &'a Point, + pub fn from_data(points: &'a [PlotPoint]) -> Curve { + let x_range = match points + .iter() + .map(|p| (p.x)) + .minmax() { + MinMaxResult::NoElements => 0..0, + MinMaxResult::OneElement(v) => v..v, + MinMax(min, max) => min..max, + }; + + let y_range = match points.iter().map(|p| (p.y)).minmax() { + MinMaxResult::NoElements => 0..0, + MinMaxResult::OneElement(v) => v..v, + MinMax(min, max) => min..max, + }; + + Curve { points, x_range, y_range } + } + + pub fn into_drawable_curve(&self, + top_left: &'a Point, bottom_right: &'a Point, - color: C - ) -> DrawableCurve + 'a> + color: C, + ) -> DrawableCurve + '_> where C: PixelColor { assert!(top_left.x < bottom_right.x); assert!(top_left.y < bottom_right.y); - assert!(!x_range.is_empty()); - assert!(!y_range.is_empty()); + assert!(!self.x_range.is_empty()); + assert!(!self.y_range.is_empty()); let it = self.points.iter() - .map(move |p| Point{ + .map(move |p| Point { x: p.x.scale_between_ranges( - x_range, - &Range{start: top_left.x, end: bottom_right.x} + &self.x_range, + &Range { start: top_left.x, end: bottom_right.x }, ), y: p.y.scale_between_ranges( - y_range, - &Range{start: bottom_right.y, end: top_left.y} + &self.y_range, + &Range { start: bottom_right.y, end: top_left.y }, ), }); - DrawableCurve::new(it,color) + DrawableCurve::new(it, color) } } - #[cfg(test)] mod tests { #[test] diff --git a/src/drawable_curve.rs b/src/drawable_curve.rs index 2421ed9..c082045 100644 --- a/src/drawable_curve.rs +++ b/src/drawable_curve.rs @@ -13,28 +13,29 @@ pub struct DrawableCurve color: C, } -impl DrawableCurve +impl DrawableCurve where C: PixelColor, I: Iterator, { - pub fn new(data: I,color : C) -> DrawableCurve { + pub fn new(data: I, color: C) -> DrawableCurve { DrawableCurve { scaled_data: data, color, } } } -impl Drawable for DrawableCurve + +impl Drawable for DrawableCurve where C: PixelColor, I: Iterator, { - fn draw>(self, display: &mut D) -> Result<(), >::Error> { - let style = PrimitiveStyle::with_stroke(self.color,2); + fn draw>(self, display: &mut D) -> Result<(), D::Error> { + let style = PrimitiveStyle::with_stroke(self.color, 2); let mut iter = self.scaled_data.into_iter(); let mut prev = iter.next().unwrap(); for point in iter { - Line::new(prev,point) + Line::new(prev, point) .into_styled(style) .draw(display)?; prev = point; diff --git a/src/lib.rs b/src/lib.rs index 62c1e0b..b8033c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] + pub mod curve; -pub mod plot; +pub mod polyplot; +pub mod single_plot; mod drawable_curve; mod range_conv; diff --git a/src/polyplot.rs b/src/polyplot.rs new file mode 100644 index 0000000..9ce8f82 --- /dev/null +++ b/src/polyplot.rs @@ -0,0 +1,39 @@ +use crate::curve::Curve; +use embedded_graphics::drawable::Drawable; +use embedded_graphics::DrawTarget; +use embedded_graphics::prelude::Point; +use embedded_graphics::pixelcolor::PixelColor; + +pub struct PolyPlot<'a, C> + where + C: PixelColor +{ + curves: &'a [(Curve<'a>, C)], + top_left: Point, + bottom_right: Point, +} + +impl<'a, C> PolyPlot<'a, C> + where + C: PixelColor +{ + pub fn new(curves: &'a [(Curve<'a>, C)], top_left: Point, bottom_right: Point) -> PolyPlot { + PolyPlot { curves, top_left, bottom_right } + } +} + +impl<'a, C> Drawable for PolyPlot<'a, C> + where + C: PixelColor +{ + fn draw>(self, display: &mut D) -> Result<(), D::Error> { + for (curve, color) in self.curves { + curve.into_drawable_curve( + &self.top_left, + &self.bottom_right, + *color, + ).draw(display)?; + } + Ok(()) + } +} \ No newline at end of file diff --git a/src/range_conv.rs b/src/range_conv.rs index e30c8e2..1821970 100644 --- a/src/range_conv.rs +++ b/src/range_conv.rs @@ -4,7 +4,7 @@ pub trait Scalable where T: Copy + Add + Sub + Mul + Div, { - fn scale_between_ranges(&self,input_range: &Range, output_range: &Range) -> T; + fn scale_between_ranges(&self, input_range: &Range, output_range: &Range) -> T; } impl Scalable for T @@ -19,21 +19,20 @@ impl Scalable for T } - #[cfg(test)] mod tests { use core::ops::Range; use test_case::test_case; use crate::range_conv::Scalable; - #[test_case(0..10,0..10,5 => 5 ; "equal ranges")] - #[test_case(0..10,0..20,5 => 10 ; "double")] - #[test_case(0..20,0..10,10 => 5 ; "half")] - #[test_case(-20..20,0..10,0 => 5 ; "negative input range")] - #[test_case(0..10,-20..20,5 => 0 ; "negative output range")] - #[test_case(0..10,10..0,2 => 8 ; "reversing")] - #[test_case(-20..20,0..20,-10 => 5 ; "reversing negative range")] - fn convert(in_range: Range,out_range: Range,val: i32) -> i32 { - val.scale_between_ranges(&in_range,&out_range) + #[test_case(0..10, 0..10, 5 => 5; "equal ranges")] + #[test_case(0..10, 0..20, 5 => 10; "double")] + #[test_case(0..20, 0..10, 10 => 5; "half")] + #[test_case(- 20..20, 0..10, 0 => 5; "negative input range")] + #[test_case(0..10, - 20..20, 5 => 0; "negative output range")] + #[test_case(0..10, 10..0, 2 => 8; "reversing")] + #[test_case(- 20..20, 0..20, - 10 => 5; "reversing negative range")] + fn convert(in_range: Range, out_range: Range, val: i32) -> i32 { + val.scale_between_ranges(&in_range, &out_range) } } diff --git a/src/single_plot.rs b/src/single_plot.rs new file mode 100644 index 0000000..95d4425 --- /dev/null +++ b/src/single_plot.rs @@ -0,0 +1,39 @@ +use crate::curve::Curve; +use embedded_graphics::drawable::Drawable; +use embedded_graphics::DrawTarget; +use embedded_graphics::prelude::Point; +use embedded_graphics::pixelcolor::PixelColor; + +pub struct SinglePlot<'a, C> + where + C: PixelColor +{ + curve: &'a Curve<'a>, + color: C, + top_left: Point, + bottom_right: Point, +} + +impl<'a, C> SinglePlot<'a, C> + where + C: PixelColor +{ + pub fn new(curve: &'a Curve<'a>, color: C, top_left: Point, bottom_right: Point) -> SinglePlot<'a, C> { + SinglePlot { curve, color, top_left, bottom_right} + } +} + +impl<'a, C> Drawable for SinglePlot<'a, C> + where + C: PixelColor +{ + fn draw>(self, display: &mut D) -> Result<(), D::Error> { + + self.curve.into_drawable_curve( + &self.top_left, + &self.bottom_right, + self.color, + ).draw(display)?; + Ok(()) + } +} \ No newline at end of file