1use std;
43use std::sync::LazyLock;
44use derive_more::{From, TryInto};
45
46use crate::math::Vector2;
49use crate::geometry::integer::Aabb2;
50
51pub use self::position::Position;
52pub use self::dimensions::Dimensions;
53
54pub (crate) static TILE_WH : LazyLock <[u32; 2]> = LazyLock::new (||{
55 let width = std::env::var ("GOOEY_TILE_WIDTH").unwrap().parse().unwrap();
56 let height = std::env::var ("GOOEY_TILE_HEIGHT").unwrap().parse().unwrap();
57 [width, height]
58});
59pub (crate) static SCREEN_WH : LazyLock <std::sync::RwLock <[u32; 2]>> = LazyLock::new (
60 || std::sync::RwLock::new ([0, 0]));
61
62#[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
63pub enum Coordinates {
64 Tile (position::Tile, dimensions::Tile),
65 Pixel (position::Pixel, dimensions::Pixel)
66}
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub enum Kind {
70 Tile, Pixel
71}
72
73pub fn pixel_to_tile (x : i32, y : i32) -> [i32; 2] {
75 let [tile_w, tile_h] = *TILE_WH;
76 let [_screen_w, screen_h] = *SCREEN_WH.read().unwrap();
77 let column = x / tile_w as i32;
78 let row = (screen_h as i32 - y) / tile_h as i32;
79 [row, column]
80}
81
82pub fn pixel_to_tile_aabb (aabb : Aabb2 <i32>) -> Aabb2 <i32> {
84 let [min_x, min_y] = aabb.min().0.into_array();
85 let [max_x, max_y] = aabb.max().0.into_array();
86 let min = pixel_to_tile (min_x, max_y).into();
87 let max = pixel_to_tile (max_x, min_y).into();
88 Aabb2::with_minmax (min, max)
89}
90
91pub fn tile_to_pixel (row : i32, column : i32) -> [i32; 2] {
93 let [tile_w, tile_h] = *TILE_WH;
94 let [_screen_w, screen_h] = *SCREEN_WH.read().unwrap();
95 let x = column * tile_w as i32;
96 let y = screen_h as i32 - row * tile_h as i32;
97 [x, y]
98}
99
100pub fn tile_to_pixel_aabb (aabb : Aabb2 <i32>) -> Aabb2 <i32> {
102 let aabb = Aabb2::with_minmax (
103 *aabb.min(),
104 aabb.max() + Vector2::new (1, 1)
105 );
106 let [min_row, min_col] = aabb.min().0.into_array();
107 let [max_row, max_col] = aabb.max().0.into_array();
108 let min = tile_to_pixel (max_row, min_col).into();
109 let max = tile_to_pixel (min_row, max_col).into();
110 Aabb2::with_minmax (min, max)
111}
112
113impl Coordinates {
114 #[inline]
115 pub fn default_tile() -> Self {
116 (position::Tile::default(), dimensions::Tile::default()).into()
117 }
118 #[inline]
119 pub fn default_pixel() -> Self {
120 (position::Pixel::default(), dimensions::Pixel::default()).into()
121 }
122 pub fn tile_from_aabb (aabb : Aabb2 <i32>) -> Self {
123 let position = (*aabb.min()).into();
124 let dimensions = ((*aabb.max() - aabb.min()) + Vector2::new (1, 1))
125 .numcast().unwrap().into();
126 Coordinates::Tile (position, dimensions)
127 }
128 pub fn pixel_from_aabb (aabb : Aabb2 <i32>) -> Self {
129 let position = (*aabb.min()).into();
130 let dimensions = ((*aabb.max() - aabb.min()) + Vector2::new (1, 1))
131 .numcast().unwrap().into();
132 Coordinates::Pixel (position, dimensions)
133 }
134 #[inline]
135 pub const fn kind (&self) -> Kind {
136 match self {
137 Coordinates::Tile (_, _) => Kind::Tile,
138 Coordinates::Pixel (_, _) => Kind::Pixel
139 }
140 }
141 #[inline]
142 pub fn dimensions (&self) -> Dimensions {
143 match self {
144 Coordinates::Tile (_, dimensions) => (*dimensions).into(),
145 Coordinates::Pixel (_, dimensions) => (*dimensions).into()
146 }
147 }
148 #[inline]
149 pub fn position (&self) -> Position {
150 match self {
151 Coordinates::Tile (position, _) => (*position).into(),
152 Coordinates::Pixel (position, _) => (*position).into()
153 }
154 }
155 #[inline]
156 pub const fn dimensions_horizontal (&self) -> u32 {
157 match self {
158 Coordinates::Tile (_, dimensions) => dimensions.columns(),
159 Coordinates::Pixel (_, dimensions) => dimensions.width()
160 }
161 }
162 #[inline]
163 pub const fn dimensions_vertical (&self) -> u32 {
164 match self {
165 Coordinates::Tile (_, dimensions) => dimensions.rows(),
166 Coordinates::Pixel (_, dimensions) => dimensions.height()
167 }
168 }
169 #[inline]
170 pub fn position_horizontal (&self) -> i32 {
171 match self {
172 Coordinates::Tile (position, _) => position.column(),
173 Coordinates::Pixel (position, _) => position.0.x
174 }
175 }
176 #[inline]
177 pub fn position_vertical (&self) -> i32 {
178 match self {
179 Coordinates::Tile (position, _) => position.row(),
180 Coordinates::Pixel (position, _) => position.0.y
181 }
182 }
183 #[inline]
184 pub fn modify_dimensions_horizontal (&mut self, d : i32) {
185 match self {
186 Coordinates::Tile (_, dimensions) =>
187 *dimensions.columns_mut() =
188 std::cmp::max (0, dimensions.columns() as i32 + d) as u32,
189 Coordinates::Pixel (_, dimensions) =>
190 dimensions.x = std::cmp::max (0, dimensions.x as i32 + d) as u32
191 }
192 }
193 #[inline]
194 pub fn modify_dimensions_vertical (&mut self, d : i32) {
195 match self {
196 Coordinates::Tile (_, dimensions) =>
197 *dimensions.rows_mut() =
198 std::cmp::max (0, dimensions.rows() as i32 + d) as u32,
199 Coordinates::Pixel (_, dimensions) =>
200 dimensions.y = std::cmp::max (0, dimensions.y as i32 + d) as u32
201 }
202 }
203 #[inline]
204 pub fn modify_position_horizontal (&mut self, d : i32) {
205 match self {
206 Coordinates::Tile (position, _) => *position.column_mut() = position.column() + d,
207 Coordinates::Pixel (position, _) => position.0.x += d
208 }
209 }
210 #[inline]
211 pub fn modify_position_vertical (&mut self, d : i32) {
212 match self {
213 Coordinates::Tile (position, _) => *position.row_mut() = position.row() + d,
214 Coordinates::Pixel (position, _) => position.0.y += d
215 }
216 }
217 #[inline]
218 pub fn set_position (&mut self, position : Position) {
220 match (self, position) {
221 (Coordinates::Tile (position, _), Position::Tile (p)) => *position = p,
222 (Coordinates::Pixel (position, _), Position::Pixel (p)) => *position = p,
223 _ => unreachable!()
224 }
225 }
226 #[inline]
227 pub fn set_dimensions (&mut self, dimensions : Dimensions) {
229 match (self, dimensions) {
230 (Coordinates::Tile (_, dimensions), Dimensions::Tile (d)) =>
231 *dimensions = d,
232 (Coordinates::Pixel (_, dimensions), Dimensions::Pixel (d)) =>
233 *dimensions = d,
234 _ => unreachable!()
235 }
236 }
237}
238
239impl From <Coordinates> for Aabb2 <i32> {
240 fn from (coordinates : Coordinates) -> Aabb2 <i32> {
241 log::trace!("aabb2 from coordinates: {coordinates:?}");
242 let (min, max) = match coordinates {
246 Coordinates::Tile (position, dimensions) => {
247 let mut max = *position + dimensions.numcast().unwrap();
248 if dimensions.x > 0 {
249 max.0.x -= 1;
250 }
251 if dimensions.y > 0 {
252 max.0.y -= 1;
253 }
254 (*position, max)
255 }
256 Coordinates::Pixel (position, dimensions) => {
257 let mut max = *position + dimensions.numcast().unwrap();
258 if dimensions.x > 0 {
259 max.0.x -= 1;
260 }
261 if dimensions.y > 0 {
262 max.0.y -= 1;
263 }
264 (*position, max)
265 }
266 };
267 log::trace!("aabb2 with min, max: {:?}", (min, max));
268 Aabb2::with_minmax (min, max)
269 }
270}
271
272impl TryFrom <(Position, Dimensions)> for Coordinates {
273 type Error = (Position, Dimensions);
274 fn try_from ((position, dimensions) : (Position, Dimensions))
275 -> Result <Coordinates, (Position, Dimensions)>
276 {
277 let coordinates = match (position, dimensions) {
278 (Position::Tile (position), Dimensions::Tile (dimensions)) =>
279 Coordinates::Tile (position, dimensions),
280 (Position::Pixel (position), Dimensions::Pixel (dimensions)) =>
281 Coordinates::Pixel (position, dimensions),
282 _ => return Err ((position, dimensions))
283 };
284 Ok (coordinates)
285 }
286}
287
288pub mod position {
289 use derive_more::{From, TryInto};
290 use crate::math::Point2;
291
292 #[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
293 pub enum Position {
294 Tile (Tile),
295 Pixel (Pixel)
296 }
297
298 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
299 pub struct Tile {
300 pub position_rc : Point2 <i32>
301 }
302
303 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
304 pub struct Pixel {
305 pub position_xy : Point2 <i32>
306 }
307
308 impl Tile {
311 #[inline]
312 pub const fn origin() -> Self {
313 Tile {
314 position_rc: Point2::new (0, 0)
315 }
316 }
317 #[inline]
318 pub fn new_rc (row : i32, column : i32) -> Self {
319 Point2::new (row, column).into()
320 }
321 #[inline]
322 pub const fn row (&self) -> i32 {
323 self.position_rc.0.x
324 }
325 #[inline]
326 pub const fn column (&self) -> i32 {
327 self.position_rc.0.y
328 }
329 #[inline]
330 pub const fn row_mut (&mut self) -> &mut i32 {
331 &mut self.position_rc.0.x
332 }
333 #[inline]
334 pub const fn column_mut (&mut self) -> &mut i32 {
335 &mut self.position_rc.0.y
336 }
337 }
338
339 impl Default for Tile {
340 fn default() -> Self {
341 Tile { position_rc: [0,0].into() }
342 }
343 }
344
345 impl From <Point2 <i32>> for Tile {
346 fn from (position_rc : Point2 <i32>) -> Self {
347 Tile { position_rc }
348 }
349 }
350
351 impl std::ops::Deref for Tile {
352 type Target = Point2 <i32>;
353 fn deref (&self) -> &Point2 <i32> {
354 &self.position_rc
355 }
356 }
357
358 impl std::ops::DerefMut for Tile {
359 fn deref_mut (&mut self) -> &mut Point2 <i32> {
360 &mut self.position_rc
361 }
362 }
363
364 impl Pixel {
365 #[inline]
366 pub const fn origin() -> Self {
367 Pixel {
368 position_xy: Point2::new (0, 0)
369 }
370 }
371 #[inline]
372 pub fn new_xy (x : i32, y : i32) -> Self {
373 Point2::new (x, y).into()
374 }
375 }
376
377 impl Default for Pixel {
378 fn default() -> Self {
379 Pixel { position_xy: [0,0].into() }
380 }
381 }
382
383 impl From <Point2 <i32>> for Pixel {
384 fn from (position_xy : Point2 <i32>) -> Self {
385 Pixel { position_xy }
386 }
387 }
388
389 impl std::ops::Deref for Pixel {
390 type Target = Point2 <i32>;
391 fn deref (&self) -> &Point2 <i32> {
392 &self.position_xy
393 }
394 }
395
396 impl std::ops::DerefMut for Pixel {
397 fn deref_mut (&mut self) -> &mut Point2 <i32> {
398 &mut self.position_xy
399 }
400 }
401}
402
403pub mod dimensions {
404 use derive_more::{From, TryInto};
405 use crate::math::Vector2;
406
407 #[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
408 pub enum Dimensions {
409 Tile (Tile),
410 Pixel (Pixel)
411 }
412
413 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
414 pub struct Tile {
415 pub dimensions_rc : Vector2 <u32>
416 }
417
418 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
419 pub struct Pixel {
420 pub dimensions_xy : Vector2 <u32>
421 }
422
423 impl Dimensions {
426 pub const fn horizontal (&self) -> u32 {
427 match self {
428 Dimensions::Tile (tile) => tile.columns(),
429 Dimensions::Pixel (pixel) => pixel.width()
430 }
431 }
432
433 pub const fn vertical (&self) -> u32 {
434 match self {
435 Dimensions::Tile (tile) => tile.rows(),
436 Dimensions::Pixel (pixel) => pixel.height()
437 }
438 }
439
440 pub fn vec (&self) -> Vector2 <u32> {
441 match self {
442 Dimensions::Tile (tile) => **tile,
443 Dimensions::Pixel (pixel) => **pixel
444 }
445 }
446 }
447
448 impl Tile {
449 #[inline]
450 pub fn new_rc (rows : u32, columns : u32) -> Self {
451 Vector2::new (rows, columns).into()
452 }
453 #[inline]
454 pub const fn columns (&self) -> u32 {
455 self.dimensions_rc.y
456 }
457 #[inline]
458 pub const fn rows (&self) -> u32 {
459 self.dimensions_rc.x
460 }
461 #[inline]
462 pub const fn rows_mut (&mut self) -> &mut u32 {
463 &mut self.dimensions_rc.x
464 }
465 #[inline]
466 pub const fn columns_mut (&mut self) -> &mut u32 {
467 &mut self.dimensions_rc.y
468 }
469 #[inline]
470 pub fn to_pixel (self) -> Pixel {
471 Vector2::new (self.columns(), self.rows()).into()
472 }
473 }
474
475 impl Pixel {
476 #[inline]
477 pub fn new_wh (width : u32, height : u32) -> Self {
478 Vector2::new (width, height).into()
479 }
480 #[inline]
481 pub const fn width (&self) -> u32 {
482 self.dimensions_xy.x
483 }
484 #[inline]
485 pub const fn height (&self) -> u32 {
486 self.dimensions_xy.y
487 }
488 #[inline]
489 pub fn to_tile (self) -> Tile {
490 Vector2::new (self.height(), self.width()).into()
491 }
492 }
493
494 impl Default for Tile {
495 fn default() -> Self {
496 Tile { dimensions_rc: [0,0].into() }
497 }
498 }
499
500 impl From <Vector2 <u32>> for Tile {
501 fn from (dimensions_rc : Vector2 <u32>) -> Self {
502 Tile { dimensions_rc }
503 }
504 }
505
506 impl std::ops::Deref for Tile {
507 type Target = Vector2 <u32>;
508 fn deref (&self) -> &Self::Target {
509 &self.dimensions_rc
510 }
511 }
512
513 impl std::ops::DerefMut for Tile {
514 fn deref_mut (&mut self) -> &mut Self::Target {
515 &mut self.dimensions_rc
516 }
517 }
518
519 impl Default for Pixel {
520 fn default() -> Self {
521 Pixel { dimensions_xy: [0,0].into() }
522 }
523 }
524
525 impl From <Vector2 <u32>> for Pixel {
526 fn from (dimensions_xy : Vector2 <u32>) -> Self {
527 Pixel { dimensions_xy }
528 }
529 }
530
531 impl std::ops::Deref for Pixel {
532 type Target = Vector2 <u32>;
533 fn deref (&self) -> &Self::Target {
534 &self.dimensions_xy
535 }
536 }
537
538 impl std::ops::DerefMut for Pixel {
539 fn deref_mut (&mut self) -> &mut Self::Target {
540 &mut self.dimensions_xy
541 }
542 }
543}
544
545impl std::ops::Add <dimensions::Tile> for position::Tile {
546 type Output = Self;
547 fn add (self, rhs : dimensions::Tile) -> Self {
548 (self.position_rc + rhs.dimensions_rc.numcast().unwrap()).into()
549 }
550}
551
552impl std::ops::Add <dimensions::Pixel> for position::Pixel {
553 type Output = Self;
554 fn add (self, rhs : dimensions::Pixel) -> Self {
555 (self.position_xy + rhs.dimensions_xy.numcast().unwrap()).into()
556 }
557}
558
559impl std::ops::Sub <dimensions::Tile> for position::Tile {
560 type Output = Self;
561 fn sub (self, rhs : dimensions::Tile) -> Self {
562 (self.position_rc - rhs.dimensions_rc.numcast().unwrap()).into()
563 }
564}
565
566impl std::ops::Sub <dimensions::Pixel> for position::Pixel {
567 type Output = Self;
568 fn sub (self, rhs : dimensions::Pixel) -> Self {
569 (self.position_xy - rhs.dimensions_xy.numcast().unwrap()).into()
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use crate::geometry::integer::Aabb2;
576 use super::*;
577 #[test]
599 fn tile_to_pixel_aabb() {
600 use super::tile_to_pixel_aabb;
601 unsafe {
602 std::env::set_var ("GOOEY_TILE_WIDTH", "8");
603 std::env::set_var ("GOOEY_TILE_HEIGHT", "8");
604 }
605 *SCREEN_WH.write().unwrap() = [100, 100];
606 let coordinates = Coordinates::Tile (
607 position::Tile::new_rc (0, 0),
608 dimensions::Tile::new_rc (1, 1));
609 let aabb = Aabb2::from (coordinates);
610 assert!(aabb.contains (&[0, 0].into()));
611 assert!(!aabb.contains (&[1, 1].into()));
612 let aabb_pixel = tile_to_pixel_aabb (aabb);
613 assert_eq!(*aabb_pixel.min(), [0, 92].into());
614 assert_eq!(*aabb_pixel.max(), [8, 100].into());
615 }
616}