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 01/16] 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 From 1f0e72977755d3d640fdffdb07f5e67987aa2c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Mon, 21 Dec 2020 01:28:22 +0100 Subject: [PATCH 02/16] Draft: Axis --- src/axis.rs | 82 +++++++++++++++++++++++++++++++++++++++++++ src/drawable_curve.rs | 2 -- src/lib.rs | 1 + src/plot.rs | 37 ------------------- src/polyplot.rs | 2 -- 5 files changed, 83 insertions(+), 41 deletions(-) create mode 100644 src/axis.rs delete mode 100644 src/plot.rs diff --git a/src/axis.rs b/src/axis.rs new file mode 100644 index 0000000..a09c678 --- /dev/null +++ b/src/axis.rs @@ -0,0 +1,82 @@ +use embedded_graphics::primitives::Line; +use embedded_graphics::drawable::Drawable; +use embedded_graphics::DrawTarget; +use core::ops::Range; +use embedded_graphics::prelude::*; +use embedded_graphics::style::{PrimitiveStyle}; +use core::borrow::Borrow; + +enum Orientation { + X { + x1: i32, + x2: i32, + y: i32, + }, + Y { + y1: i32, + y2: i32, + x: i32, + }, +} + +enum Scale { + Fixed { + interval: usize, + }, + RangeFraction { + fraction: usize, + }, +} + +pub struct Axis { + orientation: Orientation, + range: Range, + scale: Scale, + color: C +} + +impl Axis + where + C: PixelColor, +{ + fn new(orientation: Orientation, range: Range, scale: Scale, color: C) -> Axis { + Axis { orientation, range, scale, color } + } +} + + +impl Drawable for Axis + where + C: PixelColor, +{ + fn draw>(self, display: &mut D) -> Result<(), D::Error> { + let lines = match self.scale { + Scale::Fixed { interval } => { + self.range.step_by(interval) + } + Scale::RangeFraction { fraction } => { + let len = self.range.len(); + self.range.step_by(len / fraction) + } + }; + match self.orientation { + Orientation::X{x1,x2,y} => { + Line{start: Point{x: x1,y}, end: Point{x: x2,y}} + .into_styled(PrimitiveStyle::with_stroke(self.color,1)) + .draw(display)?; + for line in lines { + line.abs(); + } + } + Orientation::Y{y1,y2,x} => { + Line{start: Point{x,y: y1}, end: Point{x,y: y2}} + .into_styled(PrimitiveStyle::with_stroke(self.color,1)) + .draw(display)?; + for line in lines { + line.abs(); + } + } + } + Ok(()) + } +} \ No newline at end of file diff --git a/src/drawable_curve.rs b/src/drawable_curve.rs index c082045..4699bb6 100644 --- a/src/drawable_curve.rs +++ b/src/drawable_curve.rs @@ -6,8 +6,6 @@ use embedded_graphics::primitives::{Line, Primitive}; use embedded_graphics::style::PrimitiveStyle; pub struct DrawableCurve - where - I: Iterator, { scaled_data: I, color: C, diff --git a/src/lib.rs b/src/lib.rs index b8033c5..b4e540a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,4 @@ pub mod single_plot; mod drawable_curve; mod range_conv; +mod axis; diff --git a/src/plot.rs b/src/plot.rs deleted file mode 100644 index 44d6f95..0000000 --- a/src/plot.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::curve::Curve; -use embedded_graphics::drawable::Drawable; -use embedded_graphics::DrawTarget; -use embedded_graphics::prelude::Point; -use embedded_graphics::pixelcolor::PixelColor; - -struct Plot<'a, C> - where - C: PixelColor -{ - curves: &'a [Curve<'a>], - color: C, -} - -impl<'a, C> Plot<'a, C> { - fn new(curves: &'a [Curve<'a>], color : C) -> Plot { - Plot{curves, color} - } -} - -impl Drawable for Plot<'_, C> - where - C: PixelColor -{ - fn draw>(self, display: &mut D) -> Result<(), >::Error> { - for curve in self.curves { - curve.into_drawable_curve( - &(0..50), - &(-10..10), - &Point{x: 10,y: 10}, - &Point{x: 470, y: 270}, - self.color - ).draw(display)?; - } - Ok(()) - } -} \ No newline at end of file diff --git a/src/polyplot.rs b/src/polyplot.rs index 9ce8f82..34ae499 100644 --- a/src/polyplot.rs +++ b/src/polyplot.rs @@ -5,8 +5,6 @@ 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, From 975cf9b38afbafe3455e96bd9fdce5b68bbe0ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Wed, 30 Dec 2020 16:33:08 +0100 Subject: [PATCH 03/16] Dirty axis implementation --- Cargo.toml | 2 +- examples/basic-plot/main.rs | 32 -------------- examples/free_axis.rs | 51 ++++++++++++++++++++++ src/axis.rs | 84 ++++++++++++++++++++++++------------- src/lib.rs | 2 +- 5 files changed, 109 insertions(+), 62 deletions(-) delete mode 100644 examples/basic-plot/main.rs create mode 100644 examples/free_axis.rs diff --git a/Cargo.toml b/Cargo.toml index a4cb1d0..68ed282 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ edition = "2018" [dependencies] embedded-graphics = "0.6.0" itertools = "0.9.0" +heapless = "0.5.6" [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 deleted file mode 100644 index aefa48b..0000000 --- a/examples/basic-plot/main.rs +++ /dev/null @@ -1,32 +0,0 @@ -use embedded_graphics::{ - pixelcolor::Rgb565, - prelude::*, -}; - -use embedded_graphics_simulator::{ - SimulatorDisplay, - Window, - OutputSettingsBuilder -}; - -use embedded_plots::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}, - ]; - Curve::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() - .build(); - Window::new("Hello World", &output_settings).show_static(&display); - - Ok(()) -} \ No newline at end of file diff --git a/examples/free_axis.rs b/examples/free_axis.rs new file mode 100644 index 0000000..2376c61 --- /dev/null +++ b/examples/free_axis.rs @@ -0,0 +1,51 @@ +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::*, +}; + +use embedded_graphics_simulator::{ + SimulatorDisplay, + Window, + OutputSettingsBuilder, + +}; + +use embedded_plots::{ + axis::{Axis,Orientation,Scale}, +}; + +fn main() -> Result<(), core::convert::Infallible> { + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); + + Axis::new("X Fixed 0-100(10)",Orientation::X{x1: 10, x2: 230, y: 10},0..100,Scale::Fixed(10),RgbColor::WHITE) + .draw(&mut display)?; + + Axis::new("X Fixed 0-200(100)",Orientation::X{x1: 240, x2: 470, y: 10},0..200,Scale::Fixed(100),RgbColor::YELLOW) + .draw(&mut display)?; + + Axis::new("X Fixed 0-100(7)",Orientation::X{x1: 20, x2: 220, y: 30},0..100,Scale::RangeFraction(7),RgbColor::BLUE) + .draw(&mut display)?; + + Axis::new("X Fixed 0-200(4)",Orientation::X{x1: 250, x2: 460, y: 30},0..200,Scale::RangeFraction(4),RgbColor::RED) + .draw(&mut display)?; + + Axis::new("Y Fixed 0-100(10)",Orientation::Y{y1: 60, y2: 250, x: 90},0..100,Scale::Fixed(10),RgbColor::WHITE) + .draw(&mut display)?; + + Axis::new("Y Fixed 0-200(100)",Orientation::Y{y1: 70, y2: 230, x: 210},0..200,Scale::Fixed(100),RgbColor::YELLOW) + .draw(&mut display)?; + + Axis::new("Y Fixed 0-100(7)",Orientation::Y{y1: 60, y2: 180, x: 330},0..100,Scale::RangeFraction(7),RgbColor::BLUE) + .draw(&mut display)?; + + Axis::new("Y Fixed 0-200(4)",Orientation::Y{y1: 90, y2: 260, x: 450},0..200,Scale::RangeFraction(4),RgbColor::RED) + .draw(&mut display)?; + + + let output_settings = OutputSettingsBuilder::new() + // .pixel_spacing(1) + .build(); + Window::new("Basic plot", &output_settings).show_static(&display); + + Ok(()) +} \ No newline at end of file diff --git a/src/axis.rs b/src/axis.rs index a09c678..32e48ec 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -3,10 +3,13 @@ use embedded_graphics::drawable::Drawable; use embedded_graphics::DrawTarget; use core::ops::Range; use embedded_graphics::prelude::*; -use embedded_graphics::style::{PrimitiveStyle}; -use core::borrow::Borrow; +use embedded_graphics::style::{PrimitiveStyle, TextStyleBuilder}; +use crate::range_conv::Scalable; +use embedded_graphics::fonts::{Text, Font6x8}; +use heapless::{consts::*, String}; +use core::fmt::Write; -enum Orientation { +pub enum Orientation { X { x1: i32, x2: i32, @@ -19,61 +22,86 @@ enum Orientation { }, } -enum Scale { - Fixed { - interval: usize, - }, - RangeFraction { - fraction: usize, - }, +pub enum Scale { + Fixed(usize), + RangeFraction(usize), } -pub struct Axis { +pub struct Axis<'a, C> { + title: &'a str, orientation: Orientation, range: Range, scale: Scale, - color: C + color: C, } -impl Axis +impl<'a, C> Axis<'a, C> where C: PixelColor, { - fn new(orientation: Orientation, range: Range, scale: Scale, color: C) -> Axis { - Axis { orientation, range, scale, color } + pub fn new(title: &'a str, orientation: Orientation, range: Range, scale: Scale, color: C) -> Axis<'a, C> { + Axis { title, orientation, range, scale, color } } } -impl Drawable for Axis +impl<'a, C> Drawable for Axis<'a, C> where C: PixelColor, { fn draw>(self, display: &mut D) -> Result<(), D::Error> { let lines = match self.scale { - Scale::Fixed { interval } => { - self.range.step_by(interval) + Scale::Fixed(interval) => { + self.range.clone().into_iter().step_by(interval) } - Scale::RangeFraction { fraction } => { + Scale::RangeFraction(fraction) => { let len = self.range.len(); - self.range.step_by(len / fraction) + self.range.clone().into_iter().step_by(len / fraction) } }; + // Create a new text style + let style = TextStyleBuilder::new(Font6x8) + .text_color(self.color) + .build(); match self.orientation { - Orientation::X{x1,x2,y} => { - Line{start: Point{x: x1,y}, end: Point{x: x2,y}} - .into_styled(PrimitiveStyle::with_stroke(self.color,1)) + Orientation::X { x1, x2, y } => { + Line { start: Point { x: x1, y }, end: Point { x: x2, y } } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; + let title = Text::new(self.title, Point { x: x1, y: y + 10 }) + .into_styled(style); + let title = title.translate(Point { x: (x2 - x1) / 2 - title.size().width as i32 / 2, y: 0 }); + title.draw(display)?; + for line in lines { - line.abs(); + let x = line.scale_between_ranges(&self.range, &(x1..x2)); + Line { start: Point { x, y: y - 2 }, end: Point { x, y: y + 2 } } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .draw(display)?; + let mut buf: String:: = String::new(); + write!(buf, "{}", line).unwrap(); + Text::new(&buf, Point { x: x + 1, y: y + 1 }).into_styled(style).draw(display)?; } } - Orientation::Y{y1,y2,x} => { - Line{start: Point{x,y: y1}, end: Point{x,y: y2}} - .into_styled(PrimitiveStyle::with_stroke(self.color,1)) + Orientation::Y { y1, y2, x } => { + Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; + let title = Text::new(self.title, Point { x, y: y1 }) + .into_styled(style); + let title = title.translate(Point { x: -(title.size().width as i32) - 5, y: (y2-y1)/2 }); + title.draw(display)?; + for line in lines { - line.abs(); + let y = line.scale_between_ranges(&self.range, &(y2..y1)); + Line { start: Point { x: x - 2, y }, end: Point { x: x + 2, y} } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .draw(display)?; + let mut buf: String:: = String::new(); + write!(buf, "{}", line).unwrap(); + let tick = Text::new(&buf, Point { x, y}).into_styled(style); + let tick = tick.translate(Point{ x: -(tick.size().width as i32), y: 0 }); + tick.draw(display)?; } } } diff --git a/src/lib.rs b/src/lib.rs index b4e540a..60972ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,4 +6,4 @@ pub mod single_plot; mod drawable_curve; mod range_conv; -mod axis; +pub mod axis; From 7790a4fa4dfd40609bb43791d16528d31962a2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Thu, 31 Dec 2020 00:04:24 +0100 Subject: [PATCH 04/16] Move text style building out of Axis --- examples/free_axis.rs | 24 +++++++++++++++--------- src/axis.rs | 36 +++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/examples/free_axis.rs b/examples/free_axis.rs index 2376c61..23c2928 100644 --- a/examples/free_axis.rs +++ b/examples/free_axis.rs @@ -13,37 +13,43 @@ use embedded_graphics_simulator::{ use embedded_plots::{ axis::{Axis,Orientation,Scale}, }; +use embedded_graphics::style::TextStyleBuilder; +use embedded_graphics::fonts::Font6x8; fn main() -> Result<(), core::convert::Infallible> { let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); - Axis::new("X Fixed 0-100(10)",Orientation::X{x1: 10, x2: 230, y: 10},0..100,Scale::Fixed(10),RgbColor::WHITE) + let text_style = TextStyleBuilder::new(Font6x8) + .text_color(RgbColor::WHITE) + .build(); + + Axis::new("X Fixed 0-100(10)",Orientation::X{x1: 10, x2: 230, y: 10},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style) .draw(&mut display)?; - Axis::new("X Fixed 0-200(100)",Orientation::X{x1: 240, x2: 470, y: 10},0..200,Scale::Fixed(100),RgbColor::YELLOW) + Axis::new("X Fixed 0-200(100)",Orientation::X{x1: 240, x2: 470, y: 10},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style) .draw(&mut display)?; - Axis::new("X Fixed 0-100(7)",Orientation::X{x1: 20, x2: 220, y: 30},0..100,Scale::RangeFraction(7),RgbColor::BLUE) + Axis::new("X Frac 0-100(7)",Orientation::X{x1: 20, x2: 220, y: 30},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style) .draw(&mut display)?; - Axis::new("X Fixed 0-200(4)",Orientation::X{x1: 250, x2: 460, y: 30},0..200,Scale::RangeFraction(4),RgbColor::RED) + Axis::new("X Frac 0-200(4)",Orientation::X{x1: 250, x2: 460, y: 30},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style) .draw(&mut display)?; - Axis::new("Y Fixed 0-100(10)",Orientation::Y{y1: 60, y2: 250, x: 90},0..100,Scale::Fixed(10),RgbColor::WHITE) + Axis::new("Y Fixed 0-100(10)",Orientation::Y{y1: 60, y2: 250, x: 110},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style) .draw(&mut display)?; - Axis::new("Y Fixed 0-200(100)",Orientation::Y{y1: 70, y2: 230, x: 210},0..200,Scale::Fixed(100),RgbColor::YELLOW) + Axis::new("Y Fixed 0-200(100)",Orientation::Y{y1: 70, y2: 230, x: 230},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style) .draw(&mut display)?; - Axis::new("Y Fixed 0-100(7)",Orientation::Y{y1: 60, y2: 180, x: 330},0..100,Scale::RangeFraction(7),RgbColor::BLUE) + Axis::new("Y Frac 0-100(7)",Orientation::Y{y1: 60, y2: 180, x: 350},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style) .draw(&mut display)?; - Axis::new("Y Fixed 0-200(4)",Orientation::Y{y1: 90, y2: 260, x: 450},0..200,Scale::RangeFraction(4),RgbColor::RED) + Axis::new("Y Frac 0-200(4)",Orientation::Y{y1: 90, y2: 260, x: 470},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style) .draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() - // .pixel_spacing(1) + .pixel_spacing(1) .build(); Window::new("Basic plot", &output_settings).show_static(&display); diff --git a/src/axis.rs b/src/axis.rs index 32e48ec..177087d 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -3,9 +3,9 @@ use embedded_graphics::drawable::Drawable; use embedded_graphics::DrawTarget; use core::ops::Range; use embedded_graphics::prelude::*; -use embedded_graphics::style::{PrimitiveStyle, TextStyleBuilder}; +use embedded_graphics::style::{PrimitiveStyle, TextStyle}; use crate::range_conv::Scalable; -use embedded_graphics::fonts::{Text, Font6x8}; +use embedded_graphics::fonts::Text; use heapless::{consts::*, String}; use core::fmt::Write; @@ -27,27 +27,37 @@ pub enum Scale { RangeFraction(usize), } -pub struct Axis<'a, C> { +pub struct Axis<'a, C, F> + where + C: PixelColor, + F: Font, + TextStyle: Clone, +{ title: &'a str, orientation: Orientation, range: Range, scale: Scale, color: C, + text_style: TextStyle } -impl<'a, C> Axis<'a, C> +impl<'a, C, F> Axis<'a, C, F> where C: PixelColor, + F: Font, + TextStyle: Clone, { - pub fn new(title: &'a str, orientation: Orientation, range: Range, scale: Scale, color: C) -> Axis<'a, C> { - Axis { title, orientation, range, scale, color } + pub fn new(title: &'a str, orientation: Orientation, range: Range, scale: Scale, color: C, text_style: TextStyle) -> Axis<'a, C, F> { + Axis { title, orientation, range, scale, color, text_style } } } -impl<'a, C> Drawable for Axis<'a, C> +impl<'a, C, F> Drawable for Axis<'a, C, F> where C: PixelColor, + F: Font + Copy, + TextStyle: Clone, { fn draw>(self, display: &mut D) -> Result<(), D::Error> { let lines = match self.scale { @@ -59,17 +69,13 @@ impl<'a, C> Drawable for Axis<'a, C> self.range.clone().into_iter().step_by(len / fraction) } }; - // Create a new text style - let style = TextStyleBuilder::new(Font6x8) - .text_color(self.color) - .build(); match self.orientation { Orientation::X { x1, x2, y } => { Line { start: Point { x: x1, y }, end: Point { x: x2, y } } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; let title = Text::new(self.title, Point { x: x1, y: y + 10 }) - .into_styled(style); + .into_styled(self.text_style); let title = title.translate(Point { x: (x2 - x1) / 2 - title.size().width as i32 / 2, y: 0 }); title.draw(display)?; @@ -80,7 +86,7 @@ impl<'a, C> Drawable for Axis<'a, C> .draw(display)?; let mut buf: String:: = String::new(); write!(buf, "{}", line).unwrap(); - Text::new(&buf, Point { x: x + 1, y: y + 1 }).into_styled(style).draw(display)?; + Text::new(&buf, Point { x: x + 1, y: y + 1 }).into_styled(self.text_style).draw(display)?; } } Orientation::Y { y1, y2, x } => { @@ -88,7 +94,7 @@ impl<'a, C> Drawable for Axis<'a, C> .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; let title = Text::new(self.title, Point { x, y: y1 }) - .into_styled(style); + .into_styled(self.text_style); let title = title.translate(Point { x: -(title.size().width as i32) - 5, y: (y2-y1)/2 }); title.draw(display)?; @@ -99,7 +105,7 @@ impl<'a, C> Drawable for Axis<'a, C> .draw(display)?; let mut buf: String:: = String::new(); write!(buf, "{}", line).unwrap(); - let tick = Text::new(&buf, Point { x, y}).into_styled(style); + let tick = Text::new(&buf, Point { x, y}).into_styled(self.text_style); let tick = tick.translate(Point{ x: -(tick.size().width as i32), y: 0 }); tick.draw(display)?; } From 3dd3d856b758a208ee1a869645bb02d4eb0c163c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Thu, 31 Dec 2020 01:13:28 +0100 Subject: [PATCH 05/16] Make axis tick size an argument --- examples/free_axis.rs | 27 +++++++++++++++------------ src/axis.rs | 29 ++++++++++++++++------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/examples/free_axis.rs b/examples/free_axis.rs index 23c2928..119f86e 100644 --- a/examples/free_axis.rs +++ b/examples/free_axis.rs @@ -1,50 +1,53 @@ use embedded_graphics::{ pixelcolor::Rgb565, prelude::*, + style::TextStyleBuilder, + fonts::{Font6x8, Font6x6}, }; use embedded_graphics_simulator::{ SimulatorDisplay, Window, OutputSettingsBuilder, - }; use embedded_plots::{ axis::{Axis,Orientation,Scale}, }; -use embedded_graphics::style::TextStyleBuilder; -use embedded_graphics::fonts::Font6x8; fn main() -> Result<(), core::convert::Infallible> { let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); - let text_style = TextStyleBuilder::new(Font6x8) + let text_style_white = TextStyleBuilder::new(Font6x8) .text_color(RgbColor::WHITE) .build(); - Axis::new("X Fixed 0-100(10)",Orientation::X{x1: 10, x2: 230, y: 10},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style) + let text_style_yellow_compact = TextStyleBuilder::new(Font6x6) + .text_color(RgbColor::YELLOW) + .build(); + + Axis::new("X Fixed 0-100(10)",Orientation::X{x1: 10, x2: 230, y: 10},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style_white, 2) .draw(&mut display)?; - Axis::new("X Fixed 0-200(100)",Orientation::X{x1: 240, x2: 470, y: 10},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style) + Axis::new("X Fixed 0-200(100)",Orientation::X{x1: 240, x2: 470, y: 10},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style_yellow_compact, 1) .draw(&mut display)?; - Axis::new("X Frac 0-100(7)",Orientation::X{x1: 20, x2: 220, y: 30},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style) + Axis::new("X Frac 0-100(7)",Orientation::X{x1: 20, x2: 220, y: 30},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style_white, 3) .draw(&mut display)?; - Axis::new("X Frac 0-200(4)",Orientation::X{x1: 250, x2: 460, y: 30},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style) + Axis::new("X Frac 0-200(4)",Orientation::X{x1: 250, x2: 460, y: 30},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style_yellow_compact, 7) .draw(&mut display)?; - Axis::new("Y Fixed 0-100(10)",Orientation::Y{y1: 60, y2: 250, x: 110},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style) + Axis::new("Y Fixed 0-100(10)",Orientation::Y{y1: 70, y2: 250, x: 120},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style_white, 2) .draw(&mut display)?; - Axis::new("Y Fixed 0-200(100)",Orientation::Y{y1: 70, y2: 230, x: 230},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style) + Axis::new("Y Fixed 0-200(100)",Orientation::Y{y1: 70, y2: 230, x: 230},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style_yellow_compact, 1) .draw(&mut display)?; - Axis::new("Y Frac 0-100(7)",Orientation::Y{y1: 60, y2: 180, x: 350},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style) + Axis::new("Y Frac 0-100(7)",Orientation::Y{y1: 60, y2: 180, x: 350},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style_white, 3) .draw(&mut display)?; - Axis::new("Y Frac 0-200(4)",Orientation::Y{y1: 90, y2: 260, x: 470},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style) + Axis::new("Y Frac 0-200(4)",Orientation::Y{y1: 90, y2: 260, x: 470},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style_yellow_compact, 7) .draw(&mut display)?; diff --git a/src/axis.rs b/src/axis.rs index 177087d..c9d2aa1 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -38,7 +38,8 @@ pub struct Axis<'a, C, F> range: Range, scale: Scale, color: C, - text_style: TextStyle + text_style: TextStyle, + tick_size: usize, } impl<'a, C, F> Axis<'a, C, F> @@ -47,8 +48,8 @@ impl<'a, C, F> Axis<'a, C, F> F: Font, TextStyle: Clone, { - pub fn new(title: &'a str, orientation: Orientation, range: Range, scale: Scale, color: C, text_style: TextStyle) -> Axis<'a, C, F> { - Axis { title, orientation, range, scale, color, text_style } + pub fn new(title: &'a str, orientation: Orientation, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize) -> Axis<'a, C, F> { + Axis { title, orientation, range, scale, color, text_style, tick_size: tick_height } } } @@ -81,34 +82,36 @@ impl<'a, C, F> Drawable for Axis<'a, C, F> for line in lines { let x = line.scale_between_ranges(&self.range, &(x1..x2)); - Line { start: Point { x, y: y - 2 }, end: Point { x, y: y + 2 } } + Line { start: Point { x, y: y - self.tick_size as i32 }, end: Point { x, y: y + self.tick_size as i32 } } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; let mut buf: String:: = String::new(); write!(buf, "{}", line).unwrap(); - Text::new(&buf, Point { x: x + 1, y: y + 1 }).into_styled(self.text_style).draw(display)?; + Text::new(&buf, Point { x: x + 2, y: y + 2 }).into_styled(self.text_style).draw(display)?; } } Orientation::Y { y1, y2, x } => { Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; - let title = Text::new(self.title, Point { x, y: y1 }) - .into_styled(self.text_style); - let title = title.translate(Point { x: -(title.size().width as i32) - 5, y: (y2-y1)/2 }); - title.draw(display)?; + let mut max_tick_text_width = 0; for line in lines { let y = line.scale_between_ranges(&self.range, &(y2..y1)); - Line { start: Point { x: x - 2, y }, end: Point { x: x + 2, y} } + Line { start: Point { x: x - self.tick_size as i32, y }, end: Point { x: x + self.tick_size as i32, y} } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; let mut buf: String:: = String::new(); write!(buf, "{}", line).unwrap(); - let tick = Text::new(&buf, Point { x, y}).into_styled(self.text_style); - let tick = tick.translate(Point{ x: -(tick.size().width as i32), y: 0 }); - tick.draw(display)?; + let tick_val = Text::new(&buf, Point { x, y}).into_styled(self.text_style); + let tick_val = tick_val.translate(Point{ x: -(tick_val.size().width as i32) -2, y: 2 }); + if tick_val.size().width > max_tick_text_width { max_tick_text_width = tick_val.size().width } + tick_val.draw(display)?; } + let title = Text::new(self.title, Point { x, y: y1 }) + .into_styled(self.text_style); + let title = title.translate(Point { x: -(title.size().width as i32) - max_tick_text_width as i32 - self.tick_size as i32 - 2, y: (y2-y1)/2 }); + title.draw(display)?; } } Ok(()) From 098f89d908810bc29b01c92f8dee26c0f0c70d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Thu, 31 Dec 2020 01:44:57 +0100 Subject: [PATCH 06/16] Rename axis internals and add XY axis to example --- examples/free_axis.rs | 23 ++++++++++++++--------- src/axis.rs | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/examples/free_axis.rs b/examples/free_axis.rs index 119f86e..9181814 100644 --- a/examples/free_axis.rs +++ b/examples/free_axis.rs @@ -12,7 +12,7 @@ use embedded_graphics_simulator::{ }; use embedded_plots::{ - axis::{Axis,Orientation,Scale}, + axis::{Axis, Placement, Scale}, }; fn main() -> Result<(), core::convert::Infallible> { @@ -26,28 +26,33 @@ fn main() -> Result<(), core::convert::Infallible> { .text_color(RgbColor::YELLOW) .build(); - Axis::new("X Fixed 0-100(10)",Orientation::X{x1: 10, x2: 230, y: 10},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style_white, 2) + Axis::new("X Fixed 0-100(10)", Placement::X{x1: 40, x2: 230, y: 10}, 0..100, Scale::Fixed(10), RgbColor::WHITE, text_style_white, 2) .draw(&mut display)?; - Axis::new("X Fixed 0-200(100)",Orientation::X{x1: 240, x2: 470, y: 10},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style_yellow_compact, 1) + Axis::new("X Fixed 0-200(100)", Placement::X{x1: 240, x2: 470, y: 10}, 0..200, Scale::Fixed(100), RgbColor::YELLOW, text_style_yellow_compact, 1) .draw(&mut display)?; - Axis::new("X Frac 0-100(7)",Orientation::X{x1: 20, x2: 220, y: 30},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style_white, 3) + Axis::new("X Frac 0-100(7)", Placement::X{x1: 50, x2: 220, y: 30}, 0..100, Scale::RangeFraction(7), RgbColor::BLUE, text_style_white, 3) .draw(&mut display)?; - Axis::new("X Frac 0-200(4)",Orientation::X{x1: 250, x2: 460, y: 30},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style_yellow_compact, 7) + Axis::new("X Frac 0-200(4)", Placement::X{x1: 250, x2: 460, y: 40}, 0..200, Scale::RangeFraction(4), RgbColor::RED, text_style_yellow_compact, 7) .draw(&mut display)?; - Axis::new("Y Fixed 0-100(10)",Orientation::Y{y1: 70, y2: 250, x: 120},0..100,Scale::Fixed(10),RgbColor::WHITE, text_style_white, 2) + Axis::new("Y Fixed 0-100(10)", Placement::Y{y1: 70, y2: 230, x: 160}, 0..100, Scale::Fixed(10), RgbColor::WHITE, text_style_white, 2) .draw(&mut display)?; - Axis::new("Y Fixed 0-200(100)",Orientation::Y{y1: 70, y2: 230, x: 230},0..200,Scale::Fixed(100),RgbColor::YELLOW, text_style_yellow_compact, 1) + Axis::new("Y Fixed 0-200(100)", Placement::Y{y1: 70, y2: 210, x: 260}, 0..200, Scale::Fixed(100), RgbColor::YELLOW, text_style_yellow_compact, 1) .draw(&mut display)?; - Axis::new("Y Frac 0-100(7)",Orientation::Y{y1: 60, y2: 180, x: 350},0..100,Scale::RangeFraction(7),RgbColor::BLUE, text_style_white, 3) + Axis::new("Y Frac 0-100(7)", Placement::Y{y1: 60, y2: 180, x: 370}, 0..100, Scale::RangeFraction(7), RgbColor::BLUE, text_style_white, 3) .draw(&mut display)?; - Axis::new("Y Frac 0-200(4)",Orientation::Y{y1: 90, y2: 260, x: 470},0..200,Scale::RangeFraction(4),RgbColor::RED, text_style_yellow_compact, 7) + Axis::new("Y Frac 0-200(4)", Placement::Y{y1: 90, y2: 220, x: 470}, 0..200, Scale::RangeFraction(4), RgbColor::RED, text_style_yellow_compact, 7) + .draw(&mut display)?; + + Axis::new("X", Placement::X{x1: 30, x2: 470, y: 250}, 123..2137, Scale::Fixed(150), RgbColor::YELLOW, text_style_white, 2) + .draw(&mut display)?; + Axis::new("Y", Placement::Y{y1: 10, y2: 250, x: 30}, 0..2137, Scale::RangeFraction(15), RgbColor::WHITE, text_style_white, 2) .draw(&mut display)?; diff --git a/src/axis.rs b/src/axis.rs index c9d2aa1..32501f1 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -9,7 +9,7 @@ use embedded_graphics::fonts::Text; use heapless::{consts::*, String}; use core::fmt::Write; -pub enum Orientation { +pub enum Placement { X { x1: i32, x2: i32, @@ -34,7 +34,7 @@ pub struct Axis<'a, C, F> TextStyle: Clone, { title: &'a str, - orientation: Orientation, + placement: Placement, range: Range, scale: Scale, color: C, @@ -48,8 +48,8 @@ impl<'a, C, F> Axis<'a, C, F> F: Font, TextStyle: Clone, { - pub fn new(title: &'a str, orientation: Orientation, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize) -> Axis<'a, C, F> { - Axis { title, orientation, range, scale, color, text_style, tick_size: tick_height } + pub fn new(title: &'a str, orientation: Placement, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize) -> Axis<'a, C, F> { + Axis { title, placement: orientation, range, scale, color, text_style, tick_size: tick_height } } } @@ -61,7 +61,7 @@ impl<'a, C, F> Drawable for Axis<'a, C, F> TextStyle: Clone, { fn draw>(self, display: &mut D) -> Result<(), D::Error> { - let lines = match self.scale { + let scale_marks = match self.scale { Scale::Fixed(interval) => { self.range.clone().into_iter().step_by(interval) } @@ -70,8 +70,8 @@ impl<'a, C, F> Drawable for Axis<'a, C, F> self.range.clone().into_iter().step_by(len / fraction) } }; - match self.orientation { - Orientation::X { x1, x2, y } => { + match self.placement { + Placement::X { x1, x2, y } => { Line { start: Point { x: x1, y }, end: Point { x: x2, y } } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; @@ -80,29 +80,29 @@ impl<'a, C, F> Drawable for Axis<'a, C, F> let title = title.translate(Point { x: (x2 - x1) / 2 - title.size().width as i32 / 2, y: 0 }); title.draw(display)?; - for line in lines { - let x = line.scale_between_ranges(&self.range, &(x1..x2)); + for mark in scale_marks { + let x = mark.scale_between_ranges(&self.range, &(x1..x2)); Line { start: Point { x, y: y - self.tick_size as i32 }, end: Point { x, y: y + self.tick_size as i32 } } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; let mut buf: String:: = String::new(); - write!(buf, "{}", line).unwrap(); + write!(buf, "{}", mark).unwrap(); Text::new(&buf, Point { x: x + 2, y: y + 2 }).into_styled(self.text_style).draw(display)?; } } - Orientation::Y { y1, y2, x } => { + Placement::Y { y1, y2, x } => { Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; let mut max_tick_text_width = 0; - for line in lines { - let y = line.scale_between_ranges(&self.range, &(y2..y1)); + for mark in scale_marks { + let y = mark.scale_between_ranges(&self.range, &(y2..y1)); Line { start: Point { x: x - self.tick_size as i32, y }, end: Point { x: x + self.tick_size as i32, y} } .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) .draw(display)?; let mut buf: String:: = String::new(); - write!(buf, "{}", line).unwrap(); + write!(buf, "{}", mark).unwrap(); let tick_val = Text::new(&buf, Point { x, y}).into_styled(self.text_style); let tick_val = tick_val.translate(Point{ x: -(tick_val.size().width as i32) -2, y: 2 }); if tick_val.size().width > max_tick_text_width { max_tick_text_width = tick_val.size().width } From dc2edc77f3adafe328a8e40fd2df2808cdd6aa15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Sat, 2 Jan 2021 10:28:38 +0100 Subject: [PATCH 07/16] Use axis in single plot --- examples/single_plot_rgb.rs | 2 +- src/axis.rs | 4 ++++ src/single_plot.rs | 13 +++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/examples/single_plot_rgb.rs b/examples/single_plot_rgb.rs index 3276bd2..d413c68 100644 --- a/examples/single_plot_rgb.rs +++ b/examples/single_plot_rgb.rs @@ -26,7 +26,7 @@ fn main() -> Result<(), core::convert::Infallible> { ]; 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 }); + let plot = SinglePlot::new(&curve, RgbColor::YELLOW, Point { x: 50, y: 10 }, Point { x: 430, y: 250 }); plot.draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() diff --git a/src/axis.rs b/src/axis.rs index 32501f1..629dc94 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -51,6 +51,10 @@ impl<'a, C, F> Axis<'a, C, F> pub fn new(title: &'a str, orientation: Placement, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize) -> Axis<'a, C, F> { Axis { title, placement: orientation, range, scale, color, text_style, tick_size: tick_height } } + + pub fn size(&self) -> Point { + Point{x: 50, y: 50} + } } diff --git a/src/single_plot.rs b/src/single_plot.rs index 95d4425..49ad5b3 100644 --- a/src/single_plot.rs +++ b/src/single_plot.rs @@ -3,6 +3,9 @@ use embedded_graphics::drawable::Drawable; use embedded_graphics::DrawTarget; use embedded_graphics::prelude::Point; use embedded_graphics::pixelcolor::PixelColor; +use crate::axis::{Scale, Placement, Axis}; +use embedded_graphics::style::TextStyleBuilder; +use embedded_graphics::fonts::Font6x8; pub struct SinglePlot<'a, C> where @@ -29,6 +32,16 @@ impl<'a, C> Drawable for SinglePlot<'a, C> { fn draw>(self, display: &mut D) -> Result<(), D::Error> { + let text_style = TextStyleBuilder::new(Font6x8) + .text_color(self.color) + .build(); + + Axis::new("X", Placement::X{x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y}, 0..100, Scale::Fixed(10), self.color, text_style, 2) + .draw(display)?; + + Axis::new("Y", Placement::Y{y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x}, 0..200, Scale::Fixed(10), self.color, text_style, 1) + .draw(display)?; + self.curve.into_drawable_curve( &self.top_left, &self.bottom_right, From f0909be13d97ec7c9288cc8a588d37996fa54a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Mon, 4 Jan 2021 22:20:07 +0100 Subject: [PATCH 08/16] Refactor - split axis --- examples/free_axis.rs | 83 +++++++++++++++++++++++---- examples/single_plot_mono.rs | 16 ++++-- examples/single_plot_rgb.rs | 14 ++++- src/axis.rs | 106 +++++------------------------------ src/curve.rs | 4 +- src/drawable_axis.rs | 105 ++++++++++++++++++++++++++++++++++ src/drawable_curve.rs | 2 +- src/lib.rs | 3 +- src/single_plot.rs | 12 ++-- 9 files changed, 227 insertions(+), 118 deletions(-) create mode 100644 src/drawable_axis.rs diff --git a/examples/free_axis.rs b/examples/free_axis.rs index 9181814..662662b 100644 --- a/examples/free_axis.rs +++ b/examples/free_axis.rs @@ -26,40 +26,101 @@ fn main() -> Result<(), core::convert::Infallible> { .text_color(RgbColor::YELLOW) .build(); - Axis::new("X Fixed 0-100(10)", Placement::X{x1: 40, x2: 230, y: 10}, 0..100, Scale::Fixed(10), RgbColor::WHITE, text_style_white, 2) + Axis::new("X Fixed 0-100(10)", 0..100, Scale::Fixed(10)) + .into_drawable_axis( + Placement::X{x1: 40, x2: 230, y: 10}, + RgbColor::WHITE, + text_style_white, + 2 + ) .draw(&mut display)?; - Axis::new("X Fixed 0-200(100)", Placement::X{x1: 240, x2: 470, y: 10}, 0..200, Scale::Fixed(100), RgbColor::YELLOW, text_style_yellow_compact, 1) + Axis::new("X Fixed 0-200(100)", 0..200, Scale::Fixed(100)) + .into_drawable_axis( + Placement::X{x1: 240, x2: 470, y: 10}, + RgbColor::YELLOW, + text_style_yellow_compact, + 1 + ) .draw(&mut display)?; - Axis::new("X Frac 0-100(7)", Placement::X{x1: 50, x2: 220, y: 30}, 0..100, Scale::RangeFraction(7), RgbColor::BLUE, text_style_white, 3) + Axis::new("X Frac 0-100(7)", 0..100, Scale::RangeFraction(7)) + .into_drawable_axis( + Placement::X{x1: 50, x2: 220, y: 30}, + RgbColor::BLUE, + text_style_white, + 3 + ) .draw(&mut display)?; - Axis::new("X Frac 0-200(4)", Placement::X{x1: 250, x2: 460, y: 40}, 0..200, Scale::RangeFraction(4), RgbColor::RED, text_style_yellow_compact, 7) + Axis::new("X Frac 0-200(4)", 0..200, Scale::RangeFraction(4)) + .into_drawable_axis( + Placement::X{x1: 250, x2: 460, y: 40}, + RgbColor::RED, + text_style_yellow_compact, + 7 + ) .draw(&mut display)?; - Axis::new("Y Fixed 0-100(10)", Placement::Y{y1: 70, y2: 230, x: 160}, 0..100, Scale::Fixed(10), RgbColor::WHITE, text_style_white, 2) + Axis::new("Y Fixed 0-100(10)", 0..100, Scale::Fixed(10)) + .into_drawable_axis( + Placement::Y{y1: 70, y2: 230, x: 160}, + RgbColor::WHITE, + text_style_white, + 2 + ) .draw(&mut display)?; - Axis::new("Y Fixed 0-200(100)", Placement::Y{y1: 70, y2: 210, x: 260}, 0..200, Scale::Fixed(100), RgbColor::YELLOW, text_style_yellow_compact, 1) + Axis::new("Y Fixed 0-200(100)", 0..200, Scale::Fixed(100)) + .into_drawable_axis( + Placement::Y{y1: 70, y2: 210, x: 260}, + RgbColor::YELLOW, + text_style_yellow_compact, + 1 + ) .draw(&mut display)?; - Axis::new("Y Frac 0-100(7)", Placement::Y{y1: 60, y2: 180, x: 370}, 0..100, Scale::RangeFraction(7), RgbColor::BLUE, text_style_white, 3) + Axis::new("Y Frac 0-100(7)", 0..100, Scale::RangeFraction(7)) + .into_drawable_axis( + Placement::Y{y1: 60, y2: 180, x: 370}, + RgbColor::BLUE, + text_style_white, + 3 + ) .draw(&mut display)?; - Axis::new("Y Frac 0-200(4)", Placement::Y{y1: 90, y2: 220, x: 470}, 0..200, Scale::RangeFraction(4), RgbColor::RED, text_style_yellow_compact, 7) + Axis::new("Y Frac 0-200(4)", 0..200, Scale::RangeFraction(4)) + .into_drawable_axis( + Placement::Y{y1: 90, y2: 220, x: 470}, + RgbColor::RED, + text_style_yellow_compact, + 7 + ) .draw(&mut display)?; - Axis::new("X", Placement::X{x1: 30, x2: 470, y: 250}, 123..2137, Scale::Fixed(150), RgbColor::YELLOW, text_style_white, 2) + Axis::new("X", 123..2137, Scale::Fixed(150)) + .into_drawable_axis( + Placement::X{x1: 30, x2: 470, y: 250}, + RgbColor::YELLOW, + text_style_white, + 2 + ) .draw(&mut display)?; - Axis::new("Y", Placement::Y{y1: 10, y2: 250, x: 30}, 0..2137, Scale::RangeFraction(15), RgbColor::WHITE, text_style_white, 2) + + Axis::new("Y", 0..2137, Scale::RangeFraction(15)) + .into_drawable_axis( + Placement::Y{y1: 10, y2: 250, x: 30}, + RgbColor::WHITE, + text_style_white, + 2 + ) .draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() .pixel_spacing(1) .build(); - Window::new("Basic plot", &output_settings).show_static(&display); + Window::new("Free axis", &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 index 6796886..803ab3e 100644 --- a/examples/single_plot_mono.rs +++ b/examples/single_plot_mono.rs @@ -1,5 +1,6 @@ use embedded_graphics::{ prelude::*, + pixelcolor::BinaryColor, }; use embedded_graphics_simulator::{SimulatorDisplay, Window, OutputSettingsBuilder, BinaryColorTheme}; @@ -7,21 +8,28 @@ use embedded_graphics_simulator::{SimulatorDisplay, Window, OutputSettingsBuilde use embedded_plots::{ single_plot::{SinglePlot}, curve::{PlotPoint, Curve}, + axis::Scale, }; -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: 1, y: 2 }, + PlotPoint { x: 2, y: 2 }, 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 }); + let plot = SinglePlot::new( + &curve, + BinaryColor::On, + Point { x: 9, y: 3 }, + Point { x: 120, y: 45 }, + Scale::RangeFraction(3), + Scale::RangeFraction(2) + ); plot.draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() diff --git a/examples/single_plot_rgb.rs b/examples/single_plot_rgb.rs index d413c68..922d16b 100644 --- a/examples/single_plot_rgb.rs +++ b/examples/single_plot_rgb.rs @@ -13,6 +13,7 @@ use embedded_graphics_simulator::{ use embedded_plots::{ single_plot::{SinglePlot}, curve::{PlotPoint, Curve}, + axis::Scale, }; fn main() -> Result<(), core::convert::Infallible> { @@ -20,13 +21,20 @@ fn main() -> Result<(), core::convert::Infallible> { let data = vec![ PlotPoint { x: 0, y: 0 }, - PlotPoint { x: 1, y: 1 }, - PlotPoint { x: 2, y: 1 }, + PlotPoint { x: 1, y: 2 }, + PlotPoint { x: 2, y: 2 }, PlotPoint { x: 3, y: 0 }, ]; let curve = Curve::from_data(data.as_slice()); - let plot = SinglePlot::new(&curve, RgbColor::YELLOW, Point { x: 50, y: 10 }, Point { x: 430, y: 250 }); + let plot = SinglePlot::new( + &curve, + RgbColor::YELLOW, + Point { x: 50, y: 10 }, + Point { x: 430, y: 250 }, + Scale::RangeFraction(3), + Scale::RangeFraction(2) + ); plot.draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() diff --git a/src/axis.rs b/src/axis.rs index 629dc94..d5a1a42 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -1,13 +1,8 @@ -use embedded_graphics::primitives::Line; -use embedded_graphics::drawable::Drawable; -use embedded_graphics::DrawTarget; use core::ops::Range; use embedded_graphics::prelude::*; -use embedded_graphics::style::{PrimitiveStyle, TextStyle}; -use crate::range_conv::Scalable; -use embedded_graphics::fonts::Text; -use heapless::{consts::*, String}; -use core::fmt::Write; +use embedded_graphics::style::{TextStyle}; + +use crate::drawable_axis::DrawableAxis; pub enum Placement { X { @@ -27,97 +22,24 @@ pub enum Scale { RangeFraction(usize), } -pub struct Axis<'a, C, F> - where - C: PixelColor, - F: Font, - TextStyle: Clone, -{ +pub struct Axis<'a> { title: &'a str, - placement: Placement, range: Range, scale: Scale, - color: C, - text_style: TextStyle, - tick_size: usize, } -impl<'a, C, F> Axis<'a, C, F> - where - C: PixelColor, - F: Font, - TextStyle: Clone, +impl<'a> Axis<'a> { - pub fn new(title: &'a str, orientation: Placement, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize) -> Axis<'a, C, F> { - Axis { title, placement: orientation, range, scale, color, text_style, tick_size: tick_height } + pub fn new(title: &'a str, range: Range, scale: Scale) -> Axis<'a> { + Axis{title, range, scale} } - pub fn size(&self) -> Point { - Point{x: 50, y: 50} + pub fn into_drawable_axis(self, placement: Placement, plot_color: C, text_style: TextStyle, tick_height: usize) -> DrawableAxis<'a, C, F> + where + C: PixelColor, + F: Font, + TextStyle: Clone, + { + DrawableAxis::new(self.title,placement,self.range,self.scale,plot_color,text_style,tick_height) } } - - -impl<'a, C, F> Drawable for Axis<'a, C, F> - where - C: PixelColor, - F: Font + Copy, - TextStyle: Clone, -{ - fn draw>(self, display: &mut D) -> Result<(), D::Error> { - let scale_marks = match self.scale { - Scale::Fixed(interval) => { - self.range.clone().into_iter().step_by(interval) - } - Scale::RangeFraction(fraction) => { - let len = self.range.len(); - self.range.clone().into_iter().step_by(len / fraction) - } - }; - match self.placement { - Placement::X { x1, x2, y } => { - Line { start: Point { x: x1, y }, end: Point { x: x2, y } } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) - .draw(display)?; - let title = Text::new(self.title, Point { x: x1, y: y + 10 }) - .into_styled(self.text_style); - let title = title.translate(Point { x: (x2 - x1) / 2 - title.size().width as i32 / 2, y: 0 }); - title.draw(display)?; - - for mark in scale_marks { - let x = mark.scale_between_ranges(&self.range, &(x1..x2)); - Line { start: Point { x, y: y - self.tick_size as i32 }, end: Point { x, y: y + self.tick_size as i32 } } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) - .draw(display)?; - let mut buf: String:: = String::new(); - write!(buf, "{}", mark).unwrap(); - Text::new(&buf, Point { x: x + 2, y: y + 2 }).into_styled(self.text_style).draw(display)?; - } - } - Placement::Y { y1, y2, x } => { - Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) - .draw(display)?; - - let mut max_tick_text_width = 0; - for mark in scale_marks { - let y = mark.scale_between_ranges(&self.range, &(y2..y1)); - Line { start: Point { x: x - self.tick_size as i32, y }, end: Point { x: x + self.tick_size as i32, y} } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) - .draw(display)?; - let mut buf: String:: = String::new(); - write!(buf, "{}", mark).unwrap(); - let tick_val = Text::new(&buf, Point { x, y}).into_styled(self.text_style); - let tick_val = tick_val.translate(Point{ x: -(tick_val.size().width as i32) -2, y: 2 }); - if tick_val.size().width > max_tick_text_width { max_tick_text_width = tick_val.size().width } - tick_val.draw(display)?; - } - let title = Text::new(self.title, Point { x, y: y1 }) - .into_styled(self.text_style); - let title = title.translate(Point { x: -(title.size().width as i32) - max_tick_text_width as i32 - self.tick_size as i32 - 2, y: (y2-y1)/2 }); - title.draw(display)?; - } - } - Ok(()) - } -} \ No newline at end of file diff --git a/src/curve.rs b/src/curve.rs index a05aba9..a5f58e8 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -13,8 +13,8 @@ pub struct PlotPoint { pub struct Curve<'a> { points: &'a [PlotPoint], - x_range: Range, - y_range: Range, + pub x_range: Range, + pub y_range: Range, } impl<'a> Curve<'a> { diff --git a/src/drawable_axis.rs b/src/drawable_axis.rs new file mode 100644 index 0000000..b93359b --- /dev/null +++ b/src/drawable_axis.rs @@ -0,0 +1,105 @@ +use embedded_graphics::prelude::*; +use crate::axis::{Placement, Scale}; +use core::ops::Range; +use embedded_graphics::style::{TextStyle, PrimitiveStyle}; +use embedded_graphics::fonts::Text; +use crate::range_conv::Scalable; +use embedded_graphics::primitives::Line; +use heapless::{consts::*, String}; +use core::fmt::Write; + + +pub struct DrawableAxis<'a, C, F> + where + C: PixelColor, + F: Font, + TextStyle: Clone, +{ + title: &'a str, + placement: Placement, + range: Range, + scale: Scale, + color: C, + text_style: TextStyle, + tick_size: usize, +} + +impl<'a, C, F> DrawableAxis<'a, C, F> + where + C: PixelColor, + F: Font, + TextStyle: Clone, +{ + pub(in crate) fn new(title: &'a str, placement: Placement, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize) -> DrawableAxis<'a, C, F> { + DrawableAxis { title, placement, range, scale, color, text_style, tick_size: tick_height } + } + + pub fn size(&self) -> Point { + Point { x: 50, y: 50 } + } +} + + +impl<'a, C, F> Drawable for DrawableAxis<'a, C, F> + where + C: PixelColor, + F: Font + Copy, + TextStyle: Clone, +{ + fn draw>(self, display: &mut D) -> Result<(), D::Error> { + let scale_marks = match self.scale { + Scale::Fixed(interval) => { + self.range.clone().into_iter().step_by(interval) + } + Scale::RangeFraction(fraction) => { + let len = self.range.len(); + self.range.clone().into_iter().step_by(len / fraction) + } + }; + match self.placement { + Placement::X { x1, x2, y } => { + Line { start: Point { x: x1, y }, end: Point { x: x2, y } } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .draw(display)?; + let title = Text::new(self.title, Point { x: x1, y: y + 10 }) + .into_styled(self.text_style); + let title = title.translate(Point { x: (x2 - x1) / 2 - title.size().width as i32 / 2, y: 0 }); + title.draw(display)?; + + for mark in scale_marks { + let x = mark.scale_between_ranges(&self.range, &(x1..x2)); + Line { start: Point { x, y: y - self.tick_size as i32 }, end: Point { x, y: y + self.tick_size as i32 } } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .draw(display)?; + let mut buf: String:: = String::new(); + write!(buf, "{}", mark).unwrap(); + Text::new(&buf, Point { x: x + 2, y: y + 2 }).into_styled(self.text_style).draw(display)?; + } + } + Placement::Y { y1, y2, x } => { + Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .draw(display)?; + + let mut max_tick_text_width = 0; + for mark in scale_marks { + let y = mark.scale_between_ranges(&self.range, &(y2..y1)); + Line { start: Point { x: x - self.tick_size as i32, y }, end: Point { x: x + self.tick_size as i32, y } } + .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .draw(display)?; + let mut buf: String:: = String::new(); + write!(buf, "{}", mark).unwrap(); + let tick_val = Text::new(&buf, Point { x, y }).into_styled(self.text_style); + let tick_val = tick_val.translate(Point { x: -(tick_val.size().width as i32) - 2, y: 2 }); + if tick_val.size().width > max_tick_text_width { max_tick_text_width = tick_val.size().width } + tick_val.draw(display)?; + } + let title = Text::new(self.title, Point { x, y: y1 }) + .into_styled(self.text_style); + let title = title.translate(Point { x: -(title.size().width as i32) - max_tick_text_width as i32 - self.tick_size as i32 - 2, y: (y2 - y1) / 2 }); + title.draw(display)?; + } + } + Ok(()) + } +} \ No newline at end of file diff --git a/src/drawable_curve.rs b/src/drawable_curve.rs index 4699bb6..4b65cf5 100644 --- a/src/drawable_curve.rs +++ b/src/drawable_curve.rs @@ -16,7 +16,7 @@ impl DrawableCurve C: PixelColor, I: Iterator, { - pub fn new(data: I, color: C) -> DrawableCurve { + pub(in crate) fn new(data: I, color: C) -> DrawableCurve { DrawableCurve { scaled_data: data, color, diff --git a/src/lib.rs b/src/lib.rs index 60972ac..a41e3cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,10 @@ #![no_std] pub mod curve; +pub mod axis; pub mod polyplot; pub mod single_plot; mod drawable_curve; mod range_conv; -pub mod axis; +mod drawable_axis; diff --git a/src/single_plot.rs b/src/single_plot.rs index 49ad5b3..e885baa 100644 --- a/src/single_plot.rs +++ b/src/single_plot.rs @@ -12,6 +12,8 @@ pub struct SinglePlot<'a, C> C: PixelColor { curve: &'a Curve<'a>, + x_scale: Scale, + y_scale: Scale, color: C, top_left: Point, bottom_right: Point, @@ -21,8 +23,8 @@ 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} + pub fn new(curve: &'a Curve<'a>, color: C, top_left: Point, bottom_right: Point, x_scale: Scale, y_scale: Scale) -> SinglePlot<'a, C> { + SinglePlot { curve, color, top_left, bottom_right, x_scale, y_scale} } } @@ -36,10 +38,12 @@ impl<'a, C> Drawable for SinglePlot<'a, C> .text_color(self.color) .build(); - Axis::new("X", Placement::X{x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y}, 0..100, Scale::Fixed(10), self.color, text_style, 2) + Axis::new("X",self.curve.x_range.clone(),self.x_scale) + .into_drawable_axis(Placement::X{x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y},self.color,text_style,2) .draw(display)?; - Axis::new("Y", Placement::Y{y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x}, 0..200, Scale::Fixed(10), self.color, text_style, 1) + Axis::new("Y", self.curve.y_range.clone(), self.y_scale) + .into_drawable_axis(Placement::Y{y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x},self.color,text_style,2) .draw(display)?; self.curve.into_drawable_curve( From 547b5f13d22e533e959a4b682e4f048bcfbc604a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Tue, 16 Feb 2021 20:51:09 +0100 Subject: [PATCH 09/16] Refactor split SinglePlot from Drawable --- examples/single_plot_mono.rs | 9 ++++---- examples/single_plot_rgb.rs | 9 ++++---- src/single_plot.rs | 44 ++++++++++++++++++++---------------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/examples/single_plot_mono.rs b/examples/single_plot_mono.rs index 803ab3e..569c6ff 100644 --- a/examples/single_plot_mono.rs +++ b/examples/single_plot_mono.rs @@ -24,11 +24,12 @@ fn main() -> Result<(), core::convert::Infallible> { let plot = SinglePlot::new( &curve, - BinaryColor::On, - Point { x: 9, y: 3 }, - Point { x: 120, y: 45 }, Scale::RangeFraction(3), - Scale::RangeFraction(2) + Scale::RangeFraction(2), + ).into_drawable( + BinaryColor::On, + Point { x: 18, y: 2 }, + Point { x: 120, y: 30 }, ); plot.draw(&mut display)?; diff --git a/examples/single_plot_rgb.rs b/examples/single_plot_rgb.rs index 922d16b..87ed2c5 100644 --- a/examples/single_plot_rgb.rs +++ b/examples/single_plot_rgb.rs @@ -7,7 +7,6 @@ use embedded_graphics_simulator::{ SimulatorDisplay, Window, OutputSettingsBuilder, - }; use embedded_plots::{ @@ -29,11 +28,13 @@ fn main() -> Result<(), core::convert::Infallible> { let plot = SinglePlot::new( &curve, + Scale::RangeFraction(3), + Scale::RangeFraction(2), + ) + .into_drawable( RgbColor::YELLOW, Point { x: 50, y: 10 }, - Point { x: 430, y: 250 }, - Scale::RangeFraction(3), - Scale::RangeFraction(2) + Point { x: 430, y: 250 } ); plot.draw(&mut display)?; diff --git a/src/single_plot.rs b/src/single_plot.rs index e885baa..a1a6d0a 100644 --- a/src/single_plot.rs +++ b/src/single_plot.rs @@ -7,46 +7,50 @@ use crate::axis::{Scale, Placement, Axis}; use embedded_graphics::style::TextStyleBuilder; use embedded_graphics::fonts::Font6x8; -pub struct SinglePlot<'a, C> - where - C: PixelColor -{ +pub struct SinglePlot<'a> { curve: &'a Curve<'a>, x_scale: Scale, y_scale: Scale, +} + +impl<'a> SinglePlot<'a> { + pub fn new(curve: &'a Curve<'a>, x_scale: Scale, y_scale: Scale) -> SinglePlot { + SinglePlot { curve, x_scale, y_scale } + } + + pub fn into_drawable(self, color: C, top_left: Point, bottom_right: Point) -> DrawableSinglePlot<'a, C> { + DrawableSinglePlot { plot: self, color, top_left, bottom_right } + } +} + +pub struct DrawableSinglePlot<'a, C> + where + C: PixelColor +{ + plot: SinglePlot<'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, x_scale: Scale, y_scale: Scale) -> SinglePlot<'a, C> { - SinglePlot { curve, color, top_left, bottom_right, x_scale, y_scale} - } -} - -impl<'a, C> Drawable for SinglePlot<'a, C> +impl<'a, C> Drawable for DrawableSinglePlot<'a, C> where C: PixelColor { fn draw>(self, display: &mut D) -> Result<(), D::Error> { - let text_style = TextStyleBuilder::new(Font6x8) .text_color(self.color) .build(); - Axis::new("X",self.curve.x_range.clone(),self.x_scale) - .into_drawable_axis(Placement::X{x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y},self.color,text_style,2) + Axis::new("X", self.plot.curve.x_range.clone(), self.plot.x_scale) + .into_drawable_axis(Placement::X { x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y }, self.color, text_style, 2) .draw(display)?; - Axis::new("Y", self.curve.y_range.clone(), self.y_scale) - .into_drawable_axis(Placement::Y{y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x},self.color,text_style,2) + Axis::new("Y", self.plot.curve.y_range.clone(), self.plot.y_scale) + .into_drawable_axis(Placement::Y { y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x }, self.color, text_style, 2) .draw(display)?; - self.curve.into_drawable_curve( + self.plot.curve.into_drawable_curve( &self.top_left, &self.bottom_right, self.color, From 350e5a029a03c59f181c1bc09706d83dab0c1eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Tue, 16 Feb 2021 23:36:45 +0100 Subject: [PATCH 10/16] Add very basic CI script --- .gitlab-ci.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..8b197b3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,16 @@ +# todo: spin our own image +image: jamwaffles/circleci-embedded-graphics:1.40.0-3 + +stages: + - build + - test + +build: + stage: build + script: + - cargo build + +test: + stage: test + script: + - cargo test \ No newline at end of file From fc9cef912089da16eeeacd2e36242d0cc4d55839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Wed, 17 Feb 2021 00:58:28 +0100 Subject: [PATCH 11/16] Fix code to build on CI --- .gitlab-ci.yml | 2 +- src/curve.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8b197b3..e078438 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ # todo: spin our own image -image: jamwaffles/circleci-embedded-graphics:1.40.0-3 +image: registry.gitlab.com/mchodzikiewicz/embedded-plots-docker:latest stages: - build diff --git a/src/curve.rs b/src/curve.rs index a5f58e8..8d32245 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -3,8 +3,7 @@ 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}; +use itertools::{Itertools, MinMaxResult::MinMax, MinMaxResult}; pub struct PlotPoint { pub x: i32, From a555d74a256af99fcf2350e34ac3a652d623be39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Thu, 25 Feb 2021 23:53:10 +0100 Subject: [PATCH 12/16] Refactor single_plot to builder pattern --- examples/free_axis.rs | 30 +++++++++----- examples/single_plot_mono.rs | 3 +- examples/single_plot_rgb.rs | 9 ++--- src/axis.rs | 4 +- src/curve.rs | 3 +- src/drawable_axis.rs | 13 +++--- src/drawable_curve.rs | 6 ++- src/polyplot.rs | 1 + src/single_plot.rs | 77 +++++++++++++++++++++++++++++++----- 9 files changed, 108 insertions(+), 38 deletions(-) diff --git a/examples/free_axis.rs b/examples/free_axis.rs index 662662b..1db5319 100644 --- a/examples/free_axis.rs +++ b/examples/free_axis.rs @@ -31,7 +31,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::X{x1: 40, x2: 230, y: 10}, RgbColor::WHITE, text_style_white, - 2 + 2, + 2, ) .draw(&mut display)?; @@ -40,7 +41,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::X{x1: 240, x2: 470, y: 10}, RgbColor::YELLOW, text_style_yellow_compact, - 1 + 1, + 2, ) .draw(&mut display)?; @@ -49,7 +51,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::X{x1: 50, x2: 220, y: 30}, RgbColor::BLUE, text_style_white, - 3 + 3, + 1, ) .draw(&mut display)?; @@ -58,7 +61,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::X{x1: 250, x2: 460, y: 40}, RgbColor::RED, text_style_yellow_compact, - 7 + 7, + 1, ) .draw(&mut display)?; @@ -67,7 +71,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::Y{y1: 70, y2: 230, x: 160}, RgbColor::WHITE, text_style_white, - 2 + 2, + 1, ) .draw(&mut display)?; @@ -76,7 +81,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::Y{y1: 70, y2: 210, x: 260}, RgbColor::YELLOW, text_style_yellow_compact, - 1 + 1, + 1, ) .draw(&mut display)?; @@ -85,7 +91,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::Y{y1: 60, y2: 180, x: 370}, RgbColor::BLUE, text_style_white, - 3 + 3, + 1, ) .draw(&mut display)?; @@ -94,7 +101,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::Y{y1: 90, y2: 220, x: 470}, RgbColor::RED, text_style_yellow_compact, - 7 + 7, + 1, ) .draw(&mut display)?; @@ -103,7 +111,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::X{x1: 30, x2: 470, y: 250}, RgbColor::YELLOW, text_style_white, - 2 + 2, + 1, ) .draw(&mut display)?; @@ -112,7 +121,8 @@ fn main() -> Result<(), core::convert::Infallible> { Placement::Y{y1: 10, y2: 250, x: 30}, RgbColor::WHITE, text_style_white, - 2 + 2, + 1, ) .draw(&mut display)?; diff --git a/examples/single_plot_mono.rs b/examples/single_plot_mono.rs index 569c6ff..5fc011a 100644 --- a/examples/single_plot_mono.rs +++ b/examples/single_plot_mono.rs @@ -27,10 +27,9 @@ fn main() -> Result<(), core::convert::Infallible> { Scale::RangeFraction(3), Scale::RangeFraction(2), ).into_drawable( - BinaryColor::On, Point { x: 18, y: 2 }, Point { x: 120, y: 30 }, - ); + ).set_color(BinaryColor::On); plot.draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() diff --git a/examples/single_plot_rgb.rs b/examples/single_plot_rgb.rs index 87ed2c5..f3c0c29 100644 --- a/examples/single_plot_rgb.rs +++ b/examples/single_plot_rgb.rs @@ -29,13 +29,10 @@ fn main() -> Result<(), core::convert::Infallible> { let plot = SinglePlot::new( &curve, Scale::RangeFraction(3), - Scale::RangeFraction(2), - ) - .into_drawable( - RgbColor::YELLOW, + Scale::RangeFraction(2)).into_drawable( Point { x: 50, y: 10 }, - Point { x: 430, y: 250 } - ); + Point { x: 430, y: 250 }, + ).set_color(RgbColor::YELLOW).set_text_color(RgbColor::WHITE); plot.draw(&mut display)?; let output_settings = OutputSettingsBuilder::new() diff --git a/src/axis.rs b/src/axis.rs index d5a1a42..bf71e42 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -34,12 +34,12 @@ impl<'a> Axis<'a> Axis{title, range, scale} } - pub fn into_drawable_axis(self, placement: Placement, plot_color: C, text_style: TextStyle, tick_height: usize) -> DrawableAxis<'a, C, F> + pub fn into_drawable_axis(self, placement: Placement, plot_color: C, text_style: TextStyle, tick_height: usize, thickness: usize) -> DrawableAxis<'a, C, F> where C: PixelColor, F: Font, TextStyle: Clone, { - DrawableAxis::new(self.title,placement,self.range,self.scale,plot_color,text_style,tick_height) + DrawableAxis::new(self.title,placement,self.range,self.scale,plot_color,text_style,tick_height,thickness) } } diff --git a/src/curve.rs b/src/curve.rs index 8d32245..e077825 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -44,6 +44,7 @@ impl<'a> Curve<'a> { top_left: &'a Point, bottom_right: &'a Point, color: C, + thickness: usize, ) -> DrawableCurve + '_> where C: PixelColor { @@ -63,7 +64,7 @@ impl<'a> Curve<'a> { &Range { start: bottom_right.y, end: top_left.y }, ), }); - DrawableCurve::new(it, color) + DrawableCurve::new(it, color,thickness) } } diff --git a/src/drawable_axis.rs b/src/drawable_axis.rs index b93359b..48e18fc 100644 --- a/src/drawable_axis.rs +++ b/src/drawable_axis.rs @@ -22,6 +22,7 @@ pub struct DrawableAxis<'a, C, F> color: C, text_style: TextStyle, tick_size: usize, + thickness: usize, } impl<'a, C, F> DrawableAxis<'a, C, F> @@ -30,8 +31,8 @@ impl<'a, C, F> DrawableAxis<'a, C, F> F: Font, TextStyle: Clone, { - pub(in crate) fn new(title: &'a str, placement: Placement, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize) -> DrawableAxis<'a, C, F> { - DrawableAxis { title, placement, range, scale, color, text_style, tick_size: tick_height } + pub(in crate) fn new(title: &'a str, placement: Placement, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize, thickness: usize) -> DrawableAxis<'a, C, F> { + DrawableAxis { title, placement, range, scale, color, text_style, tick_size: tick_height, thickness } } pub fn size(&self) -> Point { @@ -59,7 +60,7 @@ impl<'a, C, F> Drawable for DrawableAxis<'a, C, F> match self.placement { Placement::X { x1, x2, y } => { Line { start: Point { x: x1, y }, end: Point { x: x2, y } } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) .draw(display)?; let title = Text::new(self.title, Point { x: x1, y: y + 10 }) .into_styled(self.text_style); @@ -69,7 +70,7 @@ impl<'a, C, F> Drawable for DrawableAxis<'a, C, F> for mark in scale_marks { let x = mark.scale_between_ranges(&self.range, &(x1..x2)); Line { start: Point { x, y: y - self.tick_size as i32 }, end: Point { x, y: y + self.tick_size as i32 } } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) .draw(display)?; let mut buf: String:: = String::new(); write!(buf, "{}", mark).unwrap(); @@ -78,14 +79,14 @@ impl<'a, C, F> Drawable for DrawableAxis<'a, C, F> } Placement::Y { y1, y2, x } => { Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) .draw(display)?; let mut max_tick_text_width = 0; for mark in scale_marks { let y = mark.scale_between_ranges(&self.range, &(y2..y1)); Line { start: Point { x: x - self.tick_size as i32, y }, end: Point { x: x + self.tick_size as i32, y } } - .into_styled(PrimitiveStyle::with_stroke(self.color, 1)) + .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) .draw(display)?; let mut buf: String:: = String::new(); write!(buf, "{}", mark).unwrap(); diff --git a/src/drawable_curve.rs b/src/drawable_curve.rs index 4b65cf5..fcc513f 100644 --- a/src/drawable_curve.rs +++ b/src/drawable_curve.rs @@ -9,6 +9,7 @@ pub struct DrawableCurve { scaled_data: I, color: C, + thickness: usize, } impl DrawableCurve @@ -16,10 +17,11 @@ impl DrawableCurve C: PixelColor, I: Iterator, { - pub(in crate) fn new(data: I, color: C) -> DrawableCurve { + pub(in crate) fn new(data: I, color: C, thickness: usize) -> DrawableCurve { DrawableCurve { scaled_data: data, color, + thickness, } } } @@ -29,7 +31,7 @@ impl Drawable for DrawableCurve I: Iterator, { fn draw>(self, display: &mut D) -> Result<(), D::Error> { - let style = PrimitiveStyle::with_stroke(self.color, 2); + let style = PrimitiveStyle::with_stroke(self.color, self.thickness as u32); let mut iter = self.scaled_data.into_iter(); let mut prev = iter.next().unwrap(); for point in iter { diff --git a/src/polyplot.rs b/src/polyplot.rs index 34ae499..6c3984f 100644 --- a/src/polyplot.rs +++ b/src/polyplot.rs @@ -30,6 +30,7 @@ impl<'a, C> Drawable for PolyPlot<'a, C> &self.top_left, &self.bottom_right, *color, + 2 ).draw(display)?; } Ok(()) diff --git a/src/single_plot.rs b/src/single_plot.rs index a1a6d0a..230d962 100644 --- a/src/single_plot.rs +++ b/src/single_plot.rs @@ -18,42 +18,101 @@ impl<'a> SinglePlot<'a> { SinglePlot { curve, x_scale, y_scale } } - pub fn into_drawable(self, color: C, top_left: Point, bottom_right: Point) -> DrawableSinglePlot<'a, C> { - DrawableSinglePlot { plot: self, color, top_left, bottom_right } + pub fn into_drawable(self, top_left: Point, bottom_right: Point) -> DrawableSinglePlot<'a, C> { + DrawableSinglePlot { plot: self, color: None, text_color: None, axis_color: None, thickness: None, axis_thickness: None, top_left, bottom_right } } } pub struct DrawableSinglePlot<'a, C> where - C: PixelColor + C: PixelColor + Default, { plot: SinglePlot<'a>, - color: C, + color: Option, + text_color: Option, + axis_color: Option, + thickness: Option, + axis_thickness: Option, top_left: Point, bottom_right: Point, } +impl<'a, C> DrawableSinglePlot<'a, C> + where + C: PixelColor + Default, +{ + pub fn set_color(mut self, color: C) -> DrawableSinglePlot<'a, C> { + self.color = Some(color); + self + } + + pub fn set_text_color(mut self, color: C) -> DrawableSinglePlot<'a, C> { + self.text_color = Some(color); + self + } + + pub fn set_axis_color(mut self, color: C) -> DrawableSinglePlot<'a, C> { + self.axis_color = Some(color); + self + } + + pub fn set_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> { + self.thickness = Some(thickness); + self + } + + pub fn set_axis_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> { + self.axis_thickness = Some(thickness); + self + } + +} + impl<'a, C> Drawable for DrawableSinglePlot<'a, C> where - C: PixelColor + C: PixelColor + Default, { fn draw>(self, display: &mut D) -> Result<(), D::Error> { + let color = match self.color { + None => C::default(), + Some(c) => c, + }; + let text_color = match self.text_color { + None => color, + Some(c) => c, + }; + let axis_color = match self.axis_color { + None => color, + Some(c) => c, + }; + + let thickness = match self.thickness { + None => 2, + Some(t) => t, + }; + + let axis_thickness = match self.axis_thickness { + None => thickness, + Some(t) => t, + }; + let text_style = TextStyleBuilder::new(Font6x8) - .text_color(self.color) + .text_color(text_color) .build(); Axis::new("X", self.plot.curve.x_range.clone(), self.plot.x_scale) - .into_drawable_axis(Placement::X { x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y }, self.color, text_style, 2) + .into_drawable_axis(Placement::X { x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y }, axis_color, text_style, 2, axis_thickness) .draw(display)?; Axis::new("Y", self.plot.curve.y_range.clone(), self.plot.y_scale) - .into_drawable_axis(Placement::Y { y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x }, self.color, text_style, 2) + .into_drawable_axis(Placement::Y { y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x }, axis_color, text_style, 2,axis_thickness) .draw(display)?; self.plot.curve.into_drawable_curve( &self.top_left, &self.bottom_right, - self.color, + color, + thickness, ).draw(display)?; Ok(()) } From 5139122a958b1c66802b91560169d70b0a29f6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Fri, 26 Feb 2021 01:03:14 +0100 Subject: [PATCH 13/16] Refactor curve to builder pattern --- src/curve.rs | 65 +++++++++++++++++++++++++++++++++++++++---- src/drawable_curve.rs | 45 ------------------------------ src/lib.rs | 1 - src/polyplot.rs | 8 +++--- src/single_plot.rs | 6 ++-- 5 files changed, 67 insertions(+), 58 deletions(-) delete mode 100644 src/drawable_curve.rs diff --git a/src/curve.rs b/src/curve.rs index e077825..0036141 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -1,10 +1,16 @@ use core::ops::{Range}; use crate::range_conv::Scalable; -use crate::drawable_curve::DrawableCurve; -use embedded_graphics::prelude::*; use itertools::{Itertools, MinMaxResult::MinMax, MinMaxResult}; +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 PlotPoint { pub x: i32, pub y: i32, @@ -43,8 +49,6 @@ impl<'a> Curve<'a> { pub fn into_drawable_curve(&self, top_left: &'a Point, bottom_right: &'a Point, - color: C, - thickness: usize, ) -> DrawableCurve + '_> where C: PixelColor { @@ -64,10 +68,61 @@ impl<'a> Curve<'a> { &Range { start: bottom_right.y, end: top_left.y }, ), }); - DrawableCurve::new(it, color,thickness) + DrawableCurve { + scaled_data: it, + color: None, + thickness: None, + } } } +pub struct DrawableCurve +{ + scaled_data: I, + color: Option, + thickness: Option, +} + +impl DrawableCurve + where + C: PixelColor, + I: Iterator, +{ + pub fn set_color(mut self, color: C) -> DrawableCurve { + self.color = Some(color); + self + } + pub fn set_thickness(mut self, thickness: usize) -> DrawableCurve { + self.thickness = Some(thickness); + self + } +} + +impl Drawable for DrawableCurve + where C: PixelColor + Default, + I: Iterator, +{ + fn draw>(self, display: &mut D) -> Result<(), D::Error> { + let color = match self.color { + None => C::default(), + Some(c) => c, + }; + let thickness = match self.thickness { + None => 2, + Some(t) => t, + }; + let style = PrimitiveStyle::with_stroke(color, thickness as u32); + 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(()) + } +} #[cfg(test)] mod tests { diff --git a/src/drawable_curve.rs b/src/drawable_curve.rs deleted file mode 100644 index fcc513f..0000000 --- a/src/drawable_curve.rs +++ /dev/null @@ -1,45 +0,0 @@ -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 DrawableCurve -{ - scaled_data: I, - color: C, - thickness: usize, -} - -impl DrawableCurve - where - C: PixelColor, - I: Iterator, -{ - pub(in crate) fn new(data: I, color: C, thickness: usize) -> DrawableCurve { - DrawableCurve { - scaled_data: data, - color, - thickness, - } - } -} - -impl Drawable for DrawableCurve - where C: PixelColor, - I: Iterator, -{ - fn draw>(self, display: &mut D) -> Result<(), D::Error> { - let style = PrimitiveStyle::with_stroke(self.color, self.thickness as u32); - 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 a41e3cb..fafaba5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,5 @@ pub mod axis; pub mod polyplot; pub mod single_plot; -mod drawable_curve; mod range_conv; mod drawable_axis; diff --git a/src/polyplot.rs b/src/polyplot.rs index 6c3984f..279d1c7 100644 --- a/src/polyplot.rs +++ b/src/polyplot.rs @@ -22,16 +22,16 @@ impl<'a, C> PolyPlot<'a, C> impl<'a, C> Drawable for PolyPlot<'a, C> where - C: PixelColor + C: PixelColor + Default, { 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, - 2 - ).draw(display)?; + ).set_color(*color) + .set_thickness(2) + .draw(display)?; } Ok(()) } diff --git a/src/single_plot.rs b/src/single_plot.rs index 230d962..59b233b 100644 --- a/src/single_plot.rs +++ b/src/single_plot.rs @@ -111,9 +111,9 @@ impl<'a, C> Drawable for DrawableSinglePlot<'a, C> self.plot.curve.into_drawable_curve( &self.top_left, &self.bottom_right, - color, - thickness, - ).draw(display)?; + ).set_color(color) + .set_thickness(thickness) + .draw(display)?; Ok(()) } } \ No newline at end of file From 76c399ac6c7b9bd4c2db6b141112f9cc1a0958b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Tue, 2 Mar 2021 21:18:53 +0100 Subject: [PATCH 14/16] Refactor axis to builder pattern --- examples/free_axis.rs | 149 ++++++++++++++++++-------------------- src/axis.rs | 165 +++++++++++++++++++++++++++++++++++++++--- src/drawable_axis.rs | 106 --------------------------- src/lib.rs | 3 +- src/single_plot.rs | 47 ++++++------ 5 files changed, 247 insertions(+), 223 deletions(-) delete mode 100644 src/drawable_axis.rs diff --git a/examples/free_axis.rs b/examples/free_axis.rs index 1db5319..b47b82c 100644 --- a/examples/free_axis.rs +++ b/examples/free_axis.rs @@ -26,104 +26,97 @@ fn main() -> Result<(), core::convert::Infallible> { .text_color(RgbColor::YELLOW) .build(); - Axis::new("X Fixed 0-100(10)", 0..100, Scale::Fixed(10)) - .into_drawable_axis( - Placement::X{x1: 40, x2: 230, y: 10}, - RgbColor::WHITE, - text_style_white, - 2, - 2, - ) + Axis::new(0..100) + .set_title("X Fixed 0-100(10)") + .set_scale(Scale::Fixed(10)) + .into_drawable_axis(Placement::X { x1: 40, x2: 230, y: 10 }) + .set_color(RgbColor::WHITE) + .set_text_style(text_style_white) + .set_thickness(2) + .set_tick_size(2) .draw(&mut display)?; - Axis::new("X Fixed 0-200(100)", 0..200, Scale::Fixed(100)) - .into_drawable_axis( - Placement::X{x1: 240, x2: 470, y: 10}, - RgbColor::YELLOW, - text_style_yellow_compact, - 1, - 2, - ) + Axis::new(0..200) + .set_title("X Fixed 0-200(100)") + .set_scale(Scale::Fixed(100)) + .into_drawable_axis(Placement::X { x1: 240, x2: 470, y: 10 }) + .set_color(RgbColor::YELLOW) + .set_text_style(text_style_yellow_compact) + .set_tick_size(2) .draw(&mut display)?; - Axis::new("X Frac 0-100(7)", 0..100, Scale::RangeFraction(7)) - .into_drawable_axis( - Placement::X{x1: 50, x2: 220, y: 30}, - RgbColor::BLUE, - text_style_white, - 3, - 1, - ) + Axis::new(0..100) + .set_title("X Frac 0-100(7)") + .set_scale(Scale::RangeFraction(7)) + .into_drawable_axis(Placement::X { x1: 50, x2: 220, y: 30 }) + .set_color(RgbColor::BLUE) + .set_text_style(text_style_white) + .set_tick_size(3) .draw(&mut display)?; - Axis::new("X Frac 0-200(4)", 0..200, Scale::RangeFraction(4)) - .into_drawable_axis( - Placement::X{x1: 250, x2: 460, y: 40}, - RgbColor::RED, - text_style_yellow_compact, - 7, - 1, - ) + Axis::new(0..200) + .set_title("X Frac 0-200(4)") + .set_scale(Scale::RangeFraction(4)) + .into_drawable_axis(Placement::X { x1: 250, x2: 460, y: 40 }) + .set_color(RgbColor::RED) + .set_text_style(text_style_yellow_compact) + .set_tick_size(7) .draw(&mut display)?; - Axis::new("Y Fixed 0-100(10)", 0..100, Scale::Fixed(10)) + Axis::new(0..100) + .set_title("Y Fixed 0-100(10)") + .set_scale(Scale::Fixed(10)) .into_drawable_axis( - Placement::Y{y1: 70, y2: 230, x: 160}, - RgbColor::WHITE, - text_style_white, - 2, - 1, - ) + Placement::Y { y1: 70, y2: 230, x: 160 }) + .set_color(RgbColor::WHITE) + .set_text_style(text_style_white) + .set_tick_size(2) .draw(&mut display)?; - Axis::new("Y Fixed 0-200(100)", 0..200, Scale::Fixed(100)) + Axis::new(0..200) + .set_title("Y Fixed 0-200(100)") + .set_scale(Scale::Fixed(100)) .into_drawable_axis( - Placement::Y{y1: 70, y2: 210, x: 260}, - RgbColor::YELLOW, - text_style_yellow_compact, - 1, - 1, - ) + Placement::Y { y1: 70, y2: 210, x: 260 }) + .set_color(RgbColor::YELLOW) + .set_text_style(text_style_yellow_compact) + .set_tick_size(1) .draw(&mut display)?; - Axis::new("Y Frac 0-100(7)", 0..100, Scale::RangeFraction(7)) - .into_drawable_axis( - Placement::Y{y1: 60, y2: 180, x: 370}, - RgbColor::BLUE, - text_style_white, - 3, - 1, - ) + Axis::new(0..100) + .set_title("Y Frac 0-100(7)") + .set_scale(Scale::RangeFraction(7)) + .into_drawable_axis(Placement::Y { y1: 60, y2: 180, x: 370 }) + .set_color(RgbColor::BLUE) + .set_text_style(text_style_white) + .set_tick_size(3) .draw(&mut display)?; - Axis::new("Y Frac 0-200(4)", 0..200, Scale::RangeFraction(4)) - .into_drawable_axis( - Placement::Y{y1: 90, y2: 220, x: 470}, - RgbColor::RED, - text_style_yellow_compact, - 7, - 1, - ) + Axis::new(0..200) + .set_title("Y Frac 0-200(4)") + .set_scale(Scale::RangeFraction(4)) + .into_drawable_axis(Placement::Y { y1: 90, y2: 220, x: 470 }) + .set_color(RgbColor::RED) + .set_text_style(text_style_yellow_compact) + .set_tick_size(7) .draw(&mut display)?; - Axis::new("X", 123..2137, Scale::Fixed(150)) - .into_drawable_axis( - Placement::X{x1: 30, x2: 470, y: 250}, - RgbColor::YELLOW, - text_style_white, - 2, - 1, - ) + Axis::new(123..2137) + .set_title("X") + .set_scale(Scale::Fixed(150)) + .into_drawable_axis(Placement::X { x1: 30, x2: 470, y: 250 }) + .set_color(RgbColor::YELLOW) + .set_text_style(text_style_white) + .set_tick_size(2) .draw(&mut display)?; - Axis::new("Y", 0..2137, Scale::RangeFraction(15)) - .into_drawable_axis( - Placement::Y{y1: 10, y2: 250, x: 30}, - RgbColor::WHITE, - text_style_white, - 2, - 1, - ) + Axis::new(0..2137) + .set_title("Y") + .set_scale(Scale::RangeFraction(15)) + .into_drawable_axis(Placement::Y { y1: 10, y2: 250, x: 30 }) + .set_color(RgbColor::WHITE) + .set_text_style(text_style_white) + .set_tick_size(2) .draw(&mut display)?; diff --git a/src/axis.rs b/src/axis.rs index bf71e42..f3e8ab3 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -1,8 +1,15 @@ use core::ops::Range; -use embedded_graphics::prelude::*; -use embedded_graphics::style::{TextStyle}; +use core::fmt::Write; +use heapless::{consts::*, String}; + +use embedded_graphics::{ + prelude::*, + style::{TextStyle, PrimitiveStyle}, + primitives::Line, + fonts::Text, +}; +use crate::range_conv::Scalable; -use crate::drawable_axis::DrawableAxis; pub enum Placement { X { @@ -22,24 +29,160 @@ pub enum Scale { RangeFraction(usize), } +impl Default for Scale { + fn default() -> Self { + Scale::RangeFraction(5) + } +} + pub struct Axis<'a> { - title: &'a str, range: Range, - scale: Scale, + title: Option<&'a str>, + scale: Option, } impl<'a> Axis<'a> { - pub fn new(title: &'a str, range: Range, scale: Scale) -> Axis<'a> { - Axis{title, range, scale} + pub fn new(range: Range) -> Axis<'a> { + Axis { range, title: None, scale: None } } - pub fn into_drawable_axis(self, placement: Placement, plot_color: C, text_style: TextStyle, tick_height: usize, thickness: usize) -> DrawableAxis<'a, C, F> + pub fn set_scale(mut self, scale: Scale) -> Axis<'a> { + self.scale = Some(scale); + self + } + + pub fn set_title(mut self, title: &'a str) -> Axis<'a> { + self.title = Some(title); + self + } + + pub fn into_drawable_axis(self, placement: Placement) -> DrawableAxis<'a, C, F> where - C: PixelColor, + C: PixelColor + Default, F: Font, - TextStyle: Clone, + TextStyle: Clone + Default, { - DrawableAxis::new(self.title,placement,self.range,self.scale,plot_color,text_style,tick_height,thickness) + DrawableAxis{ + axis: self, + placement, + color: None, + text_style: None, + tick_size: None, + thickness: None, + } } } + +pub struct DrawableAxis<'a, C, F> + where + C: PixelColor, + F: Font, + TextStyle: Clone + Default, +{ + axis: Axis<'a>, + placement: Placement, + color: Option, + text_style: Option>, + tick_size: Option, + thickness: Option, +} + +impl<'a, C, F> DrawableAxis<'a, C, F> + where + C: PixelColor + Default, + F: Font, + TextStyle: Clone + Default, +{ + pub fn set_color(mut self, val: C) -> DrawableAxis<'a, C, F> { + self.color = Some(val); + self + } + pub fn set_text_style(mut self, val: TextStyle) -> DrawableAxis<'a, C, F> { + self.text_style = Some(val); + self + } + pub fn set_tick_size(mut self, val: usize) -> DrawableAxis<'a, C, F> { + self.tick_size = Some(val); + self + } + pub fn set_thickness(mut self, val: usize) -> DrawableAxis<'a, C, F> { + self.thickness = Some(val); + self + } +} + + +impl<'a, C, F> Drawable for DrawableAxis<'a, C, F> + where + C: PixelColor + Default, + F: Font + Copy, + TextStyle: Clone + Default, +{ + fn draw>(self, display: &mut D) -> Result<(), D::Error> { + let color = self.color.unwrap_or_default(); + let text_style = self.text_style.unwrap_or_default(); + let thickness = self.thickness.unwrap_or(1); + let tick_size = self.tick_size.unwrap_or(2); + + + let scale_marks = match self.axis.scale.unwrap_or_default() { + Scale::Fixed(interval) => { + self.axis.range.clone().into_iter().step_by(interval) + } + Scale::RangeFraction(fraction) => { + let len = self.axis.range.len(); + self.axis.range.clone().into_iter().step_by(len / fraction) + } + }; + match self.placement { + Placement::X { x1, x2, y } => { + Line { start: Point { x: x1, y }, end: Point { x: x2, y } } + .into_styled(PrimitiveStyle::with_stroke(color, thickness as u32)) + .draw(display)?; + if let Some(title) = self.axis.title { + let title = Text::new(title, Point { x: x1, y: y + 10 }) + .into_styled(text_style); + let title = title.translate(Point { x: (x2 - x1) / 2 - title.size().width as i32 / 2, y: 0 }); + title.draw(display)?; + } + + for mark in scale_marks { + let x = mark.scale_between_ranges(&self.axis.range, &(x1..x2)); + Line { start: Point { x, y: y - tick_size as i32 }, end: Point { x, y: y + tick_size as i32 } } + .into_styled(PrimitiveStyle::with_stroke(color, thickness as u32)) + .draw(display)?; + let mut buf: String:: = String::new(); + write!(buf, "{}", mark).unwrap(); + Text::new(&buf, Point { x: x + 2, y: y + 2 }).into_styled(text_style).draw(display)?; + } + } + Placement::Y { y1, y2, x } => { + Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } + .into_styled(PrimitiveStyle::with_stroke(color, thickness as u32)) + .draw(display)?; + + let mut max_tick_text_width = 0; + for mark in scale_marks { + let y = mark.scale_between_ranges(&self.axis.range, &(y2..y1)); + Line { start: Point { x: x - tick_size as i32, y }, end: Point { x: x + tick_size as i32, y } } + .into_styled(PrimitiveStyle::with_stroke(color, thickness as u32)) + .draw(display)?; + let mut buf: String:: = String::new(); + write!(buf, "{}", mark).unwrap(); + let tick_val = Text::new(&buf, Point { x, y }).into_styled(text_style); + let tick_val = tick_val.translate(Point { x: -(tick_val.size().width as i32) - 2, y: 2 }); + if tick_val.size().width > max_tick_text_width { max_tick_text_width = tick_val.size().width } + tick_val.draw(display)?; + } + if let Some(title) = self.axis.title { + let title = Text::new(title, Point { x, y: y1 }) + .into_styled(text_style); + let title = title.translate(Point { x: -(title.size().width as i32) - max_tick_text_width as i32 - tick_size as i32 - 2, y: (y2 - y1) / 2 }); + title.draw(display)?; + } + } + } + Ok(()) + } +} \ No newline at end of file diff --git a/src/drawable_axis.rs b/src/drawable_axis.rs deleted file mode 100644 index 48e18fc..0000000 --- a/src/drawable_axis.rs +++ /dev/null @@ -1,106 +0,0 @@ -use embedded_graphics::prelude::*; -use crate::axis::{Placement, Scale}; -use core::ops::Range; -use embedded_graphics::style::{TextStyle, PrimitiveStyle}; -use embedded_graphics::fonts::Text; -use crate::range_conv::Scalable; -use embedded_graphics::primitives::Line; -use heapless::{consts::*, String}; -use core::fmt::Write; - - -pub struct DrawableAxis<'a, C, F> - where - C: PixelColor, - F: Font, - TextStyle: Clone, -{ - title: &'a str, - placement: Placement, - range: Range, - scale: Scale, - color: C, - text_style: TextStyle, - tick_size: usize, - thickness: usize, -} - -impl<'a, C, F> DrawableAxis<'a, C, F> - where - C: PixelColor, - F: Font, - TextStyle: Clone, -{ - pub(in crate) fn new(title: &'a str, placement: Placement, range: Range, scale: Scale, color: C, text_style: TextStyle, tick_height: usize, thickness: usize) -> DrawableAxis<'a, C, F> { - DrawableAxis { title, placement, range, scale, color, text_style, tick_size: tick_height, thickness } - } - - pub fn size(&self) -> Point { - Point { x: 50, y: 50 } - } -} - - -impl<'a, C, F> Drawable for DrawableAxis<'a, C, F> - where - C: PixelColor, - F: Font + Copy, - TextStyle: Clone, -{ - fn draw>(self, display: &mut D) -> Result<(), D::Error> { - let scale_marks = match self.scale { - Scale::Fixed(interval) => { - self.range.clone().into_iter().step_by(interval) - } - Scale::RangeFraction(fraction) => { - let len = self.range.len(); - self.range.clone().into_iter().step_by(len / fraction) - } - }; - match self.placement { - Placement::X { x1, x2, y } => { - Line { start: Point { x: x1, y }, end: Point { x: x2, y } } - .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) - .draw(display)?; - let title = Text::new(self.title, Point { x: x1, y: y + 10 }) - .into_styled(self.text_style); - let title = title.translate(Point { x: (x2 - x1) / 2 - title.size().width as i32 / 2, y: 0 }); - title.draw(display)?; - - for mark in scale_marks { - let x = mark.scale_between_ranges(&self.range, &(x1..x2)); - Line { start: Point { x, y: y - self.tick_size as i32 }, end: Point { x, y: y + self.tick_size as i32 } } - .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) - .draw(display)?; - let mut buf: String:: = String::new(); - write!(buf, "{}", mark).unwrap(); - Text::new(&buf, Point { x: x + 2, y: y + 2 }).into_styled(self.text_style).draw(display)?; - } - } - Placement::Y { y1, y2, x } => { - Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } } - .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) - .draw(display)?; - - let mut max_tick_text_width = 0; - for mark in scale_marks { - let y = mark.scale_between_ranges(&self.range, &(y2..y1)); - Line { start: Point { x: x - self.tick_size as i32, y }, end: Point { x: x + self.tick_size as i32, y } } - .into_styled(PrimitiveStyle::with_stroke(self.color, self.thickness as u32)) - .draw(display)?; - let mut buf: String:: = String::new(); - write!(buf, "{}", mark).unwrap(); - let tick_val = Text::new(&buf, Point { x, y }).into_styled(self.text_style); - let tick_val = tick_val.translate(Point { x: -(tick_val.size().width as i32) - 2, y: 2 }); - if tick_val.size().width > max_tick_text_width { max_tick_text_width = tick_val.size().width } - tick_val.draw(display)?; - } - let title = Text::new(self.title, Point { x, y: y1 }) - .into_styled(self.text_style); - let title = title.translate(Point { x: -(title.size().width as i32) - max_tick_text_width as i32 - self.tick_size as i32 - 2, y: (y2 - y1) / 2 }); - title.draw(display)?; - } - } - Ok(()) - } -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index fafaba5..3b7e2e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,5 +5,4 @@ pub mod axis; pub mod polyplot; pub mod single_plot; -mod range_conv; -mod drawable_axis; +mod range_conv; \ No newline at end of file diff --git a/src/single_plot.rs b/src/single_plot.rs index 59b233b..58f5ddc 100644 --- a/src/single_plot.rs +++ b/src/single_plot.rs @@ -73,39 +73,34 @@ impl<'a, C> Drawable for DrawableSinglePlot<'a, C> C: PixelColor + Default, { fn draw>(self, display: &mut D) -> Result<(), D::Error> { - let color = match self.color { - None => C::default(), - Some(c) => c, - }; - let text_color = match self.text_color { - None => color, - Some(c) => c, - }; - let axis_color = match self.axis_color { - None => color, - Some(c) => c, - }; - - let thickness = match self.thickness { - None => 2, - Some(t) => t, - }; - - let axis_thickness = match self.axis_thickness { - None => thickness, - Some(t) => t, - }; + let color = self.color.unwrap_or_default(); + let text_color = self.text_color.unwrap_or(color); + let axis_color = self.axis_color.unwrap_or(color); + let thickness = self.thickness.unwrap_or(2); + let axis_thickness = self.axis_thickness.unwrap_or(thickness); let text_style = TextStyleBuilder::new(Font6x8) .text_color(text_color) .build(); - Axis::new("X", self.plot.curve.x_range.clone(), self.plot.x_scale) - .into_drawable_axis(Placement::X { x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y }, axis_color, text_style, 2, axis_thickness) + Axis::new( self.plot.curve.x_range.clone()) + .set_title("X") + .set_scale(self.plot.x_scale) + .into_drawable_axis(Placement::X { x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y }) + .set_color(axis_color) + .set_text_style(text_style) + .set_tick_size(2) + .set_thickness(axis_thickness) .draw(display)?; - Axis::new("Y", self.plot.curve.y_range.clone(), self.plot.y_scale) - .into_drawable_axis(Placement::Y { y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x }, axis_color, text_style, 2,axis_thickness) + Axis::new(self.plot.curve.y_range.clone()) + .set_title("Y") + .set_scale(self.plot.y_scale) + .into_drawable_axis(Placement::Y { y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x }) + .set_color(axis_color) + .set_text_style(text_style) + .set_tick_size(2) + .set_thickness(axis_thickness) .draw(display)?; self.plot.curve.into_drawable_curve( From 16231f31b36163c69cf2fe76a29c68bda52b7043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Tue, 2 Mar 2021 21:19:26 +0100 Subject: [PATCH 15/16] Remove polyplot --- examples/polyplot_mono.rs | 50 --------------------------------------- examples/polyplot_rgb.rs | 49 -------------------------------------- src/lib.rs | 1 - src/polyplot.rs | 38 ----------------------------- 4 files changed, 138 deletions(-) delete mode 100644 examples/polyplot_mono.rs delete mode 100644 examples/polyplot_rgb.rs delete mode 100644 src/polyplot.rs diff --git a/examples/polyplot_mono.rs b/examples/polyplot_mono.rs deleted file mode 100644 index d767a12..0000000 --- a/examples/polyplot_mono.rs +++ /dev/null @@ -1,50 +0,0 @@ -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 deleted file mode 100644 index 056e3d5..0000000 --- a/examples/polyplot_rgb.rs +++ /dev/null @@ -1,49 +0,0 @@ -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/src/lib.rs b/src/lib.rs index 3b7e2e2..7b2b50c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ pub mod curve; pub mod axis; -pub mod polyplot; pub mod single_plot; mod range_conv; \ No newline at end of file diff --git a/src/polyplot.rs b/src/polyplot.rs deleted file mode 100644 index 279d1c7..0000000 --- a/src/polyplot.rs +++ /dev/null @@ -1,38 +0,0 @@ -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> -{ - 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 + Default, -{ - 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, - ).set_color(*color) - .set_thickness(2) - .draw(display)?; - } - Ok(()) - } -} \ No newline at end of file From cdf9c6b9ab6445274a2f4651ebedc4e434c105b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Wed, 3 Mar 2021 00:40:27 +0100 Subject: [PATCH 16/16] Add cargo metadata and README --- Cargo.toml | 7 +++++++ README.md | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 README.md diff --git a/Cargo.toml b/Cargo.toml index 68ed282..e765d10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,13 @@ name = "embedded-plots" version = "0.1.0" authors = ["MichaƂ Chodzikiewicz "] edition = "2018" +license-file = "LICENSE" +description = "Heapless plotting library for embedded targets based on embedded-graphics crate" +homepage = "https://gitlab.com/mchodzikiewicz/embedded-plots" +repository = "https://gitlab.com/mchodzikiewicz/embedded-plots" +readme = "README.md" +keywords = ["embedded", "plot", "graphics"] +categories = ["embedded","visualization"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..54e712e --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Embedded Plots +Heapless plotting library for small embedded targets, based on [embedded-graphics](https://crates.io/crates/embedded-graphics) +crate. + +This is very beginning of the development, however it is functional to the point where single plot can be drawn. \ No newline at end of file