embedded-plots/src/axis.rs

119 lines
4.4 KiB
Rust
Raw Normal View History

2020-12-21 00:28:22 +00:00
use embedded_graphics::primitives::Line;
use embedded_graphics::drawable::Drawable;
use embedded_graphics::DrawTarget;
use core::ops::Range;
use embedded_graphics::prelude::*;
2020-12-30 23:04:24 +00:00
use embedded_graphics::style::{PrimitiveStyle, TextStyle};
2020-12-30 15:33:08 +00:00
use crate::range_conv::Scalable;
2020-12-30 23:04:24 +00:00
use embedded_graphics::fonts::Text;
2020-12-30 15:33:08 +00:00
use heapless::{consts::*, String};
use core::fmt::Write;
2020-12-21 00:28:22 +00:00
pub enum Placement {
2020-12-21 00:28:22 +00:00
X {
x1: i32,
x2: i32,
y: i32,
},
Y {
y1: i32,
y2: i32,
x: i32,
},
}
2020-12-30 15:33:08 +00:00
pub enum Scale {
Fixed(usize),
RangeFraction(usize),
2020-12-21 00:28:22 +00:00
}
2020-12-30 23:04:24 +00:00
pub struct Axis<'a, C, F>
where
C: PixelColor,
F: Font,
TextStyle<C,F>: Clone,
{
2020-12-30 15:33:08 +00:00
title: &'a str,
placement: Placement,
2020-12-21 00:28:22 +00:00
range: Range<i32>,
scale: Scale,
2020-12-30 15:33:08 +00:00
color: C,
2020-12-31 00:13:28 +00:00
text_style: TextStyle<C,F>,
tick_size: usize,
2020-12-21 00:28:22 +00:00
}
2020-12-30 23:04:24 +00:00
impl<'a, C, F> Axis<'a, C, F>
2020-12-21 00:28:22 +00:00
where
C: PixelColor,
2020-12-30 23:04:24 +00:00
F: Font,
TextStyle<C,F>: Clone,
2020-12-21 00:28:22 +00:00
{
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> {
Axis { title, placement: orientation, range, scale, color, text_style, tick_size: tick_height }
2020-12-21 00:28:22 +00:00
}
}
2020-12-30 23:04:24 +00:00
impl<'a, C, F> Drawable<C> for Axis<'a, C, F>
2020-12-21 00:28:22 +00:00
where
C: PixelColor,
2020-12-30 23:04:24 +00:00
F: Font + Copy,
TextStyle<C,F>: Clone,
2020-12-21 00:28:22 +00:00
{
fn draw<D: DrawTarget<C>>(self, display: &mut D) -> Result<(), D::Error> {
let scale_marks = match self.scale {
2020-12-30 15:33:08 +00:00
Scale::Fixed(interval) => {
self.range.clone().into_iter().step_by(interval)
2020-12-21 00:28:22 +00:00
}
2020-12-30 15:33:08 +00:00
Scale::RangeFraction(fraction) => {
2020-12-21 00:28:22 +00:00
let len = self.range.len();
2020-12-30 15:33:08 +00:00
self.range.clone().into_iter().step_by(len / fraction)
2020-12-21 00:28:22 +00:00
}
};
match self.placement {
Placement::X { x1, x2, y } => {
2020-12-30 15:33:08 +00:00
Line { start: Point { x: x1, y }, end: Point { x: x2, y } }
.into_styled(PrimitiveStyle::with_stroke(self.color, 1))
2020-12-21 00:28:22 +00:00
.draw(display)?;
2020-12-30 15:33:08 +00:00
let title = Text::new(self.title, Point { x: x1, y: y + 10 })
2020-12-30 23:04:24 +00:00
.into_styled(self.text_style);
2020-12-30 15:33:08 +00:00
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));
2020-12-31 00:13:28 +00:00
Line { start: Point { x, y: y - self.tick_size as i32 }, end: Point { x, y: y + self.tick_size as i32 } }
2020-12-30 15:33:08 +00:00
.into_styled(PrimitiveStyle::with_stroke(self.color, 1))
.draw(display)?;
let mut buf: String::<U8> = String::new();
write!(buf, "{}", mark).unwrap();
2020-12-31 00:13:28 +00:00
Text::new(&buf, Point { x: x + 2, y: y + 2 }).into_styled(self.text_style).draw(display)?;
2020-12-21 00:28:22 +00:00
}
}
Placement::Y { y1, y2, x } => {
2020-12-30 15:33:08 +00:00
Line { start: Point { x, y: y1 }, end: Point { x, y: y2 } }
.into_styled(PrimitiveStyle::with_stroke(self.color, 1))
2020-12-21 00:28:22 +00:00
.draw(display)?;
2020-12-30 15:33:08 +00:00
2020-12-31 00:13:28 +00:00
let mut max_tick_text_width = 0;
for mark in scale_marks {
let y = mark.scale_between_ranges(&self.range, &(y2..y1));
2020-12-31 00:13:28 +00:00
Line { start: Point { x: x - self.tick_size as i32, y }, end: Point { x: x + self.tick_size as i32, y} }
2020-12-30 15:33:08 +00:00
.into_styled(PrimitiveStyle::with_stroke(self.color, 1))
.draw(display)?;
let mut buf: String::<U8> = String::new();
write!(buf, "{}", mark).unwrap();
2020-12-31 00:13:28 +00:00
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)?;
2020-12-21 00:28:22 +00:00
}
2020-12-31 00:13:28 +00:00
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)?;
2020-12-21 00:28:22 +00:00
}
}
Ok(())
}
}