Skip to main content

render_agnostic/renderers/
macroquad.rs

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