mirror of
https://gitlab.com/feliix42/embedded-plots.git
synced 2024-11-22 09:56:31 +00:00
Merge branch 'feature/builder-pattern' into 'develop'
Feature/builder pattern See merge request mchodzikiewicz/embedded-plots!6
This commit is contained in:
commit
d470b916b5
12 changed files with 356 additions and 391 deletions
|
@ -26,94 +26,97 @@ fn main() -> Result<(), core::convert::Infallible> {
|
||||||
.text_color(RgbColor::YELLOW)
|
.text_color(RgbColor::YELLOW)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Axis::new("X Fixed 0-100(10)", 0..100, Scale::Fixed(10))
|
Axis::new(0..100)
|
||||||
.into_drawable_axis(
|
.set_title("X Fixed 0-100(10)")
|
||||||
Placement::X{x1: 40, x2: 230, y: 10},
|
.set_scale(Scale::Fixed(10))
|
||||||
RgbColor::WHITE,
|
.into_drawable_axis(Placement::X { x1: 40, x2: 230, y: 10 })
|
||||||
text_style_white,
|
.set_color(RgbColor::WHITE)
|
||||||
2
|
.set_text_style(text_style_white)
|
||||||
)
|
.set_thickness(2)
|
||||||
|
.set_tick_size(2)
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
Axis::new("X Fixed 0-200(100)", 0..200, Scale::Fixed(100))
|
Axis::new(0..200)
|
||||||
.into_drawable_axis(
|
.set_title("X Fixed 0-200(100)")
|
||||||
Placement::X{x1: 240, x2: 470, y: 10},
|
.set_scale(Scale::Fixed(100))
|
||||||
RgbColor::YELLOW,
|
.into_drawable_axis(Placement::X { x1: 240, x2: 470, y: 10 })
|
||||||
text_style_yellow_compact,
|
.set_color(RgbColor::YELLOW)
|
||||||
1
|
.set_text_style(text_style_yellow_compact)
|
||||||
)
|
.set_tick_size(2)
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
Axis::new("X Frac 0-100(7)", 0..100, Scale::RangeFraction(7))
|
Axis::new(0..100)
|
||||||
.into_drawable_axis(
|
.set_title("X Frac 0-100(7)")
|
||||||
Placement::X{x1: 50, x2: 220, y: 30},
|
.set_scale(Scale::RangeFraction(7))
|
||||||
RgbColor::BLUE,
|
.into_drawable_axis(Placement::X { x1: 50, x2: 220, y: 30 })
|
||||||
text_style_white,
|
.set_color(RgbColor::BLUE)
|
||||||
3
|
.set_text_style(text_style_white)
|
||||||
)
|
.set_tick_size(3)
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
Axis::new("X Frac 0-200(4)", 0..200, Scale::RangeFraction(4))
|
Axis::new(0..200)
|
||||||
.into_drawable_axis(
|
.set_title("X Frac 0-200(4)")
|
||||||
Placement::X{x1: 250, x2: 460, y: 40},
|
.set_scale(Scale::RangeFraction(4))
|
||||||
RgbColor::RED,
|
.into_drawable_axis(Placement::X { x1: 250, x2: 460, y: 40 })
|
||||||
text_style_yellow_compact,
|
.set_color(RgbColor::RED)
|
||||||
7
|
.set_text_style(text_style_yellow_compact)
|
||||||
)
|
.set_tick_size(7)
|
||||||
.draw(&mut display)?;
|
.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(
|
.into_drawable_axis(
|
||||||
Placement::Y{y1: 70, y2: 230, x: 160},
|
Placement::Y { y1: 70, y2: 230, x: 160 })
|
||||||
RgbColor::WHITE,
|
.set_color(RgbColor::WHITE)
|
||||||
text_style_white,
|
.set_text_style(text_style_white)
|
||||||
2
|
.set_tick_size(2)
|
||||||
)
|
|
||||||
.draw(&mut display)?;
|
.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(
|
.into_drawable_axis(
|
||||||
Placement::Y{y1: 70, y2: 210, x: 260},
|
Placement::Y { y1: 70, y2: 210, x: 260 })
|
||||||
RgbColor::YELLOW,
|
.set_color(RgbColor::YELLOW)
|
||||||
text_style_yellow_compact,
|
.set_text_style(text_style_yellow_compact)
|
||||||
1
|
.set_tick_size(1)
|
||||||
)
|
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
Axis::new("Y Frac 0-100(7)", 0..100, Scale::RangeFraction(7))
|
Axis::new(0..100)
|
||||||
.into_drawable_axis(
|
.set_title("Y Frac 0-100(7)")
|
||||||
Placement::Y{y1: 60, y2: 180, x: 370},
|
.set_scale(Scale::RangeFraction(7))
|
||||||
RgbColor::BLUE,
|
.into_drawable_axis(Placement::Y { y1: 60, y2: 180, x: 370 })
|
||||||
text_style_white,
|
.set_color(RgbColor::BLUE)
|
||||||
3
|
.set_text_style(text_style_white)
|
||||||
)
|
.set_tick_size(3)
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
Axis::new("Y Frac 0-200(4)", 0..200, Scale::RangeFraction(4))
|
Axis::new(0..200)
|
||||||
.into_drawable_axis(
|
.set_title("Y Frac 0-200(4)")
|
||||||
Placement::Y{y1: 90, y2: 220, x: 470},
|
.set_scale(Scale::RangeFraction(4))
|
||||||
RgbColor::RED,
|
.into_drawable_axis(Placement::Y { y1: 90, y2: 220, x: 470 })
|
||||||
text_style_yellow_compact,
|
.set_color(RgbColor::RED)
|
||||||
7
|
.set_text_style(text_style_yellow_compact)
|
||||||
)
|
.set_tick_size(7)
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
Axis::new("X", 123..2137, Scale::Fixed(150))
|
Axis::new(123..2137)
|
||||||
.into_drawable_axis(
|
.set_title("X")
|
||||||
Placement::X{x1: 30, x2: 470, y: 250},
|
.set_scale(Scale::Fixed(150))
|
||||||
RgbColor::YELLOW,
|
.into_drawable_axis(Placement::X { x1: 30, x2: 470, y: 250 })
|
||||||
text_style_white,
|
.set_color(RgbColor::YELLOW)
|
||||||
2
|
.set_text_style(text_style_white)
|
||||||
)
|
.set_tick_size(2)
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
Axis::new("Y", 0..2137, Scale::RangeFraction(15))
|
Axis::new(0..2137)
|
||||||
.into_drawable_axis(
|
.set_title("Y")
|
||||||
Placement::Y{y1: 10, y2: 250, x: 30},
|
.set_scale(Scale::RangeFraction(15))
|
||||||
RgbColor::WHITE,
|
.into_drawable_axis(Placement::Y { y1: 10, y2: 250, x: 30 })
|
||||||
text_style_white,
|
.set_color(RgbColor::WHITE)
|
||||||
2
|
.set_text_style(text_style_white)
|
||||||
)
|
.set_tick_size(2)
|
||||||
.draw(&mut display)?;
|
.draw(&mut display)?;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<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(())
|
|
||||||
}
|
|
|
@ -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<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(())
|
|
||||||
}
|
|
|
@ -27,10 +27,9 @@ fn main() -> Result<(), core::convert::Infallible> {
|
||||||
Scale::RangeFraction(3),
|
Scale::RangeFraction(3),
|
||||||
Scale::RangeFraction(2),
|
Scale::RangeFraction(2),
|
||||||
).into_drawable(
|
).into_drawable(
|
||||||
BinaryColor::On,
|
|
||||||
Point { x: 18, y: 2 },
|
Point { x: 18, y: 2 },
|
||||||
Point { x: 120, y: 30 },
|
Point { x: 120, y: 30 },
|
||||||
);
|
).set_color(BinaryColor::On);
|
||||||
|
|
||||||
plot.draw(&mut display)?;
|
plot.draw(&mut display)?;
|
||||||
let output_settings = OutputSettingsBuilder::new()
|
let output_settings = OutputSettingsBuilder::new()
|
||||||
|
|
|
@ -29,13 +29,10 @@ fn main() -> Result<(), core::convert::Infallible> {
|
||||||
let plot = SinglePlot::new(
|
let plot = SinglePlot::new(
|
||||||
&curve,
|
&curve,
|
||||||
Scale::RangeFraction(3),
|
Scale::RangeFraction(3),
|
||||||
Scale::RangeFraction(2),
|
Scale::RangeFraction(2)).into_drawable(
|
||||||
)
|
|
||||||
.into_drawable(
|
|
||||||
RgbColor::YELLOW,
|
|
||||||
Point { x: 50, y: 10 },
|
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)?;
|
plot.draw(&mut display)?;
|
||||||
let output_settings = OutputSettingsBuilder::new()
|
let output_settings = OutputSettingsBuilder::new()
|
||||||
|
|
165
src/axis.rs
165
src/axis.rs
|
@ -1,8 +1,15 @@
|
||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
use embedded_graphics::prelude::*;
|
use core::fmt::Write;
|
||||||
use embedded_graphics::style::{TextStyle};
|
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 {
|
pub enum Placement {
|
||||||
X {
|
X {
|
||||||
|
@ -22,24 +29,160 @@ pub enum Scale {
|
||||||
RangeFraction(usize),
|
RangeFraction(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Scale {
|
||||||
|
fn default() -> Self {
|
||||||
|
Scale::RangeFraction(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Axis<'a> {
|
pub struct Axis<'a> {
|
||||||
title: &'a str,
|
|
||||||
range: Range<i32>,
|
range: Range<i32>,
|
||||||
scale: Scale,
|
title: Option<&'a str>,
|
||||||
|
scale: Option<Scale>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Axis<'a>
|
impl<'a> Axis<'a>
|
||||||
{
|
{
|
||||||
pub fn new(title: &'a str, range: Range<i32>, scale: Scale) -> Axis<'a> {
|
pub fn new(range: Range<i32>) -> Axis<'a> {
|
||||||
Axis{title, range, scale}
|
Axis { range, title: None, scale: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_drawable_axis<C, F>(self, placement: Placement, plot_color: C, text_style: TextStyle<C, F>, tick_height: 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<C, F>(self, placement: Placement) -> DrawableAxis<'a, C, F>
|
||||||
where
|
where
|
||||||
C: PixelColor,
|
C: PixelColor + Default,
|
||||||
F: Font,
|
F: Font,
|
||||||
TextStyle<C, F>: Clone,
|
TextStyle<C, F>: Clone + Default,
|
||||||
{
|
{
|
||||||
DrawableAxis::new(self.title,placement,self.range,self.scale,plot_color,text_style,tick_height)
|
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<C, F>: Clone + Default,
|
||||||
|
{
|
||||||
|
axis: Axis<'a>,
|
||||||
|
placement: Placement,
|
||||||
|
color: Option<C>,
|
||||||
|
text_style: Option<TextStyle<C, F>>,
|
||||||
|
tick_size: Option<usize>,
|
||||||
|
thickness: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C, F> DrawableAxis<'a, C, F>
|
||||||
|
where
|
||||||
|
C: PixelColor + Default,
|
||||||
|
F: Font,
|
||||||
|
TextStyle<C, F>: 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<C, F>) -> 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<C> for DrawableAxis<'a, C, F>
|
||||||
|
where
|
||||||
|
C: PixelColor + Default,
|
||||||
|
F: Font + Copy,
|
||||||
|
TextStyle<C, F>: Clone + Default,
|
||||||
|
{
|
||||||
|
fn draw<D: DrawTarget<C>>(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::<U8> = 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::<U8> = 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(())
|
||||||
|
}
|
||||||
|
}
|
64
src/curve.rs
64
src/curve.rs
|
@ -1,10 +1,16 @@
|
||||||
use core::ops::{Range};
|
use core::ops::{Range};
|
||||||
|
|
||||||
use crate::range_conv::Scalable;
|
use crate::range_conv::Scalable;
|
||||||
use crate::drawable_curve::DrawableCurve;
|
|
||||||
use embedded_graphics::prelude::*;
|
|
||||||
use itertools::{Itertools, MinMaxResult::MinMax, MinMaxResult};
|
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 struct PlotPoint {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
|
@ -43,7 +49,6 @@ impl<'a> Curve<'a> {
|
||||||
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,
|
||||||
color: C,
|
|
||||||
) -> DrawableCurve<C, impl Iterator<Item=Point> + '_>
|
) -> DrawableCurve<C, impl Iterator<Item=Point> + '_>
|
||||||
where C: PixelColor
|
where C: PixelColor
|
||||||
{
|
{
|
||||||
|
@ -63,10 +68,61 @@ impl<'a> Curve<'a> {
|
||||||
&Range { start: bottom_right.y, end: top_left.y },
|
&Range { start: bottom_right.y, end: top_left.y },
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
DrawableCurve::new(it, color)
|
DrawableCurve {
|
||||||
|
scaled_data: it,
|
||||||
|
color: None,
|
||||||
|
thickness: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DrawableCurve<C, I>
|
||||||
|
{
|
||||||
|
scaled_data: I,
|
||||||
|
color: Option<C>,
|
||||||
|
thickness: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, I> DrawableCurve<C, I>
|
||||||
|
where
|
||||||
|
C: PixelColor,
|
||||||
|
I: Iterator<Item=Point>,
|
||||||
|
{
|
||||||
|
pub fn set_color(mut self, color: C) -> DrawableCurve<C, I> {
|
||||||
|
self.color = Some(color);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn set_thickness(mut self, thickness: usize) -> DrawableCurve<C,I> {
|
||||||
|
self.thickness = Some(thickness);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, I> Drawable<C> for DrawableCurve<C, I>
|
||||||
|
where C: PixelColor + Default,
|
||||||
|
I: Iterator<Item=Point>,
|
||||||
|
{
|
||||||
|
fn draw<D: DrawTarget<C>>(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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -1,105 +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<C, F>: Clone,
|
|
||||||
{
|
|
||||||
title: &'a str,
|
|
||||||
placement: Placement,
|
|
||||||
range: Range<i32>,
|
|
||||||
scale: Scale,
|
|
||||||
color: C,
|
|
||||||
text_style: TextStyle<C, F>,
|
|
||||||
tick_size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, C, F> DrawableAxis<'a, C, F>
|
|
||||||
where
|
|
||||||
C: PixelColor,
|
|
||||||
F: Font,
|
|
||||||
TextStyle<C, F>: Clone,
|
|
||||||
{
|
|
||||||
pub(in crate) fn new(title: &'a str, placement: Placement, range: Range<i32>, scale: Scale, color: C, text_style: TextStyle<C, F>, 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<C> for DrawableAxis<'a, C, F>
|
|
||||||
where
|
|
||||||
C: PixelColor,
|
|
||||||
F: Font + Copy,
|
|
||||||
TextStyle<C, F>: Clone,
|
|
||||||
{
|
|
||||||
fn draw<D: DrawTarget<C>>(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::<U8> = 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::<U8> = 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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +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<C, I>
|
|
||||||
{
|
|
||||||
scaled_data: I,
|
|
||||||
color: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C, I> DrawableCurve<C, I>
|
|
||||||
where
|
|
||||||
C: PixelColor,
|
|
||||||
I: Iterator<Item=Point>,
|
|
||||||
{
|
|
||||||
pub(in crate) fn new(data: I, color: C) -> DrawableCurve<C, I> {
|
|
||||||
DrawableCurve {
|
|
||||||
scaled_data: data,
|
|
||||||
color,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C, I> Drawable<C> for DrawableCurve<C, I>
|
|
||||||
where C: PixelColor,
|
|
||||||
I: Iterator<Item=Point>,
|
|
||||||
{
|
|
||||||
fn draw<D: DrawTarget<C>>(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)
|
|
||||||
.into_styled(style)
|
|
||||||
.draw(display)?;
|
|
||||||
prev = point;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
pub mod curve;
|
pub mod curve;
|
||||||
pub mod axis;
|
pub mod axis;
|
||||||
pub mod polyplot;
|
|
||||||
pub mod single_plot;
|
pub mod single_plot;
|
||||||
|
|
||||||
mod drawable_curve;
|
mod range_conv;
|
||||||
mod range_conv;
|
|
||||||
mod drawable_axis;
|
|
|
@ -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;
|
|
||||||
|
|
||||||
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<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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,43 +18,97 @@ impl<'a> SinglePlot<'a> {
|
||||||
SinglePlot { curve, x_scale, y_scale }
|
SinglePlot { curve, x_scale, y_scale }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_drawable<C: PixelColor>(self, color: C, 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, top_left, bottom_right }
|
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>
|
pub struct DrawableSinglePlot<'a, C>
|
||||||
where
|
where
|
||||||
C: PixelColor
|
C: PixelColor + Default,
|
||||||
{
|
{
|
||||||
plot: SinglePlot<'a>,
|
plot: SinglePlot<'a>,
|
||||||
color: C,
|
color: Option<C>,
|
||||||
|
text_color: Option<C>,
|
||||||
|
axis_color: Option<C>,
|
||||||
|
thickness: Option<usize>,
|
||||||
|
axis_thickness: Option<usize>,
|
||||||
top_left: Point,
|
top_left: Point,
|
||||||
bottom_right: 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<C> for DrawableSinglePlot<'a, C>
|
impl<'a, C> Drawable<C> for DrawableSinglePlot<'a, C>
|
||||||
where
|
where
|
||||||
C: PixelColor
|
C: PixelColor + Default,
|
||||||
{
|
{
|
||||||
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 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)
|
let text_style = TextStyleBuilder::new(Font6x8)
|
||||||
.text_color(self.color)
|
.text_color(text_color)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Axis::new("X", self.plot.curve.x_range.clone(), self.plot.x_scale)
|
Axis::new( self.plot.curve.x_range.clone())
|
||||||
.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)
|
.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)?;
|
.draw(display)?;
|
||||||
|
|
||||||
Axis::new("Y", self.plot.curve.y_range.clone(), self.plot.y_scale)
|
Axis::new(self.plot.curve.y_range.clone())
|
||||||
.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)
|
.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)?;
|
.draw(display)?;
|
||||||
|
|
||||||
self.plot.curve.into_drawable_curve(
|
self.plot.curve.into_drawable_curve(
|
||||||
&self.top_left,
|
&self.top_left,
|
||||||
&self.bottom_right,
|
&self.bottom_right,
|
||||||
self.color,
|
).set_color(color)
|
||||||
).draw(display)?;
|
.set_thickness(thickness)
|
||||||
|
.draw(display)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue