use crate::element::{
Circle, DashedPathElement, DottedPathElement, DynElement, IntoDynElement, PathElement,
};
use crate::style::{ShapeStyle, SizeDesc};
use plotters_backend::{BackendCoord, DrawingBackend};
use std::marker::PhantomData;
pub struct LineSeries<DB: DrawingBackend, Coord> {
style: ShapeStyle,
data: Vec<Coord>,
point_idx: usize,
point_size: u32,
phantom: PhantomData<DB>,
}
impl<DB: DrawingBackend, Coord: Clone + 'static> Iterator for LineSeries<DB, Coord> {
type Item = DynElement<'static, DB, Coord>;
fn next(&mut self) -> Option<Self::Item> {
if !self.data.is_empty() {
if self.point_size > 0 && self.point_idx < self.data.len() {
let idx = self.point_idx;
self.point_idx += 1;
return Some(
Circle::new(self.data[idx].clone(), self.point_size, self.style).into_dyn(),
);
}
let mut data = vec![];
std::mem::swap(&mut self.data, &mut data);
Some(PathElement::new(data, self.style).into_dyn())
} else {
None
}
}
}
impl<DB: DrawingBackend, Coord> LineSeries<DB, Coord> {
pub fn new<I: IntoIterator<Item = Coord>, S: Into<ShapeStyle>>(iter: I, style: S) -> Self {
Self {
style: style.into(),
data: iter.into_iter().collect(),
point_size: 0,
point_idx: 0,
phantom: PhantomData,
}
}
pub fn point_size(mut self, size: u32) -> Self {
self.point_size = size;
self
}
}
pub struct DashedLineSeries<I: Iterator + Clone, Size: SizeDesc> {
points: I,
size: Size,
spacing: Size,
style: ShapeStyle,
}
impl<I: Iterator + Clone, Size: SizeDesc> DashedLineSeries<I, Size> {
pub fn new<I0>(points: I0, size: Size, spacing: Size, style: ShapeStyle) -> Self
where
I0: IntoIterator<IntoIter = I>,
{
Self {
points: points.into_iter(),
size,
spacing,
style,
}
}
}
impl<I: Iterator + Clone, Size: SizeDesc> IntoIterator for DashedLineSeries<I, Size> {
type Item = DashedPathElement<I, Size>;
type IntoIter = std::iter::Once<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
std::iter::once(DashedPathElement::new(
self.points,
self.size,
self.spacing,
self.style,
))
}
}
pub struct DottedLineSeries<I: Iterator + Clone, Size: SizeDesc, Marker> {
points: I,
shift: Size,
spacing: Size,
func: Box<dyn Fn(BackendCoord) -> Marker>,
}
impl<I: Iterator + Clone, Size: SizeDesc, Marker> DottedLineSeries<I, Size, Marker> {
pub fn new<I0, F>(points: I0, shift: Size, spacing: Size, func: F) -> Self
where
I0: IntoIterator<IntoIter = I>,
F: Fn(BackendCoord) -> Marker + 'static,
{
Self {
points: points.into_iter(),
shift,
spacing,
func: Box::new(func),
}
}
}
impl<I: Iterator + Clone, Size: SizeDesc, Marker: 'static> IntoIterator
for DottedLineSeries<I, Size, Marker>
{
type Item = DottedPathElement<I, Size, Marker>;
type IntoIter = std::iter::Once<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
std::iter::once(DottedPathElement::new(
self.points,
self.shift,
self.spacing,
self.func,
))
}
}
#[cfg(test)]
mod test {
use crate::prelude::*;
#[test]
fn test_line_series() {
let drawing_area = create_mocked_drawing_area(200, 200, |m| {
m.check_draw_path(|c, s, _path| {
assert_eq!(c, RED.to_rgba());
assert_eq!(s, 3);
});
m.drop_check(|b| {
assert_eq!(b.num_draw_path_call, 8);
assert_eq!(b.draw_count, 27);
});
});
let mut chart = ChartBuilder::on(&drawing_area)
.build_cartesian_2d(0..100, 0..100)
.expect("Build chart error");
chart
.draw_series(LineSeries::new(
(0..100).map(|x| (x, x)),
Into::<ShapeStyle>::into(RED).stroke_width(3),
))
.expect("Drawing Error");
chart
.draw_series(DashedLineSeries::new(
(0..=50).map(|x| (0, x)),
10,
5,
Into::<ShapeStyle>::into(RED).stroke_width(3),
))
.expect("Drawing Error");
let mk_f = |c| Circle::new(c, 3, Into::<ShapeStyle>::into(RED).filled());
chart
.draw_series(DottedLineSeries::new((0..=50).map(|x| (x, 0)), 5, 5, mk_f))
.expect("Drawing Error");
}
}