Refactor axis to builder pattern

This commit is contained in:
Michał Chodzikiewicz 2021-03-02 21:18:53 +01:00
parent 5139122a95
commit 76c399ac6c
5 changed files with 247 additions and 223 deletions

View file

@ -26,104 +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)
2, .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)
2, .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)
1, .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)
1, .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)
1,
)
.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)
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)
1, .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)
1, .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)
1, .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)
1, .set_tick_size(2)
)
.draw(&mut display)?; .draw(&mut display)?;

View file

@ -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, thickness: 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,thickness) 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(())
} }
} }

View file

@ -1,106 +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,
thickness: 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, thickness: usize) -> DrawableAxis<'a, C, F> {
DrawableAxis { title, placement, range, scale, color, text_style, tick_size: tick_height, thickness }
}
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, self.thickness as u32))
.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, self.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(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, self.thickness as u32))
.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, self.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(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

@ -6,4 +6,3 @@ pub mod polyplot;
pub mod single_plot; pub mod single_plot;
mod range_conv; mod range_conv;
mod drawable_axis;

View file

@ -73,39 +73,34 @@ impl<'a, C> Drawable<C> for DrawableSinglePlot<'a, C>
C: PixelColor + Default, 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 = match self.color { let color = self.color.unwrap_or_default();
None => C::default(), let text_color = self.text_color.unwrap_or(color);
Some(c) => c, let axis_color = self.axis_color.unwrap_or(color);
}; let thickness = self.thickness.unwrap_or(2);
let text_color = match self.text_color { let axis_thickness = self.axis_thickness.unwrap_or(thickness);
None => color,
Some(c) => c,
};
let axis_color = match self.axis_color {
None => color,
Some(c) => c,
};
let thickness = match self.thickness {
None => 2,
Some(t) => t,
};
let axis_thickness = match self.axis_thickness {
None => thickness,
Some(t) => t,
};
let text_style = TextStyleBuilder::new(Font6x8) let text_style = TextStyleBuilder::new(Font6x8)
.text_color(text_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 }, axis_color, text_style, 2, axis_thickness) .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 }, axis_color, text_style, 2,axis_thickness) .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(