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}