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 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}