1use std::sync::Arc;
2
3use super::ChartContext;
4use crate::coord::{CoordTranslate, Shift};
5use crate::drawing::DrawingArea;
6use 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)]
35pub struct ChartState<CT: CoordTranslate> {
36 drawing_area_pos: (i32, i32),
37 drawing_area_size: (u32, u32),
38 coord: CT,
39}
40
41impl<'a, DB: DrawingBackend, CT: CoordTranslate> From<ChartContext<'a, DB, CT>> for 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
51impl<'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.
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
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
71impl<'a, DB, CT> From<&ChartContext<'a, DB, CT>> for ChartState<CT>
72where
73 DB: DrawingBackend,
74 CT: CoordTranslate + Clone,
75{
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
85impl<'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
87 pub fn to_chart_state(&self) -> ChartState<CT> {
88 self.into()
89 }
90}
91
92impl<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
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