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