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