1use std::collections::HashMap;
2
3use anchor2d::{Anchor2D, HorizontalAnchor, VerticalAnchorContext, VerticalAnchorValue};
4use macroquad::prelude::*;
5use palette::Srgba;
6
7use crate::Renderer;
8
9fn srgba_to_color(srgba: Srgba) -> Color {
10 Color {
11 r: srgba.red,
12 g: srgba.green,
13 b: srgba.blue,
14 a: srgba.alpha,
15 }
16}
17
18#[derive(Debug, Default, Clone)]
19pub struct MacroquadRenderer {
20 font: Option<Font>,
21 images: HashMap<String, Texture2D>,
22}
23
24impl MacroquadRenderer {
25 pub fn new(font: Option<Font>) -> Self {
26 Self {
27 font,
28 images: HashMap::default(),
29 }
30 }
31
32 pub fn get_font(&self) -> Option<&Font> {
33 self.font.as_ref()
34 }
35
36 pub fn set_font(&mut self, font: Option<Font>) {
37 self.font = font;
38 }
39
40 pub fn register_image(&mut self, image_name: String, image: Texture2D) {
41 self.images.insert(image_name, image);
42 }
43}
44
45impl Renderer for MacroquadRenderer {
46 fn render_point(&mut self, position: ::glam::DVec2, color: Srgba) {
47 draw_rectangle(
48 position.x as f32,
49 position.y as f32,
50 1.0,
51 1.0,
52 srgba_to_color(color),
53 );
54 }
55
56 fn render_line(
57 &mut self,
58 start: ::glam::DVec2,
59 end: ::glam::DVec2,
60 thickness: f64,
61 color: Srgba,
62 ) {
63 draw_line(
64 start.x as f32,
65 start.y as f32,
66 end.x as f32,
67 end.y as f32,
68 thickness as f32,
69 srgba_to_color(color),
70 );
71 }
72
73 fn render_circle(&mut self, position: ::glam::DVec2, radius: f64, color: Srgba) {
74 draw_circle(
75 position.x as f32,
76 position.y as f32,
77 radius as f32,
78 srgba_to_color(color),
79 );
80 }
81
82 fn render_circle_lines(
83 &mut self,
84 position: ::glam::DVec2,
85 radius: f64,
86 thickness: f64,
87 color: Srgba,
88 ) {
89 draw_circle_lines(
90 position.x as f32,
91 position.y as f32,
92 radius as f32,
93 thickness as f32,
94 srgba_to_color(color),
95 );
96 }
97
98 fn render_arc(
99 &mut self,
100 position: ::glam::DVec2,
101 radius: f64,
102 rotation: f64,
103 arc: f64,
104 color: Srgba,
105 ) {
106 draw_arc(
108 position.x as f32,
109 position.y as f32,
110 64,
111 radius as f32,
112 rotation.to_degrees() as f32,
113 1.0,
114 arc.to_degrees() as f32,
115 srgba_to_color(color),
116 );
117 }
118
119 fn render_arc_lines(
120 &mut self,
121 position: ::glam::DVec2,
122 radius: f64,
123 rotation: f64,
124 arc: f64,
125 thickness: f64,
126 color: Srgba,
127 ) {
128 draw_arc(
129 position.x as f32,
130 position.y as f32,
131 64,
132 radius as f32,
133 rotation.to_degrees() as f32,
134 thickness as f32,
135 arc.to_degrees() as f32,
136 srgba_to_color(color),
137 );
138 }
139
140 fn render_text(
141 &mut self,
142 text: &str,
143 position: ::glam::DVec2,
144 anchor: Anchor2D,
145 size: f64,
146 color: Srgba,
147 ) {
148 let measurement = measure_text(text, self.font.as_ref(), size as u16, 1.0);
149
150 let x = match anchor.get_horizontal() {
151 HorizontalAnchor::Left => position.x,
152 HorizontalAnchor::Center => position.x - measurement.width as f64 / 2.0,
153 HorizontalAnchor::Right => position.x - measurement.width as f64,
154 };
155
156 let vertical_anchor = anchor.get_vertical();
157
158 let y = match (vertical_anchor.get_context(), vertical_anchor.get_value()) {
159 (VerticalAnchorContext::Graphics, VerticalAnchorValue::Bottom) => position.y,
160 (VerticalAnchorContext::Math, VerticalAnchorValue::Bottom) => {
161 position.y + measurement.offset_y as f64
162 }
163 (_, VerticalAnchorValue::Center) => position.y + measurement.offset_y as f64 / 2.0,
164 (VerticalAnchorContext::Graphics, VerticalAnchorValue::Top) => {
165 position.y + measurement.offset_y as f64
166 }
167 (VerticalAnchorContext::Math, VerticalAnchorValue::Top) => position.y,
168 };
169
170 draw_text_ex(
171 text,
172 x as f32,
173 y as f32,
174 TextParams {
175 font: self.font.as_ref(),
176 font_size: size as u16,
177 color: srgba_to_color(color),
178 ..TextParams::default()
179 },
180 );
181 }
182
183 fn render_text_outline(
184 &mut self,
185 text: &str,
186 position: ::glam::DVec2,
187 anchor: Anchor2D,
188 size: f64,
189 outline_thickness: f64,
190 color: Srgba,
191 outline_color: Srgba,
192 ) {
193 let measurement = measure_text(text, self.font.as_ref(), size as u16, 1.0);
194
195 let x = match anchor.get_horizontal() {
196 HorizontalAnchor::Left => position.x,
197 HorizontalAnchor::Center => position.x - measurement.width as f64 / 2.0,
198 HorizontalAnchor::Right => position.x - measurement.width as f64,
199 };
200
201 let vertical_anchor = anchor.get_vertical();
202
203 let y = match (vertical_anchor.get_context(), vertical_anchor.get_value()) {
204 (VerticalAnchorContext::Graphics, VerticalAnchorValue::Bottom) => position.y,
205 (VerticalAnchorContext::Math, VerticalAnchorValue::Bottom) => {
206 position.y + measurement.offset_y as f64
207 }
208 (_, VerticalAnchorValue::Center) => position.y + measurement.offset_y as f64 / 2.0,
209 (VerticalAnchorContext::Graphics, VerticalAnchorValue::Top) => {
210 position.y + measurement.offset_y as f64
211 }
212 (VerticalAnchorContext::Math, VerticalAnchorValue::Top) => position.y,
213 };
214
215 for i in -1..=1 {
216 for j in -1..=1 {
217 if i != 0 || j != 0 {
218 draw_text_ex(
219 text,
220 x as f32 - i as f32 * outline_thickness as f32,
221 y as f32 - j as f32 * outline_thickness as f32,
222 TextParams {
223 font: self.font.as_ref(),
224 font_size: size as u16,
225 color: srgba_to_color(outline_color),
226 ..TextParams::default()
227 },
228 );
229 }
230 }
231 }
232
233 draw_text_ex(
234 text,
235 x as f32,
236 y as f32,
237 TextParams {
238 font: self.font.as_ref(),
239 font_size: size as u16,
240 color: srgba_to_color(color),
241 ..TextParams::default()
242 },
243 );
244 }
245
246 fn render_rectangle(
247 &mut self,
248 position: ::glam::DVec2,
249 width: f64,
250 height: f64,
251 offset: ::glam::DVec2,
252 rotation: f64,
253 color: Srgba,
254 ) {
255 draw_rectangle_ex(
256 position.x as f32,
257 position.y as f32,
258 width as f32,
259 height as f32,
260 DrawRectangleParams {
261 offset: vec2(offset.x as f32, offset.y as f32),
262 rotation: rotation as f32,
263 color: srgba_to_color(color),
264 },
265 );
266 }
267
268 fn render_rectangle_lines(
269 &mut self,
270 position: ::glam::DVec2,
271 width: f64,
272 height: f64,
273 offset: ::glam::DVec2,
274 rotation: f64,
275 thickness: f64,
276 color: Srgba,
277 ) {
278 draw_rectangle_lines_ex(
279 position.x as f32,
280 position.y as f32,
281 width as f32,
282 height as f32,
283 thickness as f32,
284 DrawRectangleParams {
285 offset: vec2(offset.x as f32, offset.y as f32),
286 rotation: rotation as f32,
287 color: srgba_to_color(color),
288 },
289 );
290 }
291
292 fn render_equilateral_triangle(
293 &mut self,
294 position: ::glam::DVec2,
295 radius: f64,
296 rotation: f64,
297 color: Srgba,
298 ) {
299 draw_poly(
300 position.x as f32,
301 position.y as f32,
302 3,
303 radius as f32,
304 rotation.to_degrees() as f32,
305 srgba_to_color(color),
306 );
307 }
308
309 fn render_equilateral_triangle_lines(
310 &mut self,
311 position: ::glam::DVec2,
312 radius: f64,
313 rotation: f64,
314 thickness: f64,
315 color: Srgba,
316 ) {
317 draw_poly_lines(
318 position.x as f32,
319 position.y as f32,
320 3,
321 radius as f32,
322 rotation.to_degrees() as f32,
323 thickness as f32,
324 srgba_to_color(color),
325 );
326 }
327
328 fn render_image(
329 &mut self,
330 image_name: &str,
331 position: ::glam::DVec2,
332 width: f64,
333 height: f64,
334 offset: ::glam::DVec2,
335 rotation: f64,
336 ) {
337 if let Some(image) = self.images.get(image_name) {
338 draw_texture_ex(
339 image,
340 (position.x - width * offset.x) as f32,
341 (position.y - height * offset.y) as f32,
342 WHITE,
343 DrawTextureParams {
344 dest_size: Some(vec2(width as f32, height as f32)),
345 source: None,
346 rotation: rotation as f32,
347 flip_x: false,
348 flip_y: false,
349 pivot: Some(dvec2(position.x, position.y).as_vec2()),
350 },
351 );
352 }
353 }
354}