plotters_piston/
backend.rs

1use piston_window::context::Context;
2use piston_window::ellipse::circle;
3use piston_window::{circle_arc, ellipse, line, rectangle, Event, Loop};
4use piston_window::{G2d, PistonWindow};
5
6use plotters_backend::{
7    BackendColor, BackendCoord, BackendStyle, DrawingBackend, DrawingErrorKind,
8};
9
10#[derive(Debug)]
11pub struct DummyBackendError;
12
13impl std::fmt::Display for DummyBackendError {
14    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
15        write!(fmt, "{:?}", self)
16    }
17}
18
19impl std::error::Error for DummyBackendError {}
20
21pub struct PistonBackend<'a, 'b> {
22    size: (u32, u32),
23    scale: f64,
24    context: Context,
25    graphics: &'b mut G2d<'a>,
26}
27
28fn make_piston_rgba(color: &BackendColor) -> [f32; 4] {
29    let (r, g, b) = color.rgb;
30    let a = color.alpha;
31
32    [
33        r as f32 / 255.0,
34        g as f32 / 255.0,
35        b as f32 / 255.0,
36        a as f32,
37    ]
38}
39fn make_point_pair(a: BackendCoord, b: BackendCoord, scale: f64) -> [f64; 4] {
40    [
41        a.0 as f64 * scale,
42        a.1 as f64 * scale,
43        b.0 as f64 * scale,
44        b.1 as f64 * scale,
45    ]
46}
47
48impl<'a, 'b> PistonBackend<'a, 'b> {
49    pub fn new(size: (u32, u32), scale: f64, context: Context, graphics: &'b mut G2d<'a>) -> Self {
50        Self {
51            size,
52            context,
53            graphics,
54            scale,
55        }
56    }
57}
58
59impl<'a, 'b> DrawingBackend for PistonBackend<'a, 'b> {
60    type ErrorType = DummyBackendError;
61
62    fn get_size(&self) -> (u32, u32) {
63        self.size
64    }
65
66    fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<DummyBackendError>> {
67        Ok(())
68    }
69
70    fn present(&mut self) -> Result<(), DrawingErrorKind<DummyBackendError>> {
71        Ok(())
72    }
73
74    fn draw_pixel(
75        &mut self,
76        point: BackendCoord,
77        color: BackendColor,
78    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
79        piston_window::rectangle(
80            make_piston_rgba(&color),
81            make_point_pair(point, (1, 1), self.scale),
82            self.context.transform,
83            self.graphics,
84        );
85        Ok(())
86    }
87
88    fn draw_line<S: BackendStyle>(
89        &mut self,
90        from: BackendCoord,
91        to: BackendCoord,
92        style: &S,
93    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
94        line(
95            make_piston_rgba(&style.color()),
96            self.scale,
97            make_point_pair(from, to, self.scale),
98            self.context.transform,
99            self.graphics,
100        );
101        Ok(())
102    }
103
104    fn draw_rect<S: BackendStyle>(
105        &mut self,
106        upper_left: BackendCoord,
107        bottom_right: BackendCoord,
108        style: &S,
109        fill: bool,
110    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
111        if fill {
112            rectangle(
113                make_piston_rgba(&style.color()),
114                make_point_pair(
115                    upper_left,
116                    (bottom_right.0 - upper_left.0, bottom_right.1 - upper_left.1),
117                    self.scale,
118                ),
119                self.context.transform,
120                self.graphics,
121            );
122        } else {
123            let color = make_piston_rgba(&style.color());
124            let [x0, y0, x1, y1] = make_point_pair(upper_left, bottom_right, self.scale);
125            line(
126                color,
127                self.scale,
128                [x0, y0, x0, y1],
129                self.context.transform,
130                self.graphics,
131            );
132            line(
133                color,
134                self.scale,
135                [x0, y1, x1, y1],
136                self.context.transform,
137                self.graphics,
138            );
139            line(
140                color,
141                self.scale,
142                [x1, y1, x1, y0],
143                self.context.transform,
144                self.graphics,
145            );
146            line(
147                color,
148                self.scale,
149                [x1, y0, x0, y0],
150                self.context.transform,
151                self.graphics,
152            );
153        }
154        Ok(())
155    }
156
157    fn draw_circle<S: BackendStyle>(
158        &mut self,
159        center: BackendCoord,
160        radius: u32,
161        style: &S,
162        fill: bool,
163    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
164        let rect = circle(center.0 as f64, center.1 as f64, radius as f64);
165        if fill {
166            ellipse(
167                make_piston_rgba(&style.color()),
168                rect,
169                self.context.transform,
170                self.graphics,
171            );
172        } else {
173            circle_arc(
174                make_piston_rgba(&style.color()),
175                self.scale,
176                std::f64::consts::PI,
177                0.0,
178                rect,
179                self.context.transform,
180                self.graphics,
181            );
182            circle_arc(
183                make_piston_rgba(&style.color()),
184                self.scale,
185                0.0,
186                std::f64::consts::PI,
187                rect,
188                self.context.transform,
189                self.graphics,
190            );
191        }
192        Ok(())
193    }
194}
195
196#[allow(clippy::single_match)]
197pub fn draw_piston_window<F: FnOnce(PistonBackend) -> Result<(), Box<dyn std::error::Error>>>(
198    window: &mut PistonWindow,
199    draw: F,
200) -> Option<Event> {
201    if let Some(event) = window.next() {
202        window.draw_2d(&event, |c, g, _| match event {
203            Event::Loop(Loop::Render(arg)) => {
204                draw(PistonBackend::new(
205                    (arg.draw_size[0], arg.draw_size[1]),
206                    arg.window_size[0] / arg.draw_size[0] as f64,
207                    c,
208                    g,
209                ))
210                .ok();
211            }
212            _ => {}
213        });
214        return Some(event);
215    }
216    None
217}