1use super::display::{DisplayItem, DisplayList};
8use super::gpar::Gpar;
9use super::grob::{Grob, GrobStore};
10use super::viewport::{ViewportStack, ViewportTransform};
11
12pub trait GridRenderer {
19 fn line(&mut self, x0_cm: f64, y0_cm: f64, x1_cm: f64, y1_cm: f64, gp: &Gpar);
21
22 fn polyline(&mut self, x_cm: &[f64], y_cm: &[f64], gp: &Gpar);
24
25 fn rect(&mut self, x_cm: f64, y_cm: f64, w_cm: f64, h_cm: f64, gp: &Gpar);
27
28 fn circle(&mut self, x_cm: f64, y_cm: f64, r_cm: f64, gp: &Gpar);
30
31 fn polygon(&mut self, x_cm: &[f64], y_cm: &[f64], gp: &Gpar);
33
34 fn text(&mut self, x_cm: f64, y_cm: f64, label: &str, rot: f64, gp: &Gpar);
36
37 fn point(&mut self, x_cm: f64, y_cm: f64, pch: u8, size_cm: f64, gp: &Gpar);
39
40 fn clip(&mut self, x_cm: f64, y_cm: f64, w_cm: f64, h_cm: f64);
42
43 fn unclip(&mut self);
45
46 fn device_size_cm(&self) -> (f64, f64);
48}
49
50pub fn replay(list: &DisplayList, store: &GrobStore, renderer: &mut dyn GridRenderer) {
62 let (dev_w, dev_h) = renderer.device_size_cm();
63 let mut vp_stack = ViewportStack::new(dev_w, dev_h);
64 let mut transform = ViewportTransform::root(dev_w, dev_h);
65 let mut transform_stack: Vec<ViewportTransform> = vec![transform.clone()];
66 let mut clip_depth: usize = 0;
67
68 for item in list.items() {
69 match item {
70 DisplayItem::PushViewport(vp) => {
71 transform = ViewportTransform::from_viewport(vp, &transform);
72 transform_stack.push(transform.clone());
73 vp_stack.push((**vp).clone());
74
75 if vp.clip {
76 renderer.clip(
77 transform.x_offset_cm,
78 transform.y_offset_cm,
79 transform.width_cm,
80 transform.height_cm,
81 );
82 clip_depth += 1;
83 }
84 }
85 DisplayItem::PopViewport => {
86 let popped = vp_stack.pop();
87 if transform_stack.len() > 1 {
88 transform_stack.pop();
89 }
90 transform = transform_stack
91 .last()
92 .expect("transform stack always has at least the root")
93 .clone();
94
95 if let Some(vp) = popped {
96 if vp.clip && clip_depth > 0 {
97 renderer.unclip();
98 clip_depth -= 1;
99 }
100 }
101 }
102 DisplayItem::Draw(grob_id) => {
103 if let Some(grob) = store.get(*grob_id) {
104 render_grob(grob, &transform, store, renderer);
105 }
106 }
107 }
108 }
109
110 for _ in 0..clip_depth {
112 renderer.unclip();
113 }
114}
115
116fn render_grob(
118 grob: &Grob,
119 transform: &ViewportTransform,
120 store: &GrobStore,
121 renderer: &mut dyn GridRenderer,
122) {
123 let ctx = transform.unit_context();
124
125 match grob {
126 Grob::Lines { x, y, gp } => {
127 let n = x.len().min(y.len());
128 if n < 2 {
129 return;
130 }
131 let x_cm: Vec<f64> = (0..n)
132 .map(|i| transform.x_offset_cm + ctx.resolve_x(x, i))
133 .collect();
134 let y_cm: Vec<f64> = (0..n)
135 .map(|i| transform.y_offset_cm + ctx.resolve_y(y, i))
136 .collect();
137 renderer.polyline(&x_cm, &y_cm, gp);
138 }
139
140 Grob::Segments { x0, y0, x1, y1, gp } => {
141 let n = x0.len().min(y0.len()).min(x1.len()).min(y1.len());
142 for i in 0..n {
143 renderer.line(
144 transform.x_offset_cm + ctx.resolve_x(x0, i),
145 transform.y_offset_cm + ctx.resolve_y(y0, i),
146 transform.x_offset_cm + ctx.resolve_x(x1, i),
147 transform.y_offset_cm + ctx.resolve_y(y1, i),
148 gp,
149 );
150 }
151 }
152
153 Grob::Points {
154 x,
155 y,
156 pch,
157 size,
158 gp,
159 } => {
160 let n = x.len().min(y.len());
161 for i in 0..n {
162 let size_cm = ctx.resolve_size(size, i.min(size.len().saturating_sub(1)));
163 renderer.point(
164 transform.x_offset_cm + ctx.resolve_x(x, i),
165 transform.y_offset_cm + ctx.resolve_y(y, i),
166 *pch,
167 size_cm,
168 gp,
169 );
170 }
171 }
172
173 Grob::Rect {
174 x,
175 y,
176 width,
177 height,
178 just,
179 gp,
180 } => {
181 let n = x.len().min(y.len());
182 for i in 0..n {
183 let cx = transform.x_offset_cm + ctx.resolve_x(x, i);
184 let cy = transform.y_offset_cm + ctx.resolve_y(y, i);
185 let w = ctx.resolve_x(width, i.min(width.len().saturating_sub(1)));
186 let h = ctx.resolve_y(height, i.min(height.len().saturating_sub(1)));
187 let rx = cx - just.0.as_fraction() * w;
188 let ry = cy - just.1.as_fraction() * h;
189 renderer.rect(rx, ry, w, h, gp);
190 }
191 }
192
193 Grob::Circle { x, y, r, gp } => {
194 let n = x.len().min(y.len());
195 for i in 0..n {
196 let r_cm = ctx.resolve_size(r, i.min(r.len().saturating_sub(1)));
197 renderer.circle(
198 transform.x_offset_cm + ctx.resolve_x(x, i),
199 transform.y_offset_cm + ctx.resolve_y(y, i),
200 r_cm,
201 gp,
202 );
203 }
204 }
205
206 Grob::Polygon { x, y, gp } => {
207 let n = x.len().min(y.len());
208 if n < 3 {
209 return;
210 }
211 let x_cm: Vec<f64> = (0..n)
212 .map(|i| transform.x_offset_cm + ctx.resolve_x(x, i))
213 .collect();
214 let y_cm: Vec<f64> = (0..n)
215 .map(|i| transform.y_offset_cm + ctx.resolve_y(y, i))
216 .collect();
217 renderer.polygon(&x_cm, &y_cm, gp);
218 }
219
220 Grob::Text {
221 label,
222 x,
223 y,
224 just: _just,
225 rot,
226 gp,
227 } => {
228 let n = label.len().min(x.len()).min(y.len());
229 for (i, lbl) in label.iter().enumerate().take(n) {
230 renderer.text(
231 transform.x_offset_cm + ctx.resolve_x(x, i),
232 transform.y_offset_cm + ctx.resolve_y(y, i),
233 lbl,
234 *rot,
235 gp,
236 );
237 }
238 }
239
240 Grob::Collection { children } => {
241 for &child_id in children {
242 if let Some(child) = store.get(child_id) {
243 render_grob(child, transform, store, renderer);
244 }
245 }
246 }
247 }
248}
249
250