1use super::CallArgs;
10use crate::interpreter::grid;
11use crate::interpreter::value::*;
12use crate::interpreter::BuiltinContext;
13use minir_macros::interpreter_builtin;
14
15fn make_grid_object(entries: Vec<(String, RValue)>, classes: &[&str]) -> RValue {
19 let values: Vec<(Option<String>, RValue)> =
20 entries.into_iter().map(|(k, v)| (Some(k), v)).collect();
21 let mut list = RList::new(values);
22 let class_vec: Vec<Option<String>> = classes.iter().map(|c| Some(c.to_string())).collect();
23 list.set_attr(
24 "class".to_string(),
25 RValue::vec(Vector::Character(class_vec.into())),
26 );
27 RValue::List(list)
28}
29
30fn opt_value(args: &CallArgs, name: &str, pos: usize) -> RValue {
32 args.value(name, pos).cloned().unwrap_or(RValue::Null)
33}
34
35fn normalize_just(ca: &CallArgs, name: &str, pos: usize) -> RValue {
38 let val = ca.value(name, pos).cloned().unwrap_or(RValue::Null);
39 if let RValue::Vector(ref rv) = val {
40 if let Some(s) = rv.inner.as_character_scalar() {
41 let (h, v) = match s.as_str() {
42 "left" => (0.0, 0.5),
43 "right" => (1.0, 0.5),
44 "top" => (0.5, 1.0),
45 "bottom" => (0.5, 0.0),
46 "centre" | "center" => (0.5, 0.5),
47 "bottom.left" | "bottomleft" => (0.0, 0.0),
48 "bottom.right" | "bottomright" => (1.0, 0.0),
49 "top.left" | "topleft" => (0.0, 1.0),
50 "top.right" | "topright" => (1.0, 1.0),
51 _ => return val, };
53 return RValue::vec(Vector::Double(vec![Some(h), Some(v)].into()));
54 }
55 }
56 val
57}
58
59fn auto_grob_name(prefix: &str, ctx: &BuiltinContext) -> String {
61 let n = ctx.interpreter().grid_display_list.borrow().len();
63 format!("{prefix}.{n}")
64}
65
66fn record_on_display_list(grob: &RValue, ctx: &BuiltinContext) {
68 ctx.interpreter()
69 .grid_display_list
70 .borrow_mut()
71 .push(grob.clone());
72}
73
74fn ensure_unit(value: &RValue, default_units: &str) -> RValue {
77 if let RValue::List(list) = value {
79 if let Some(classes) = list.class() {
80 if classes.iter().any(|c| c == "unit") {
81 return value.clone();
82 }
83 }
84 }
85 make_grid_object(
87 vec![
88 ("value".to_string(), value.clone()),
89 (
90 "units".to_string(),
91 RValue::vec(Vector::Character(
92 vec![Some(default_units.to_string())].into(),
93 )),
94 ),
95 ],
96 &["unit"],
97 )
98}
99
100fn default_npc(val: f64) -> RValue {
102 make_grid_object(
103 vec![
104 (
105 "value".to_string(),
106 RValue::vec(Vector::Double(vec![Some(val)].into())),
107 ),
108 (
109 "units".to_string(),
110 RValue::vec(Vector::Character(vec![Some("npc".to_string())].into())),
111 ),
112 ],
113 &["unit"],
114 )
115}
116
117fn default_npc_zero() -> RValue {
119 default_npc(0.0)
120}
121
122fn default_npc_one() -> RValue {
124 default_npc(1.0)
125}
126
127fn default_npc_half() -> RValue {
129 default_npc(0.5)
130}
131
132fn find_viewport_by_name(name: &str, ctx: &BuiltinContext) -> Option<usize> {
135 let stack = ctx.interpreter().grid_viewport_stack.borrow();
136 for (i, vp) in stack.iter().enumerate() {
137 if let RValue::List(list) = vp {
138 for (key, val) in &list.values {
139 if key.as_deref() == Some("name") {
140 if let RValue::Vector(rv) = val {
141 if rv.inner.as_character_scalar().as_deref() == Some(name) {
142 return Some(i);
143 }
144 }
145 }
146 }
147 }
148 }
149 None
150}
151
152fn extract_unit_from_rvalue(val: &RValue) -> grid::units::Unit {
162 if let RValue::List(list) = val {
163 let mut values_opt = None;
165 let mut units_opt = None;
166 for (key, v) in &list.values {
167 match key.as_deref() {
168 Some("value") => values_opt = Some(v),
169 Some("units") => units_opt = Some(v),
170 _ => {}
171 }
172 }
173
174 if let (Some(values_val), Some(units_val)) = (values_opt, units_opt) {
175 let nums: Vec<f64> = if let Some(rv) = values_val.as_vector() {
176 rv.to_doubles()
177 .into_iter()
178 .map(|v| v.unwrap_or(0.0))
179 .collect()
180 } else {
181 vec![0.0]
182 };
183
184 let unit_strs: Vec<String> = if let Some(rv) = units_val.as_vector() {
185 rv.to_characters()
186 .into_iter()
187 .map(|v: Option<String>| v.unwrap_or_else(|| "npc".to_string()))
188 .collect()
189 } else {
190 vec!["npc".to_string()]
191 };
192
193 let mut unit_types = Vec::with_capacity(unit_strs.len());
194 for s in &unit_strs {
195 unit_types.push(parse_unit_type(s));
196 }
197
198 let n = nums.len().max(unit_types.len());
200 let values: Vec<f64> = (0..n).map(|i| nums[i % nums.len()]).collect();
201 let units: Vec<grid::units::UnitType> = (0..n)
202 .map(|i| unit_types[i % unit_types.len()].clone())
203 .collect();
204
205 return grid::units::Unit { values, units };
206 }
207 }
208
209 if let Some(rv) = val.as_vector() {
211 let nums: Vec<f64> = rv
212 .to_doubles()
213 .into_iter()
214 .map(|v| v.unwrap_or(0.0))
215 .collect();
216 if !nums.is_empty() {
217 return grid::units::Unit {
218 values: nums.clone(),
219 units: nums.iter().map(|_| grid::units::UnitType::Npc).collect(),
220 };
221 }
222 }
223
224 grid::units::Unit::npc(0.5)
226}
227
228fn parse_unit_type(s: &str) -> grid::units::UnitType {
230 match s {
231 "npc" => grid::units::UnitType::Npc,
232 "cm" => grid::units::UnitType::Cm,
233 "inches" | "in" => grid::units::UnitType::Inches,
234 "mm" => grid::units::UnitType::Mm,
235 "points" | "pt" | "bigpts" | "picas" | "dida" | "cicero" | "scaledpts" => {
236 grid::units::UnitType::Points
237 }
238 "lines" => grid::units::UnitType::Lines,
239 "char" => grid::units::UnitType::Char,
240 "native" => grid::units::UnitType::Native,
241 "null" => grid::units::UnitType::Null,
242 "snpc" => grid::units::UnitType::Snpc,
243 "strwidth" => grid::units::UnitType::StrWidth(String::new()),
244 "strheight" => grid::units::UnitType::StrHeight(String::new()),
245 "grobwidth" => grid::units::UnitType::GrobWidth(String::new()),
246 "grobheight" => grid::units::UnitType::GrobHeight(String::new()),
247 _ => grid::units::UnitType::Npc,
248 }
249}
250
251fn parse_grid_color(value: &RValue) -> Option<[u8; 4]> {
256 match value {
257 RValue::Vector(rv) => match &rv.inner {
258 Vector::Character(c) => {
259 if let Some(Some(s)) = c.first() {
260 Some(parse_color_string(s))
261 } else {
262 None
263 }
264 }
265 Vector::Integer(iv) => {
266 iv.first_opt().map(|i| match i {
268 0 => [255, 255, 255, 0], 1 => [0, 0, 0, 255], 2 => [255, 0, 0, 255], 3 => [0, 128, 0, 255], 4 => [0, 0, 255, 255], 5 => [0, 255, 255, 255], 6 => [255, 0, 255, 255], 7 => [255, 255, 0, 255], 8 => [128, 128, 128, 255], _ => [0, 0, 0, 255], })
279 }
280 _ => None,
281 },
282 RValue::Null => None,
283 _ => None,
284 }
285}
286
287fn parse_color_string(s: &str) -> [u8; 4] {
289 match s.to_lowercase().as_str() {
291 "black" => return [0, 0, 0, 255],
292 "white" => return [255, 255, 255, 255],
293 "red" => return [255, 0, 0, 255],
294 "green" => return [0, 128, 0, 255],
295 "blue" => return [0, 0, 255, 255],
296 "cyan" => return [0, 255, 255, 255],
297 "magenta" => return [255, 0, 255, 255],
298 "yellow" => return [255, 255, 0, 255],
299 "gray" | "grey" => return [190, 190, 190, 255],
300 "orange" => return [255, 165, 0, 255],
301 "purple" => return [128, 0, 128, 255],
302 "brown" => return [165, 42, 42, 255],
303 "pink" => return [255, 192, 203, 255],
304 "transparent" | "na" => return [0, 0, 0, 0],
305 _ => {}
306 }
307
308 if let Some(hex) = s.strip_prefix('#') {
310 match hex.len() {
311 6 => {
312 let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
313 let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
314 let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
315 return [r, g, b, 255];
316 }
317 8 => {
318 let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
319 let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
320 let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
321 let a = u8::from_str_radix(&hex[6..8], 16).unwrap_or(255);
322 return [r, g, b, a];
323 }
324 _ => {}
325 }
326 }
327
328 [0, 0, 0, 255] }
330
331fn extract_gpar_from_rvalue(val: &RValue) -> grid::gpar::Gpar {
333 let mut gp = grid::gpar::Gpar::new();
334
335 if let RValue::List(list) = val {
336 for (key, v) in &list.values {
337 match key.as_deref() {
338 Some("col") => {
339 gp.col = parse_grid_color(v);
340 }
341 Some("fill") => {
342 gp.fill = parse_grid_color(v);
343 }
344 Some("lwd") => {
345 if let Some(rv) = v.as_vector() {
346 gp.lwd = rv.as_double_scalar();
347 }
348 }
349 Some("fontsize") => {
350 if let Some(rv) = v.as_vector() {
351 gp.fontsize = rv.as_double_scalar();
352 }
353 }
354 Some("lineheight") => {
355 if let Some(rv) = v.as_vector() {
356 gp.lineheight = rv.as_double_scalar();
357 }
358 }
359 Some("cex") => {
360 if let Some(rv) = v.as_vector() {
361 gp.cex = rv.as_double_scalar();
362 }
363 }
364 Some("alpha") => {
365 if let Some(rv) = v.as_vector() {
366 gp.alpha = rv.as_double_scalar();
367 }
368 }
369 _ => {}
370 }
371 }
372 }
373
374 gp
375}
376
377fn extract_justification(
382 val: &RValue,
383) -> (grid::viewport::Justification, grid::viewport::Justification) {
384 use grid::viewport::Justification;
385
386 match val {
387 RValue::Vector(rv) => match &rv.inner {
388 Vector::Character(c) => {
389 if c.len() >= 2 {
390 let h = c[0]
391 .as_deref()
392 .and_then(Justification::parse)
393 .unwrap_or(Justification::Centre);
394 let v_j = c[1]
395 .as_deref()
396 .and_then(Justification::parse)
397 .unwrap_or(Justification::Centre);
398 (h, v_j)
399 } else if let Some(Some(s)) = c.first() {
400 let j = Justification::parse(s).unwrap_or(Justification::Centre);
401 (j, j)
402 } else {
403 (Justification::Centre, Justification::Centre)
404 }
405 }
406 Vector::Double(d) => {
407 let h = d.first_opt().unwrap_or(0.5);
408 let v_val = d.get_opt(1).unwrap_or(h);
409 (num_to_just(h), num_to_just(v_val))
410 }
411 _ => (Justification::Centre, Justification::Centre),
412 },
413 RValue::Null => (Justification::Centre, Justification::Centre),
414 _ => (Justification::Centre, Justification::Centre),
415 }
416}
417
418fn num_to_just(v: f64) -> grid::viewport::Justification {
420 use grid::viewport::Justification;
421 if v <= 0.25 {
422 Justification::Left
423 } else if v >= 0.75 {
424 Justification::Right
425 } else {
426 Justification::Centre
427 }
428}
429
430fn extract_viewport_from_rvalue(val: &RValue) -> grid::viewport::Viewport {
432 let mut vp = grid::viewport::Viewport::new();
433
434 if let RValue::List(list) = val {
435 for (key, v) in &list.values {
436 match key.as_deref() {
437 Some("x") => vp.x = extract_unit_from_rvalue(v),
438 Some("y") => vp.y = extract_unit_from_rvalue(v),
439 Some("width") => vp.width = extract_unit_from_rvalue(v),
440 Some("height") => vp.height = extract_unit_from_rvalue(v),
441 Some("just") => {
442 let (h, v_just) = extract_justification(v);
443 vp.just = (h, v_just);
444 }
445 Some("xscale") => {
446 if let Some(rv) = v.as_vector() {
447 let doubles = rv.to_doubles();
448 if doubles.len() >= 2 {
449 vp.xscale = (doubles[0].unwrap_or(0.0), doubles[1].unwrap_or(1.0));
450 }
451 }
452 }
453 Some("yscale") => {
454 if let Some(rv) = v.as_vector() {
455 let doubles = rv.to_doubles();
456 if doubles.len() >= 2 {
457 vp.yscale = (doubles[0].unwrap_or(0.0), doubles[1].unwrap_or(1.0));
458 }
459 }
460 }
461 Some("angle") => {
462 if let Some(rv) = v.as_vector() {
463 vp.angle = rv.as_double_scalar().unwrap_or(0.0);
464 }
465 }
466 Some("clip") => {
467 if let Some(rv) = v.as_vector() {
468 if let Some(s) = rv.as_character_scalar() {
469 vp.clip = s == "on";
470 }
471 }
472 }
473 Some("gp") => {
474 vp.gp = extract_gpar_from_rvalue(v);
475 }
476 Some("name") => {
477 if let Some(rv) = v.as_vector() {
478 vp.name = rv.as_character_scalar();
479 }
480 }
481 _ => {}
482 }
483 }
484 }
485
486 vp
487}
488
489fn record_rust_grob(grob: grid::grob::Grob, ctx: &BuiltinContext) {
494 let grob_id = ctx.interpreter().grid_grob_store.borrow_mut().add(grob);
495 ctx.interpreter()
496 .grid_rust_display_list
497 .borrow_mut()
498 .record(grid::display::DisplayItem::Draw(grob_id));
499}
500
501fn extract_labels(val: &RValue) -> Vec<String> {
503 match val {
504 RValue::Vector(rv) => rv
505 .inner
506 .to_characters()
507 .into_iter()
508 .map(|v| v.unwrap_or_default())
509 .collect(),
510 _ => vec![],
511 }
512}
513
514fn extract_rot(val: &RValue) -> f64 {
516 if let Some(rv) = val.as_vector() {
517 rv.as_double_scalar().unwrap_or(0.0)
518 } else {
519 0.0
520 }
521}
522
523fn extract_pch(val: &RValue) -> u8 {
525 if let Some(rv) = val.as_vector() {
526 rv.as_integer_scalar()
527 .and_then(|i| u8::try_from(i).ok())
528 .unwrap_or(1)
529 } else {
530 1
531 }
532}
533
534#[interpreter_builtin(name = "grid.newpage", namespace = "grid")]
542fn interp_grid_newpage(
543 _args: &[RValue],
544 _named: &[(String, RValue)],
545 context: &BuiltinContext,
546) -> Result<RValue, RError> {
547 flush_grid_to_plot(context);
549
550 context.interpreter().grid_display_list.borrow_mut().clear();
552 context
553 .interpreter()
554 .grid_viewport_stack
555 .borrow_mut()
556 .clear();
557
558 context
560 .interpreter()
561 .grid_rust_display_list
562 .borrow_mut()
563 .clear();
564 *context.interpreter().grid_grob_store.borrow_mut() = grid::grob::GrobStore::new();
565 *context.interpreter().grid_rust_viewport_stack.borrow_mut() =
566 grid::viewport::ViewportStack::new(17.78, 17.78);
567
568 context.interpreter().set_invisible();
569 Ok(RValue::Null)
570}
571
572#[interpreter_builtin(name = "grid.draw", namespace = "grid", min_args = 1)]
577fn interp_grid_draw(
578 args: &[RValue],
579 _named: &[(String, RValue)],
580 context: &BuiltinContext,
581) -> Result<RValue, RError> {
582 let grob = args[0].clone();
583 record_on_display_list(&grob, context);
584
585 if let RValue::List(list) = &grob {
587 if let Some(classes) = list.class() {
588 let type_class = classes
590 .iter()
591 .find(|c| *c != "grob")
592 .cloned()
593 .unwrap_or_default();
594
595 let entries: Vec<(String, RValue)> = list
597 .values
598 .iter()
599 .filter_map(|(k, v)| k.as_ref().map(|k| (k.clone(), v.clone())))
600 .collect();
601
602 if let Some(rust_grob) = build_rust_grob(&type_class, &entries) {
603 record_rust_grob(rust_grob, context);
604 }
605 }
606 }
607
608 context.interpreter().set_invisible();
609 Ok(grob)
610}
611
612#[interpreter_builtin(namespace = "grid", min_args = 2)]
626fn interp_unit(
627 args: &[RValue],
628 named: &[(String, RValue)],
629 _context: &BuiltinContext,
630) -> Result<RValue, RError> {
631 let ca = CallArgs::new(args, named);
632
633 let x = ca.value("x", 0).ok_or_else(|| {
634 RError::new(
635 RErrorKind::Argument,
636 "unit() requires an 'x' argument".to_string(),
637 )
638 })?;
639 let units_str = ca.string("units", 1)?;
640 let data = opt_value(&ca, "data", 2);
641
642 let valid_units = [
644 "npc",
645 "cm",
646 "inches",
647 "mm",
648 "points",
649 "lines",
650 "native",
651 "null",
652 "char",
653 "grobwidth",
654 "grobheight",
655 "strwidth",
656 "strheight",
657 "picas",
658 "bigpts",
659 "dida",
660 "cicero",
661 "scaledpts",
662 ];
663 if !valid_units.contains(&units_str.as_str()) {
664 return Err(RError::new(
665 RErrorKind::Argument,
666 format!(
667 "invalid unit '{}'. Valid units are: {}",
668 units_str,
669 valid_units.join(", ")
670 ),
671 ));
672 }
673
674 let units_val = if let Some(rv) = x.as_vector() {
676 let n = rv.len();
677 let units_vec: Vec<Option<String>> = (0..n).map(|_| Some(units_str.clone())).collect();
678 RValue::vec(Vector::Character(units_vec.into()))
679 } else {
680 RValue::vec(Vector::Character(vec![Some(units_str.clone())].into()))
681 };
682
683 let mut entries = vec![
684 ("value".to_string(), x.clone()),
685 ("units".to_string(), units_val),
686 ];
687 if !matches!(data, RValue::Null) {
688 entries.push(("data".to_string(), data));
689 }
690
691 Ok(make_grid_object(entries, &["unit"]))
692}
693
694#[interpreter_builtin(namespace = "grid")]
712fn interp_gpar(
713 _args: &[RValue],
714 named: &[(String, RValue)],
715 _context: &BuiltinContext,
716) -> Result<RValue, RError> {
717 let mut entries: Vec<(String, RValue)> = Vec::new();
718
719 let param_names = [
720 "col",
721 "fill",
722 "lwd",
723 "lty",
724 "fontsize",
725 "font",
726 "fontfamily",
727 "lineheight",
728 "cex",
729 "alpha",
730 "lex",
731 "lineend",
732 "linejoin",
733 "linemitre",
734 ];
735
736 for &name in ¶m_names {
737 if let Some((_, val)) = named.iter().find(|(k, _)| k == name) {
738 entries.push((name.to_string(), val.clone()));
739 }
740 }
741
742 Ok(make_grid_object(entries, &["gpar"]))
743}
744
745#[interpreter_builtin(namespace = "grid")]
765fn interp_viewport(
766 _args: &[RValue],
767 named: &[(String, RValue)],
768 _context: &BuiltinContext,
769) -> Result<RValue, RError> {
770 let get = |name: &str| -> Option<RValue> {
771 named
772 .iter()
773 .find(|(k, _)| k == name)
774 .map(|(_, v)| v.clone())
775 };
776
777 let x = get("x").unwrap_or_else(default_npc_half);
778 let y = get("y").unwrap_or_else(default_npc_half);
779 let width = get("width").unwrap_or_else(default_npc_one);
780 let height = get("height").unwrap_or_else(default_npc_one);
781 let just = get("just")
782 .unwrap_or_else(|| RValue::vec(Vector::Character(vec![Some("centre".to_string())].into())));
783 let xscale = get("xscale")
784 .unwrap_or_else(|| RValue::vec(Vector::Double(vec![Some(0.0), Some(1.0)].into())));
785 let yscale = get("yscale")
786 .unwrap_or_else(|| RValue::vec(Vector::Double(vec![Some(0.0), Some(1.0)].into())));
787 let angle = get("angle").unwrap_or_else(|| RValue::vec(Vector::Double(vec![Some(0.0)].into())));
788 let clip = get("clip").unwrap_or_else(|| {
789 RValue::vec(Vector::Character(vec![Some("inherit".to_string())].into()))
790 });
791 let gp = get("gp").unwrap_or(RValue::Null);
792 let layout = get("layout").unwrap_or(RValue::Null);
793 let name = get("name").unwrap_or(RValue::Null);
794 let layout_pos_row = get("layout.pos.row").unwrap_or(RValue::Null);
795 let layout_pos_col = get("layout.pos.col").unwrap_or(RValue::Null);
796
797 let entries = vec![
798 ("x".to_string(), x),
799 ("y".to_string(), y),
800 ("width".to_string(), width),
801 ("height".to_string(), height),
802 ("just".to_string(), just),
803 ("xscale".to_string(), xscale),
804 ("yscale".to_string(), yscale),
805 ("angle".to_string(), angle),
806 ("clip".to_string(), clip),
807 ("gp".to_string(), gp),
808 ("layout".to_string(), layout),
809 ("name".to_string(), name),
810 ("layout.pos.row".to_string(), layout_pos_row),
811 ("layout.pos.col".to_string(), layout_pos_col),
812 ];
813
814 Ok(make_grid_object(entries, &["viewport"]))
815}
816
817#[interpreter_builtin(name = "pushViewport", namespace = "grid", min_args = 1)]
822fn interp_push_viewport(
823 args: &[RValue],
824 _named: &[(String, RValue)],
825 context: &BuiltinContext,
826) -> Result<RValue, RError> {
827 let vp = args[0].clone();
828
829 let push_record = make_grid_object(
831 vec![
832 (
833 "type".to_string(),
834 RValue::vec(Vector::Character(
835 vec![Some("pushViewport".to_string())].into(),
836 )),
837 ),
838 ("viewport".to_string(), vp.clone()),
839 ],
840 &["vpOperation"],
841 );
842 record_on_display_list(&push_record, context);
843
844 let rust_vp = extract_viewport_from_rvalue(&vp);
846 context
847 .interpreter()
848 .grid_rust_viewport_stack
849 .borrow_mut()
850 .push(rust_vp.clone());
851 context
852 .interpreter()
853 .grid_rust_display_list
854 .borrow_mut()
855 .record(grid::display::DisplayItem::PushViewport(Box::new(rust_vp)));
856
857 context
858 .interpreter()
859 .grid_viewport_stack
860 .borrow_mut()
861 .push(vp);
862 context.interpreter().set_invisible();
863 Ok(RValue::Null)
864}
865
866#[interpreter_builtin(name = "popViewport", namespace = "grid")]
871fn interp_pop_viewport(
872 args: &[RValue],
873 named: &[(String, RValue)],
874 context: &BuiltinContext,
875) -> Result<RValue, RError> {
876 let ca = CallArgs::new(args, named);
877 let n = ca.integer_or("n", 0, 1);
878 let n = usize::try_from(n).unwrap_or(0);
879
880 let mut stack = context.interpreter().grid_viewport_stack.borrow_mut();
881 for _ in 0..n {
882 if stack.is_empty() {
883 return Err(RError::new(
884 RErrorKind::Other,
885 "cannot pop the top-level viewport".to_string(),
886 ));
887 }
888 stack.pop();
889 }
890
891 drop(stack);
893 {
894 let mut rust_stack = context.interpreter().grid_rust_viewport_stack.borrow_mut();
895 let mut rust_dl = context.interpreter().grid_rust_display_list.borrow_mut();
896 for _ in 0..n {
897 rust_stack.pop();
898 rust_dl.record(grid::display::DisplayItem::PopViewport);
899 }
900 }
901
902 let pop_record = make_grid_object(
904 vec![
905 (
906 "type".to_string(),
907 RValue::vec(Vector::Character(
908 vec![Some("popViewport".to_string())].into(),
909 )),
910 ),
911 (
912 "n".to_string(),
913 RValue::vec(Vector::Integer(vec![Some(i64::from(n as i32))].into())),
914 ),
915 ],
916 &["vpOperation"],
917 );
918 record_on_display_list(&pop_record, context);
919
920 context.interpreter().set_invisible();
921 Ok(RValue::Null)
922}
923
924#[interpreter_builtin(name = "current.viewport", namespace = "grid")]
928fn interp_current_viewport(
929 _args: &[RValue],
930 _named: &[(String, RValue)],
931 context: &BuiltinContext,
932) -> Result<RValue, RError> {
933 let stack = context.interpreter().grid_viewport_stack.borrow();
934 match stack.last() {
935 Some(vp) => Ok(vp.clone()),
936 None => {
937 Ok(make_grid_object(
939 vec![
940 ("x".to_string(), default_npc_half()),
941 ("y".to_string(), default_npc_half()),
942 ("width".to_string(), default_npc_one()),
943 ("height".to_string(), default_npc_one()),
944 (
945 "name".to_string(),
946 RValue::vec(Vector::Character(vec![Some("ROOT".to_string())].into())),
947 ),
948 ],
949 &["viewport"],
950 ))
951 }
952 }
953}
954
955#[interpreter_builtin(name = "upViewport", namespace = "grid")]
960fn interp_up_viewport(
961 args: &[RValue],
962 named: &[(String, RValue)],
963 context: &BuiltinContext,
964) -> Result<RValue, RError> {
965 let ca = CallArgs::new(args, named);
966 let n = ca.integer_or("n", 0, 1);
967 let n = usize::try_from(n).unwrap_or(0);
968
969 let stack_len = context.interpreter().grid_viewport_stack.borrow().len();
970 if n > stack_len {
971 return Err(RError::new(
972 RErrorKind::Other,
973 format!("cannot navigate up {n} viewport(s) — only {stack_len} on the stack"),
974 ));
975 }
976
977 let up_record = make_grid_object(
980 vec![
981 (
982 "type".to_string(),
983 RValue::vec(Vector::Character(
984 vec![Some("upViewport".to_string())].into(),
985 )),
986 ),
987 (
988 "n".to_string(),
989 RValue::vec(Vector::Integer(vec![Some(i64::from(n as i32))].into())),
990 ),
991 ],
992 &["vpOperation"],
993 );
994 record_on_display_list(&up_record, context);
995
996 context.interpreter().set_invisible();
997 Ok(RValue::Null)
998}
999
1000#[interpreter_builtin(name = "downViewport", namespace = "grid", min_args = 1)]
1005fn interp_down_viewport(
1006 args: &[RValue],
1007 named: &[(String, RValue)],
1008 context: &BuiltinContext,
1009) -> Result<RValue, RError> {
1010 let ca = CallArgs::new(args, named);
1011 let name = ca.string("name", 0)?;
1012
1013 match find_viewport_by_name(&name, context) {
1014 Some(idx) => {
1015 let stack_len = context.interpreter().grid_viewport_stack.borrow().len();
1016 let depth = stack_len.saturating_sub(idx + 1);
1017 Ok(RValue::vec(Vector::Integer(
1018 vec![Some(i64::try_from(depth).unwrap_or(0))].into(),
1019 )))
1020 }
1021 None => Err(RError::new(
1022 RErrorKind::Other,
1023 format!("viewport '{name}' was not found"),
1024 )),
1025 }
1026}
1027
1028#[interpreter_builtin(name = "seekViewport", namespace = "grid", min_args = 1)]
1033fn interp_seek_viewport(
1034 args: &[RValue],
1035 named: &[(String, RValue)],
1036 context: &BuiltinContext,
1037) -> Result<RValue, RError> {
1038 let ca = CallArgs::new(args, named);
1039 let name = ca.string("name", 0)?;
1040
1041 match find_viewport_by_name(&name, context) {
1042 Some(idx) => {
1043 let stack_len = context.interpreter().grid_viewport_stack.borrow().len();
1044 let depth = stack_len.saturating_sub(idx + 1);
1045 Ok(RValue::vec(Vector::Integer(
1046 vec![Some(i64::try_from(depth).unwrap_or(0))].into(),
1047 )))
1048 }
1049 None => Err(RError::new(
1050 RErrorKind::Other,
1051 format!("viewport '{name}' was not found"),
1052 )),
1053 }
1054}
1055
1056#[interpreter_builtin(name = "plotViewport", namespace = "grid")]
1064fn interp_plot_viewport(
1065 args: &[RValue],
1066 named: &[(String, RValue)],
1067 _context: &BuiltinContext,
1068) -> Result<RValue, RError> {
1069 let ca = CallArgs::new(args, named);
1070
1071 let margins = if let Some(val) = ca.value("margins", 0) {
1072 if let Some(rv) = val.as_vector() {
1073 rv.to_doubles()
1074 } else {
1075 vec![Some(5.1), Some(4.1), Some(4.1), Some(2.1)]
1076 }
1077 } else {
1078 vec![Some(5.1), Some(4.1), Some(4.1), Some(2.1)]
1079 };
1080
1081 let bottom = margins.first().copied().flatten().unwrap_or(5.1);
1083 let left = margins.get(1).copied().flatten().unwrap_or(4.1);
1084 let top = margins.get(2).copied().flatten().unwrap_or(4.1);
1085 let right = margins.get(3).copied().flatten().unwrap_or(2.1);
1086
1087 let entries = vec![
1089 (
1090 "x".to_string(),
1091 make_grid_object(
1092 vec![
1093 (
1094 "value".to_string(),
1095 RValue::vec(Vector::Double(vec![Some(0.5 * (left - right))].into())),
1096 ),
1097 (
1098 "units".to_string(),
1099 RValue::vec(Vector::Character(vec![Some("lines".to_string())].into())),
1100 ),
1101 ],
1102 &["unit"],
1103 ),
1104 ),
1105 (
1106 "y".to_string(),
1107 make_grid_object(
1108 vec![
1109 (
1110 "value".to_string(),
1111 RValue::vec(Vector::Double(vec![Some(0.5 * (bottom - top))].into())),
1112 ),
1113 (
1114 "units".to_string(),
1115 RValue::vec(Vector::Character(vec![Some("lines".to_string())].into())),
1116 ),
1117 ],
1118 &["unit"],
1119 ),
1120 ),
1121 (
1122 "width".to_string(),
1123 make_grid_object(
1124 vec![
1125 (
1126 "value".to_string(),
1127 RValue::vec(Vector::Double(vec![Some(-(left + right))].into())),
1128 ),
1129 (
1130 "units".to_string(),
1131 RValue::vec(Vector::Character(vec![Some("lines".to_string())].into())),
1132 ),
1133 ],
1134 &["unit"],
1135 ),
1136 ),
1137 (
1138 "height".to_string(),
1139 make_grid_object(
1140 vec![
1141 (
1142 "value".to_string(),
1143 RValue::vec(Vector::Double(vec![Some(-(bottom + top))].into())),
1144 ),
1145 (
1146 "units".to_string(),
1147 RValue::vec(Vector::Character(vec![Some("lines".to_string())].into())),
1148 ),
1149 ],
1150 &["unit"],
1151 ),
1152 ),
1153 (
1154 "just".to_string(),
1155 RValue::vec(Vector::Character(vec![Some("centre".to_string())].into())),
1156 ),
1157 ];
1158
1159 Ok(make_grid_object(entries, &["viewport"]))
1160}
1161
1162#[interpreter_builtin(name = "dataViewport", namespace = "grid")]
1169fn interp_data_viewport(
1170 args: &[RValue],
1171 named: &[(String, RValue)],
1172 _context: &BuiltinContext,
1173) -> Result<RValue, RError> {
1174 let ca = CallArgs::new(args, named);
1175
1176 let extension = ca
1177 .value("extension", 2)
1178 .and_then(|v| v.as_vector()?.as_double_scalar())
1179 .unwrap_or(0.05);
1180
1181 let xscale = if let Some(xdata) = ca.value("xData", 0) {
1183 if let Some(rv) = xdata.as_vector() {
1184 let doubles: Vec<f64> = rv.to_doubles().into_iter().flatten().collect();
1185 if doubles.is_empty() {
1186 vec![Some(0.0), Some(1.0)]
1187 } else {
1188 let min = doubles.iter().copied().fold(f64::INFINITY, f64::min);
1189 let max = doubles.iter().copied().fold(f64::NEG_INFINITY, f64::max);
1190 let range = max - min;
1191 vec![Some(min - range * extension), Some(max + range * extension)]
1192 }
1193 } else {
1194 vec![Some(0.0), Some(1.0)]
1195 }
1196 } else {
1197 vec![Some(0.0), Some(1.0)]
1198 };
1199
1200 let yscale = if let Some(ydata) = ca.value("yData", 1) {
1202 if let Some(rv) = ydata.as_vector() {
1203 let doubles: Vec<f64> = rv.to_doubles().into_iter().flatten().collect();
1204 if doubles.is_empty() {
1205 vec![Some(0.0), Some(1.0)]
1206 } else {
1207 let min = doubles.iter().copied().fold(f64::INFINITY, f64::min);
1208 let max = doubles.iter().copied().fold(f64::NEG_INFINITY, f64::max);
1209 let range = max - min;
1210 vec![Some(min - range * extension), Some(max + range * extension)]
1211 }
1212 } else {
1213 vec![Some(0.0), Some(1.0)]
1214 }
1215 } else {
1216 vec![Some(0.0), Some(1.0)]
1217 };
1218
1219 let entries = vec![
1220 ("x".to_string(), default_npc_half()),
1221 ("y".to_string(), default_npc_half()),
1222 ("width".to_string(), default_npc_one()),
1223 ("height".to_string(), default_npc_one()),
1224 (
1225 "just".to_string(),
1226 RValue::vec(Vector::Character(vec![Some("centre".to_string())].into())),
1227 ),
1228 (
1229 "xscale".to_string(),
1230 RValue::vec(Vector::Double(xscale.into())),
1231 ),
1232 (
1233 "yscale".to_string(),
1234 RValue::vec(Vector::Double(yscale.into())),
1235 ),
1236 ];
1237
1238 Ok(make_grid_object(entries, &["viewport"]))
1239}
1240
1241fn make_grob(
1252 type_class: &str,
1253 entries: Vec<(String, RValue)>,
1254 draw: bool,
1255 vp: RValue,
1256 ctx: &BuiltinContext,
1257) -> Result<RValue, RError> {
1258 let grob = make_grid_object(entries.clone(), &[type_class, "grob"]);
1259
1260 if draw {
1261 let rust_grob = build_rust_grob(type_class, &entries);
1263
1264 match vp {
1265 RValue::Null => {
1266 record_on_display_list(&grob, ctx);
1267 if let Some(rg) = rust_grob {
1268 record_rust_grob(rg, ctx);
1269 }
1270 }
1271 _ => {
1272 let push_record = make_grid_object(
1274 vec![
1275 (
1276 "type".to_string(),
1277 RValue::vec(Vector::Character(
1278 vec![Some("pushViewport".to_string())].into(),
1279 )),
1280 ),
1281 ("viewport".to_string(), vp.clone()),
1282 ],
1283 &["vpOperation"],
1284 );
1285 record_on_display_list(&push_record, ctx);
1286
1287 let rust_vp = extract_viewport_from_rvalue(&vp);
1289 ctx.interpreter()
1290 .grid_rust_viewport_stack
1291 .borrow_mut()
1292 .push(rust_vp.clone());
1293 ctx.interpreter()
1294 .grid_rust_display_list
1295 .borrow_mut()
1296 .record(grid::display::DisplayItem::PushViewport(Box::new(rust_vp)));
1297
1298 ctx.interpreter().grid_viewport_stack.borrow_mut().push(vp);
1299
1300 record_on_display_list(&grob, ctx);
1301 if let Some(rg) = rust_grob {
1302 record_rust_grob(rg, ctx);
1303 }
1304
1305 ctx.interpreter().grid_viewport_stack.borrow_mut().pop();
1306
1307 ctx.interpreter()
1309 .grid_rust_viewport_stack
1310 .borrow_mut()
1311 .pop();
1312 ctx.interpreter()
1313 .grid_rust_display_list
1314 .borrow_mut()
1315 .record(grid::display::DisplayItem::PopViewport);
1316
1317 let pop_record = make_grid_object(
1318 vec![
1319 (
1320 "type".to_string(),
1321 RValue::vec(Vector::Character(
1322 vec![Some("popViewport".to_string())].into(),
1323 )),
1324 ),
1325 (
1326 "n".to_string(),
1327 RValue::vec(Vector::Integer(vec![Some(1)].into())),
1328 ),
1329 ],
1330 &["vpOperation"],
1331 );
1332 record_on_display_list(&pop_record, ctx);
1333 }
1334 }
1335 }
1336
1337 ctx.interpreter().set_invisible();
1338 Ok(grob)
1339}
1340
1341fn build_rust_grob(type_class: &str, entries: &[(String, RValue)]) -> Option<grid::grob::Grob> {
1346 let get =
1348 |name: &str| -> Option<&RValue> { entries.iter().find(|(k, _)| k == name).map(|(_, v)| v) };
1349
1350 match type_class {
1351 "lines" => {
1352 let x = extract_unit_from_rvalue(get("x").unwrap_or(&RValue::Null));
1353 let y = extract_unit_from_rvalue(get("y").unwrap_or(&RValue::Null));
1354 let gp = extract_gpar_from_rvalue(get("gp").unwrap_or(&RValue::Null));
1355 Some(grid::grob::Grob::Lines { x, y, gp })
1356 }
1357 "segments" => {
1358 let x0 = extract_unit_from_rvalue(get("x0").unwrap_or(&RValue::Null));
1359 let y0 = extract_unit_from_rvalue(get("y0").unwrap_or(&RValue::Null));
1360 let x1 = extract_unit_from_rvalue(get("x1").unwrap_or(&RValue::Null));
1361 let y1 = extract_unit_from_rvalue(get("y1").unwrap_or(&RValue::Null));
1362 let gp = extract_gpar_from_rvalue(get("gp").unwrap_or(&RValue::Null));
1363 Some(grid::grob::Grob::Segments { x0, y0, x1, y1, gp })
1364 }
1365 "points" => {
1366 let x = extract_unit_from_rvalue(get("x").unwrap_or(&RValue::Null));
1367 let y = extract_unit_from_rvalue(get("y").unwrap_or(&RValue::Null));
1368 let pch = extract_pch(get("pch").unwrap_or(&RValue::Null));
1369 let size = if let Some(sv) = get("size") {
1370 if matches!(sv, RValue::Null) {
1371 grid::units::Unit::points(4.0)
1372 } else {
1373 extract_unit_from_rvalue(sv)
1374 }
1375 } else {
1376 grid::units::Unit::points(4.0)
1377 };
1378 let gp = extract_gpar_from_rvalue(get("gp").unwrap_or(&RValue::Null));
1379 Some(grid::grob::Grob::Points {
1380 x,
1381 y,
1382 pch,
1383 size,
1384 gp,
1385 })
1386 }
1387 "rect" => {
1388 let x = extract_unit_from_rvalue(get("x").unwrap_or(&RValue::Null));
1389 let y = extract_unit_from_rvalue(get("y").unwrap_or(&RValue::Null));
1390 let width = extract_unit_from_rvalue(get("width").unwrap_or(&RValue::Null));
1391 let height = extract_unit_from_rvalue(get("height").unwrap_or(&RValue::Null));
1392 let just = extract_justification(get("just").unwrap_or(&RValue::Null));
1393 let gp = extract_gpar_from_rvalue(get("gp").unwrap_or(&RValue::Null));
1394 Some(grid::grob::Grob::Rect {
1395 x,
1396 y,
1397 width,
1398 height,
1399 just,
1400 gp,
1401 })
1402 }
1403 "circle" => {
1404 let x = extract_unit_from_rvalue(get("x").unwrap_or(&RValue::Null));
1405 let y = extract_unit_from_rvalue(get("y").unwrap_or(&RValue::Null));
1406 let r = extract_unit_from_rvalue(get("r").unwrap_or(&RValue::Null));
1407 let gp = extract_gpar_from_rvalue(get("gp").unwrap_or(&RValue::Null));
1408 Some(grid::grob::Grob::Circle { x, y, r, gp })
1409 }
1410 "polygon" => {
1411 let x = extract_unit_from_rvalue(get("x").unwrap_or(&RValue::Null));
1412 let y = extract_unit_from_rvalue(get("y").unwrap_or(&RValue::Null));
1413 let gp = extract_gpar_from_rvalue(get("gp").unwrap_or(&RValue::Null));
1414 Some(grid::grob::Grob::Polygon { x, y, gp })
1415 }
1416 "text" => {
1417 let label = extract_labels(get("label").unwrap_or(&RValue::Null));
1418 let x = extract_unit_from_rvalue(get("x").unwrap_or(&RValue::Null));
1419 let y = extract_unit_from_rvalue(get("y").unwrap_or(&RValue::Null));
1420 let just = extract_justification(get("just").unwrap_or(&RValue::Null));
1421 let rot = extract_rot(get("rot").unwrap_or(&RValue::Null));
1422 let gp = extract_gpar_from_rvalue(get("gp").unwrap_or(&RValue::Null));
1423 Some(grid::grob::Grob::Text {
1424 label,
1425 x,
1426 y,
1427 just,
1428 rot,
1429 gp,
1430 })
1431 }
1432 _ => None,
1434 }
1435}
1436
1437#[interpreter_builtin(name = "grid.lines", namespace = "grid")]
1448fn interp_grid_lines(
1449 args: &[RValue],
1450 named: &[(String, RValue)],
1451 context: &BuiltinContext,
1452) -> Result<RValue, RError> {
1453 let ca = CallArgs::new(args, named);
1454 let default_units = ca
1455 .optional_string("default.units", 2)
1456 .unwrap_or_else(|| "npc".to_string());
1457 let draw = ca.logical_flag("draw", 6, true);
1458 let vp = opt_value(&ca, "vp", 4);
1459 let name = ca
1460 .optional_string("name", 5)
1461 .unwrap_or_else(|| auto_grob_name("GRID.lines", context));
1462
1463 let x = ca
1464 .value("x", 0)
1465 .cloned()
1466 .unwrap_or_else(|| RValue::vec(Vector::Double(vec![Some(0.0), Some(1.0)].into())));
1467 let y = ca
1468 .value("y", 1)
1469 .cloned()
1470 .unwrap_or_else(|| RValue::vec(Vector::Double(vec![Some(0.0), Some(1.0)].into())));
1471
1472 let x = ensure_unit(&x, &default_units);
1473 let y = ensure_unit(&y, &default_units);
1474
1475 let gp = opt_value(&ca, "gp", 3);
1476
1477 let entries = vec![
1478 ("x".to_string(), x),
1479 ("y".to_string(), y),
1480 ("gp".to_string(), gp),
1481 (
1482 "name".to_string(),
1483 RValue::vec(Vector::Character(vec![Some(name)].into())),
1484 ),
1485 ];
1486
1487 make_grob("lines", entries, draw, vp, context)
1488}
1489
1490#[interpreter_builtin(name = "grid.segments", namespace = "grid")]
1503fn interp_grid_segments(
1504 args: &[RValue],
1505 named: &[(String, RValue)],
1506 context: &BuiltinContext,
1507) -> Result<RValue, RError> {
1508 let ca = CallArgs::new(args, named);
1509 let default_units = ca
1510 .optional_string("default.units", 4)
1511 .unwrap_or_else(|| "npc".to_string());
1512 let draw = ca.logical_flag("draw", 8, true);
1513 let vp = opt_value(&ca, "vp", 6);
1514 let name = ca
1515 .optional_string("name", 7)
1516 .unwrap_or_else(|| auto_grob_name("GRID.segments", context));
1517
1518 let x0 = ensure_unit(
1519 &ca.value("x0", 0).cloned().unwrap_or_else(default_npc_half),
1520 &default_units,
1521 );
1522 let y0 = ensure_unit(
1523 &ca.value("y0", 1).cloned().unwrap_or_else(default_npc_half),
1524 &default_units,
1525 );
1526 let x1 = ensure_unit(
1527 &ca.value("x1", 2).cloned().unwrap_or_else(default_npc_half),
1528 &default_units,
1529 );
1530 let y1 = ensure_unit(
1531 &ca.value("y1", 3).cloned().unwrap_or_else(default_npc_half),
1532 &default_units,
1533 );
1534 let gp = opt_value(&ca, "gp", 5);
1535
1536 let entries = vec![
1537 ("x0".to_string(), x0),
1538 ("y0".to_string(), y0),
1539 ("x1".to_string(), x1),
1540 ("y1".to_string(), y1),
1541 ("gp".to_string(), gp),
1542 (
1543 "name".to_string(),
1544 RValue::vec(Vector::Character(vec![Some(name)].into())),
1545 ),
1546 ];
1547
1548 make_grob("segments", entries, draw, vp, context)
1549}
1550
1551#[interpreter_builtin(name = "grid.points", namespace = "grid")]
1564fn interp_grid_points(
1565 args: &[RValue],
1566 named: &[(String, RValue)],
1567 context: &BuiltinContext,
1568) -> Result<RValue, RError> {
1569 let ca = CallArgs::new(args, named);
1570 let default_units = ca
1571 .optional_string("default.units", 4)
1572 .unwrap_or_else(|| "npc".to_string());
1573 let draw = ca.logical_flag("draw", 8, true);
1574 let vp = opt_value(&ca, "vp", 6);
1575 let name = ca
1576 .optional_string("name", 7)
1577 .unwrap_or_else(|| auto_grob_name("GRID.points", context));
1578
1579 let x = ensure_unit(
1580 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
1581 &default_units,
1582 );
1583 let y = ensure_unit(
1584 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
1585 &default_units,
1586 );
1587 let pch = opt_value(&ca, "pch", 2);
1588 let size = opt_value(&ca, "size", 3);
1589 let gp = opt_value(&ca, "gp", 5);
1590
1591 let entries = vec![
1592 ("x".to_string(), x),
1593 ("y".to_string(), y),
1594 ("pch".to_string(), pch),
1595 ("size".to_string(), size),
1596 ("gp".to_string(), gp),
1597 (
1598 "name".to_string(),
1599 RValue::vec(Vector::Character(vec![Some(name)].into())),
1600 ),
1601 ];
1602
1603 make_grob("points", entries, draw, vp, context)
1604}
1605
1606#[interpreter_builtin(name = "grid.rect", namespace = "grid")]
1620fn interp_grid_rect(
1621 args: &[RValue],
1622 named: &[(String, RValue)],
1623 context: &BuiltinContext,
1624) -> Result<RValue, RError> {
1625 let ca = CallArgs::new(args, named);
1626 let default_units = ca
1627 .optional_string("default.units", 5)
1628 .unwrap_or_else(|| "npc".to_string());
1629 let draw = ca.logical_flag("draw", 9, true);
1630 let vp = opt_value(&ca, "vp", 7);
1631 let name = ca
1632 .optional_string("name", 8)
1633 .unwrap_or_else(|| auto_grob_name("GRID.rect", context));
1634
1635 let x = ensure_unit(
1636 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
1637 &default_units,
1638 );
1639 let y = ensure_unit(
1640 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
1641 &default_units,
1642 );
1643 let width = ensure_unit(
1644 &ca.value("width", 2)
1645 .cloned()
1646 .unwrap_or_else(default_npc_one),
1647 &default_units,
1648 );
1649 let height = ensure_unit(
1650 &ca.value("height", 3)
1651 .cloned()
1652 .unwrap_or_else(default_npc_one),
1653 &default_units,
1654 );
1655 let just = opt_value(&ca, "just", 4);
1656 let gp = opt_value(&ca, "gp", 6);
1657
1658 let entries = vec![
1659 ("x".to_string(), x),
1660 ("y".to_string(), y),
1661 ("width".to_string(), width),
1662 ("height".to_string(), height),
1663 ("just".to_string(), just),
1664 ("gp".to_string(), gp),
1665 (
1666 "name".to_string(),
1667 RValue::vec(Vector::Character(vec![Some(name)].into())),
1668 ),
1669 ];
1670
1671 make_grob("rect", entries, draw, vp, context)
1672}
1673
1674#[interpreter_builtin(name = "grid.circle", namespace = "grid")]
1686fn interp_grid_circle(
1687 args: &[RValue],
1688 named: &[(String, RValue)],
1689 context: &BuiltinContext,
1690) -> Result<RValue, RError> {
1691 let ca = CallArgs::new(args, named);
1692 let default_units = ca
1693 .optional_string("default.units", 3)
1694 .unwrap_or_else(|| "npc".to_string());
1695 let draw = ca.logical_flag("draw", 7, true);
1696 let vp = opt_value(&ca, "vp", 5);
1697 let name = ca
1698 .optional_string("name", 6)
1699 .unwrap_or_else(|| auto_grob_name("GRID.circle", context));
1700
1701 let x = ensure_unit(
1702 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
1703 &default_units,
1704 );
1705 let y = ensure_unit(
1706 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
1707 &default_units,
1708 );
1709 let r = ensure_unit(
1710 &ca.value("r", 2).cloned().unwrap_or_else(default_npc_half),
1711 &default_units,
1712 );
1713 let gp = opt_value(&ca, "gp", 4);
1714
1715 let entries = vec![
1716 ("x".to_string(), x),
1717 ("y".to_string(), y),
1718 ("r".to_string(), r),
1719 ("gp".to_string(), gp),
1720 (
1721 "name".to_string(),
1722 RValue::vec(Vector::Character(vec![Some(name)].into())),
1723 ),
1724 ];
1725
1726 make_grob("circle", entries, draw, vp, context)
1727}
1728
1729#[interpreter_builtin(name = "grid.polygon", namespace = "grid")]
1740fn interp_grid_polygon(
1741 args: &[RValue],
1742 named: &[(String, RValue)],
1743 context: &BuiltinContext,
1744) -> Result<RValue, RError> {
1745 let ca = CallArgs::new(args, named);
1746 let default_units = ca
1747 .optional_string("default.units", 2)
1748 .unwrap_or_else(|| "npc".to_string());
1749 let draw = ca.logical_flag("draw", 6, true);
1750 let vp = opt_value(&ca, "vp", 4);
1751 let name = ca
1752 .optional_string("name", 5)
1753 .unwrap_or_else(|| auto_grob_name("GRID.polygon", context));
1754
1755 let x = ensure_unit(
1756 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
1757 &default_units,
1758 );
1759 let y = ensure_unit(
1760 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
1761 &default_units,
1762 );
1763 let gp = opt_value(&ca, "gp", 3);
1764
1765 let entries = vec![
1766 ("x".to_string(), x),
1767 ("y".to_string(), y),
1768 ("gp".to_string(), gp),
1769 (
1770 "name".to_string(),
1771 RValue::vec(Vector::Character(vec![Some(name)].into())),
1772 ),
1773 ];
1774
1775 make_grob("polygon", entries, draw, vp, context)
1776}
1777
1778#[interpreter_builtin(name = "grid.text", namespace = "grid")]
1792fn interp_grid_text(
1793 args: &[RValue],
1794 named: &[(String, RValue)],
1795 context: &BuiltinContext,
1796) -> Result<RValue, RError> {
1797 let ca = CallArgs::new(args, named);
1798 let default_units = ca
1799 .optional_string("default.units", 5)
1800 .unwrap_or_else(|| "npc".to_string());
1801 let draw = ca.logical_flag("draw", 9, true);
1802 let vp = opt_value(&ca, "vp", 7);
1803 let name = ca
1804 .optional_string("name", 8)
1805 .unwrap_or_else(|| auto_grob_name("GRID.text", context));
1806
1807 let label = opt_value(&ca, "label", 0);
1808 let x = ensure_unit(
1809 &ca.value("x", 1).cloned().unwrap_or_else(default_npc_half),
1810 &default_units,
1811 );
1812 let y = ensure_unit(
1813 &ca.value("y", 2).cloned().unwrap_or_else(default_npc_half),
1814 &default_units,
1815 );
1816 let just = opt_value(&ca, "just", 3);
1817 let rot = opt_value(&ca, "rot", 4);
1818 let gp = opt_value(&ca, "gp", 6);
1819
1820 let entries = vec![
1821 ("label".to_string(), label),
1822 ("x".to_string(), x),
1823 ("y".to_string(), y),
1824 ("just".to_string(), just),
1825 ("rot".to_string(), rot),
1826 ("gp".to_string(), gp),
1827 (
1828 "name".to_string(),
1829 RValue::vec(Vector::Character(vec![Some(name)].into())),
1830 ),
1831 ];
1832
1833 make_grob("text", entries, draw, vp, context)
1834}
1835
1836#[interpreter_builtin(name = "textGrob", namespace = "grid")]
1854fn interp_text_grob(
1855 args: &[RValue],
1856 named: &[(String, RValue)],
1857 context: &BuiltinContext,
1858) -> Result<RValue, RError> {
1859 let ca = CallArgs::new(args, named);
1860 let default_units = ca
1861 .optional_string("default.units", 5)
1862 .unwrap_or_else(|| "npc".to_string());
1863 let vp = opt_value(&ca, "vp", 7);
1864 let name = ca
1865 .optional_string("name", 8)
1866 .unwrap_or_else(|| auto_grob_name("GRID.text", context));
1867
1868 let label = opt_value(&ca, "label", 0);
1869 let x = ensure_unit(
1870 &ca.value("x", 1).cloned().unwrap_or_else(default_npc_half),
1871 &default_units,
1872 );
1873 let y = ensure_unit(
1874 &ca.value("y", 2).cloned().unwrap_or_else(default_npc_half),
1875 &default_units,
1876 );
1877 let just = normalize_just(&ca, "just", 3);
1878 let hjust = opt_value(&ca, "hjust", 9);
1879 let vjust = opt_value(&ca, "vjust", 10);
1880 let rot = opt_value(&ca, "rot", 4);
1881 let check_overlap = opt_value(&ca, "check.overlap", 11);
1882 let gp = opt_value(&ca, "gp", 6);
1883
1884 let entries = vec![
1885 ("label".to_string(), label),
1886 ("x".to_string(), x),
1887 ("y".to_string(), y),
1888 ("just".to_string(), just),
1889 ("hjust".to_string(), hjust),
1890 ("vjust".to_string(), vjust),
1891 ("rot".to_string(), rot),
1892 ("check.overlap".to_string(), check_overlap),
1893 ("gp".to_string(), gp),
1894 (
1895 "name".to_string(),
1896 RValue::vec(Vector::Character(vec![Some(name)].into())),
1897 ),
1898 ];
1899
1900 make_grob("text", entries, false, vp, context)
1901}
1902
1903#[interpreter_builtin(name = "linesGrob", namespace = "grid")]
1912fn interp_lines_grob(
1913 args: &[RValue],
1914 named: &[(String, RValue)],
1915 context: &BuiltinContext,
1916) -> Result<RValue, RError> {
1917 let ca = CallArgs::new(args, named);
1918 let default_units = ca
1919 .optional_string("default.units", 4)
1920 .unwrap_or_else(|| "npc".to_string());
1921 let vp = opt_value(&ca, "vp", 5);
1922 let name = ca
1923 .optional_string("name", 3)
1924 .unwrap_or_else(|| auto_grob_name("GRID.lines", context));
1925
1926 let x = ensure_unit(
1927 &ca.value("x", 0)
1928 .cloned()
1929 .unwrap_or_else(|| RValue::vec(Vector::Double(vec![Some(0.0), Some(1.0)].into()))),
1930 &default_units,
1931 );
1932 let y = ensure_unit(
1933 &ca.value("y", 1)
1934 .cloned()
1935 .unwrap_or_else(|| RValue::vec(Vector::Double(vec![Some(0.0), Some(1.0)].into()))),
1936 &default_units,
1937 );
1938 let gp = opt_value(&ca, "gp", 2);
1939
1940 let entries = vec![
1941 ("x".to_string(), x),
1942 ("y".to_string(), y),
1943 ("gp".to_string(), gp),
1944 (
1945 "name".to_string(),
1946 RValue::vec(Vector::Character(vec![Some(name)].into())),
1947 ),
1948 ];
1949
1950 make_grob("lines", entries, false, vp, context)
1951}
1952
1953#[interpreter_builtin(name = "pointsGrob", namespace = "grid")]
1964fn interp_points_grob(
1965 args: &[RValue],
1966 named: &[(String, RValue)],
1967 context: &BuiltinContext,
1968) -> Result<RValue, RError> {
1969 let ca = CallArgs::new(args, named);
1970 let default_units = ca
1971 .optional_string("default.units", 5)
1972 .unwrap_or_else(|| "npc".to_string());
1973 let vp = opt_value(&ca, "vp", 6);
1974 let name = ca
1975 .optional_string("name", 4)
1976 .unwrap_or_else(|| auto_grob_name("GRID.points", context));
1977
1978 let x = ensure_unit(
1979 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
1980 &default_units,
1981 );
1982 let y = ensure_unit(
1983 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
1984 &default_units,
1985 );
1986 let pch = opt_value(&ca, "pch", 2);
1987 let size = opt_value(&ca, "size", 3);
1988 let gp = opt_value(&ca, "gp", 7);
1989
1990 let entries = vec![
1991 ("x".to_string(), x),
1992 ("y".to_string(), y),
1993 ("pch".to_string(), pch),
1994 ("size".to_string(), size),
1995 ("gp".to_string(), gp),
1996 (
1997 "name".to_string(),
1998 RValue::vec(Vector::Character(vec![Some(name)].into())),
1999 ),
2000 ];
2001
2002 make_grob("points", entries, false, vp, context)
2003}
2004
2005#[interpreter_builtin(name = "rectGrob", namespace = "grid")]
2016fn interp_rect_grob(
2017 args: &[RValue],
2018 named: &[(String, RValue)],
2019 context: &BuiltinContext,
2020) -> Result<RValue, RError> {
2021 let ca = CallArgs::new(args, named);
2022 let default_units = ca
2023 .optional_string("default.units", 6)
2024 .unwrap_or_else(|| "npc".to_string());
2025 let vp = opt_value(&ca, "vp", 7);
2026 let name = ca
2027 .optional_string("name", 5)
2028 .unwrap_or_else(|| auto_grob_name("GRID.rect", context));
2029
2030 let x = ensure_unit(
2031 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
2032 &default_units,
2033 );
2034 let y = ensure_unit(
2035 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
2036 &default_units,
2037 );
2038 let width = opt_value(&ca, "width", 2);
2039 let height = opt_value(&ca, "height", 3);
2040 let just = normalize_just(&ca, "just", 4);
2041 let hjust = opt_value(&ca, "hjust", 9);
2042 let vjust = opt_value(&ca, "vjust", 10);
2043 let gp = opt_value(&ca, "gp", 8);
2044
2045 let entries = vec![
2046 ("x".to_string(), x),
2047 ("y".to_string(), y),
2048 ("width".to_string(), width),
2049 ("height".to_string(), height),
2050 ("just".to_string(), just),
2051 ("hjust".to_string(), hjust),
2052 ("vjust".to_string(), vjust),
2053 ("gp".to_string(), gp),
2054 (
2055 "name".to_string(),
2056 RValue::vec(Vector::Character(vec![Some(name)].into())),
2057 ),
2058 ];
2059
2060 make_grob("rect", entries, false, vp, context)
2061}
2062
2063#[interpreter_builtin(name = "circleGrob", namespace = "grid")]
2073fn interp_circle_grob(
2074 args: &[RValue],
2075 named: &[(String, RValue)],
2076 context: &BuiltinContext,
2077) -> Result<RValue, RError> {
2078 let ca = CallArgs::new(args, named);
2079 let default_units = ca
2080 .optional_string("default.units", 4)
2081 .unwrap_or_else(|| "npc".to_string());
2082 let vp = opt_value(&ca, "vp", 5);
2083 let name = ca
2084 .optional_string("name", 3)
2085 .unwrap_or_else(|| auto_grob_name("GRID.circle", context));
2086
2087 let x = ensure_unit(
2088 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
2089 &default_units,
2090 );
2091 let y = ensure_unit(
2092 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
2093 &default_units,
2094 );
2095 let r = opt_value(&ca, "r", 2);
2096 let gp = opt_value(&ca, "gp", 6);
2097
2098 let entries = vec![
2099 ("x".to_string(), x),
2100 ("y".to_string(), y),
2101 ("r".to_string(), r),
2102 ("gp".to_string(), gp),
2103 (
2104 "name".to_string(),
2105 RValue::vec(Vector::Character(vec![Some(name)].into())),
2106 ),
2107 ];
2108
2109 make_grob("circle", entries, false, vp, context)
2110}
2111
2112#[interpreter_builtin(name = "polygonGrob", namespace = "grid")]
2122fn interp_polygon_grob(
2123 args: &[RValue],
2124 named: &[(String, RValue)],
2125 context: &BuiltinContext,
2126) -> Result<RValue, RError> {
2127 let ca = CallArgs::new(args, named);
2128 let default_units = ca
2129 .optional_string("default.units", 4)
2130 .unwrap_or_else(|| "npc".to_string());
2131 let vp = opt_value(&ca, "vp", 5);
2132 let name = ca
2133 .optional_string("name", 3)
2134 .unwrap_or_else(|| auto_grob_name("GRID.polygon", context));
2135
2136 let x = ensure_unit(
2137 &ca.value("x", 0).cloned().unwrap_or_else(default_npc_half),
2138 &default_units,
2139 );
2140 let y = ensure_unit(
2141 &ca.value("y", 1).cloned().unwrap_or_else(default_npc_half),
2142 &default_units,
2143 );
2144 let id = opt_value(&ca, "id", 2);
2145 let gp = opt_value(&ca, "gp", 6);
2146
2147 let entries = vec![
2148 ("x".to_string(), x),
2149 ("y".to_string(), y),
2150 ("id".to_string(), id),
2151 ("gp".to_string(), gp),
2152 (
2153 "name".to_string(),
2154 RValue::vec(Vector::Character(vec![Some(name)].into())),
2155 ),
2156 ];
2157
2158 make_grob("polygon", entries, false, vp, context)
2159}
2160
2161#[interpreter_builtin(name = "segmentsGrob", namespace = "grid")]
2171fn interp_segments_grob(
2172 args: &[RValue],
2173 named: &[(String, RValue)],
2174 context: &BuiltinContext,
2175) -> Result<RValue, RError> {
2176 let ca = CallArgs::new(args, named);
2177 let default_units = ca
2178 .optional_string("default.units", 6)
2179 .unwrap_or_else(|| "npc".to_string());
2180 let vp = opt_value(&ca, "vp", 7);
2181 let name = ca
2182 .optional_string("name", 5)
2183 .unwrap_or_else(|| auto_grob_name("GRID.segments", context));
2184
2185 let x0 = ensure_unit(
2186 &ca.value("x0", 0).cloned().unwrap_or_else(default_npc_zero),
2187 &default_units,
2188 );
2189 let y0 = ensure_unit(
2190 &ca.value("y0", 1).cloned().unwrap_or_else(default_npc_zero),
2191 &default_units,
2192 );
2193 let x1 = ensure_unit(
2194 &ca.value("x1", 2).cloned().unwrap_or_else(default_npc_one),
2195 &default_units,
2196 );
2197 let y1 = ensure_unit(
2198 &ca.value("y1", 3).cloned().unwrap_or_else(default_npc_one),
2199 &default_units,
2200 );
2201 let gp = opt_value(&ca, "gp", 4);
2202
2203 let entries = vec![
2204 ("x0".to_string(), x0),
2205 ("y0".to_string(), y0),
2206 ("x1".to_string(), x1),
2207 ("y1".to_string(), y1),
2208 ("gp".to_string(), gp),
2209 (
2210 "name".to_string(),
2211 RValue::vec(Vector::Character(vec![Some(name)].into())),
2212 ),
2213 ];
2214
2215 make_grob("segments", entries, false, vp, context)
2216}
2217
2218#[interpreter_builtin(name = "nullGrob", namespace = "grid")]
2225fn interp_null_grob(
2226 args: &[RValue],
2227 named: &[(String, RValue)],
2228 context: &BuiltinContext,
2229) -> Result<RValue, RError> {
2230 let ca = CallArgs::new(args, named);
2231 let name = ca
2232 .optional_string("name", 0)
2233 .unwrap_or_else(|| auto_grob_name("GRID.null", context));
2234
2235 let entries = vec![(
2236 "name".to_string(),
2237 RValue::vec(Vector::Character(vec![Some(name)].into())),
2238 )];
2239
2240 make_grob("null", entries, false, RValue::Null, context)
2241}
2242
2243#[interpreter_builtin(name = "gTree", namespace = "grid")]
2252fn interp_gtree(
2253 args: &[RValue],
2254 named: &[(String, RValue)],
2255 context: &BuiltinContext,
2256) -> Result<RValue, RError> {
2257 let ca = CallArgs::new(args, named);
2258 let name = ca
2259 .optional_string("name", 1)
2260 .unwrap_or_else(|| auto_grob_name("GRID.gTree", context));
2261 let children = opt_value(&ca, "children", 0);
2262 let gp = opt_value(&ca, "gp", 2);
2263 let vp = opt_value(&ca, "vp", 3);
2264
2265 let entries = vec![
2266 ("children".to_string(), children),
2267 ("gp".to_string(), gp),
2268 (
2269 "name".to_string(),
2270 RValue::vec(Vector::Character(vec![Some(name)].into())),
2271 ),
2272 ];
2273
2274 make_grob("gTree", entries, false, vp, context)
2275}
2276
2277#[interpreter_builtin(name = "gList", namespace = "grid")]
2283fn interp_glist(
2284 args: &[RValue],
2285 _named: &[(String, RValue)],
2286 _context: &BuiltinContext,
2287) -> Result<RValue, RError> {
2288 let entries: Vec<(Option<String>, RValue)> = args.iter().map(|a| (None, a.clone())).collect();
2289 let mut list = RList::new(entries);
2290 list.set_attr(
2291 "class".to_string(),
2292 RValue::vec(Vector::Character(vec![Some("gList".to_string())].into())),
2293 );
2294 Ok(RValue::List(list))
2295}
2296
2297#[interpreter_builtin(name = "grid.layout", namespace = "grid")]
2310fn interp_grid_layout(
2311 args: &[RValue],
2312 named: &[(String, RValue)],
2313 _context: &BuiltinContext,
2314) -> Result<RValue, RError> {
2315 let ca = CallArgs::new(args, named);
2316
2317 let nrow = ca.integer_or("nrow", 0, 1);
2318 let ncol = ca.integer_or("ncol", 1, 1);
2319 let widths = opt_value(&ca, "widths", 2);
2320 let heights = opt_value(&ca, "heights", 3);
2321 let respect = opt_value(&ca, "respect", 4);
2322
2323 let widths = if matches!(widths, RValue::Null) {
2325 let vals: Vec<Option<f64>> = (0..ncol).map(|_| Some(1.0)).collect();
2326 make_grid_object(
2327 vec![
2328 (
2329 "value".to_string(),
2330 RValue::vec(Vector::Double(vals.into())),
2331 ),
2332 (
2333 "units".to_string(),
2334 RValue::vec(Vector::Character(
2335 (0..ncol)
2336 .map(|_| Some("null".to_string()))
2337 .collect::<Vec<_>>()
2338 .into(),
2339 )),
2340 ),
2341 ],
2342 &["unit"],
2343 )
2344 } else {
2345 widths
2346 };
2347
2348 let heights = if matches!(heights, RValue::Null) {
2349 let vals: Vec<Option<f64>> = (0..nrow).map(|_| Some(1.0)).collect();
2350 make_grid_object(
2351 vec![
2352 (
2353 "value".to_string(),
2354 RValue::vec(Vector::Double(vals.into())),
2355 ),
2356 (
2357 "units".to_string(),
2358 RValue::vec(Vector::Character(
2359 (0..nrow)
2360 .map(|_| Some("null".to_string()))
2361 .collect::<Vec<_>>()
2362 .into(),
2363 )),
2364 ),
2365 ],
2366 &["unit"],
2367 )
2368 } else {
2369 heights
2370 };
2371
2372 let entries = vec![
2373 (
2374 "nrow".to_string(),
2375 RValue::vec(Vector::Integer(vec![Some(nrow)].into())),
2376 ),
2377 (
2378 "ncol".to_string(),
2379 RValue::vec(Vector::Integer(vec![Some(ncol)].into())),
2380 ),
2381 ("widths".to_string(), widths),
2382 ("heights".to_string(), heights),
2383 ("respect".to_string(), respect),
2384 ];
2385
2386 Ok(make_grid_object(entries, &["layout"]))
2387}
2388
2389#[interpreter_builtin(name = "grid.show.layout", namespace = "grid", min_args = 1)]
2394fn interp_grid_show_layout(
2395 args: &[RValue],
2396 _named: &[(String, RValue)],
2397 context: &BuiltinContext,
2398) -> Result<RValue, RError> {
2399 let layout = &args[0];
2400
2401 let (nrow, ncol) = if let RValue::List(list) = layout {
2403 let mut nr = 1i64;
2404 let mut nc = 1i64;
2405 for (key, val) in &list.values {
2406 match key.as_deref() {
2407 Some("nrow") => {
2408 if let Some(rv) = val.as_vector() {
2409 nr = rv.as_integer_scalar().unwrap_or(1);
2410 }
2411 }
2412 Some("ncol") => {
2413 if let Some(rv) = val.as_vector() {
2414 nc = rv.as_integer_scalar().unwrap_or(1);
2415 }
2416 }
2417 _ => {}
2418 }
2419 }
2420 (nr.max(1) as usize, nc.max(1) as usize)
2421 } else {
2422 (1, 1)
2423 };
2424
2425 let cell_width = 1.0 / ncol as f64;
2427 let cell_height = 1.0 / nrow as f64;
2428
2429 for row in 0..nrow {
2430 for col in 0..ncol {
2431 let x_center = (col as f64 + 0.5) * cell_width;
2432 let y_center = 1.0 - (row as f64 + 0.5) * cell_height;
2433
2434 let rect_gp = grid::gpar::Gpar {
2436 col: Some([0, 0, 0, 255]),
2437 fill: Some([255, 255, 255, 0]),
2438 lwd: Some(0.5),
2439 ..Default::default()
2440 };
2441 let rect_grob = grid::grob::Grob::Rect {
2442 x: grid::units::Unit::npc(x_center),
2443 y: grid::units::Unit::npc(y_center),
2444 width: grid::units::Unit::npc(cell_width),
2445 height: grid::units::Unit::npc(cell_height),
2446 just: (
2447 grid::viewport::Justification::Centre,
2448 grid::viewport::Justification::Centre,
2449 ),
2450 gp: rect_gp,
2451 };
2452 record_rust_grob(rect_grob, context);
2453
2454 let label = format!("({}, {})", row + 1, col + 1);
2456 let text_gp = grid::gpar::Gpar {
2457 col: Some([100, 100, 100, 255]),
2458 fontsize: Some(8.0),
2459 ..Default::default()
2460 };
2461 let text_grob = grid::grob::Grob::Text {
2462 label: vec![label],
2463 x: grid::units::Unit::npc(x_center),
2464 y: grid::units::Unit::npc(y_center),
2465 just: (
2466 grid::viewport::Justification::Centre,
2467 grid::viewport::Justification::Centre,
2468 ),
2469 rot: 0.0,
2470 gp: text_gp,
2471 };
2472 record_rust_grob(text_grob, context);
2473 }
2474 }
2475
2476 context.interpreter().set_invisible();
2477 Ok(RValue::Null)
2478}
2479
2480#[interpreter_builtin(name = "grid.get", namespace = "grid", min_args = 1)]
2489fn interp_grid_get(
2490 args: &[RValue],
2491 named: &[(String, RValue)],
2492 context: &BuiltinContext,
2493) -> Result<RValue, RError> {
2494 let ca = CallArgs::new(args, named);
2495 let target_name = ca.string("name", 0)?;
2496
2497 let display_list = context.interpreter().grid_display_list.borrow();
2498 for item in display_list.iter() {
2499 if let RValue::List(list) = item {
2500 for (key, val) in &list.values {
2501 if key.as_deref() == Some("name") {
2502 if let RValue::Vector(rv) = val {
2503 if rv.inner.as_character_scalar().as_deref() == Some(target_name.as_str()) {
2504 return Ok(item.clone());
2505 }
2506 }
2507 }
2508 }
2509 }
2510 }
2511
2512 Ok(RValue::Null)
2513}
2514
2515#[interpreter_builtin(name = "grid.edit", namespace = "grid", min_args = 1)]
2521fn interp_grid_edit(
2522 args: &[RValue],
2523 named: &[(String, RValue)],
2524 context: &BuiltinContext,
2525) -> Result<RValue, RError> {
2526 let ca = CallArgs::new(args, named);
2527 let target_name = ca.string("name", 0)?;
2528
2529 let mut display_list = context.interpreter().grid_display_list.borrow_mut();
2530 for item in display_list.iter_mut() {
2531 if let RValue::List(list) = item {
2532 let is_target = list.values.iter().any(|(key, val)| {
2534 key.as_deref() == Some("name")
2535 && matches!(val, RValue::Vector(rv) if rv.inner.as_character_scalar().as_deref() == Some(target_name.as_str()))
2536 });
2537
2538 if is_target {
2539 for (key, val) in named {
2541 if key == "name" {
2542 continue;
2543 }
2544 let mut found = false;
2546 for (entry_key, entry_val) in list.values.iter_mut() {
2547 if entry_key.as_deref() == Some(key.as_str()) {
2548 *entry_val = val.clone();
2549 found = true;
2550 break;
2551 }
2552 }
2553 if !found {
2554 list.values.push((Some(key.clone()), val.clone()));
2555 }
2556 }
2557 break;
2558 }
2559 }
2560 }
2561
2562 context.interpreter().set_invisible();
2563 Ok(RValue::Null)
2564}
2565
2566#[interpreter_builtin(name = "grid.remove", namespace = "grid", min_args = 1)]
2571fn interp_grid_remove(
2572 args: &[RValue],
2573 named: &[(String, RValue)],
2574 context: &BuiltinContext,
2575) -> Result<RValue, RError> {
2576 let ca = CallArgs::new(args, named);
2577 let target_name = ca.string("name", 0)?;
2578
2579 let mut display_list = context.interpreter().grid_display_list.borrow_mut();
2580 display_list.retain(|item| {
2581 if let RValue::List(list) = item {
2582 !list.values.iter().any(|(key, val)| {
2583 key.as_deref() == Some("name")
2584 && matches!(val, RValue::Vector(rv) if rv.inner.as_character_scalar().as_deref() == Some(target_name.as_str()))
2585 })
2586 } else {
2587 true
2588 }
2589 });
2590
2591 context.interpreter().set_invisible();
2592 Ok(RValue::Null)
2593}
2594
2595#[interpreter_builtin(name = "grid.xaxis", namespace = "grid")]
2610fn interp_grid_xaxis(
2611 args: &[RValue],
2612 named: &[(String, RValue)],
2613 context: &BuiltinContext,
2614) -> Result<RValue, RError> {
2615 let ca = CallArgs::new(args, named);
2616 let draw = ca.logical_flag("draw", 6, true);
2617 let vp = opt_value(&ca, "vp", 4);
2618 let name = ca
2619 .optional_string("name", 5)
2620 .unwrap_or_else(|| auto_grob_name("GRID.xaxis", context));
2621
2622 let at = opt_value(&ca, "at", 0);
2623 let label = opt_value(&ca, "label", 1);
2624 let main = ca.logical_flag("main", 2, true);
2625 let gp = opt_value(&ca, "gp", 3);
2626
2627 let entries = vec![
2628 ("at".to_string(), at),
2629 ("label".to_string(), label),
2630 (
2631 "main".to_string(),
2632 RValue::vec(Vector::Logical(vec![Some(main)].into())),
2633 ),
2634 ("gp".to_string(), gp),
2635 (
2636 "name".to_string(),
2637 RValue::vec(Vector::Character(vec![Some(name)].into())),
2638 ),
2639 ];
2640
2641 make_grob("xaxis", entries, draw, vp, context)
2642}
2643
2644#[interpreter_builtin(name = "grid.yaxis", namespace = "grid")]
2655fn interp_grid_yaxis(
2656 args: &[RValue],
2657 named: &[(String, RValue)],
2658 context: &BuiltinContext,
2659) -> Result<RValue, RError> {
2660 let ca = CallArgs::new(args, named);
2661 let draw = ca.logical_flag("draw", 6, true);
2662 let vp = opt_value(&ca, "vp", 4);
2663 let name = ca
2664 .optional_string("name", 5)
2665 .unwrap_or_else(|| auto_grob_name("GRID.yaxis", context));
2666
2667 let at = opt_value(&ca, "at", 0);
2668 let label = opt_value(&ca, "label", 1);
2669 let main = ca.logical_flag("main", 2, true);
2670 let gp = opt_value(&ca, "gp", 3);
2671
2672 let entries = vec![
2673 ("at".to_string(), at),
2674 ("label".to_string(), label),
2675 (
2676 "main".to_string(),
2677 RValue::vec(Vector::Logical(vec![Some(main)].into())),
2678 ),
2679 ("gp".to_string(), gp),
2680 (
2681 "name".to_string(),
2682 RValue::vec(Vector::Character(vec![Some(name)].into())),
2683 ),
2684 ];
2685
2686 make_grob("yaxis", entries, draw, vp, context)
2687}
2688
2689use crate::interpreter::graphics::plot_data::{PlotItem, PlotState};
2694
2695const DEVICE_WIDTH_CM: f64 = 17.78;
2697const DEVICE_HEIGHT_CM: f64 = 17.78;
2698
2699fn grid_to_plot_state(
2705 display_list: &grid::display::DisplayList,
2706 grob_store: &grid::grob::GrobStore,
2707) -> PlotState {
2708 let mut renderer = PlotStateRenderer::new(DEVICE_WIDTH_CM, DEVICE_HEIGHT_CM);
2709 grid::render::replay(display_list, grob_store, &mut renderer);
2710 renderer.into_plot_state()
2711}
2712
2713struct PlotStateRenderer {
2719 items: Vec<PlotItem>,
2720 device_width_cm: f64,
2721 device_height_cm: f64,
2722}
2723
2724impl PlotStateRenderer {
2725 fn new(width_cm: f64, height_cm: f64) -> Self {
2726 PlotStateRenderer {
2727 items: Vec::new(),
2728 device_width_cm: width_cm,
2729 device_height_cm: height_cm,
2730 }
2731 }
2732
2733 fn into_plot_state(self) -> PlotState {
2734 PlotState {
2735 items: self.items,
2736 title: None,
2737 x_label: None,
2738 y_label: None,
2739 x_lim: Some((0.0, self.device_width_cm)),
2740 y_lim: Some((0.0, self.device_height_cm)),
2741 show_legend: false,
2742 }
2743 }
2744
2745 fn apply_alpha(rgba: [u8; 4], gp: &grid::gpar::Gpar) -> [u8; 4] {
2747 let alpha = gp.effective_alpha();
2748 if (alpha - 1.0).abs() < f64::EPSILON {
2749 rgba
2750 } else {
2751 let a = (f64::from(rgba[3]) * alpha) as u8;
2752 [rgba[0], rgba[1], rgba[2], a]
2753 }
2754 }
2755}
2756
2757impl grid::render::GridRenderer for PlotStateRenderer {
2758 fn line(&mut self, x0_cm: f64, y0_cm: f64, x1_cm: f64, y1_cm: f64, gp: &grid::gpar::Gpar) {
2759 let col = Self::apply_alpha(gp.effective_col(), gp);
2760 let width = gp.effective_lwd() as f32;
2761 self.items.push(PlotItem::Line {
2762 x: vec![x0_cm, x1_cm],
2763 y: vec![y0_cm, y1_cm],
2764 color: col,
2765 width,
2766 label: None,
2767 });
2768 }
2769
2770 fn polyline(&mut self, x_cm: &[f64], y_cm: &[f64], gp: &grid::gpar::Gpar) {
2771 let col = Self::apply_alpha(gp.effective_col(), gp);
2772 let width = gp.effective_lwd() as f32;
2773 self.items.push(PlotItem::Line {
2774 x: x_cm.to_vec(),
2775 y: y_cm.to_vec(),
2776 color: col,
2777 width,
2778 label: None,
2779 });
2780 }
2781
2782 fn rect(&mut self, x_cm: f64, y_cm: f64, w_cm: f64, h_cm: f64, gp: &grid::gpar::Gpar) {
2783 let fill = gp.effective_fill();
2784 let col = Self::apply_alpha(gp.effective_col(), gp);
2785 let width = gp.effective_lwd() as f32;
2786
2787 if fill[3] > 0 {
2789 let fill_color = Self::apply_alpha(fill, gp);
2791 self.items.push(PlotItem::Line {
2792 x: vec![x_cm, x_cm + w_cm, x_cm + w_cm, x_cm, x_cm],
2793 y: vec![y_cm, y_cm, y_cm + h_cm, y_cm + h_cm, y_cm],
2794 color: fill_color,
2795 width: 0.5,
2796 label: None,
2797 });
2798 }
2799
2800 if col[3] > 0 {
2802 self.items.push(PlotItem::Line {
2803 x: vec![x_cm, x_cm + w_cm, x_cm + w_cm, x_cm, x_cm],
2804 y: vec![y_cm, y_cm, y_cm + h_cm, y_cm + h_cm, y_cm],
2805 color: col,
2806 width,
2807 label: None,
2808 });
2809 }
2810 }
2811
2812 fn circle(&mut self, x_cm: f64, y_cm: f64, r_cm: f64, gp: &grid::gpar::Gpar) {
2813 let col = Self::apply_alpha(gp.effective_col(), gp);
2814 let n = 24;
2816 let mut xs = Vec::with_capacity(n + 1);
2817 let mut ys = Vec::with_capacity(n + 1);
2818 for i in 0..=n {
2819 let theta = 2.0 * std::f64::consts::PI * (i as f64 / n as f64);
2820 xs.push(x_cm + r_cm * theta.cos());
2821 ys.push(y_cm + r_cm * theta.sin());
2822 }
2823 self.items.push(PlotItem::Line {
2824 x: xs,
2825 y: ys,
2826 color: col,
2827 width: gp.effective_lwd() as f32,
2828 label: None,
2829 });
2830 }
2831
2832 fn polygon(&mut self, x_cm: &[f64], y_cm: &[f64], gp: &grid::gpar::Gpar) {
2833 let col = Self::apply_alpha(gp.effective_col(), gp);
2834 let mut xs = x_cm.to_vec();
2835 let mut ys = y_cm.to_vec();
2836 if let (Some(&first_x), Some(&first_y)) = (x_cm.first(), y_cm.first()) {
2838 xs.push(first_x);
2839 ys.push(first_y);
2840 }
2841 self.items.push(PlotItem::Line {
2842 x: xs,
2843 y: ys,
2844 color: col,
2845 width: gp.effective_lwd() as f32,
2846 label: None,
2847 });
2848 }
2849
2850 fn text(&mut self, x_cm: f64, y_cm: f64, label: &str, _rot: f64, gp: &grid::gpar::Gpar) {
2851 let col = Self::apply_alpha(gp.effective_col(), gp);
2852 self.items.push(PlotItem::Text {
2853 x: x_cm,
2854 y: y_cm,
2855 text: label.to_string(),
2856 color: col,
2857 });
2858 }
2859
2860 fn point(&mut self, x_cm: f64, y_cm: f64, pch: u8, size_cm: f64, gp: &grid::gpar::Gpar) {
2861 let col = Self::apply_alpha(gp.effective_col(), gp);
2862 self.items.push(PlotItem::Points {
2863 x: vec![x_cm],
2864 y: vec![y_cm],
2865 color: col,
2866 size: size_cm as f32 * 4.0, shape: pch,
2868 label: None,
2869 });
2870 }
2871
2872 fn clip(&mut self, _x_cm: f64, _y_cm: f64, _w_cm: f64, _h_cm: f64) {
2873 }
2875
2876 fn unclip(&mut self) {
2877 }
2879
2880 fn device_size_cm(&self) -> (f64, f64) {
2881 (self.device_width_cm, self.device_height_cm)
2882 }
2883}
2884
2885fn flush_grid_to_plot(ctx: &BuiltinContext) {
2888 let dl = ctx.interpreter().grid_rust_display_list.borrow();
2889 if dl.is_empty() {
2890 return;
2891 }
2892 let store = ctx.interpreter().grid_grob_store.borrow();
2893 let plot_state = grid_to_plot_state(&dl, &store);
2894 drop(dl);
2895 drop(store);
2896
2897 *ctx.interpreter().current_plot.borrow_mut() = Some(plot_state);
2899}
2900
2901pub fn flush_grid(interp: &crate::interpreter::Interpreter) {
2906 let dl = interp.grid_rust_display_list.borrow();
2907 if dl.is_empty() {
2908 return;
2909 }
2910 let store = interp.grid_grob_store.borrow();
2911 let plot_state = grid_to_plot_state(&dl, &store);
2912 drop(dl);
2913 drop(store);
2914
2915 if interp.current_plot.borrow().is_some() {
2918 return;
2919 }
2920
2921 *interp.current_plot.borrow_mut() = Some(plot_state);
2922 }
2924
2925