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