Refactor - split axis

This commit is contained in:
Michał Chodzikiewicz 2021-01-04 22:20:07 +01:00
parent dc2edc77f3
commit f0909be13d
9 changed files with 227 additions and 118 deletions

View file

@ -26,40 +26,101 @@ fn main() -> Result<(), core::convert::Infallible> {
.text_color(RgbColor::YELLOW) .text_color(RgbColor::YELLOW)
.build(); .build();
Axis::new("X Fixed 0-100(10)", Placement::X{x1: 40, x2: 230, y: 10}, 0..100, Scale::Fixed(10), RgbColor::WHITE, text_style_white, 2) Axis::new("X Fixed 0-100(10)", 0..100, Scale::Fixed(10))
.into_drawable_axis(
Placement::X{x1: 40, x2: 230, y: 10},
RgbColor::WHITE,
text_style_white,
2
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("X Fixed 0-200(100)", Placement::X{x1: 240, x2: 470, y: 10}, 0..200, Scale::Fixed(100), RgbColor::YELLOW, text_style_yellow_compact, 1) Axis::new("X Fixed 0-200(100)", 0..200, Scale::Fixed(100))
.into_drawable_axis(
Placement::X{x1: 240, x2: 470, y: 10},
RgbColor::YELLOW,
text_style_yellow_compact,
1
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("X Frac 0-100(7)", Placement::X{x1: 50, x2: 220, y: 30}, 0..100, Scale::RangeFraction(7), RgbColor::BLUE, text_style_white, 3) Axis::new("X Frac 0-100(7)", 0..100, Scale::RangeFraction(7))
.into_drawable_axis(
Placement::X{x1: 50, x2: 220, y: 30},
RgbColor::BLUE,
text_style_white,
3
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("X Frac 0-200(4)", Placement::X{x1: 250, x2: 460, y: 40}, 0..200, Scale::RangeFraction(4), RgbColor::RED, text_style_yellow_compact, 7) Axis::new("X Frac 0-200(4)", 0..200, Scale::RangeFraction(4))
.into_drawable_axis(
Placement::X{x1: 250, x2: 460, y: 40},
RgbColor::RED,
text_style_yellow_compact,
7
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("Y Fixed 0-100(10)", Placement::Y{y1: 70, y2: 230, x: 160}, 0..100, Scale::Fixed(10), RgbColor::WHITE, text_style_white, 2) Axis::new("Y Fixed 0-100(10)", 0..100, Scale::Fixed(10))
.into_drawable_axis(
Placement::Y{y1: 70, y2: 230, x: 160},
RgbColor::WHITE,
text_style_white,
2
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("Y Fixed 0-200(100)", Placement::Y{y1: 70, y2: 210, x: 260}, 0..200, Scale::Fixed(100), RgbColor::YELLOW, text_style_yellow_compact, 1) Axis::new("Y Fixed 0-200(100)", 0..200, Scale::Fixed(100))
.into_drawable_axis(
Placement::Y{y1: 70, y2: 210, x: 260},
RgbColor::YELLOW,
text_style_yellow_compact,
1
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("Y Frac 0-100(7)", Placement::Y{y1: 60, y2: 180, x: 370}, 0..100, Scale::RangeFraction(7), RgbColor::BLUE, text_style_white, 3) Axis::new("Y Frac 0-100(7)", 0..100, Scale::RangeFraction(7))
.into_drawable_axis(
Placement::Y{y1: 60, y2: 180, x: 370},
RgbColor::BLUE,
text_style_white,
3
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("Y Frac 0-200(4)", Placement::Y{y1: 90, y2: 220, x: 470}, 0..200, Scale::RangeFraction(4), RgbColor::RED, text_style_yellow_compact, 7) Axis::new("Y Frac 0-200(4)", 0..200, Scale::RangeFraction(4))
.into_drawable_axis(
Placement::Y{y1: 90, y2: 220, x: 470},
RgbColor::RED,
text_style_yellow_compact,
7
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("X", Placement::X{x1: 30, x2: 470, y: 250}, 123..2137, Scale::Fixed(150), RgbColor::YELLOW, text_style_white, 2) Axis::new("X", 123..2137, Scale::Fixed(150))
.into_drawable_axis(
Placement::X{x1: 30, x2: 470, y: 250},
RgbColor::YELLOW,
text_style_white,
2
)
.draw(&mut display)?; .draw(&mut display)?;
Axis::new("Y", Placement::Y{y1: 10, y2: 250, x: 30}, 0..2137, Scale::RangeFraction(15), RgbColor::WHITE, text_style_white, 2)
Axis::new("Y", 0..2137, Scale::RangeFraction(15))
.into_drawable_axis(
Placement::Y{y1: 10, y2: 250, x: 30},
RgbColor::WHITE,
text_style_white,
2
)
.draw(&mut display)?; .draw(&mut display)?;
let output_settings = OutputSettingsBuilder::new() let output_settings = OutputSettingsBuilder::new()
.pixel_spacing(1) .pixel_spacing(1)
.build(); .build();
Window::new("Basic plot", &output_settings).show_static(&display); Window::new("Free axis", &output_settings).show_static(&display);
Ok(()) Ok(())
} }

View file

@ -1,5 +1,6 @@
use embedded_graphics::{ use embedded_graphics::{
prelude::*, prelude::*,
pixelcolor::BinaryColor,
}; };
use embedded_graphics_simulator::{SimulatorDisplay, Window, OutputSettingsBuilder, BinaryColorTheme}; use embedded_graphics_simulator::{SimulatorDisplay, Window, OutputSettingsBuilder, BinaryColorTheme};
@ -7,21 +8,28 @@ use embedded_graphics_simulator::{SimulatorDisplay, Window, OutputSettingsBuilde
use embedded_plots::{ use embedded_plots::{
single_plot::{SinglePlot}, single_plot::{SinglePlot},
curve::{PlotPoint, Curve}, curve::{PlotPoint, Curve},
axis::Scale,
}; };
use embedded_graphics::pixelcolor::BinaryColor;
fn main() -> Result<(), core::convert::Infallible> { fn main() -> Result<(), core::convert::Infallible> {
let mut display: SimulatorDisplay<BinaryColor> = SimulatorDisplay::new(Size::new(128, 48)); let mut display: SimulatorDisplay<BinaryColor> = SimulatorDisplay::new(Size::new(128, 48));
let data = vec![ let data = vec![
PlotPoint { x: 0, y: 0 }, PlotPoint { x: 0, y: 0 },
PlotPoint { x: 1, y: 1 }, PlotPoint { x: 1, y: 2 },
PlotPoint { x: 2, y: 1 }, PlotPoint { x: 2, y: 2 },
PlotPoint { x: 3, y: 0 }, PlotPoint { x: 3, y: 0 },
]; ];
let curve = Curve::from_data(data.as_slice()); let curve = Curve::from_data(data.as_slice());
let plot = SinglePlot::new(&curve, BinaryColor::On, Point { x: 9, y: 3 }, Point { x: 120, y: 45 }); let plot = SinglePlot::new(
&curve,
BinaryColor::On,
Point { x: 9, y: 3 },
Point { x: 120, y: 45 },
Scale::RangeFraction(3),
Scale::RangeFraction(2)
);
plot.draw(&mut display)?; plot.draw(&mut display)?;
let output_settings = OutputSettingsBuilder::new() let output_settings = OutputSettingsBuilder::new()

View file

@ -13,6 +13,7 @@ use embedded_graphics_simulator::{
use embedded_plots::{ use embedded_plots::{
single_plot::{SinglePlot}, single_plot::{SinglePlot},
curve::{PlotPoint, Curve}, curve::{PlotPoint, Curve},
axis::Scale,
}; };
fn main() -> Result<(), core::convert::Infallible> { fn main() -> Result<(), core::convert::Infallible> {
@ -20,13 +21,20 @@ fn main() -> Result<(), core::convert::Infallible> {
let data = vec![ let data = vec![
PlotPoint { x: 0, y: 0 }, PlotPoint { x: 0, y: 0 },
PlotPoint { x: 1, y: 1 }, PlotPoint { x: 1, y: 2 },
PlotPoint { x: 2, y: 1 }, PlotPoint { x: 2, y: 2 },
PlotPoint { x: 3, y: 0 }, PlotPoint { x: 3, y: 0 },
]; ];
let curve = Curve::from_data(data.as_slice()); let curve = Curve::from_data(data.as_slice());
let plot = SinglePlot::new(&curve, RgbColor::YELLOW, Point { x: 50, y: 10 }, Point { x: 430, y: 250 }); let plot = SinglePlot::new(
&curve,
RgbColor::YELLOW,
Point { x: 50, y: 10 },
Point { x: 430, y: 250 },
Scale::RangeFraction(3),
Scale::RangeFraction(2)
);
plot.draw(&mut display)?; plot.draw(&mut display)?;
let output_settings = OutputSettingsBuilder::new() let output_settings = OutputSettingsBuilder::new()

View file

@ -1,13 +1,8 @@
use embedded_graphics::primitives::Line;
use embedded_graphics::drawable::Drawable;
use embedded_graphics::DrawTarget;
use core::ops::Range; use core::ops::Range;
use embedded_graphics::prelude::*; use embedded_graphics::prelude::*;
use embedded_graphics::style::{PrimitiveStyle, TextStyle}; use embedded_graphics::style::{TextStyle};
use crate::range_conv::Scalable;
use embedded_graphics::fonts::Text; use crate::drawable_axis::DrawableAxis;
use heapless::{consts::*, String};
use core::fmt::Write;
pub enum Placement { pub enum Placement {
X { X {
@ -27,97 +22,24 @@ pub enum Scale {
RangeFraction(usize), RangeFraction(usize),
} }
pub struct Axis<'a, C, F> pub struct Axis<'a> {
where
C: PixelColor,
F: Font,
TextStyle<C,F>: Clone,
{
title: &'a str, title: &'a str,
placement: Placement,
range: Range<i32>, range: Range<i32>,
scale: Scale, scale: Scale,
color: C,
text_style: TextStyle<C,F>,
tick_size: usize,
} }
impl<'a, C, F> Axis<'a, C, F> impl<'a> Axis<'a>
where
C: PixelColor,
F: Font,
TextStyle<C,F>: Clone,
{ {
pub fn new(title: &'a str, orientation: Placement, range: Range<i32>, scale: Scale, color: C, text_style: TextStyle<C,F>, tick_height: usize) -> Axis<'a, C, F> { pub fn new(title: &'a str, range: Range<i32>, scale: Scale) -> Axis<'a> {
Axis { title, placement: orientation, range, scale, color, text_style, tick_size: tick_height } Axis{title, range, scale}
} }
pub fn size(&self) -> Point { 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>
Point{x: 50, y: 50} where
C: PixelColor,
F: Font,
TextStyle<C, F>: Clone,
{
DrawableAxis::new(self.title,placement,self.range,self.scale,plot_color,text_style,tick_height)
} }
} }
impl<'a, C, F> Drawable<C> for Axis<'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(())
}
}

View file

@ -13,8 +13,8 @@ pub struct PlotPoint {
pub struct Curve<'a> { pub struct Curve<'a> {
points: &'a [PlotPoint], points: &'a [PlotPoint],
x_range: Range<i32>, pub x_range: Range<i32>,
y_range: Range<i32>, pub y_range: Range<i32>,
} }
impl<'a> Curve<'a> { impl<'a> Curve<'a> {

105
src/drawable_axis.rs Normal file
View file

@ -0,0 +1,105 @@
use embedded_graphics::prelude::*;
use crate::axis::{Placement, Scale};
use core::ops::Range;
use embedded_graphics::style::{TextStyle, PrimitiveStyle};
use embedded_graphics::fonts::Text;
use crate::range_conv::Scalable;
use embedded_graphics::primitives::Line;
use heapless::{consts::*, String};
use core::fmt::Write;
pub struct DrawableAxis<'a, C, F>
where
C: PixelColor,
F: Font,
TextStyle<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(())
}
}

View file

@ -16,7 +16,7 @@ impl<C, I> DrawableCurve<C, I>
C: PixelColor, C: PixelColor,
I: Iterator<Item=Point>, I: Iterator<Item=Point>,
{ {
pub fn new(data: I, color: C) -> DrawableCurve<C, I> { pub(in crate) fn new(data: I, color: C) -> DrawableCurve<C, I> {
DrawableCurve { DrawableCurve {
scaled_data: data, scaled_data: data,
color, color,

View file

@ -1,9 +1,10 @@
#![no_std] #![no_std]
pub mod curve; pub mod curve;
pub mod axis;
pub mod polyplot; pub mod polyplot;
pub mod single_plot; pub mod single_plot;
mod drawable_curve; mod drawable_curve;
mod range_conv; mod range_conv;
pub mod axis; mod drawable_axis;

View file

@ -12,6 +12,8 @@ pub struct SinglePlot<'a, C>
C: PixelColor C: PixelColor
{ {
curve: &'a Curve<'a>, curve: &'a Curve<'a>,
x_scale: Scale,
y_scale: Scale,
color: C, color: C,
top_left: Point, top_left: Point,
bottom_right: Point, bottom_right: Point,
@ -21,8 +23,8 @@ impl<'a, C> SinglePlot<'a, C>
where where
C: PixelColor C: PixelColor
{ {
pub fn new(curve: &'a Curve<'a>, color: C, top_left: Point, bottom_right: Point) -> SinglePlot<'a, C> { pub fn new(curve: &'a Curve<'a>, color: C, top_left: Point, bottom_right: Point, x_scale: Scale, y_scale: Scale) -> SinglePlot<'a, C> {
SinglePlot { curve, color, top_left, bottom_right} SinglePlot { curve, color, top_left, bottom_right, x_scale, y_scale}
} }
} }
@ -36,10 +38,12 @@ impl<'a, C> Drawable<C> for SinglePlot<'a, C>
.text_color(self.color) .text_color(self.color)
.build(); .build();
Axis::new("X", Placement::X{x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y}, 0..100, Scale::Fixed(10), self.color, text_style, 2) Axis::new("X",self.curve.x_range.clone(),self.x_scale)
.into_drawable_axis(Placement::X{x1: self.top_left.x, x2: self.bottom_right.x, y: self.bottom_right.y},self.color,text_style,2)
.draw(display)?; .draw(display)?;
Axis::new("Y", Placement::Y{y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x}, 0..200, Scale::Fixed(10), self.color, text_style, 1) Axis::new("Y", self.curve.y_range.clone(), self.y_scale)
.into_drawable_axis(Placement::Y{y1: self.top_left.y, y2: self.bottom_right.y, x: self.top_left.x},self.color,text_style,2)
.draw(display)?; .draw(display)?;
self.curve.into_drawable_curve( self.curve.into_drawable_curve(