mirror of
https://gitlab.com/feliix42/embedded-plots.git
synced 2024-11-22 18:06:30 +00:00
Merge branch 'feature/plot' into 'develop'
Plot See merge request mchodzikiewicz/embedded-plots!2
This commit is contained in:
commit
54b3ba7db6
11 changed files with 307 additions and 37 deletions
|
@ -8,7 +8,7 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-graphics = "0.6.0"
|
embedded-graphics = "0.6.0"
|
||||||
|
itertools = "0.9.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
embedded-graphics-simulator = "0.2.1"
|
embedded-graphics-simulator = "0.2.1"
|
||||||
|
|
50
examples/polyplot_mono.rs
Normal file
50
examples/polyplot_mono.rs
Normal file
|
@ -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<BinaryColor> = 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(())
|
||||||
|
}
|
49
examples/polyplot_rgb.rs
Normal file
49
examples/polyplot_rgb.rs
Normal file
|
@ -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<Rgb565> = 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(())
|
||||||
|
}
|
34
examples/single_plot_mono.rs
Normal file
34
examples/single_plot_mono.rs
Normal file
|
@ -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<BinaryColor> = 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(())
|
||||||
|
}
|
37
examples/single_plot_rgb.rs
Normal file
37
examples/single_plot_rgb.rs
Normal file
|
@ -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<Rgb565> = 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(())
|
||||||
|
}
|
56
src/curve.rs
56
src/curve.rs
|
@ -3,52 +3,72 @@ use core::ops::{Range};
|
||||||
use crate::range_conv::Scalable;
|
use crate::range_conv::Scalable;
|
||||||
use crate::drawable_curve::DrawableCurve;
|
use crate::drawable_curve::DrawableCurve;
|
||||||
use embedded_graphics::prelude::*;
|
use embedded_graphics::prelude::*;
|
||||||
|
use itertools::MinMaxResult::MinMax;
|
||||||
|
use itertools::{Itertools, MinMaxResult};
|
||||||
|
|
||||||
pub struct PlotPoint {
|
pub struct PlotPoint {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Curve<'a>{
|
pub struct Curve<'a> {
|
||||||
points: &'a [PlotPoint],
|
points: &'a [PlotPoint],
|
||||||
|
x_range: Range<i32>,
|
||||||
|
y_range: Range<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Curve<'a> {
|
impl<'a> Curve<'a> {
|
||||||
pub fn new(points: &'a [PlotPoint]) -> Curve {
|
pub fn new(points: &'a [PlotPoint], x_range: Range<i32>, y_range: Range<i32>) -> Curve {
|
||||||
Curve {points}
|
Curve { points, x_range, y_range }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_drawable_curve<C>(self,
|
pub fn from_data(points: &'a [PlotPoint]) -> Curve {
|
||||||
x_range: &'a Range<i32>,
|
let x_range = match points
|
||||||
y_range: &'a Range<i32>,
|
.iter()
|
||||||
top_left : &'a Point,
|
.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<C>(&self,
|
||||||
|
top_left: &'a Point,
|
||||||
bottom_right: &'a Point,
|
bottom_right: &'a Point,
|
||||||
color: C
|
color: C,
|
||||||
) -> DrawableCurve<C,impl Iterator<Item=Point> + 'a>
|
) -> DrawableCurve<C, impl Iterator<Item=Point> + '_>
|
||||||
where C: PixelColor
|
where C: PixelColor
|
||||||
{
|
{
|
||||||
assert!(top_left.x < bottom_right.x);
|
assert!(top_left.x < bottom_right.x);
|
||||||
assert!(top_left.y < bottom_right.y);
|
assert!(top_left.y < bottom_right.y);
|
||||||
assert!(!x_range.is_empty());
|
assert!(!self.x_range.is_empty());
|
||||||
assert!(!y_range.is_empty());
|
assert!(!self.y_range.is_empty());
|
||||||
|
|
||||||
let it = self.points.iter()
|
let it = self.points.iter()
|
||||||
.map(move |p| Point{
|
.map(move |p| Point {
|
||||||
x: p.x.scale_between_ranges(
|
x: p.x.scale_between_ranges(
|
||||||
x_range,
|
&self.x_range,
|
||||||
&Range{start: top_left.x, end: bottom_right.x}
|
&Range { start: top_left.x, end: bottom_right.x },
|
||||||
),
|
),
|
||||||
y: p.y.scale_between_ranges(
|
y: p.y.scale_between_ranges(
|
||||||
y_range,
|
&self.y_range,
|
||||||
&Range{start: bottom_right.y, end: top_left.y}
|
&Range { start: bottom_right.y, end: top_left.y },
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
DrawableCurve::new(it,color)
|
DrawableCurve::new(it, color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -13,28 +13,29 @@ pub struct DrawableCurve<C, I>
|
||||||
color: C,
|
color: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
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>,
|
||||||
{
|
{
|
||||||
pub fn new(data: I,color : C) -> DrawableCurve<C,I> {
|
pub fn new(data: I, color: C) -> DrawableCurve<C, I> {
|
||||||
DrawableCurve {
|
DrawableCurve {
|
||||||
scaled_data: data,
|
scaled_data: data,
|
||||||
color,
|
color,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<C,I> Drawable<C> for DrawableCurve<C,I>
|
|
||||||
|
impl<C, I> Drawable<C> for DrawableCurve<C, I>
|
||||||
where C: PixelColor,
|
where C: PixelColor,
|
||||||
I: Iterator<Item=Point>,
|
I: Iterator<Item=Point>,
|
||||||
{
|
{
|
||||||
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), <D as DrawTarget<C>>::Error> {
|
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
||||||
let style = PrimitiveStyle::with_stroke(self.color,2);
|
let style = PrimitiveStyle::with_stroke(self.color, 2);
|
||||||
let mut iter = self.scaled_data.into_iter();
|
let mut iter = self.scaled_data.into_iter();
|
||||||
let mut prev = iter.next().unwrap();
|
let mut prev = iter.next().unwrap();
|
||||||
for point in iter {
|
for point in iter {
|
||||||
Line::new(prev,point)
|
Line::new(prev, point)
|
||||||
.into_styled(style)
|
.into_styled(style)
|
||||||
.draw(display)?;
|
.draw(display)?;
|
||||||
prev = point;
|
prev = point;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
pub mod curve;
|
pub mod curve;
|
||||||
pub mod plot;
|
pub mod polyplot;
|
||||||
|
pub mod single_plot;
|
||||||
|
|
||||||
mod drawable_curve;
|
mod drawable_curve;
|
||||||
mod range_conv;
|
mod range_conv;
|
||||||
|
|
39
src/polyplot.rs
Normal file
39
src/polyplot.rs
Normal file
|
@ -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<C> {
|
||||||
|
PolyPlot { curves, top_left, bottom_right }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C> Drawable<C> for PolyPlot<'a, C>
|
||||||
|
where
|
||||||
|
C: PixelColor
|
||||||
|
{
|
||||||
|
fn draw<D: DrawTarget<C>>(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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ pub trait Scalable<T>
|
||||||
where
|
where
|
||||||
T: Copy + Add<Output=T> + Sub<Output=T> + Mul<Output=T> + Div<Output=T>,
|
T: Copy + Add<Output=T> + Sub<Output=T> + Mul<Output=T> + Div<Output=T>,
|
||||||
{
|
{
|
||||||
fn scale_between_ranges(&self,input_range: &Range<T>, output_range: &Range<T>) -> T;
|
fn scale_between_ranges(&self, input_range: &Range<T>, output_range: &Range<T>) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Scalable<T> for T
|
impl<T> Scalable<T> for T
|
||||||
|
@ -19,21 +19,20 @@ impl<T> Scalable<T> for T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
use crate::range_conv::Scalable;
|
use crate::range_conv::Scalable;
|
||||||
|
|
||||||
#[test_case(0..10,0..10,5 => 5 ; "equal ranges")]
|
#[test_case(0..10, 0..10, 5 => 5; "equal ranges")]
|
||||||
#[test_case(0..10,0..20,5 => 10 ; "double")]
|
#[test_case(0..10, 0..20, 5 => 10; "double")]
|
||||||
#[test_case(0..20,0..10,10 => 5 ; "half")]
|
#[test_case(0..20, 0..10, 10 => 5; "half")]
|
||||||
#[test_case(-20..20,0..10,0 => 5 ; "negative input range")]
|
#[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, - 20..20, 5 => 0; "negative output range")]
|
||||||
#[test_case(0..10,10..0,2 => 8 ; "reversing")]
|
#[test_case(0..10, 10..0, 2 => 8; "reversing")]
|
||||||
#[test_case(-20..20,0..20,-10 => 5 ; "reversing negative range")]
|
#[test_case(- 20..20, 0..20, - 10 => 5; "reversing negative range")]
|
||||||
fn convert(in_range: Range<i32>,out_range: Range<i32>,val: i32) -> i32 {
|
fn convert(in_range: Range<i32>, out_range: Range<i32>, val: i32) -> i32 {
|
||||||
val.scale_between_ranges(&in_range,&out_range)
|
val.scale_between_ranges(&in_range, &out_range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
39
src/single_plot.rs
Normal file
39
src/single_plot.rs
Normal file
|
@ -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<C> for SinglePlot<'a, C>
|
||||||
|
where
|
||||||
|
C: PixelColor
|
||||||
|
{
|
||||||
|
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
|
||||||
|
|
||||||
|
self.curve.into_drawable_curve(
|
||||||
|
&self.top_left,
|
||||||
|
&self.bottom_right,
|
||||||
|
self.color,
|
||||||
|
).draw(display)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue