diff --git a/Cargo.toml b/Cargo.toml index 0ddc088..5ae3ceb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,5 @@ embedded-graphics = "0.6.0" [dev-dependencies] embedded-graphics-simulator = "0.2.1" - +test-case = "1.0.0" diff --git a/examples/basic-plot/main.rs b/examples/basic-plot/main.rs index 2d090a2..4a41585 100644 --- a/examples/basic-plot/main.rs +++ b/examples/basic-plot/main.rs @@ -2,19 +2,26 @@ use embedded_graphics::{ pixelcolor::Rgb565, prelude::*, }; -use embedded_graphics_simulator::{SimulatorDisplay, Window, OutputSettingsBuilder}; -use embedded_plots::Plot; +use embedded_graphics_simulator::{ + SimulatorDisplay, + Window, + OutputSettingsBuilder +}; + +use embedded_plots::curve::{PlotPoint, CurvePoints}; fn main() -> Result<(), core::convert::Infallible> { let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); let data = vec![ - Point::new(100, 100), - Point::new(150, 100), - Point::new(200, 200)]; - Plot::new(data.as_slice() - ,RgbColor::GREEN) + PlotPoint{x: 0,y: 0}, + PlotPoint{x: 1,y: 1}, + PlotPoint{x: 2,y: 1}, + PlotPoint{x: 3,y: 0}, + ]; + CurvePoints::new(data.as_slice()) + .into_drawable_curve(&(0..3),&(0..1),&Point{x: 20, y: 20}, &Point{x:450,y:250},RgbColor::WHITE) .draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() diff --git a/src/curve.rs b/src/curve.rs new file mode 100644 index 0000000..93333d7 --- /dev/null +++ b/src/curve.rs @@ -0,0 +1,46 @@ +use core::ops::{Range}; + +use crate::range_conv::Scalable; +use crate::drawable_curve::DrawableCurve; +use embedded_graphics::prelude::*; + +pub struct PlotPoint { + pub x: i32, + pub y: i32, +} + +pub struct CurvePoints<'a>{ + points: &'a [PlotPoint], +} + +impl<'a> CurvePoints<'a> { + pub fn new(points: &'a [PlotPoint]) -> CurvePoints { + CurvePoints{points} + } + + pub fn into_drawable_curve(self, x_range: &'a Range, y_range: &'a Range, top_left : &'a Point, bottom_right: &'a Point, color: C) -> DrawableCurve + 'a> + 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()); + + let it = self.points.iter() + .map(move |p| Point{ + x: p.x.scale_between_ranges(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}), + }); + DrawableCurve::new(it,color) + } +} + + + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/drawable_curve.rs b/src/drawable_curve.rs new file mode 100644 index 0000000..a45656a --- /dev/null +++ b/src/drawable_curve.rs @@ -0,0 +1,42 @@ +use embedded_graphics::drawable::{Drawable, Pixel}; +use embedded_graphics::DrawTarget; +use embedded_graphics::geometry::Point; +use embedded_graphics::pixelcolor::{PixelColor}; +use embedded_graphics::primitives::{Line, Primitive}; +use embedded_graphics::style::PrimitiveStyle; + +pub struct DrawableCurve + where + I: Iterator, +{ + scaled_data: I, + color: C, +} + +impl<'a, C,I> DrawableCurve + where + C: PixelColor, + I: Iterator, +{ + pub fn new(data: I,color : C) -> DrawableCurve { + DrawableCurve { + scaled_data: data, + color, + } + } +} +impl<'a, C,I> Drawable for DrawableCurve + where C: PixelColor, + I: Iterator, +{ + fn draw>(self, display: &mut D) -> Result<(), >::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).into_styled(style).draw(display)?; + prev = point; + } + Ok(()) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index afd3ccd..d9d35b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,45 +1,5 @@ #![no_std] +pub mod curve; +pub mod drawable_curve; -use embedded_graphics::drawable::{Drawable}; -use embedded_graphics::DrawTarget; -use embedded_graphics::geometry::Point; -use embedded_graphics::pixelcolor::{PixelColor}; -use embedded_graphics::primitives::{Line, Primitive}; -use embedded_graphics::style::PrimitiveStyle; - -pub struct Plot<'a, C> -{ - data: &'a [Point], - color: C, -} - -impl<'a, C> Plot<'a, C> - where C: PixelColor -{ - pub fn new(data: &'a [Point],color : C) -> Plot { - Plot { - data, - color, - } - } -} - -impl<'a, C> Drawable for Plot<'a, C> - where C: PixelColor -{ - fn draw>(self, display: &mut D) -> Result<(), >::Error> { - let style = PrimitiveStyle::with_stroke(self.color,2); - for i in 1..self.data.len() { - Line::new(self.data[i-1],self.data[i]).into_styled(style).draw(display)?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +mod range_conv; diff --git a/src/range_conv.rs b/src/range_conv.rs new file mode 100644 index 0000000..e30c8e2 --- /dev/null +++ b/src/range_conv.rs @@ -0,0 +1,39 @@ +use core::ops::{Range, Add, Sub, Mul, Div}; + +pub trait Scalable + where + T: Copy + Add + Sub + Mul + Div, +{ + fn scale_between_ranges(&self,input_range: &Range, output_range: &Range) -> T; +} + +impl Scalable for T + where + T: Copy + Add + Sub + Mul + Div, +{ + fn scale_between_ranges(&self, input_range: &Range, output_range: &Range) -> T + { + (*self - input_range.start) * (output_range.end - output_range.start) + / (input_range.end - input_range.start) + output_range.start + } +} + + + +#[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) + } +}