1use macroquad::{input::*, prelude::*};
2use std::collections::HashMap;
3
4pub const WHITE1: Color = color_u8!(236, 236, 236, 255);
5pub const WHITE2: Color = color_u8!(204, 204, 204, 255);
6pub const BLACK1: Color = color_u8!(18, 18, 18, 255);
7pub const BLACK2: Color = color_u8!(31, 31, 31, 255);
8
9pub struct D2 {
10 w: f32,
11 h: f32,
12 pub background: Color,
13 screen_w: f32,
14 screen_h: f32,
15 pub objects: HashMap<String, GObject>,
16 sorted_objects: Vec<String>,
17 object_z: f32,
18 touched: HashMap<u64, (String, Vec2, TouchPhase)>,
19}
20
21impl D2 {
22 pub fn new(w: f32, h: f32) -> Self {
23 simulate_mouse_with_touch(false);
24 Self {
25 w: w,
26 h: h,
27 background: BLACK1,
28 screen_w: screen_width(),
29 screen_h: screen_height(),
30 objects: HashMap::new(),
31 sorted_objects: Vec::new(),
32 object_z: 0.,
33 touched: HashMap::new(),
34 }
35 }
36
37 pub fn update(&mut self) {
38 self.screen_w = screen_width();
39 self.screen_h = screen_height();
40
41 self.touched
42 .retain(|_, (_, _, phase)| *phase != TouchPhase::Ended);
43
44 for touch in touches() {
45 match touch.phase {
46 TouchPhase::Started => {
47 self.in_touch(touch.id, touch.position.x, touch.position.y);
48 }
49 TouchPhase::Moved | TouchPhase::Stationary => {
50 if let Some(s) = self.touched.get_mut(&touch.id) {
51 s.1 = touch.position;
52 s.2 = touch.phase;
53 }
54 }
55 TouchPhase::Ended | TouchPhase::Cancelled => {
56 if let Some(s) = self.touched.get_mut(&touch.id) {
57 s.2 = TouchPhase::Ended;
58 }
59 }
60 }
61 }
62
63 if is_mouse_button_pressed(MouseButton::Left) {
64 self.in_touch(0, mouse_position().0, mouse_position().1);
65 } else if is_mouse_button_released(MouseButton::Left) {
66 if let Some(s) = self.touched.get_mut(&0) {
67 s.2 = TouchPhase::Ended;
68 }
69 } else if is_mouse_button_down(MouseButton::Left) {
70 if let Some(s) = self.touched.get_mut(&0) {
71 s.1 = vec2(mouse_position().0, mouse_position().1);
72 s.2 = TouchPhase::Moved;
73 }
74 }
75 }
76
77 pub fn draw(&mut self) {
78 clear_background(self.background);
79
80 let t = get_time();
81
82 for s in &self.sorted_objects {
83 if let Some(obj) = self.objects.get_mut(s) {
84 if obj.anim {
85 if t < obj.anim_time_start + obj.anim_time_end {
86 obj.x = obj.anim_start.x
87 + (obj.anim_end.x
88 * curve(
89 ((t - obj.anim_time_start) / obj.anim_time_end) as f32,
90 obj.anim_curve,
91 ));
92 obj.y = obj.anim_start.y
93 + (obj.anim_end.y
94 * curve(
95 ((t - obj.anim_time_start) / obj.anim_time_end) as f32,
96 obj.anim_curve,
97 ));
98 } else {
99 println!(
100 "{} {}",
101 obj.anim_start.x + obj.anim_end.x,
102 obj.anim_start.y + obj.anim_end.y
103 );
104 obj.anim = false;
105 obj.x = obj.anim_start.x + obj.anim_end.x;
106 obj.y = obj.anim_start.y + obj.anim_end.y;
107 }
108 }
109 }
110 if let Some(obj) = self.objects.get(s) {
111 if obj.visible {
112 match &obj.object_type {
113 GOType::Circle(r) => draw_circle(
114 self.x(obj.x + (r * obj.sx)),
115 self.y(obj.y + (r * obj.sy)),
116 self.s(*r),
117 obj.color,
118 ),
119 GOType::Rect(w, h) => draw_rectangle(
120 self.x((obj.x + ((w / 2.) * obj.sx)) - (w / 2.)),
121 self.y((obj.y + ((h / 2.) * obj.sy)) - (h / 2.)),
122 self.s(*w),
123 self.s(*h),
124 obj.color,
125 ),
126 GOType::RoundedRect(w, h, r) => draw_rounded_rect(
127 self.x((obj.x + ((w / 2.) * obj.sx)) - (w / 2.)),
128 self.y((obj.y + ((h / 2.) * obj.sy)) - (h / 2.)),
129 self.s(*w),
130 self.s(*h),
131 self.s(*r),
132 obj.color,
133 ),
134 GOType::Text(t, f, s) => {
135 let c = get_text_center(&t, f.as_ref(), self.s(*s) as u16, 1.0, 0.0);
136 draw_text_ex(
137 &t,
138 (self.x(obj.x) + (c.x * obj.sx)) - c.x,
139 (self.y(obj.y) + (c.y * obj.sy)) - c.y,
140 TextParams {
141 font: f.as_ref(),
142 font_size: self.s(*s) as u16,
143 color: obj.color,
144 ..Default::default()
145 },
146 );
147 }
148 }
149 }
150 }
151 }
152 }
153
154 pub fn is_touch_pressed(&self, name: &str) -> Option<Vec2> {
155 for (_, (obj_name, position, phase)) in &self.touched {
156 if obj_name == name && *phase == TouchPhase::Started {
157 return Some(*position);
158 }
159 }
160 None
161 }
162
163 pub fn is_touch_released(&mut self, name: &str) -> bool {
164 for (_, (obj_name, _, phase)) in &self.touched {
165 if obj_name == name && *phase == TouchPhase::Ended {
166 return true;
167 }
168 }
169 false
170 }
171
172 pub fn is_touch_down(&self, name: &str) -> Option<Vec2> {
173 for (_, (obj_name, position, phase)) in &self.touched {
174 if obj_name == name && (*phase == TouchPhase::Moved || *phase == TouchPhase::Stationary)
175 {
176 return Some(*position);
177 }
178 }
179 None
180 }
181
182 pub fn anim(&mut self, name: &str, curve: f32, x: f32, y: f32, time: f64) {
183 if let Some(obj) = self.objects.get_mut(name) {
184 if obj.x != x || obj.y != y {
185 obj.anim = true;
186 obj.anim_curve = curve;
187 obj.anim_start = vec2(obj.x, obj.y);
188 obj.anim_end = vec2(x - obj.anim_start.x, y - obj.anim_start.y);
189 obj.anim_time_start = get_time();
190 obj.anim_time_end = time;
191 }
192 }
193 }
194
195 pub fn add(&mut self, name: &str, obj: GObject) {
196 self.objects.insert(name.to_string(), obj);
197 self.sorted_objects.push(name.to_string());
198
199 self.go_sort();
200
201 if let Some(objs) = self.objects.get_mut(name) {
202 objs.z = self.object_z;
203 self.object_z += 0.001;
204 }
205 }
206
207 pub fn del(&mut self, name: &str) {
208 self.objects.remove(name);
209 self.sorted_objects.retain(|obj_name| obj_name != name);
210 }
211
212 fn in_touch(&mut self, touch_id: u64, x: f32, y: f32) {
213 for s in self.sorted_objects.iter().rev() {
214 if let Some(obj) = self.objects.get(s) {
215 if obj.visible {
216 if match &obj.object_type {
217 GOType::Circle(r) => {
218 ((x - self.x(obj.x + (r * obj.sx))).powi(2)
219 + (y - self.y(obj.y + (r * obj.sy))).powi(2))
220 .sqrt()
221 < self.s(*r)
222 }
223 GOType::Rect(w, h) => {
224 (x - self.x(obj.x + ((w / 2.) * obj.sx))).abs() < self.s(w / 2.)
225 && (y - self.y(obj.y + ((h / 2.) * obj.sy))).abs() < self.s(h / 2.)
226 }
227 GOType::RoundedRect(w, h, _) => {
228 (x - self.x(obj.x + ((w / 2.) * obj.sx))).abs() < self.s(w / 2.)
229 && (y - self.y(obj.y + ((h / 2.) * obj.sy))).abs() < self.s(h / 2.)
230 }
231 GOType::Text(t, f, s) => {
232 let c = get_text_center(&t, f.as_ref(), self.s(*s) as u16, 1.0, 0.0);
233 (x - (self.x(obj.x) + (c.x * obj.sx))).abs() < c.x.abs()
234 && (y - (self.y(obj.y) + (c.y * obj.sy))).abs() < c.y.abs()
235 }
236 } {
237 self.touched
238 .insert(touch_id, (s.to_string(), vec2(x, y), TouchPhase::Started));
239 break;
240 }
241 }
242 }
243 }
244 }
245
246 fn go_sort(&mut self) {
247 self.sorted_objects.sort_by(|a, b| {
248 let obj_a = self.objects.get(a).unwrap(); let obj_b = self.objects.get(b).unwrap(); obj_a
251 .z
252 .partial_cmp(&obj_b.z)
253 .unwrap_or(std::cmp::Ordering::Equal)
254 });
255 }
256
257 fn x(&self, x: f32) -> f32 {
258 (self.screen_w / 2.)
259 + ((x / (self.w / 2.)) * (self.screen_w.min((self.w / self.h) * self.screen_h) / 2.))
260 }
261
262 fn y(&self, y: f32) -> f32 {
263 (self.screen_h / 2.)
264 + ((y / (self.h / 2.)) * (self.screen_h.min((self.h / self.w) * self.screen_w) / 2.))
265 }
266
267 fn s(&self, s: f32) -> f32 {
268 (s / self.w) * self.screen_w.min((self.w / self.h) * self.screen_h)
269 }
270}
271
272pub struct GObject {
273 pub object_type: GOType,
274 pub x: f32,
275 pub y: f32,
276 pub z: f32,
277 pub color: Color,
278 pub visible: bool,
279 pub sx: f32,
280 pub sy: f32,
281 pub anim: bool,
282 pub anim_curve: f32,
283 pub anim_start: Vec2,
284 pub anim_end: Vec2,
285 pub anim_time_start: f64,
286 pub anim_time_end: f64,
287}
288
289impl GObject {
290 pub fn new(gt: GOType, x: f32, y: f32) -> Self {
291 Self {
292 object_type: gt,
293 x: x,
294 y: y,
295 z: 0.,
296 color: WHITE1,
297 visible: true,
298 sx: 0.,
299 sy: 0.,
300 anim: false,
301 anim_curve: 0.,
302 anim_start: Vec2 { x: 0., y: 0. },
303 anim_end: Vec2 { x: 0., y: 0. },
304 anim_time_start: 0.,
305 anim_time_end: 0.,
306 }
307 }
308}
309
310pub enum GOType {
311 Circle(f32),
312 Rect(f32, f32),
313 RoundedRect(f32, f32, f32),
314 Text(String, Option<Font>, f32),
315}
316
317#[inline(always)]
318fn curve(t: f32, tension: f32) -> f32 {
319 let t2 = t * t;
320 let t3 = t2 * t;
321
322 let h00 = 2.0 * t3 - 3.0 * t2 + 1.0;
323 let h10 = t3 - 2.0 * t2 + t;
324 let h01 = -2.0 * t3 + 3.0 * t2;
325 let h11 = t3 - t2;
326
327 let m0 = tension;
329 let m1 = tension;
330
331 h00 + h10 * m0 + h01 + h11 * m1
332}
333
334pub fn draw_rounded_rect(x: f32, y: f32, w: f32, h: f32, r: f32, color: Color) {
335 draw_rectangle(x + r, y, w - 2.0 * r, h, color);
336 draw_rectangle(x, y + r, w, h - 2.0 * r, color);
337
338 draw_circle(x + r, y + r, r, color);
339 draw_circle(x + w - r, y + r, r, color);
340 draw_circle(x + r, y + h - r, r, color);
341 draw_circle(x + w - r, y + h - r, r, color);
342}
343
344pub fn draw_rounded_line(x: f32, y: f32, x2: f32, y2: f32, r: f32, color: Color) {
345 draw_line(x, y, x2, y2, r, color);
346
347 draw_circle(x, y, r/2., color);
348 draw_circle(x2, y2, r/2., color);
349}