1#![forbid(unsafe_code)]
2#![warn(
3 clippy::cargo_common_metadata,
4 clippy::cloned_instead_of_copied,
5 clippy::equatable_if_let,
6 clippy::if_then_some_else_none,
7 clippy::lossy_float_literal,
8 clippy::map_unwrap_or,
9 missing_docs,
10 clippy::doc_link_with_quotes,
11 clippy::doc_markdown,
12 clippy::missing_errors_doc,
13 clippy::missing_panics_doc
14)]
15#![doc = include_str!("../README.md")]
16
17use macroquad::prelude::*;
20use std::ops::{Deref, DerefMut};
21
22pub struct Canvas2D {
50 pub camera: Camera2D,
52}
53
54impl Deref for Canvas2D {
55 type Target = Camera2D;
56
57 fn deref(&self) -> &Self::Target {
58 &self.camera
59 }
60}
61
62impl DerefMut for Canvas2D {
63 fn deref_mut(&mut self) -> &mut Self::Target {
64 &mut self.camera
65 }
66}
67
68impl Canvas2D {
69 pub fn new(width: f32, height: f32) -> Self {
76 let mut camera = Camera2D::from_display_rect(Rect::new(0.0, 0.0, width, height));
77 camera.render_target = Some(render_target(width as u32, height as u32));
78 camera.zoom.y = -camera.zoom.y;
80
81 Self { camera }
82 }
83
84 #[inline]
86 pub fn draw(&self) {
87 self.draw_ex(screen_width(), screen_height());
88 }
89
90 pub fn draw_ex(&self, target_width: f32, target_height: f32) {
92 let (left_padding, top_padding, dimensions) =
93 self.get_size_and_padding(target_width, target_height);
94
95 draw_texture_ex(
96 self.get_texture(),
97 left_padding,
98 top_padding,
99 WHITE,
100 DrawTextureParams {
101 dest_size: Some(dimensions),
102 ..Default::default()
103 },
104 );
105 }
106
107 #[inline]
109 pub fn width(&self) -> f32 {
110 self.get_texture().width()
111 }
112
113 #[inline]
115 pub fn height(&self) -> f32 {
116 self.get_texture().height()
117 }
118
119 #[inline]
121 pub fn mouse_position(&self) -> (f32, f32) {
122 self.mouse_position_ex(screen_width(), screen_height())
123 }
124
125 pub fn mouse_position_ex(&self, target_width: f32, target_height: f32) -> (f32, f32) {
127 let (mouse_x, mouse_y) = mouse_position();
129
130 let scale = self.get_min_scale_factor(target_width, target_height);
131
132 let virtual_mouse_x = (mouse_x - (target_width - (self.width() * scale)) * 0.5) / scale;
134 let virtual_mouse_y = (mouse_y - (target_height - (self.height() * scale)) * 0.5) / scale;
135
136 (
137 virtual_mouse_x.clamp(0.0, self.width()).floor(),
138 virtual_mouse_y.clamp(0.0, self.height()).floor(),
139 )
140 }
141
142 #[inline]
144 #[allow(clippy::missing_panics_doc)]
145 pub fn get_texture(&self) -> &Texture2D {
146 &self.render_target.as_ref().unwrap().texture
147 }
148
149 #[inline]
151 #[allow(clippy::missing_panics_doc)]
152 pub fn get_texture_mut(&mut self) -> &mut Texture2D {
153 &mut self.render_target.as_mut().unwrap().texture
154 }
155
156 pub fn get_size(&self, target_width: f32, target_height: f32) -> Vec2 {
158 let min_scale_factor: f32 = self.get_min_scale_factor(target_width, target_height);
160
161 let new_width: f32 = self.width() * min_scale_factor;
163 let new_height: f32 = self.height() * min_scale_factor;
164
165 Vec2::new(new_width, new_height)
166 }
167
168 pub fn get_padding(&self, target_width: f32, target_height: f32) -> (f32, f32) {
175 let (left_padding, top_padding, _) = self.get_size_and_padding(target_width, target_height);
176
177 (left_padding, top_padding)
178 }
179
180 pub fn get_size_and_padding(&self, target_width: f32, target_height: f32) -> (f32, f32, Vec2) {
183 let new_size: Vec2 = self.get_size(target_width, target_height);
184
185 let left_padding: f32 = (target_width - new_size.x) / 2.0;
187 let top_padding: f32 = (target_height - new_size.y) / 2.0;
188
189 (left_padding, top_padding, new_size)
190 }
191
192 pub fn get_scale_factor(&self, target_width: f32, target_height: f32) -> (f32, f32) {
194 (target_width / self.width(), target_height / self.height())
195 }
196
197 pub fn get_min_scale_factor(&self, target_width: f32, target_height: f32) -> f32 {
199 let (scale_width, scale_height) = self.get_scale_factor(target_width, target_height);
200
201 f32::min(scale_width, scale_height)
202 }
203}