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        sides: u8,
104        arc: f64,
105        color: Srgba,
106    ) {
107        // TODO: yeeeaaah... this is just the outline
108        draw_arc(
109            position.x as f32,
110            position.y as f32,
111            sides,
112            radius as f32,
113            rotation.to_degrees() as f32,
114            1.0,
115            arc.to_degrees() as f32,
116            srgba_to_color(color),
117        );
118    }
119
120    fn render_arc_lines(
121        &mut self,
122        position: ::glam::DVec2,
123        radius: f64,
124        rotation: f64,
125        sides: u8,
126        arc: f64,
127        thickness: f64,
128        color: Srgba,
129    ) {
130        draw_arc(
131            position.x as f32,
132            position.y as f32,
133            sides,
134            radius as f32,
135            rotation.to_degrees() as f32,
136            thickness as f32,
137            arc.to_degrees() as f32,
138            srgba_to_color(color),
139        );
140    }
141
142    fn render_text(
143        &mut self,
144        text: &str,
145        position: ::glam::DVec2,
146        anchor: Anchor2D,
147        size: f64,
148        color: Srgba,
149    ) {
150        let measurement = measure_text(text, self.font.as_ref(), size as u16, 1.0);
151
152        let x = match anchor.get_horizontal() {
153            HorizontalAnchor::Left => position.x,
154            HorizontalAnchor::Center => position.x - measurement.width as f64 / 2.0,
155            HorizontalAnchor::Right => position.x - measurement.width as f64,
156        };
157
158        let vertical_anchor = anchor.get_vertical();
159
160        let y = match (vertical_anchor.get_context(), vertical_anchor.get_value()) {
161            (VerticalAnchorContext::Graphics, VerticalAnchorValue::Bottom) => position.y,
162            (VerticalAnchorContext::Math, VerticalAnchorValue::Bottom) => {
163                position.y + measurement.offset_y as f64
164            }
165            (_, VerticalAnchorValue::Center) => position.y + measurement.offset_y as f64 / 2.0,
166            (VerticalAnchorContext::Graphics, VerticalAnchorValue::Top) => {
167                position.y + measurement.offset_y as f64
168            }
169            (VerticalAnchorContext::Math, VerticalAnchorValue::Top) => position.y,
170        };
171
172        draw_text_ex(
173            text,
174            x as f32,
175            y as f32,
176            TextParams {
177                font: self.font.as_ref(),
178                font_size: size as u16,
179                color: srgba_to_color(color),
180                ..TextParams::default()
181            },
182        );
183    }
184
185    fn render_text_outline(
186        &mut self,
187        text: &str,
188        position: ::glam::DVec2,
189        anchor: Anchor2D,
190        size: f64,
191        outline_thickness: f64,
192        color: Srgba,
193        outline_color: Srgba,
194    ) {
195        let measurement = measure_text(text, self.font.as_ref(), size as u16, 1.0);
196
197        let x = match anchor.get_horizontal() {
198            HorizontalAnchor::Left => position.x,
199            HorizontalAnchor::Center => position.x - measurement.width as f64 / 2.0,
200            HorizontalAnchor::Right => position.x - measurement.width as f64,
201        };
202
203        let vertical_anchor = anchor.get_vertical();
204
205        let y = match (vertical_anchor.get_context(), vertical_anchor.get_value()) {
206            (VerticalAnchorContext::Graphics, VerticalAnchorValue::Bottom) => position.y,
207            (VerticalAnchorContext::Math, VerticalAnchorValue::Bottom) => {
208                position.y + measurement.offset_y as f64
209            }
210            (_, VerticalAnchorValue::Center) => position.y + measurement.offset_y as f64 / 2.0,
211            (VerticalAnchorContext::Graphics, VerticalAnchorValue::Top) => {
212                position.y + measurement.offset_y as f64
213            }
214            (VerticalAnchorContext::Math, VerticalAnchorValue::Top) => position.y,
215        };
216
217        for i in -1..=1 {
218            for j in -1..=1 {
219                if i != 0 || j != 0 {
220                    draw_text_ex(
221                        text,
222                        x as f32 - i as f32 * outline_thickness as f32,
223                        y as f32 - j as f32 * outline_thickness as f32,
224                        TextParams {
225                            font: self.font.as_ref(),
226                            font_size: size as u16,
227                            color: srgba_to_color(outline_color),
228                            ..TextParams::default()
229                        },
230                    );
231                }
232            }
233        }
234
235        draw_text_ex(
236            text,
237            x as f32,
238            y as f32,
239            TextParams {
240                font: self.font.as_ref(),
241                font_size: size as u16,
242                color: srgba_to_color(color),
243                ..TextParams::default()
244            },
245        );
246    }
247
248    fn render_rectangle(
249        &mut self,
250        position: ::glam::DVec2,
251        width: f64,
252        height: f64,
253        offset: ::glam::DVec2,
254        rotation: f64,
255        color: Srgba,
256    ) {
257        draw_rectangle_ex(
258            position.x as f32,
259            position.y as f32,
260            width as f32,
261            height as f32,
262            DrawRectangleParams {
263                offset: vec2(offset.x as f32, offset.y as f32),
264                rotation: rotation as f32,
265                color: srgba_to_color(color),
266            },
267        );
268    }
269
270    fn render_rectangle_lines(
271        &mut self,
272        position: ::glam::DVec2,
273        width: f64,
274        height: f64,
275        offset: ::glam::DVec2,
276        rotation: f64,
277        thickness: f64,
278        color: Srgba,
279    ) {
280        draw_rectangle_lines_ex(
281            position.x as f32,
282            position.y as f32,
283            width as f32,
284            height as f32,
285            thickness as f32,
286            DrawRectangleParams {
287                offset: vec2(offset.x as f32, offset.y as f32),
288                rotation: rotation as f32,
289                color: srgba_to_color(color),
290            },
291        );
292    }
293
294    fn render_equilateral_triangle(
295        &mut self,
296        position: ::glam::DVec2,
297        radius: f64,
298        rotation: f64,
299        color: Srgba,
300    ) {
301        draw_poly(
302            position.x as f32,
303            position.y as f32,
304            3,
305            radius as f32,
306            rotation.to_degrees() as f32,
307            srgba_to_color(color),
308        );
309    }
310
311    fn render_equilateral_triangle_lines(
312        &mut self,
313        position: ::glam::DVec2,
314        radius: f64,
315        rotation: f64,
316        thickness: f64,
317        color: Srgba,
318    ) {
319        draw_poly_lines(
320            position.x as f32,
321            position.y as f32,
322            3,
323            radius as f32,
324            rotation.to_degrees() as f32,
325            thickness as f32,
326            srgba_to_color(color),
327        );
328    }
329
330    fn render_image(
331        &mut self,
332        image_name: &str,
333        position: ::glam::DVec2,
334        width: f64,
335        height: f64,
336        offset: ::glam::DVec2,
337        rotation: f64,
338    ) {
339        if let Some(image) = self.images.get(image_name) {
340            draw_texture_ex(
341                image,
342                (position.x - width * offset.x) as f32,
343                (position.y - height * offset.y) as f32,
344                WHITE,
345                DrawTextureParams {
346                    dest_size: Some(vec2(width as f32, height as f32)),
347                    source: None,
348                    rotation: rotation as f32,
349                    flip_x: false,
350                    flip_y: false,
351                    pivot: Some(dvec2(position.x, position.y).as_vec2()),
352                },
353            );
354        }
355    }
356}