Skip to main content

pineal_render/
recorder.rs

1//! `PlanRecorder` — un [`Canvas`] que graba cada llamada como `RenderCmd`
2//! en un [`RenderPlan`], en vez de dibujar.
3//!
4//! Es el puente entre los painters (que hablan contra `Canvas`) y los
5//! backends diferidos: `pineal-export` consume el plan grabado y emite
6//! SVG; los tests de snapshot comparan planes.
7
8use crate::{Canvas, Color, Point, Rect, RenderCmd, RenderPlan, StrokeStyle};
9
10/// Canvas que materializa todo lo dibujado en un `RenderPlan`.
11#[derive(Debug, Default)]
12pub struct PlanRecorder {
13    plan: RenderPlan,
14}
15
16impl PlanRecorder {
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    /// Consume el recorder y devuelve el plan acumulado.
22    pub fn into_plan(self) -> RenderPlan {
23        self.plan
24    }
25
26    /// Acceso de sólo-lectura al plan en construcción.
27    pub fn plan(&self) -> &RenderPlan {
28        &self.plan
29    }
30}
31
32impl Canvas for PlanRecorder {
33    fn push_clip(&mut self, rect: Rect) {
34        self.plan.push(RenderCmd::PushClip(rect));
35    }
36
37    fn pop_clip(&mut self) {
38        self.plan.push(RenderCmd::PopClip);
39    }
40
41    fn fill_rect(&mut self, rect: Rect, color: Color) {
42        self.plan.push(RenderCmd::FillRect { rect, color });
43    }
44
45    fn stroke_rect(&mut self, rect: Rect, stroke: StrokeStyle) {
46        self.plan.push(RenderCmd::StrokeRect { rect, stroke });
47    }
48
49    fn stroke_line(&mut self, a: Point, b: Point, stroke: StrokeStyle) {
50        self.plan.push(RenderCmd::StrokeLine { a, b, stroke });
51    }
52
53    fn stroke_polyline(&mut self, coords: &[f32], stroke: StrokeStyle) {
54        self.plan.push(RenderCmd::StrokePolyline {
55            coords: coords.to_vec(),
56            stroke,
57        });
58    }
59
60    fn fill_triangle_strip(&mut self, coords: &[f32], colors: &[Color]) {
61        self.plan.push(RenderCmd::FillTriangleStrip {
62            coords: coords.to_vec(),
63            colors: colors.to_vec(),
64        });
65    }
66
67    fn draw_text(&mut self, p: Point, text: &str, color: Color, size_px: f32) {
68        self.plan.push(RenderCmd::DrawText {
69            p,
70            text: text.to_string(),
71            color,
72            size_px,
73        });
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn records_calls_in_order() {
83        let mut rec = PlanRecorder::new();
84        rec.fill_rect(Rect::new(0.0, 0.0, 10.0, 10.0), Color::WHITE);
85        rec.stroke_line(
86            Point::new(0.0, 0.0),
87            Point::new(10.0, 10.0),
88            StrokeStyle::new(1.0, Color::BLACK),
89        );
90        let plan = rec.into_plan();
91        assert_eq!(plan.cmds.len(), 2);
92        assert!(matches!(plan.cmds[0], RenderCmd::FillRect { .. }));
93        assert!(matches!(plan.cmds[1], RenderCmd::StrokeLine { .. }));
94    }
95}