mirror of
https://gitlab.com/feliix42/embedded-plots.git
synced 2024-11-22 09:56:31 +00:00
Merge branch 'feature/docs' into 'develop'
Add initial docs See merge request mchodzikiewicz/embedded-plots!9
This commit is contained in:
commit
dfa192f99b
9 changed files with 270 additions and 7 deletions
|
@ -3,7 +3,7 @@ name = "embedded-plots"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Michał Chodzikiewicz <mchodzikiewicz@gmail.com>"]
|
authors = ["Michał Chodzikiewicz <mchodzikiewicz@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license-file = "LICENSE"
|
license = "LGPL-2.1-only"
|
||||||
description = "Heapless plotting library for embedded targets based on embedded-graphics crate"
|
description = "Heapless plotting library for embedded targets based on embedded-graphics crate"
|
||||||
homepage = "https://gitlab.com/mchodzikiewicz/embedded-plots"
|
homepage = "https://gitlab.com/mchodzikiewicz/embedded-plots"
|
||||||
repository = "https://gitlab.com/mchodzikiewicz/embedded-plots"
|
repository = "https://gitlab.com/mchodzikiewicz/embedded-plots"
|
||||||
|
|
104
README.md
104
README.md
|
@ -2,4 +2,108 @@
|
||||||
Heapless plotting library for small embedded targets, based on [embedded-graphics](https://crates.io/crates/embedded-graphics)
|
Heapless plotting library for small embedded targets, based on [embedded-graphics](https://crates.io/crates/embedded-graphics)
|
||||||
crate.
|
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<Rgb565> = 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<Rgb565> = 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.
|
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.
|
||||||
|
|
BIN
doc-resources/free-axis-example.png
Normal file
BIN
doc-resources/free-axis-example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.8 KiB |
BIN
doc-resources/single-plot-color.png
Normal file
BIN
doc-resources/single-plot-color.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2 KiB |
BIN
doc-resources/single-plot-mono.png
Normal file
BIN
doc-resources/single-plot-mono.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
23
src/axis.rs
23
src/axis.rs
|
@ -10,7 +10,7 @@ use embedded_graphics::{
|
||||||
};
|
};
|
||||||
use crate::range_conv::Scalable;
|
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 {
|
pub enum Placement {
|
||||||
X {
|
X {
|
||||||
x1: i32,
|
x1: i32,
|
||||||
|
@ -24,8 +24,13 @@ pub enum Placement {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to describe how densely ticks should be drawn
|
||||||
pub enum Scale {
|
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),
|
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),
|
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> {
|
pub struct Axis<'a> {
|
||||||
|
/// range that the scale will be drawn for
|
||||||
range: Range<i32>,
|
range: Range<i32>,
|
||||||
|
/// axis title displayed right next to it
|
||||||
title: Option<&'a str>,
|
title: Option<&'a str>,
|
||||||
|
/// Definition on how scale ticks should be drawn
|
||||||
scale: Option<Scale>,
|
scale: Option<Scale>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// builder methods to modify axis decoration
|
||||||
impl<'a> Axis<'a>
|
impl<'a> Axis<'a>
|
||||||
{
|
{
|
||||||
|
/// create new axis data
|
||||||
pub fn new(range: Range<i32>) -> Axis<'a> {
|
pub fn new(range: Range<i32>) -> Axis<'a> {
|
||||||
Axis { range, title: None, scale: None }
|
Axis { range, title: None, scale: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// define how scale ticks should be drawn
|
||||||
pub fn set_scale(mut self, scale: Scale) -> Axis<'a> {
|
pub fn set_scale(mut self, scale: Scale) -> Axis<'a> {
|
||||||
self.scale = Some(scale);
|
self.scale = Some(scale);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set axis title
|
||||||
pub fn set_title(mut self, title: &'a str) -> Axis<'a> {
|
pub fn set_title(mut self, title: &'a str) -> Axis<'a> {
|
||||||
self.title = Some(title);
|
self.title = Some(title);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// turn axis data into drawable object suitable for specific display
|
||||||
pub fn into_drawable_axis<C, F>(self, placement: Placement) -> DrawableAxis<'a, C, F>
|
pub fn into_drawable_axis<C, F>(self, placement: Placement) -> DrawableAxis<'a, C, F>
|
||||||
where
|
where
|
||||||
C: PixelColor + Default,
|
C: PixelColor + Default,
|
||||||
|
@ -74,6 +88,7 @@ impl<'a> Axis<'a>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drawable axis object, constructed for specific display
|
||||||
pub struct DrawableAxis<'a, C, F>
|
pub struct DrawableAxis<'a, C, F>
|
||||||
where
|
where
|
||||||
C: PixelColor,
|
C: PixelColor,
|
||||||
|
@ -102,10 +117,14 @@ impl<'a, C, F> DrawableAxis<'a, C, F>
|
||||||
self.text_style = Some(val);
|
self.text_style = Some(val);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set how wide tick should be drawn on the axis
|
||||||
pub fn set_tick_size(mut self, val: usize) -> DrawableAxis<'a, C, F> {
|
pub fn set_tick_size(mut self, val: usize) -> DrawableAxis<'a, C, F> {
|
||||||
self.tick_size = Some(val);
|
self.tick_size = Some(val);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set thickness of the main line of the axis
|
||||||
pub fn set_thickness(mut self, val: usize) -> DrawableAxis<'a, C, F> {
|
pub fn set_thickness(mut self, val: usize) -> DrawableAxis<'a, C, F> {
|
||||||
self.thickness = Some(val);
|
self.thickness = Some(val);
|
||||||
self
|
self
|
||||||
|
@ -119,6 +138,8 @@ impl<'a, C, F> Drawable<C> for DrawableAxis<'a, C, F>
|
||||||
F: Font + Copy,
|
F: Font + Copy,
|
||||||
TextStyle<C, F>: Clone + Default,
|
TextStyle<C, F>: Clone + Default,
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// most important function - draw the axis on the display
|
||||||
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
||||||
let color = self.color.unwrap_or_default();
|
let color = self.color.unwrap_or_default();
|
||||||
let text_style = self.text_style.unwrap_or_default();
|
let text_style = self.text_style.unwrap_or_default();
|
||||||
|
|
13
src/curve.rs
13
src/curve.rs
|
@ -10,23 +10,27 @@ use embedded_graphics::pixelcolor::{PixelColor};
|
||||||
use embedded_graphics::primitives::{Line, Primitive};
|
use embedded_graphics::primitives::{Line, Primitive};
|
||||||
use embedded_graphics::style::PrimitiveStyle;
|
use embedded_graphics::style::PrimitiveStyle;
|
||||||
|
|
||||||
|
/// representation of the single point on the curve
|
||||||
pub struct PlotPoint {
|
pub struct PlotPoint {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// curve object that contains data to be plotted
|
||||||
pub struct Curve<'a> {
|
pub struct Curve<'a> {
|
||||||
|
/// slice of points to be drawn
|
||||||
points: &'a [PlotPoint],
|
points: &'a [PlotPoint],
|
||||||
pub x_range: Range<i32>,
|
pub x_range: Range<i32>,
|
||||||
pub y_range: Range<i32>,
|
pub y_range: Range<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Curve<'a> {
|
impl<'a> Curve<'a> {
|
||||||
|
/// create new curve data with manual ranges
|
||||||
pub fn new(points: &'a [PlotPoint], x_range: Range<i32>, y_range: Range<i32>) -> Curve {
|
pub fn new(points: &'a [PlotPoint], x_range: Range<i32>, y_range: Range<i32>) -> Curve {
|
||||||
Curve { points, x_range, y_range }
|
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 {
|
pub fn from_data(points: &'a [PlotPoint]) -> Curve {
|
||||||
let x_range = match points
|
let x_range = match points
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -46,6 +50,7 @@ impl<'a> Curve<'a> {
|
||||||
Curve { points, x_range, y_range }
|
Curve { points, x_range, y_range }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// create curve that can be drawed on specific display
|
||||||
pub fn into_drawable_curve<C>(&self,
|
pub fn into_drawable_curve<C>(&self,
|
||||||
top_left: &'a Point,
|
top_left: &'a Point,
|
||||||
bottom_right: &'a Point,
|
bottom_right: &'a Point,
|
||||||
|
@ -76,6 +81,7 @@ impl<'a> Curve<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drawable curve object, constructed for specific display
|
||||||
pub struct DrawableCurve<C, I>
|
pub struct DrawableCurve<C, I>
|
||||||
{
|
{
|
||||||
scaled_data: I,
|
scaled_data: I,
|
||||||
|
@ -83,15 +89,19 @@ pub struct DrawableCurve<C, I>
|
||||||
thickness: Option<usize>,
|
thickness: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// builder methods to modify curve decoration
|
||||||
impl<C, I> DrawableCurve<C, I>
|
impl<C, I> DrawableCurve<C, I>
|
||||||
where
|
where
|
||||||
C: PixelColor,
|
C: PixelColor,
|
||||||
I: Iterator<Item=Point>,
|
I: Iterator<Item=Point>,
|
||||||
{
|
{
|
||||||
|
/// set curve color
|
||||||
pub fn set_color(mut self, color: C) -> DrawableCurve<C, I> {
|
pub fn set_color(mut self, color: C) -> DrawableCurve<C, I> {
|
||||||
self.color = Some(color);
|
self.color = Some(color);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set curve line thickness
|
||||||
pub fn set_thickness(mut self, thickness: usize) -> DrawableCurve<C,I> {
|
pub fn set_thickness(mut self, thickness: usize) -> DrawableCurve<C,I> {
|
||||||
self.thickness = Some(thickness);
|
self.thickness = Some(thickness);
|
||||||
self
|
self
|
||||||
|
@ -102,6 +112,7 @@ impl<C, I> Drawable<C> for DrawableCurve<C, I>
|
||||||
where C: PixelColor + Default,
|
where C: PixelColor + Default,
|
||||||
I: Iterator<Item=Point>,
|
I: Iterator<Item=Point>,
|
||||||
{
|
{
|
||||||
|
/// most important function - draw the curve on the display
|
||||||
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
||||||
let color = match self.color {
|
let color = match self.color {
|
||||||
None => C::default(),
|
None => C::default(),
|
||||||
|
|
112
src/lib.rs
112
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<Rgb565> = 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<Rgb565> = 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 curve;
|
||||||
pub mod axis;
|
pub mod axis;
|
||||||
|
/// plot that draws single data series
|
||||||
pub mod single_plot;
|
pub mod single_plot;
|
||||||
|
|
||||||
mod range_conv;
|
mod range_conv;
|
|
@ -7,22 +7,31 @@ use crate::axis::{Scale, Placement, Axis};
|
||||||
use embedded_graphics::style::TextStyleBuilder;
|
use embedded_graphics::style::TextStyleBuilder;
|
||||||
use embedded_graphics::fonts::Font6x8;
|
use embedded_graphics::fonts::Font6x8;
|
||||||
|
|
||||||
|
/// Display agnostic single curve plot object
|
||||||
pub struct SinglePlot<'a> {
|
pub struct SinglePlot<'a> {
|
||||||
|
/// curve to be drawn on the plot
|
||||||
curve: &'a Curve<'a>,
|
curve: &'a Curve<'a>,
|
||||||
|
/// range of X axis on which curve will be drawn
|
||||||
x_scale: Scale,
|
x_scale: Scale,
|
||||||
|
/// range of Y axis on which curve will be drawn
|
||||||
y_scale: Scale,
|
y_scale: Scale,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SinglePlot<'a> {
|
impl<'a> SinglePlot<'a> {
|
||||||
|
/// create SinglePlot object with manual range
|
||||||
pub fn new(curve: &'a Curve<'a>, x_scale: Scale, y_scale: Scale) -> SinglePlot {
|
pub fn new(curve: &'a Curve<'a>, x_scale: Scale, y_scale: Scale) -> SinglePlot {
|
||||||
SinglePlot { curve, x_scale, y_scale }
|
SinglePlot { curve, x_scale, y_scale }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: add auto range plot constructor
|
||||||
|
|
||||||
|
/// convert to drawable form for specific display
|
||||||
pub fn into_drawable<C: PixelColor + Default>(self, top_left: Point, bottom_right: Point) -> DrawableSinglePlot<'a, C> {
|
pub fn into_drawable<C: PixelColor + Default>(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 }
|
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>
|
pub struct DrawableSinglePlot<'a, C>
|
||||||
where
|
where
|
||||||
C: PixelColor + Default,
|
C: PixelColor + Default,
|
||||||
|
@ -37,6 +46,7 @@ pub struct DrawableSinglePlot<'a, C>
|
||||||
bottom_right: Point,
|
bottom_right: Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// builder methods to modify plot decoration
|
||||||
impl<'a, C> DrawableSinglePlot<'a, C>
|
impl<'a, C> DrawableSinglePlot<'a, C>
|
||||||
where
|
where
|
||||||
C: PixelColor + Default,
|
C: PixelColor + Default,
|
||||||
|
@ -46,32 +56,39 @@ impl<'a, C> DrawableSinglePlot<'a, C>
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// if not set, main color will be used
|
||||||
pub fn set_text_color(mut self, color: C) -> DrawableSinglePlot<'a, C> {
|
pub fn set_text_color(mut self, color: C) -> DrawableSinglePlot<'a, C> {
|
||||||
self.text_color = Some(color);
|
self.text_color = Some(color);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// if not set, main color will be used
|
||||||
pub fn set_axis_color(mut self, color: C) -> DrawableSinglePlot<'a, C> {
|
pub fn set_axis_color(mut self, color: C) -> DrawableSinglePlot<'a, C> {
|
||||||
self.axis_color = Some(color);
|
self.axis_color = Some(color);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set curve thickness
|
||||||
pub fn set_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> {
|
pub fn set_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> {
|
||||||
self.thickness = Some(thickness);
|
self.thickness = Some(thickness);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///set axis thickness
|
||||||
pub fn set_axis_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> {
|
pub fn set_axis_thickness(mut self, thickness: usize) -> DrawableSinglePlot<'a, C> {
|
||||||
self.axis_thickness = Some(thickness);
|
self.axis_thickness = Some(thickness);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: add axis ticks thickness
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C> Drawable<C> for DrawableSinglePlot<'a, C>
|
impl<'a, C> Drawable<C> for DrawableSinglePlot<'a, C>
|
||||||
where
|
where
|
||||||
C: PixelColor + Default,
|
C: PixelColor + Default,
|
||||||
{
|
{
|
||||||
|
/// most important function - draw the plot on the display
|
||||||
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
||||||
let color = self.color.unwrap_or_default();
|
let color = self.color.unwrap_or_default();
|
||||||
let text_color = self.text_color.unwrap_or(color);
|
let text_color = self.text_color.unwrap_or(color);
|
||||||
|
|
Loading…
Reference in a new issue