r/interpreter/grid/grob.rs
1//! Grid graphical objects (grobs) — the drawing primitives.
2//!
3//! Grobs are the leaf nodes of grid graphics: lines, rectangles, circles,
4//! text, points, etc. Each grob carries its own unit-based coordinates
5//! and graphical parameters.
6
7use super::gpar::Gpar;
8use super::units::Unit;
9use super::viewport::Justification;
10
11// region: GrobId
12
13/// Opaque identifier for a grob stored in a `GrobStore`.
14pub type GrobId = usize;
15
16// endregion
17
18// region: Grob
19
20/// A graphical object (grob) — one of grid's drawing primitives.
21///
22/// All coordinates are stored as `Unit` values and resolved at render time
23/// using the current viewport transform and unit context.
24#[derive(Clone, Debug)]
25pub enum Grob {
26 /// Connected line segments.
27 Lines { x: Unit, y: Unit, gp: Gpar },
28 /// Disconnected line segments (pairs of start/end points).
29 Segments {
30 x0: Unit,
31 y0: Unit,
32 x1: Unit,
33 y1: Unit,
34 gp: Gpar,
35 },
36 /// Points (scatter plot symbols).
37 Points {
38 x: Unit,
39 y: Unit,
40 /// Plotting character (symbol type: 0-25).
41 pch: u8,
42 /// Point size.
43 size: Unit,
44 gp: Gpar,
45 },
46 /// Rectangles.
47 Rect {
48 x: Unit,
49 y: Unit,
50 width: Unit,
51 height: Unit,
52 just: (Justification, Justification),
53 gp: Gpar,
54 },
55 /// Circles.
56 Circle {
57 x: Unit,
58 y: Unit,
59 /// Radius.
60 r: Unit,
61 gp: Gpar,
62 },
63 /// Filled polygon.
64 Polygon { x: Unit, y: Unit, gp: Gpar },
65 /// Text labels.
66 Text {
67 label: Vec<String>,
68 x: Unit,
69 y: Unit,
70 just: (Justification, Justification),
71 /// Rotation in degrees.
72 rot: f64,
73 gp: Gpar,
74 },
75 /// A collection of child grobs.
76 Collection { children: Vec<GrobId> },
77}
78
79impl Grob {
80 /// Return the graphical parameters for this grob, if it has any.
81 /// Collections don't have their own gpar.
82 pub fn gpar(&self) -> Option<&Gpar> {
83 match self {
84 Grob::Lines { gp, .. }
85 | Grob::Segments { gp, .. }
86 | Grob::Points { gp, .. }
87 | Grob::Rect { gp, .. }
88 | Grob::Circle { gp, .. }
89 | Grob::Polygon { gp, .. }
90 | Grob::Text { gp, .. } => Some(gp),
91 Grob::Collection { .. } => None,
92 }
93 }
94}
95
96// endregion
97
98// region: GrobStore
99
100/// A store for grobs, indexed by `GrobId`.
101///
102/// Grobs are added to the store and referenced by their ID in the display
103/// list and in collection grobs.
104pub struct GrobStore {
105 grobs: Vec<Grob>,
106}
107
108impl GrobStore {
109 /// Create a new empty grob store.
110 pub fn new() -> Self {
111 GrobStore { grobs: Vec::new() }
112 }
113
114 /// Add a grob and return its ID.
115 pub fn add(&mut self, grob: Grob) -> GrobId {
116 let id = self.grobs.len();
117 self.grobs.push(grob);
118 id
119 }
120
121 /// Get a grob by ID.
122 pub fn get(&self, id: GrobId) -> Option<&Grob> {
123 self.grobs.get(id)
124 }
125
126 /// Return the number of grobs in the store.
127 pub fn len(&self) -> usize {
128 self.grobs.len()
129 }
130
131 /// Whether the store is empty.
132 pub fn is_empty(&self) -> bool {
133 self.grobs.is_empty()
134 }
135}
136
137impl Default for GrobStore {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143// endregion