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