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