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}