render_agnostic/renderers/
macroquad.rs

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        // TODO: yeeeaaah... this is just the outline
107        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}