1use plotters_backend::{
2 BackendColor, BackendCoord, BackendStyle, BackendTextStyle, DrawingBackend, DrawingErrorKind,
3};
4use solstice_2d::{Draw, Stroke};
5
6pub struct SolsticeBackend {
7 ctx: solstice_2d::solstice::Context,
8 gfx: solstice_2d::Graphics,
9 font_id: solstice_2d::FontId,
10 draw_list: solstice_2d::DrawList<'static>,
11}
12
13impl SolsticeBackend {
14 #[cfg(target_arch = "wasm32")]
15 pub fn with_webgl1(
16 webgl1: web_sys::WebGlRenderingContext,
17 font_data: solstice_2d::text::FontVec,
18 width: f32,
19 height: f32,
20 line_buffer_capacity: usize,
21 mesh_buffer_capacity: usize,
22 ) -> Result<Self, solstice_2d::GraphicsError> {
23 let ctx = solstice_2d::solstice::glow::Context::from_webgl1_context(webgl1);
24 let mut ctx = solstice_2d::solstice::Context::new(ctx);
25 ctx.set_viewport(0, 0, width as _, height as _);
26 let mut gfx = solstice_2d::Graphics::with_config(
27 &mut ctx,
28 &solstice_2d::Config {
29 width,
30 height,
31 line_capacity: line_buffer_capacity,
32 mesh_capacity: mesh_buffer_capacity,
33 },
34 )?;
35 let font_id = gfx.add_font(font_data);
36 Ok(Self {
37 ctx,
38 gfx,
39 font_id,
40 draw_list: Default::default(),
41 })
42 }
43
44 pub fn resize(&mut self, width: f32, height: f32) {
45 self.ctx.set_viewport(0, 0, width as _, height as _);
46 self.gfx.set_width_height(width, height);
47 }
48}
49
50fn color_into(color: BackendColor) -> solstice_2d::Color {
51 let (r, g, b) = color.rgb;
52 solstice_2d::Color {
53 red: r as f32 / 255.,
54 green: g as f32 / 255.,
55 blue: b as f32 / 255.,
56 alpha: color.alpha as _,
57 }
58}
59
60impl DrawingBackend for SolsticeBackend {
61 type ErrorType = solstice_2d::GraphicsError;
62
63 fn get_size(&self) -> (u32, u32) {
64 let vw = self.ctx.viewport();
65 let (w, h) = vw.dimensions();
66 (w as _, h as _)
67 }
68
69 fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
70 Ok(())
71 }
72
73 fn present(&mut self) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
74 self.gfx.process(&mut self.ctx, &self.draw_list);
75 self.draw_list = solstice_2d::DrawList::default();
76 log::trace!("Presented.");
77 Ok(())
78 }
79
80 fn draw_pixel(
81 &mut self,
82 point: BackendCoord,
83 color: BackendColor,
84 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
85 let size = self.get_size();
86 if point.0 < 0 || point.1 < 0 || point.0 as u32 >= size.0 || point.1 as u32 >= size.1 {
87 return Ok(());
88 }
89
90 let (x, y) = point;
91 self.draw_list.draw_with_color(
92 solstice_2d::Rectangle {
93 x: x as _,
94 y: y as _,
95 width: 1.0,
96 height: 1.0,
97 },
98 color_into(color),
99 );
100 Ok(())
101 }
102
103 fn draw_line<S: BackendStyle>(
104 &mut self,
105 (x1, y1): BackendCoord,
106 (x2, y2): BackendCoord,
107 style: &S,
108 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
109 let width = style.stroke_width() as f32;
110 let color = color_into(style.color()).into();
111 self.draw_list.line_2d(vec![
112 solstice_2d::LineVertex {
113 position: [x1 as _, y1 as _, 0.],
114 width,
115 color,
116 },
117 solstice_2d::LineVertex {
118 position: [x2 as _, y2 as _, 0.],
119 width,
120 color,
121 },
122 ]);
123 Ok(())
124 }
125
126 fn draw_rect<S: BackendStyle>(
127 &mut self,
128 (x1, y1): BackendCoord,
129 (x2, y2): BackendCoord,
130 style: &S,
131 fill: bool,
132 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
133 let geometry = solstice_2d::Rectangle {
134 x: x1 as f32,
135 y: y1 as f32,
136 width: x2 as f32 - x1 as f32,
137 height: y2 as f32 - y1 as f32,
138 };
139 let color = color_into(style.color());
140 self.draw_list.set_line_width(style.stroke_width() as _);
141 match fill {
142 true => self.draw_list.draw_with_color(geometry, color),
143 false => self.draw_list.stroke_with_color(geometry, color),
144 }
145 Ok(())
146 }
147
148 fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
149 &mut self,
150 path: I,
151 style: &S,
152 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
153 let width = style.stroke_width() as f32;
154 let color = color_into(style.color()).into();
155 self.draw_list.line_2d(
156 path.into_iter()
157 .map(|(x, y)| solstice_2d::LineVertex {
158 position: [x as f32, y as f32, 0.],
159 width,
160 color,
161 })
162 .collect::<Vec<_>>(),
163 );
164 Ok(())
165 }
166
167 fn draw_circle<S: BackendStyle>(
168 &mut self,
169 (x, y): BackendCoord,
170 radius: u32,
171 style: &S,
172 fill: bool,
173 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
174 let geometry = solstice_2d::Circle {
175 x: x as f32,
176 y: y as f32,
177 radius: radius as f32,
178 segments: radius.max(10),
179 };
180 let color = color_into(style.color());
181 self.draw_list.set_line_width(style.stroke_width() as _);
182 match fill {
183 true => self.draw_list.draw_with_color(geometry, color),
184 false => self.draw_list.stroke_with_color(geometry, color),
185 }
186 Ok(())
187 }
188
189 fn draw_text<TStyle: BackendTextStyle>(
190 &mut self,
191 text: &str,
192 style: &TStyle,
193 (x, y): BackendCoord,
194 ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
195 let color = style.color();
196 if color.alpha == 0.0 {
197 return Ok(());
198 }
199
200 let layout = style
201 .layout_box(text)
202 .map_err(|e| DrawingErrorKind::FontError(Box::new(e)))?;
203 let ((min_x, min_y), (max_x, max_y)) = layout;
204 let width = (max_x - min_x) as i32;
205 let height = (max_y - min_y) as i32;
206 let dx = match style.anchor().h_pos {
207 plotters_backend::text_anchor::HPos::Left => 0,
208 plotters_backend::text_anchor::HPos::Right => -width,
209 plotters_backend::text_anchor::HPos::Center => -width / 2,
210 };
211 let dy = match style.anchor().v_pos {
212 plotters_backend::text_anchor::VPos::Top => 0,
213 plotters_backend::text_anchor::VPos::Center => -height / 2,
214 plotters_backend::text_anchor::VPos::Bottom => -height,
215 };
216 let trans = style.transform();
217 let (x, y) = trans.transform(x + dx - min_x, y + dy - min_y);
218 let scale = style.size() as f32;
219 let bounds = solstice_2d::Rectangle {
220 x: x as f32,
221 y: y as f32,
222 width: self.ctx.viewport().width() as f32,
223 height: self.ctx.viewport().height() as f32,
224 };
225 let text = text.to_owned();
226 self.draw_list.set_color(color_into(style.color()));
227 self.draw_list.print(text, self.font_id, scale, bounds);
228 self.draw_list.set_color([1., 1., 1., 1.]);
229 Ok(())
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 #[test]
236 fn it_works() {
237 let result = 2 + 2;
238 assert_eq!(result, 4);
239 }
240}