Skip to main content

rpic_core/
ir.rs

1//! Intermediate representation: the placed-primitive tree produced by the
2//! evaluator and consumed by the render backends. All coordinates are absolute,
3//! in pic units (inches), y pointing up.
4
5use crate::geom::{Bbox, Point};
6
7/// A fully evaluated drawing.
8#[derive(Debug, Clone, PartialEq)]
9pub struct Drawing {
10    pub shapes: Vec<Shape>,
11    pub bbox: Bbox,
12    /// Global `linethick` in points after picture-wide sizing, used only for
13    /// dpic-style backend prelude padding. Per-shape strokes keep their own
14    /// unscaled point thickness.
15    pub prelude_thick: f64,
16    /// Extra canvas whitespace in inches. This is an rpic extension inspired by
17    /// Pikchr: it affects native backend framing only, not pic geometry.
18    pub canvas_margin: CanvasMargin,
19    pub anims: Vec<Anim>,
20    /// Lines emitted by pic `print` statements, without trailing newlines.
21    pub diagnostics: Vec<String>,
22}
23
24/// Extra whitespace around the rendered canvas, in internal inches.
25#[derive(Debug, Clone, Copy, PartialEq, Default)]
26pub struct CanvasMargin {
27    pub top: f64,
28    pub right: f64,
29    pub bottom: f64,
30    pub left: f64,
31}
32
33impl CanvasMargin {
34    pub fn horizontal(self) -> f64 {
35        self.left + self.right
36    }
37
38    pub fn vertical(self) -> f64 {
39        self.top + self.bottom
40    }
41
42    pub fn scale_by(&mut self, factor: f64) {
43        self.top *= factor;
44        self.right *= factor;
45        self.bottom *= factor;
46        self.left *= factor;
47    }
48}
49
50/// A resolved animation entry. `shape` indexes into [`Drawing::shapes`]; the
51/// SVG backend gives that shape the id `s{shape}` so the player can target it.
52#[derive(Debug, Clone, PartialEq)]
53pub struct Anim {
54    pub shape: usize,
55    pub effect: String,
56    /// Absolute start time in seconds.
57    pub start: f64,
58    pub duration: f64,
59}
60
61/// Line dash style.
62#[derive(Debug, Clone, Copy, PartialEq, Default)]
63pub enum Dash {
64    #[default]
65    Solid,
66    /// Dash/gap base length in inches.
67    Dashed(f64),
68    /// Explicit dot spacing in inches; `None` means dpic's stroke-relative default.
69    Dotted(Option<f64>),
70}
71
72/// Fill specification.
73#[derive(Debug, Clone, PartialEq)]
74pub enum Fill {
75    /// Gray level, pic convention: 0 = black, 1 = white.
76    Gray(f64),
77    /// A named/explicit color.
78    Color(String),
79}
80
81/// Visual style shared by all shapes.
82#[derive(Debug, Clone, PartialEq)]
83pub struct Style {
84    /// Stroke color (CSS), or `None` to use the default.
85    pub stroke: Option<String>,
86    pub fill: Option<Fill>,
87    /// Whether open paths/splines/arcs should emit a filled area. `color` on an
88    /// open object only changes the stroke; `fill` and `shaded` fill.
89    pub fill_open: bool,
90    pub dash: Dash,
91    /// Stroke thickness in points; `None` = backend default.
92    pub thick: Option<f64>,
93    /// Invisible (used by `move` and `invis`): geometry is still available for
94    /// placement and anchors, but is not drawn.
95    pub invis: bool,
96    /// Invisible geometry that still contributes to output bounds. Dpic uses
97    /// this for `move`; explicit `invis` helpers remain bounds-neutral.
98    pub invis_bounds: bool,
99    /// Arrowhead dimensions in inches (`arrowht`/`arrowwid`), used when this
100    /// shape carries arrowheads.
101    pub arrow_ht: f64,
102    pub arrow_wid: f64,
103    /// Filled (solid triangle, `arrowhead=2`) vs open (two strokes,
104    /// `arrowhead=0`).
105    pub arrow_filled: bool,
106}
107
108impl Default for Style {
109    fn default() -> Self {
110        Style {
111            stroke: None,
112            fill: None,
113            fill_open: false,
114            dash: Dash::Solid,
115            thick: None,
116            invis: false,
117            invis_bounds: false,
118            arrow_ht: 0.1,
119            arrow_wid: 0.05,
120            arrow_filled: true,
121        }
122    }
123}
124
125/// Which ends of a path carry an arrowhead.
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
127pub enum Arrowheads {
128    #[default]
129    None,
130    Start,
131    End,
132    Both,
133}
134
135/// A line of attached text with placement hints.
136#[derive(Debug, Clone, PartialEq)]
137pub struct TextLine {
138    pub s: String,
139    /// horizontal: -1 = ljust, 0 = center, +1 = rjust.
140    pub halign: i8,
141    /// vertical: +1 = above, 0 = center, -1 = below.
142    pub valign: i8,
143    /// Extra text-position offset in inches (`textoffset`).
144    pub text_offset: f64,
145}
146
147/// A placed drawing primitive.
148#[derive(Debug, Clone, PartialEq)]
149pub enum Shape {
150    Box {
151        c: Point,
152        w: f64,
153        h: f64,
154        rad: f64,
155        style: Style,
156        text: Vec<TextLine>,
157    },
158    Circle {
159        c: Point,
160        r: f64,
161        style: Style,
162        text: Vec<TextLine>,
163    },
164    Ellipse {
165        c: Point,
166        w: f64,
167        h: f64,
168        style: Style,
169        text: Vec<TextLine>,
170    },
171    /// Straight polyline (line / arrow / move).
172    Path {
173        pts: Vec<Point>,
174        arrows: Arrowheads,
175        style: Style,
176        text: Vec<TextLine>,
177    },
178    Spline {
179        pts: Vec<Point>,
180        /// dpic spline tension. `None` = the classic pic quadratic B-spline
181        /// (straight first/last half-segments, tangent at segment midpoints);
182        /// `Some(t)` = dpic's tensioned cubic spline through `t`.
183        tension: Option<f64>,
184        arrows: Arrowheads,
185        style: Style,
186        text: Vec<TextLine>,
187    },
188    Arc {
189        c: Point,
190        r: f64,
191        /// start and end angles in radians.
192        a0: f64,
193        a1: f64,
194        cw: bool,
195        arrows: Arrowheads,
196        style: Style,
197        text: Vec<TextLine>,
198    },
199    Text {
200        at: Point,
201        text: Vec<TextLine>,
202        bbox: Bbox,
203        w: f64,
204        h: f64,
205        standalone: bool,
206    },
207}