Skip to main content

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