aboutsummaryrefslogtreecommitdiff
path: root/src/chart/state.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/chart/state.rs')
-rw-r--r--src/chart/state.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/chart/state.rs b/src/chart/state.rs
new file mode 100644
index 0000000..9383d44
--- /dev/null
+++ b/src/chart/state.rs
@@ -0,0 +1,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_ranged(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 incrmental 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, 'b, 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,
+ }
+ }
+}