plotters/chart/state.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
use std::sync::Arc;
use super::ChartContext;
use crate::coord::{CoordTranslate, Shift};
use crate::drawing::DrawingArea;
use plotters_backend::DrawingBackend;
/// A chart context state - This is the data that is needed to reconstruct the chart context
/// without actually drawing the chart. This is useful when we want to do realtime rendering and
/// want to incrementally update the chart.
///
/// For each frame, instead of updating the entire backend, we are able to keep the keep the figure
/// component like axis, labels untouched and make updates only in the plotting drawing area.
/// This is very useful for incremental render.
/// ```rust
/// use plotters::prelude::*;
/// let mut buffer = vec![0u8;1024*768*3];
/// let area = BitMapBackend::with_buffer(&mut buffer[..], (1024, 768))
/// .into_drawing_area()
/// .split_evenly((1,2));
/// let chart = ChartBuilder::on(&area[0])
/// .caption("Incremental Example", ("sans-serif", 20))
/// .set_all_label_area_size(30)
/// .build_cartesian_2d(0..10, 0..10)
/// .expect("Unable to build ChartContext");
/// // Draw the first frame at this point
/// area[0].present().expect("Present");
/// let state = chart.into_chart_state();
/// // Let's draw the second frame
/// let chart = state.restore(&area[0]);
/// chart.plotting_area().fill(&WHITE).unwrap(); // Clear the previously drawn graph
/// // At this point, you are able to draw next frame
///```
#[derive(Clone)]
pub struct ChartState<CT: CoordTranslate> {
drawing_area_pos: (i32, i32),
drawing_area_size: (u32, u32),
coord: CT,
}
impl<'a, DB: DrawingBackend, CT: CoordTranslate> From<ChartContext<'a, DB, CT>> for ChartState<CT> {
fn from(chart: ChartContext<'a, DB, CT>) -> ChartState<CT> {
ChartState {
drawing_area_pos: chart.drawing_area_pos,
drawing_area_size: chart.drawing_area.dim_in_pixel(),
coord: chart.drawing_area.into_coord_spec(),
}
}
}
impl<'a, DB: DrawingBackend, CT: CoordTranslate> ChartContext<'a, DB, CT> {
/// Convert a chart context into a chart state, by doing so, the chart context is consumed and
/// a saved chart state is created for later use. This is typically used in incremental rendering. See documentation of `ChartState` for more detailed example.
pub fn into_chart_state(self) -> ChartState<CT> {
self.into()
}
/// Convert the chart context into a sharable chart state.
/// Normally a chart state can not be clone, since the coordinate spec may not be able to be
/// cloned. In this case, we can use an `Arc` get the coordinate wrapped thus the state can be
/// cloned and shared by multiple chart context
pub fn into_shared_chart_state(self) -> ChartState<Arc<CT>> {
ChartState {
drawing_area_pos: self.drawing_area_pos,
drawing_area_size: self.drawing_area.dim_in_pixel(),
coord: Arc::new(self.drawing_area.into_coord_spec()),
}
}
}
impl<'a, DB, CT> From<&ChartContext<'a, DB, CT>> for ChartState<CT>
where
DB: DrawingBackend,
CT: CoordTranslate + Clone,
{
fn from(chart: &ChartContext<'a, DB, CT>) -> ChartState<CT> {
ChartState {
drawing_area_pos: chart.drawing_area_pos,
drawing_area_size: chart.drawing_area.dim_in_pixel(),
coord: chart.drawing_area.as_coord_spec().clone(),
}
}
}
impl<'a, DB: DrawingBackend, CT: CoordTranslate + Clone> ChartContext<'a, DB, CT> {
/// Make the chart context, do not consume the chart context and clone the coordinate spec
pub fn to_chart_state(&self) -> ChartState<CT> {
self.into()
}
}
impl<CT: CoordTranslate> ChartState<CT> {
/// Restore the chart context on the given drawing area
///
/// - `area`: The given drawing area where we want to restore the chart context
/// - **returns** The newly created chart context
pub fn restore<'a, DB: DrawingBackend>(
self,
area: &DrawingArea<DB, Shift>,
) -> ChartContext<'a, DB, CT> {
let area = area
.clone()
.shrink(self.drawing_area_pos, self.drawing_area_size);
ChartContext {
x_label_area: [None, None],
y_label_area: [None, None],
drawing_area: area.apply_coord_spec(self.coord),
series_anno: vec![],
drawing_area_pos: self.drawing_area_pos,
}
}
}