plotters_conrod/
graph.rs

1// plotters-conrod
2//
3// Conrod backend for Plotters
4// Copyright: 2020, Valerian Saliou <valerian@valeriansaliou.name>
5// License: MIT
6
7use conrod_core as conrod;
8
9const BACKEND_GRAPH_RESIZE_CHUNK: usize = 100;
10
11/// The re-usable graph of Conrod widget IDs, to be re-used for each plot draw (building it is expensive, re-using it is cheap; so build it once and re-use it across loop calls)
12pub struct ConrodBackendReusableGraph {
13    pub(crate) line: ConrodBackendReusableGraphAtom,
14    pub(crate) rect: ConrodBackendReusableGraphAtom,
15    pub(crate) path: ConrodBackendReusableGraphAtom,
16    pub(crate) circle: ConrodBackendReusableGraphAtom,
17    pub(crate) text: ConrodBackendReusableGraphAtom,
18    pub(crate) fill: ConrodBackendReusableGraphAtom,
19}
20
21pub(crate) struct ConrodBackendReusableGraphAtom(conrod::widget::id::List, usize);
22
23impl ConrodBackendReusableGraph {
24    /// Build a new Conrod backend re-usable graph of widget identifiers
25    ///
26    /// **This should be put outside of your drawer loop and built once; failing to do so will result in heavy CPU usage due to the graph being rebuilt for every frame!**
27    pub fn build() -> Self {
28        Self {
29            line: ConrodBackendReusableGraphAtom::new(),
30            rect: ConrodBackendReusableGraphAtom::new(),
31            path: ConrodBackendReusableGraphAtom::new(),
32            circle: ConrodBackendReusableGraphAtom::new(),
33            text: ConrodBackendReusableGraphAtom::new(),
34            fill: ConrodBackendReusableGraphAtom::new(),
35        }
36    }
37
38    #[inline(always)]
39    pub(crate) fn prepare(&mut self) {
40        // Notice: destructuring is used there as a safety measure, so that no field is \
41        //   forgotten, which could be dangerous (ie. risk of memory leak).
42        let Self {
43            line,
44            rect,
45            path,
46            circle,
47            text,
48            fill,
49        } = self;
50
51        // Proceed all resets
52        line.reset();
53        rect.reset();
54        path.reset();
55        circle.reset();
56        text.reset();
57        fill.reset();
58    }
59}
60
61impl ConrodBackendReusableGraphAtom {
62    fn new() -> Self {
63        Self(conrod::widget::id::List::new(), 0)
64    }
65
66    #[inline(always)]
67    pub(crate) fn next(&mut self, ui: &mut conrod::UiCell) -> conrod::widget::Id {
68        // Acquire current index (ie. last 'next index')
69        let current_index = self.1;
70
71        // IDs list has not a large-enough capacity for all dynamically-allocated IDs? Enlarge it \
72        //   by a pre-defined chunk size (this prevents enlarging the list one by one, requiring \
73        //   frequent re-allocations)
74        // Notice: this upsizes the graph list to allow current ID to be stored. This is always \
75        //   called on the very first call, and may be periodically called whenever the last \
76        //   chunked upsize was not enough to store all IDs. This is a trade-off between memory \
77        //   and performances.
78        if current_index >= self.0.len() {
79            self.0.resize(
80                self.0.len() + BACKEND_GRAPH_RESIZE_CHUNK,
81                &mut ui.widget_id_generator(),
82            );
83        }
84
85        // Mutate state for next index
86        self.1 += 1;
87
88        self.0[current_index]
89    }
90
91    #[inline(always)]
92    fn reset(&mut self) {
93        // Rollback the incremented IDs counter back to zero
94        self.1 = 0;
95    }
96}