use std::borrow::Borrow;
use plotters_backend::{BackendCoord, DrawingBackend};
use crate::chart::{SeriesAnno, SeriesLabelStyle};
use crate::coord::{CoordTranslate, ReverseCoordTranslate, Shift};
use crate::drawing::{DrawingArea, DrawingAreaErrorKind};
use crate::element::{CoordMapper, Drawable, PointCollection};
pub(super) mod cartesian2d;
pub(super) mod cartesian3d;
pub(super) use cartesian3d::Coord3D;
pub struct ChartContext<'a, DB: DrawingBackend, CT: CoordTranslate> {
pub(crate) x_label_area: [Option<DrawingArea<DB, Shift>>; 2],
pub(crate) y_label_area: [Option<DrawingArea<DB, Shift>>; 2],
pub(crate) drawing_area: DrawingArea<DB, CT>,
pub(crate) series_anno: Vec<SeriesAnno<'a, DB>>,
pub(crate) drawing_area_pos: (i32, i32),
}
impl<'a, DB: DrawingBackend, CT: ReverseCoordTranslate> ChartContext<'a, DB, CT> {
pub fn into_coord_trans(self) -> impl Fn(BackendCoord) -> Option<CT::From> {
let coord_spec = self.drawing_area.into_coord_spec();
move |coord| coord_spec.reverse_translate(coord)
}
}
impl<'a, DB: DrawingBackend, CT: CoordTranslate> ChartContext<'a, DB, CT> {
pub fn configure_series_labels<'b>(&'b mut self) -> SeriesLabelStyle<'a, 'b, DB, CT>
where
DB: 'a,
{
SeriesLabelStyle::new(self)
}
pub fn plotting_area(&self) -> &DrawingArea<DB, CT> {
&self.drawing_area
}
pub fn as_coord_spec(&self) -> &CT {
self.drawing_area.as_coord_spec()
}
pub(crate) fn draw_series_impl<B, E, R, S>(
&mut self,
series: S,
) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>>
where
B: CoordMapper,
for<'b> &'b E: PointCollection<'b, CT::From, B>,
E: Drawable<DB, B>,
R: Borrow<E>,
S: IntoIterator<Item = R>,
{
for element in series {
self.drawing_area.draw(element.borrow())?;
}
Ok(())
}
pub(crate) fn alloc_series_anno(&mut self) -> &mut SeriesAnno<'a, DB> {
let idx = self.series_anno.len();
self.series_anno.push(SeriesAnno::new());
&mut self.series_anno[idx]
}
pub fn draw_series<B, E, R, S>(
&mut self,
series: S,
) -> Result<&mut SeriesAnno<'a, DB>, DrawingAreaErrorKind<DB::ErrorType>>
where
B: CoordMapper,
for<'b> &'b E: PointCollection<'b, CT::From, B>,
E: Drawable<DB, B>,
R: Borrow<E>,
S: IntoIterator<Item = R>,
{
self.draw_series_impl(series)?;
Ok(self.alloc_series_anno())
}
}
#[cfg(test)]
mod test {
use crate::prelude::*;
#[test]
fn test_chart_context() {
let drawing_area = create_mocked_drawing_area(200, 200, |_| {});
drawing_area.fill(&WHITE).expect("Fill");
let mut chart = ChartBuilder::on(&drawing_area)
.caption("Test Title", ("serif", 10))
.x_label_area_size(20)
.y_label_area_size(20)
.set_label_area_size(LabelAreaPosition::Top, 20)
.set_label_area_size(LabelAreaPosition::Right, 20)
.build_cartesian_2d(0..10, 0..10)
.expect("Create chart")
.set_secondary_coord(0.0..1.0, 0.0..1.0);
chart
.configure_mesh()
.x_desc("X")
.y_desc("Y")
.draw()
.expect("Draw mesh");
chart
.configure_secondary_axes()
.x_desc("X")
.y_desc("Y")
.draw()
.expect("Draw Secondary axes");
let cs = chart.into_chart_state();
let mut chart = cs.clone().restore(&drawing_area);
chart
.draw_series(std::iter::once(Circle::new((5, 5), 5, &RED)))
.expect("Drawing error");
chart
.draw_secondary_series(std::iter::once(Circle::new((0.3, 0.8), 5, &GREEN)))
.expect("Drawing error")
.label("Test label")
.legend(|(x, y)| Rectangle::new([(x - 10, y - 5), (x, y + 5)], &GREEN));
chart
.configure_series_labels()
.position(SeriesLabelPosition::UpperMiddle)
.draw()
.expect("Drawing error");
}
#[test]
fn test_chart_context_3d() {
let drawing_area = create_mocked_drawing_area(200, 200, |_| {});
drawing_area.fill(&WHITE).expect("Fill");
let mut chart = ChartBuilder::on(&drawing_area)
.caption("Test Title", ("serif", 10))
.x_label_area_size(20)
.y_label_area_size(20)
.set_label_area_size(LabelAreaPosition::Top, 20)
.set_label_area_size(LabelAreaPosition::Right, 20)
.build_cartesian_3d(0..10, 0..10, 0..10)
.expect("Create chart");
chart.with_projection(|mut pb| {
pb.yaw = 0.5;
pb.pitch = 0.5;
pb.scale = 0.5;
pb.into_matrix()
});
chart.configure_axes().draw().expect("Drawing axes");
let cs = chart.into_chart_state();
let mut chart = cs.clone().restore(&drawing_area);
chart
.draw_series(std::iter::once(Circle::new((5, 5, 5), 5, &RED)))
.expect("Drawing error");
}
}