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