From bf0b70d96c2e5a503aa6856689c71cf95ebae31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Chodzikiewicz?= Date: Wed, 3 Mar 2021 22:20:44 +0100 Subject: [PATCH] Add initial docs and clarify licence in Cargo.toml --- Cargo.toml | 2 +- README.md | 110 ++++++++++++++++++++++++++- doc-resources/free-axis-example.png | Bin 0 -> 10047 bytes doc-resources/single-plot-color.png | Bin 0 -> 2020 bytes doc-resources/single-plot-mono.png | Bin 0 -> 1543 bytes src/axis.rs | 23 +++++- src/curve.rs | 13 +++- src/lib.rs | 112 +++++++++++++++++++++++++++- src/single_plot.rs | 17 +++++ 9 files changed, 270 insertions(+), 7 deletions(-) create mode 100644 doc-resources/free-axis-example.png create mode 100644 doc-resources/single-plot-color.png create mode 100644 doc-resources/single-plot-mono.png diff --git a/Cargo.toml b/Cargo.toml index e765d10..ad4a6e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "embedded-plots" version = "0.1.0" authors = ["MichaƂ Chodzikiewicz "] edition = "2018" -license-file = "LICENSE" +license = "LGPL-2.1-only" 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" diff --git a/README.md b/README.md index 54e712e..98f9550 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,109 @@ # Embedded Plots -Heapless plotting library for small embedded targets, based on [embedded-graphics](https://crates.io/crates/embedded-graphics) -crate. + 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 + Thanks to basing it on `embedded-graphics` crate the library is very portable out of the box. + It is not dependent on any hardware target. + To throw it into your project, you only need to have a display that implements `DrawTarget` trait. + For more details see [DrawTarget](https://docs.rs/embedded-graphics/latest/embedded_graphics/prelude/trait.DrawTarget.html) docs. + + Bonus feature of `embedded-graphics` is the simulator. + You can use it to develop your plots without your target hardware, easily create documentation and so on. + + Library utilizes builder pattern and type states - it allows easy separation of the data and decoration in the target application. + + ## Examples + ### Single plot + Simple plot example + #### On color display: + ![single plot on color display](doc-resources/single-plot-color.png "Color plot of single curve") + #### On monochromatic display: + ![single plot on monochromatic display](doc-resources/single-plot-mono.png "Monochromatic plot of single curve") + + Code to render: + ```rust + use embedded_plots::curve::{Curve, PlotPoint}; + use embedded_plots::single_plot::SinglePlot; + use embedded_plots::axis::Scale; + use embedded_graphics::geometry::{Point, Size}; + use embedded_graphics::pixelcolor::{RgbColor, Rgb565}; + use embedded_graphics::drawable::Drawable; + + //simulator dependencies, aka screen driver + use embedded_graphics_simulator::SimulatorDisplay; + + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); + let data = vec![ + PlotPoint { x: 0, y: 0 }, + 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, + Scale::RangeFraction(3), + Scale::RangeFraction(2)) + .into_drawable( + Point { x: 50, y: 10 }, + Point { x: 430, y: 250 }) + .set_color(RgbColor::YELLOW) + .set_text_color(RgbColor::WHITE); + + plot.draw(&mut display).unwrap(); + ``` + + ### Axis + You can also use axis on its own, it looks like this: + ![free axis examples](doc-resources/free-axis-example.png "Free axis example") + Code to render example axis: + ```rust + use embedded_plots::axis::{Axis, Scale, Placement}; + use embedded_graphics::pixelcolor::{RgbColor, Rgb565}; + use embedded_graphics::drawable::Drawable; + use embedded_graphics::geometry::Size; + + //simulator dependencies, aka screen driver + use embedded_graphics_simulator::SimulatorDisplay; + use embedded_graphics::style::TextStyleBuilder; + use embedded_graphics::fonts::Font6x8; + + let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); + + let text_style_white = TextStyleBuilder::new(Font6x8) + .text_color(RgbColor::WHITE) + .build(); + Axis::new(0..100) + .set_title("Title") + .set_scale(Scale::Fixed(10)) + .into_drawable_axis(Placement::X { x1: 40, x2: 230, y: 10 }) + .set_text_style(text_style_white) + .set_color(RgbColor::WHITE) + .draw(&mut display).unwrap(); + ``` + For more details, see `free_axis` example + + ## Current limitations and future plans + This is very beginning of the development, however it is functional to the point where single plot can be drawn. + + Main issue for now is that you need to predict on how much space will be occupied by axis ticks, + numbers and titles, points passed to `.into_drawable()` are the boundaries for which curve is scaled. + This will be fixed, please be prepared for it since it might be a breaking change for you. + + #### Main features planned soon: + * Drawing multiple curves that share the same X and Y domains on a single plot (take curves slice instead of single curve) + * Dual plot - drawing curves that have two separate domains (either only on one axis or both). + Axis on both sides, left and right (or top and bottom) will be drawn with color corresponding to plot + * Support for floating point domains + * Support for fixed point curve data with intermediate floating point scales (to avoid floating point calculations for each drawn point) + + #### Features I'd love to see in the future: + * Partial redrawing - possibility to substitute data and detect which parts of the screen needs to be redrawed + * Oscilloscope style live mode (adding new points without any redrawing, no data retention) + * Cursors - manual and math based (max,min,avg and so on...) + + ## Contributions + Contributions are more than welcome, if you have particular improvement, raise an issue or submit merge request on project's Gitlab page. + + If you just want to help but don't have anything specific in mind, please take a look at [issue tracker](https://gitlab.com/mchodzikiewicz/embedded-plots/-/issues) and pick one. diff --git a/doc-resources/free-axis-example.png b/doc-resources/free-axis-example.png new file mode 100644 index 0000000000000000000000000000000000000000..f33bb2ac7eb8aa64179293ad4299e04149f24fbf GIT binary patch literal 10047 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 literal 0 HcmV?d00001 diff --git a/doc-resources/single-plot-color.png b/doc-resources/single-plot-color.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5d33221458a2de879110f12716ed22a313dcf7 GIT binary patch literal 2020 zcmX|?e^e7!7RLuiC#+78D%xTClmUw17L_4cATZ@cDmqY_t}bNaDm|bw zDxx6ipasK*C{{YPr=m-XwOEx{WoBX!@x)eCw!ldQEvs(g`U5w`J&EV+JLmoJ-sj%$ z_uhNXJMTblPKIY37Qw)ez)&7lQnx{*xdGio7T*fgtF2r7QCF5EO9LyZsv8 z3wQ^DB#oJA%hr~ye{|2b&9pKoJ#TCE>(@s*r4irkDLN%dHw(>e=!4;F?%69Fr$GN- z1%pN9%+8P zEofo}ngP6*5bBJ)D_AsJeV|I)T*VAD`oIDs--(HdRx+(slv;H=II@)xy0yCX2p#9M zQh$gS7Zc~gd{+6KPKwYWK5)AbS8pQXLVaKvr%Oa=$(sxcR~HdSB7I;!r`v+iTi#@B z#J3j^Z_g9hgJJ3jA>GKcW{{eM(3!M`(V|mCvbB)79WFd<;&b=KhejM~%=@pZq9=KQ%L&N?(-K$a}6TdvgrO1JZM{4KlLsc#LA- zYSekb885YCA0MVp^2(f*27OH3@dU-b-Kg`RGv3dREnyuGI*hk5@uANE{9W6#6m@Ex z7%^F4u*pYuAat0|O;UvGgJ8up1>(QV5FT5nXk?mqgVbp?;25Z;@KhCzuEWI3R$Vkl zc0>q$`x(##B%AQmVj8`Oi5soDAdZwr3Vj9!90t_|I838+f<-^8tAZo{A-cA{-xH0{ za-Y|YLR@x)0X4qP>_$GVfDl53K698PRL~oF%RT@jv_GA|XwiyD!SW3z`iZVPS=7)% z1u_xCjvjKXY2qy^C-$HsSDpomhV?`z7m;8F|YYXIcD$ zdExMQh0pubBK+_XrXp5>>}(Z_4>_1DYDh<;{~}pcxNySATh=(SmsLzffMV{Ca^z@) zV0nT%XNoRY7Bw`5MiZ_o=VlwGe~80p>ZzeT#GOl5ylcnGngFbdVTF3f6(et1>cq+n zOu~K;az&2(5)>wky=szmm4qh2pX_B6>$^NHmnJ&r+Oe`h3rK!UkE8kLKi86p!D6+k z(Q&stcuB64lJ>an)18aAue)vQvbSf|W>Z5^u>OK)ft%~TJ!X1Oxg2pb{K)r&TW3c!&h`@-r<&Y}utE0A7_c)~hV9NDa_-{pI+ zt`LrF=9QOL8T?XMM-f>!ycKb8r%!b&{b{skBT=&TM!OcBd^1t7q#pFVL z7&B90j}tgDaOEI?mtmqh+ly|>uaki_%!U8-c(ZPif>MdlKL8US_@}2~=WL`h>mt1w zPc1b|=raJ``%{#_k*$5(h`4K{vT&Ujg(XVpTJRS#a6>JCAIgFF%&R;9)(w(fdydg0 z2pq|#y8!&vSzK)Jq62Gt$z0_zMzh$9R^0(`kvv%ZgMKrfO3ta#$OPzNkhhwMh7fUz z9%b{+0u!J6=SPfYfdFMTTx-{&_h-!&zo1c8Z3ZU3FYPBr6DdGn&9b&@(YDWkILkW@ zzgaXYUt27BsxG7M+2s6`nDzoUnAM5NfRMYpnq04-V($DSDNmm}*NzZP%m(Y~|?UL(^C zsk2XkJP7}*S1m9fp$7omF+(nJWNAwftoX>_cm9BpvSaZY4+BU6l1{mM%(Nk&BTK&* zLoT&Zm4UlQ;)fP3Y^fAW*gYdyi)p`Nah>YB({CL~nK^f2{Ko)i;QiX?xs~%$>bmxi zVUpq_l3l+2f_~)rlax$DqW))J z5xRU)#S8z(gGE5Kv+;Ud#L;93QoTAK_1jV~)%f%=2Ay;)smZ1)$2+is nq%o`@X~X@zgJ;~nFSh?x2pnSSoe_@D^qw=*bJ9+y6zu*V4$o>& literal 0 HcmV?d00001 diff --git a/doc-resources/single-plot-mono.png b/doc-resources/single-plot-mono.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8e3e6c02222cf934e20c8206c7eb79cc490231 GIT binary patch literal 1543 zcmZ8he^AnA7=OvsewegtvmZ{;tn6x9TQpCEVVg@iOue}@CBcrJ$f4^8v!>w}*LGdo znRK~QS%960S^@4f4HL0rH_sV=l%S6-sk(Q zjMNS5wygsIY#^njdiGW)1e*wRogw(r)I zG|U8K4Vb*qfJY+^#jzfkSp*rC%*p31t;m83>lwT%{ zY#&vce-xr%b=aDZK-nFr3YSkzj*yB{o6pZdXZ8@ng|&HW{W5C(mBpj{XcC;xR|NWD z+f25{YWD9TkeSaEe%(~*J+aJ-EE0T-pI7S#dcD~BJ14U~wPF`R#KJ)@M#2pP;Lp3W zhG|zh6jD_%>LZbK&21^Qe^W|=ZyElX;3zgB1UY%!ktEsLwVj}57JcW}J7Kp=sSTmj z3C&n5cQvL8m*ZuIX&jEv=b9?X&!lN`qNY99*pPWZjub4i)YYUcvAtrEE}tC4ci8yd zOAdWvGkgginn%zQ6m+`3*sI*99L5Il$RYvO{uT%LJnV$3aM@k%gEF?1MI~31GJ}M> zDka*Ga`)8E$&Lwo~LCJEEUx+lKhOiZeXXiS}W z$7jdsQ4V1s0iz=jgZgC=)8$UP zl@1mrrgEn_PGhV{;H&Vs^+Q(rPqjVuk4rvWWcCSXZ_}V8UxmtTE)V$u zj!-;pwgTtfB)TJykEUez|+GP^&)KNwXhGQXy?8nZ=gZLiEBzIVj|;X?l( zK#ts}=7Zt^3<2|HD#D3k*T(8D=ZIB*_D!g-0&-Gr#U3&*MFRi;>C=o9*~f(!{sjku B8@B)e literal 0 HcmV?d00001 diff --git a/src/axis.rs b/src/axis.rs index f3e8ab3..cd5b038 100644 --- a/src/axis.rs +++ b/src/axis.rs @@ -10,7 +10,7 @@ use embedded_graphics::{ }; use crate::range_conv::Scalable; - +/// Used to provide alignment of an axis, it will be drown exactly on the line marked by the points pub enum Placement { X { x1: i32, @@ -24,8 +24,13 @@ pub enum Placement { }, } +/// Used to describe how densely ticks should be drawn pub enum Scale { + /// Fixed scale means that ticks will be drawn between each increment of absolute distance provided. + /// for example, on range 0..30 and Fixed(10), ticks will be drawn for 0, 10 and 20 Fixed(usize), + /// RangeFraction means that provided number of ticks ticks will be drawn on entire range + /// for example, on range 0..60 and RangeFraction(3), ticks will be drawn for 0, 20 and 40 RangeFraction(usize), } @@ -35,28 +40,37 @@ impl Default for Scale { } } +/// Display-agnostic axis object, only contains scale range and title, can be converted to drawable axis for specific display pub struct Axis<'a> { + /// range that the scale will be drawn for range: Range, + /// axis title displayed right next to it title: Option<&'a str>, + /// Definition on how scale ticks should be drawn scale: Option, } +/// builder methods to modify axis decoration impl<'a> Axis<'a> { + /// create new axis data pub fn new(range: Range) -> Axis<'a> { Axis { range, title: None, scale: None } } + /// define how scale ticks should be drawn pub fn set_scale(mut self, scale: Scale) -> Axis<'a> { self.scale = Some(scale); self } + /// set axis title pub fn set_title(mut self, title: &'a str) -> Axis<'a> { self.title = Some(title); self } + /// turn axis data into drawable object suitable for specific display pub fn into_drawable_axis(self, placement: Placement) -> DrawableAxis<'a, C, F> where C: PixelColor + Default, @@ -74,6 +88,7 @@ impl<'a> Axis<'a> } } +/// Drawable axis object, constructed for specific display pub struct DrawableAxis<'a, C, F> where C: PixelColor, @@ -102,10 +117,14 @@ impl<'a, C, F> DrawableAxis<'a, C, F> self.text_style = Some(val); self } + + /// set how wide tick should be drawn on the axis pub fn set_tick_size(mut self, val: usize) -> DrawableAxis<'a, C, F> { self.tick_size = Some(val); self } + + /// set thickness of the main line of the axis pub fn set_thickness(mut self, val: usize) -> DrawableAxis<'a, C, F> { self.thickness = Some(val); self @@ -119,6 +138,8 @@ impl<'a, C, F> Drawable for DrawableAxis<'a, C, F> F: Font + Copy, TextStyle: Clone + Default, { + + /// most important function - draw the axis on the display 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(); diff --git a/src/curve.rs b/src/curve.rs index 0036141..0c0a736 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -10,23 +10,27 @@ use embedded_graphics::pixelcolor::{PixelColor}; use embedded_graphics::primitives::{Line, Primitive}; use embedded_graphics::style::PrimitiveStyle; - +/// representation of the single point on the curve pub struct PlotPoint { pub x: i32, pub y: i32, } +/// curve object that contains data to be plotted pub struct Curve<'a> { + /// slice of points to be drawn points: &'a [PlotPoint], pub x_range: Range, pub y_range: Range, } impl<'a> Curve<'a> { + /// create new curve data with manual ranges pub fn new(points: &'a [PlotPoint], x_range: Range, y_range: Range) -> Curve { Curve { points, x_range, y_range } } + /// create new curve data with ranges automatically deducted based on provided points pub fn from_data(points: &'a [PlotPoint]) -> Curve { let x_range = match points .iter() @@ -46,6 +50,7 @@ impl<'a> Curve<'a> { Curve { points, x_range, y_range } } + /// create curve that can be drawed on specific display pub fn into_drawable_curve(&self, top_left: &'a Point, bottom_right: &'a Point, @@ -76,6 +81,7 @@ impl<'a> Curve<'a> { } } +/// Drawable curve object, constructed for specific display pub struct DrawableCurve { scaled_data: I, @@ -83,15 +89,19 @@ pub struct DrawableCurve thickness: Option, } +/// builder methods to modify curve decoration impl DrawableCurve where C: PixelColor, I: Iterator, { + /// set curve color pub fn set_color(mut self, color: C) -> DrawableCurve { self.color = Some(color); self } + + /// set curve line thickness pub fn set_thickness(mut self, thickness: usize) -> DrawableCurve { self.thickness = Some(thickness); self @@ -102,6 +112,7 @@ impl Drawable for DrawableCurve where C: PixelColor + Default, I: Iterator, { + /// most important function - draw the curve on the display fn draw>(self, display: &mut D) -> Result<(), D::Error> { let color = match self.color { None => C::default(), diff --git a/src/lib.rs b/src/lib.rs index 7b2b50c..151f2a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,117 @@ -#![no_std] +//!# Embedded Plots +//! Heapless plotting library for small embedded targets, based on [embedded-graphics](https://crates.io/crates/embedded-graphics) +//! crate. +//! +//! Thanks to basing it on `embedded-graphics` crate the library is very portable out of the box. +//! It is not dependent on any hardware target. +//! To throw it into your project, you only need to have a display that implements `DrawTarget` trait. +//! For more details see [DrawTarget](https://docs.rs/embedded-graphics/latest/embedded_graphics/prelude/trait.DrawTarget.html) docs. +//! +//! Bonus feature of `embedded-graphics` is the simulator. +//! You can use it to develop your plots without your target hardware, easily create documentation and so on. +//! +//! Library utilizes builder pattern and type states - it allows easy separation of the data and decoration in the target application. +//! +//! ## Examples +//! ### Single plot +//! Simple plot example +//! #### On color display: +//! ![single plot on color display](doc-resources/single-plot-color.png "Color plot of single curve") +//! #### On monochromatic display: +//! ![single plot on monochromatic display](doc-resources/single-plot-mono.png "Monochromatic plot of single curve") +//! +//! Code to render: +//! ```rust +//! use embedded_plots::curve::{Curve, PlotPoint}; +//! use embedded_plots::single_plot::SinglePlot; +//! use embedded_plots::axis::Scale; +//! use embedded_graphics::geometry::{Point, Size}; +//! use embedded_graphics::pixelcolor::{RgbColor, Rgb565}; +//! use embedded_graphics::drawable::Drawable; +//! +//! //simulator dependencies, aka screen driver +//! use embedded_graphics_simulator::SimulatorDisplay; +//! +//! let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); +//! let data = vec![ +//! PlotPoint { x: 0, y: 0 }, +//! 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, +//! Scale::RangeFraction(3), +//! Scale::RangeFraction(2)) +//! .into_drawable( +//! Point { x: 50, y: 10 }, +//! Point { x: 430, y: 250 }) +//! .set_color(RgbColor::YELLOW) +//! .set_text_color(RgbColor::WHITE); +//! +//! plot.draw(&mut display).unwrap(); +//! ``` +//! +//! ### Axis +//! You can also use axis on its own, it looks like this: +//! ![free axis examples](doc-resources/free-axis-example.png "Free axis example") +//! Code to render example axis: +//! ```rust +//! use embedded_plots::axis::{Axis, Scale, Placement}; +//! use embedded_graphics::pixelcolor::{RgbColor, Rgb565}; +//! use embedded_graphics::drawable::Drawable; +//! use embedded_graphics::geometry::Size; +//! +//! //simulator dependencies, aka screen driver +//! use embedded_graphics_simulator::SimulatorDisplay; +//! use embedded_graphics::style::TextStyleBuilder; +//! use embedded_graphics::fonts::Font6x8; +//! +//! let mut display: SimulatorDisplay = SimulatorDisplay::new(Size::new(480, 272)); +//! +//! let text_style_white = TextStyleBuilder::new(Font6x8) +//! .text_color(RgbColor::WHITE) +//! .build(); +//! Axis::new(0..100) +//! .set_title("Title") +//! .set_scale(Scale::Fixed(10)) +//! .into_drawable_axis(Placement::X { x1: 40, x2: 230, y: 10 }) +//! .set_text_style(text_style_white) +//! .set_color(RgbColor::WHITE) +//! .draw(&mut display).unwrap(); +//! ``` +//! For more details, see `free_axis` example +//! +//! ## Current limitations and future plans +//! This is very beginning of the development, however it is functional to the point where single plot can be drawn. +//! +//! Main issue for now is that you need to predict on how much space will be occupied by axis ticks, +//! numbers and titles, points passed to `.into_drawable()` are the boundaries for which curve is scaled. +//! This will be fixed, please be prepared for it since it might be a breaking change for you. +//! +//! #### Main features planned soon: +//! * Drawing multiple curves that share the same X and Y domains on a single plot (take curves slice instead of single curve) +//! * Dual plot - drawing curves that have two separate domains (either only on one axis or both). +//! Axis on both sides, left and right (or top and bottom) will be drawn with color corresponding to plot +//! * Support for floating point domains +//! * Support for fixed point curve data with intermediate floating point scales (to avoid floating point calculations for each drawn point) +//! +//! #### Features I'd love to see in the future: +//! * Partial redrawing - possibility to substitute data and detect which parts of the screen needs to be redrawed +//! * Oscilloscope style live mode (adding new points without any redrawing, no data retention) +//! * Cursors - manual and math based (max,min,avg and so on...) +//! +//! ## Contributions +//! Contributions are more than welcome, if you have particular improvement, raise an issue or submit merge request on project's Gitlab page. +//! +//! If you just want to help but don't have anything specific in mind, please take a look at [issue tracker](https://gitlab.com/mchodzikiewicz/embedded-plots/-/issues) and pick one. +#![no_std] pub mod curve; pub mod axis; +/// plot that draws single data series pub mod single_plot; mod range_conv; \ No newline at end of file diff --git a/src/single_plot.rs b/src/single_plot.rs index 58f5ddc..18f65cc 100644 --- a/src/single_plot.rs +++ b/src/single_plot.rs @@ -7,22 +7,31 @@ use crate::axis::{Scale, Placement, Axis}; use embedded_graphics::style::TextStyleBuilder; use embedded_graphics::fonts::Font6x8; +/// Display agnostic single curve plot object pub struct SinglePlot<'a> { + /// curve to be drawn on the plot curve: &'a Curve<'a>, + /// range of X axis on which curve will be drawn x_scale: Scale, + /// range of Y axis on which curve will be drawn y_scale: Scale, } impl<'a> SinglePlot<'a> { + /// create SinglePlot object with manual range pub fn new(curve: &'a Curve<'a>, x_scale: Scale, y_scale: Scale) -> SinglePlot { SinglePlot { curve, x_scale, y_scale } } + //TODO: add auto range plot constructor + + /// convert to drawable form for specific display 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 } } } +/// Drawable single plot object, constructed for specific display pub struct DrawableSinglePlot<'a, C> where C: PixelColor + Default, @@ -37,6 +46,7 @@ pub struct DrawableSinglePlot<'a, C> bottom_right: Point, } +/// builder methods to modify plot decoration impl<'a, C> DrawableSinglePlot<'a, C> where C: PixelColor + Default, @@ -46,32 +56,39 @@ impl<'a, C> DrawableSinglePlot<'a, C> self } + /// if not set, main color will be used pub fn set_text_color(mut self, color: C) -> DrawableSinglePlot<'a, C> { self.text_color = Some(color); self } + /// if not set, main color will be used pub fn set_axis_color(mut self, color: C) -> DrawableSinglePlot<'a, C> { self.axis_color = Some(color); self } + /// set curve thickness pub fn set_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> { self.thickness = Some(thickness); self } + ///set axis thickness pub fn set_axis_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> { self.axis_thickness = Some(thickness); self } + //TODO: add axis ticks thickness + } impl<'a, C> Drawable for DrawableSinglePlot<'a, C> where C: PixelColor + Default, { + /// most important function - draw the plot on the display fn draw>(self, display: &mut D) -> Result<(), D::Error> { let color = self.color.unwrap_or_default(); let text_color = self.text_color.unwrap_or(color);