1use std::{
29 fmt::{Debug, Display},
30 ops::{Add, AddAssign, Neg},
31};
32
33use fxhash::FxHashMap;
34
35use crate::core::{
36 algebra::{Matrix2, Scalar, SimdPartialOrd, Vector2},
37 math::{Number, Rect},
38 reflect::prelude::*,
39 visitor::prelude::*,
40};
41
42use super::OptionTileRect;
43
44#[derive(Copy, Clone, Hash, Eq, PartialEq, Visit, Reflect)]
53pub struct OrthoTransformation(i8);
54
55#[derive(Default, Clone, Debug, Visit)]
57pub struct OrthoTransformMap<V> {
58 transform: OrthoTransformation,
59 map: FxHashMap<Vector2<i32>, V>,
60}
61
62impl Default for OrthoTransformation {
63 fn default() -> Self {
64 Self::identity()
65 }
66}
67
68const ROTATION_MATRICES: [Matrix2<f32>; 4] = [
69 Matrix2::new(1.0, 0.0, 0.0, 1.0),
70 Matrix2::new(0.0, -1.0, 1.0, 0.0),
71 Matrix2::new(-1.0, 0.0, 0.0, -1.0),
72 Matrix2::new(0.0, 1.0, -1.0, 0.0),
73];
74
75const X_FLIP_MATRIX: Matrix2<f32> = Matrix2::new(-1.0, 0.0, 0.0, 1.0);
76
77impl OrthoTransformation {
78 #[inline]
80 pub const fn identity() -> Self {
81 Self(1)
82 }
83 #[inline]
88 pub const fn new(flipped: bool, rotation: i8) -> Self {
89 let rotation = rotation.rem_euclid(4);
90 Self(if flipped { -rotation - 1 } else { rotation + 1 })
91 }
92 pub fn all() -> impl Iterator<Item = OrthoTransformation> {
94 [-4i8, -3, -2, -1, 1, 2, 3, 4]
95 .into_iter()
96 .map(OrthoTransformation)
97 }
98 #[inline]
100 pub const fn is_identity(&self) -> bool {
101 self.0 == 1
102 }
103 #[inline]
107 pub const fn inverted(self) -> Self {
108 Self(match self.0 {
109 1 => 1,
110 2 => 4,
111 3 => 3,
112 4 => 2,
113 -1 => -1,
114 -2 => -2,
115 -3 => -3,
116 -4 => -4,
117 _ => unreachable!(),
118 })
119 }
120 #[inline]
122 pub const fn is_flipped(&self) -> bool {
123 self.0 < 0
124 }
125 #[inline]
129 pub const fn rotation(&self) -> i8 {
130 self.0.abs() - 1
131 }
132 pub fn matrix(&self) -> Matrix2<f32> {
134 let matrix = if self.is_flipped() {
135 X_FLIP_MATRIX
136 } else {
137 Matrix2::identity()
138 };
139 ROTATION_MATRICES[self.rotation() as usize] * matrix
140 }
141}
142
143impl Debug for OrthoTransformation {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 f.debug_tuple("OrthoTransformation")
146 .field(&self.0)
147 .field(&self.is_flipped())
148 .field(&self.rotation())
149 .finish()
150 }
151}
152
153impl Display for OrthoTransformation {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 let rotation = match self.rotation() {
156 0 => 0,
157 1 => 90,
158 2 => 180,
159 3 => 270,
160 _ => unreachable!(),
161 };
162 if self.is_flipped() {
163 write!(f, "rotate({})(flipped)", rotation)
164 } else {
165 write!(f, "rotate({})", rotation)
166 }
167 }
168}
169
170pub trait OrthoTransform: Sized {
174 fn x_flipped(self) -> Self;
176 fn y_flipped(self) -> Self {
178 self.x_flipped().rotated(2)
179 }
180 fn rotated(self, amount: i8) -> Self;
182 fn transformed(self, amount: OrthoTransformation) -> Self {
184 (if amount.is_flipped() {
185 self.x_flipped()
186 } else {
187 self
188 })
189 .rotated(amount.rotation())
190 }
191}
192
193impl OrthoTransform for OrthoTransformation {
194 fn x_flipped(self) -> Self {
195 Self(match self.0 {
196 1 => -1,
197 2 => -4,
198 3 => -3,
199 4 => -2,
200 -1 => 1,
201 -2 => 4,
202 -3 => 3,
203 -4 => 2,
204 _ => unreachable!(),
205 })
206 }
207 fn rotated(self, amount: i8) -> Self {
208 let amount = amount.rem_euclid(4);
209 if self.0 > 0 {
210 Self((self.0 + amount - 1).rem_euclid(4) + 1)
211 } else {
212 Self(-(self.0.abs() + amount - 1).rem_euclid(4) - 1)
213 }
214 }
215}
216
217impl<V: Neg<Output = V> + Scalar + Clone> OrthoTransform for Vector2<V> {
218 fn x_flipped(self) -> Self {
219 Self::new(-self.x.clone(), self.y.clone())
220 }
221
222 fn rotated(self, amount: i8) -> Self {
223 let amount = amount.rem_euclid(4);
224 match amount {
225 0 => self,
226 1 => Self::new(-self.y.clone(), self.x.clone()),
227 2 => Self::new(-self.x.clone(), -self.y.clone()),
228 3 => Self::new(self.y.clone(), -self.x.clone()),
229 _ => unreachable!(),
230 }
231 }
232}
233
234impl<V: Number + SimdPartialOrd + Add + AddAssign + Neg<Output = V> + Scalar> OrthoTransform
235 for Rect<V>
236{
237 fn x_flipped(self) -> Self {
238 Rect::from_points(
239 self.position.x_flipped(),
240 (self.position + self.size).x_flipped(),
241 )
242 }
243
244 fn rotated(self, amount: i8) -> Self {
245 Rect::from_points(
246 self.position.rotated(amount),
247 (self.position + self.size).rotated(amount),
248 )
249 }
250}
251
252impl<V> OrthoTransformMap<V> {
253 pub fn bounding_rect(&self) -> OptionTileRect {
255 let mut result = OptionTileRect::default();
256 for position in self.keys() {
257 result.push(position);
258 }
259 result
260 }
261
262 #[inline]
264 pub fn clear(&mut self) {
265 self.map.clear()
266 }
267 #[inline]
269 pub fn is_empty(&self) -> bool {
270 self.map.is_empty()
271 }
272 #[inline]
274 pub fn contains_key(&self, position: Vector2<i32>) -> bool {
275 self.map
276 .contains_key(&position.transformed(self.transform.inverted()))
277 }
278 #[inline]
280 pub fn remove(&mut self, position: Vector2<i32>) -> Option<V> {
281 self.map
282 .remove(&position.transformed(self.transform.inverted()))
283 }
284 #[inline]
286 pub fn get(&self, position: Vector2<i32>) -> Option<&V> {
287 self.map
288 .get(&position.transformed(self.transform.inverted()))
289 }
290 #[inline]
292 pub fn get_mut(&mut self, position: Vector2<i32>) -> Option<&mut V> {
293 self.map
294 .get_mut(&position.transformed(self.transform.inverted()))
295 }
296 #[inline]
298 pub fn insert(&mut self, position: Vector2<i32>, value: V) -> Option<V> {
299 self.map
300 .insert(position.transformed(self.transform.inverted()), value)
301 }
302 #[inline]
304 pub fn iter(&self) -> Iter<V> {
305 Iter(self.transform, self.map.iter())
306 }
307 #[inline]
309 pub fn keys(&self) -> Keys<V> {
310 Keys(self.transform, self.map.keys())
311 }
312 #[inline]
314 pub fn values(&self) -> std::collections::hash_map::Values<Vector2<i32>, V> {
315 self.map.values()
316 }
317}
318impl<V> OrthoTransform for OrthoTransformMap<V> {
319 fn x_flipped(self) -> Self {
320 Self {
321 transform: self.transform.x_flipped(),
322 map: self.map,
323 }
324 }
325
326 fn rotated(self, amount: i8) -> Self {
327 Self {
328 transform: self.transform.rotated(amount),
329 map: self.map,
330 }
331 }
332}
333pub struct Iter<'a, V>(
335 OrthoTransformation,
336 std::collections::hash_map::Iter<'a, Vector2<i32>, V>,
337);
338
339pub struct Keys<'a, V>(
341 OrthoTransformation,
342 std::collections::hash_map::Keys<'a, Vector2<i32>, V>,
343);
344
345impl<'a, V> Iterator for Iter<'a, V> {
346 type Item = (Vector2<i32>, &'a V);
347
348 fn next(&mut self) -> Option<Self::Item> {
349 let (k, v) = self.1.next()?;
350 Some((k.transformed(self.0), v))
351 }
352}
353impl<V> Iterator for Keys<'_, V> {
354 type Item = Vector2<i32>;
355
356 fn next(&mut self) -> Option<Self::Item> {
357 let k = self.1.next()?;
358 Some(k.transformed(self.0))
359 }
360}
361
362#[derive(Clone, Copy, Debug, PartialEq, Eq)]
366pub struct TransformSetCell(Vector2<i32>, OrthoTransformation);
367
368fn transform_to_cell_position(rotation: i8) -> Vector2<i32> {
369 match rotation {
370 -1 => Vector2::new(3, 0),
371 -2 => Vector2::new(3, 1),
372 -3 => Vector2::new(2, 1),
373 -4 => Vector2::new(2, 0),
374 1 => Vector2::new(0, 0),
375 2 => Vector2::new(1, 0),
376 3 => Vector2::new(1, 1),
377 4 => Vector2::new(0, 1),
378 _ => panic!(),
379 }
380}
381
382fn cell_position_to_transform(position: Vector2<i32>) -> i8 {
383 match (position.x, position.y) {
384 (3, 0) => -1,
385 (3, 1) => -2,
386 (2, 1) => -3,
387 (2, 0) => -4,
388 (0, 0) => 1,
389 (1, 0) => 2,
390 (1, 1) => 3,
391 (0, 1) => 4,
392 _ => panic!(),
393 }
394}
395
396impl TransformSetCell {
397 pub fn into_position(self) -> Vector2<i32> {
399 self.0 + transform_to_cell_position(self.1 .0)
400 }
401 pub fn from_position(position: Vector2<i32>) -> Self {
403 let rem = Vector2::new(position.x.rem_euclid(4), position.y.rem_euclid(2));
404 let pos = Vector2::new(position.x - rem.x, position.y - rem.y);
405 TransformSetCell(pos, OrthoTransformation(cell_position_to_transform(rem)))
406 }
407 pub fn with_transformation(self, trans: OrthoTransformation) -> Self {
415 TransformSetCell(self.0, trans)
416 }
417}
418
419impl OrthoTransform for TransformSetCell {
420 fn x_flipped(self) -> Self {
421 Self(self.0, self.1.x_flipped())
422 }
423
424 fn rotated(self, amount: i8) -> Self {
425 Self(self.0, self.1.rotated(amount))
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432 use crate::core::algebra::Point2;
433 use OrthoTransformation as Trans;
434 use TransformSetCell as Cell;
435
436 #[test]
437 fn identity() {
438 assert_eq!(Trans::identity(), Trans::new(false, 0));
439 for v in [
440 Vector2::new(2, 1),
441 Vector2::new(1, 2),
442 Vector2::new(-1, 5),
443 Vector2::new(-2, -2),
444 ] {
445 assert_eq!(v.transformed(Trans::identity()), v);
446 }
447 }
448 fn matrix_check(trans: OrthoTransformation) {
449 let v = Vector2::new(1.0, 0.5);
450 let m = trans.matrix().to_homogeneous();
451 let p = m.transform_point(&Point2::from(v)).coords;
452 assert_eq!(p, v.transformed(trans), "{}", trans);
453 }
454 #[test]
455 fn matrix() {
456 for i in 0..4 {
457 matrix_check(Trans::new(false, i))
458 }
459 for i in 0..4 {
460 matrix_check(Trans::new(true, i))
461 }
462 }
463 #[test]
464 fn rotate_4() {
465 assert_eq!(Trans::identity(), Trans::new(false, 4))
466 }
467 #[test]
468 fn invert() {
469 for i in [-4, -3, -2, -1, 1, 2, 3, 4] {
470 assert_eq!(
471 Trans(i).transformed(Trans(i).inverted()),
472 Trans::identity(),
473 "{:?}: {:?} {:?}",
474 i,
475 Trans(i),
476 Trans(i).inverted()
477 );
478 }
479 }
480 #[test]
481 fn inverse_undoes_transform() {
482 for i in [-4, -3, -2, -1, 1, 2, 3, 4] {
483 assert_eq!(
484 Vector2::new(2, 3)
485 .transformed(Trans(i))
486 .transformed(Trans(i).inverted()),
487 Vector2::new(2, 3),
488 );
489 }
490 }
491 #[test]
492 fn rotate_trans() {
493 assert_eq!(Trans::new(false, 0).rotated(0), Trans::new(false, 0));
494 assert_eq!(Trans::new(true, 0).rotated(0), Trans::new(true, 0));
495 assert_eq!(Trans::new(true, 2).rotated(0), Trans::new(true, 2));
496 assert_eq!(Trans::new(false, 0).rotated(1), Trans::new(false, 1));
497 assert_eq!(Trans::new(true, 0).rotated(1), Trans::new(true, 1));
498 assert_eq!(Trans::new(true, 2).rotated(1), Trans::new(true, 3));
499 assert_eq!(Trans::new(false, 0).rotated(2), Trans::new(false, 2));
500 assert_eq!(Trans::new(true, 0).rotated(2), Trans::new(true, 2));
501 assert_eq!(Trans::new(true, 2).rotated(2), Trans::new(true, 0));
502 }
503 #[test]
504 fn flipped_trans() {
505 assert_eq!(Trans::new(false, 0).x_flipped(), Trans::new(true, 0));
506 assert_eq!(Trans::new(true, 0).x_flipped(), Trans::new(false, 0));
507 assert_eq!(Trans::new(true, 2).x_flipped(), Trans::new(false, 2));
508 assert_eq!(Trans::new(true, 1).x_flipped(), Trans::new(false, 3));
509 assert_eq!(Trans::new(false, 0).y_flipped(), Trans::new(true, 2));
510 assert_eq!(Trans::new(true, 0).y_flipped(), Trans::new(false, 2));
511 assert_eq!(Trans::new(true, 2).y_flipped(), Trans::new(false, 0));
512 assert_eq!(Trans::new(true, 1).y_flipped(), Trans::new(false, 1));
513 }
514 #[test]
515 fn rotate_vector() {
516 assert_eq!(Vector2::new(1, 0).rotated(0), Vector2::new(1, 0));
517 assert_eq!(Vector2::new(0, 1).rotated(0), Vector2::new(0, 1));
518 assert_eq!(Vector2::new(2, 3).rotated(0), Vector2::new(2, 3));
519 assert_eq!(Vector2::new(1, 0).rotated(1), Vector2::new(0, 1));
520 assert_eq!(Vector2::new(0, 1).rotated(1), Vector2::new(-1, 0));
521 assert_eq!(Vector2::new(2, 3).rotated(1), Vector2::new(-3, 2));
522 assert_eq!(Vector2::new(1, 0).rotated(2), Vector2::new(-1, 0));
523 assert_eq!(Vector2::new(0, 1).rotated(2), Vector2::new(0, -1));
524 assert_eq!(Vector2::new(2, 3).rotated(2), Vector2::new(-2, -3));
525 assert_eq!(Vector2::new(1, 0).rotated(3), Vector2::new(0, -1));
526 assert_eq!(Vector2::new(0, 1).rotated(3), Vector2::new(1, 0));
527 assert_eq!(Vector2::new(2, 3).rotated(3), Vector2::new(3, -2));
528 assert_eq!(Vector2::new(1, 0).rotated(4), Vector2::new(1, 0));
529 assert_eq!(Vector2::new(0, 1).rotated(4), Vector2::new(0, 1));
530 assert_eq!(Vector2::new(2, 3).rotated(4), Vector2::new(2, 3));
531 }
532 #[test]
533 fn flipped_vector() {
534 assert_eq!(Vector2::new(1, 0).x_flipped(), Vector2::new(-1, 0));
535 assert_eq!(Vector2::new(0, 1).x_flipped(), Vector2::new(0, 1));
536 assert_eq!(Vector2::new(2, 3).x_flipped(), Vector2::new(-2, 3));
537 assert_eq!(Vector2::new(1, 0).y_flipped(), Vector2::new(1, 0));
538 assert_eq!(Vector2::new(0, 1).y_flipped(), Vector2::new(0, -1));
539 assert_eq!(Vector2::new(2, 3).y_flipped(), Vector2::new(2, -3));
540 }
541 #[test]
542 fn flipped() {
543 assert!(!Trans::new(false, -3).is_flipped());
544 assert!(!Trans::new(false, -2).is_flipped());
545 assert!(!Trans::new(false, -1).is_flipped());
546 assert!(!Trans::new(false, 0).is_flipped());
547 assert!(!Trans::new(false, 1).is_flipped());
548 assert!(!Trans::new(false, 2).is_flipped());
549 assert!(!Trans::new(false, 3).is_flipped());
550 assert!(!Trans::new(false, 4).is_flipped());
551 assert!(!Trans::new(false, 5).is_flipped());
552 assert!(Trans::new(true, -3).is_flipped());
553 assert!(Trans::new(true, -2).is_flipped());
554 assert!(Trans::new(true, -1).is_flipped());
555 assert!(Trans::new(true, 0).is_flipped());
556 assert!(Trans::new(true, 1).is_flipped());
557 assert!(Trans::new(true, 2).is_flipped());
558 assert!(Trans::new(true, 3).is_flipped());
559 assert!(Trans::new(true, 4).is_flipped());
560 assert!(Trans::new(true, 5).is_flipped());
561 }
562 #[test]
563 fn rotate_amount() {
564 assert_eq!(Trans::new(false, -3).rotation(), 1);
565 assert_eq!(Trans::new(false, -2).rotation(), 2);
566 assert_eq!(Trans::new(false, -1).rotation(), 3);
567 assert_eq!(Trans::new(false, 0).rotation(), 0);
568 assert_eq!(Trans::new(false, 1).rotation(), 1);
569 assert_eq!(Trans::new(false, 2).rotation(), 2);
570 assert_eq!(Trans::new(false, 3).rotation(), 3);
571 assert_eq!(Trans::new(false, 4).rotation(), 0);
572 assert_eq!(Trans::new(false, 5).rotation(), 1);
573 }
574 #[test]
575 fn flipped_rotate_amount() {
576 assert_eq!(Trans::new(true, -3).rotation(), 1);
577 assert_eq!(Trans::new(true, -2).rotation(), 2);
578 assert_eq!(Trans::new(true, -1).rotation(), 3);
579 assert_eq!(Trans::new(true, 0).rotation(), 0);
580 assert_eq!(Trans::new(true, 1).rotation(), 1);
581 assert_eq!(Trans::new(true, 2).rotation(), 2);
582 assert_eq!(Trans::new(true, 3).rotation(), 3);
583 assert_eq!(Trans::new(true, 4).rotation(), 0);
584 assert_eq!(Trans::new(true, 5).rotation(), 1);
585 }
586 #[test]
587 fn double_x_flip() {
588 assert_eq!(Trans::identity(), Trans::identity().x_flipped().x_flipped())
589 }
590 #[test]
591 fn double_y_flip() {
592 assert_eq!(Trans::identity(), Trans::identity().y_flipped().y_flipped())
593 }
594 #[test]
595 fn cell_from_position_0() {
596 assert_eq!(
597 Cell::from_position(Vector2::new(0, 0)),
598 Cell(Vector2::new(0, 0), Trans::identity())
599 );
600 }
601 #[test]
602 fn cell_from_position_1() {
603 assert_eq!(
604 Cell::from_position(Vector2::new(1, 0)),
605 Cell(Vector2::new(0, 0), Trans::identity().rotated(1))
606 );
607 }
608 #[test]
609 fn cell_from_position_2() {
610 assert_eq!(
611 Cell::from_position(Vector2::new(2, 0)),
612 Cell(Vector2::new(0, 0), Trans::identity().x_flipped().rotated(3))
613 );
614 }
615 #[test]
616 fn cell_from_position_negative_0() {
617 assert_eq!(
618 Cell::from_position(Vector2::new(0, -2)),
619 Cell(Vector2::new(0, -2), Trans::identity())
620 );
621 }
622 #[test]
623 fn cell_into_position_0() {
624 assert_eq!(
625 Cell(Vector2::new(0, 0), Trans::identity().rotated(0)).into_position(),
626 Vector2::new(0, 0),
627 );
628 assert_eq!(
629 Cell(Vector2::new(0, 0), Trans::identity().rotated(1)).into_position(),
630 Vector2::new(1, 0),
631 );
632 assert_eq!(
633 Cell(Vector2::new(0, 0), Trans::identity().rotated(2)).into_position(),
634 Vector2::new(1, 1),
635 );
636 assert_eq!(
637 Cell(Vector2::new(0, 0), Trans::identity().rotated(3)).into_position(),
638 Vector2::new(0, 1),
639 );
640 }
641 #[test]
642 fn cell_into_position_1() {
643 assert_eq!(
644 Cell(Vector2::new(0, 0), Trans::identity().x_flipped().rotated(0)).into_position(),
645 Vector2::new(3, 0),
646 );
647 assert_eq!(
648 Cell(Vector2::new(0, 0), Trans::identity().x_flipped().rotated(1)).into_position(),
649 Vector2::new(3, 1),
650 );
651 assert_eq!(
652 Cell(Vector2::new(0, 0), Trans::identity().x_flipped().rotated(2)).into_position(),
653 Vector2::new(2, 1),
654 );
655 assert_eq!(
656 Cell(Vector2::new(0, 0), Trans::identity().x_flipped().rotated(3)).into_position(),
657 Vector2::new(2, 0),
658 );
659 }
660 #[test]
661 fn cell_into_position_negative_0() {
662 assert_eq!(
663 Cell(Vector2::new(0, -2), Trans::identity().rotated(0)).into_position(),
664 Vector2::new(0, -2),
665 );
666 assert_eq!(
667 Cell(Vector2::new(0, -2), Trans::identity().rotated(1)).into_position(),
668 Vector2::new(1, -2),
669 );
670 assert_eq!(
671 Cell(Vector2::new(0, -2), Trans::identity().rotated(2)).into_position(),
672 Vector2::new(1, -1),
673 );
674 assert_eq!(
675 Cell(Vector2::new(0, -2), Trans::identity().rotated(3)).into_position(),
676 Vector2::new(0, -1),
677 );
678 }
679 #[test]
680 fn cell_uniqueness() {
681 let mut set = fxhash::FxHashSet::<Vector2<i32>>::default();
682 for t in Trans::all() {
683 set.insert(Cell(Vector2::new(0, 0), t).into_position());
684 }
685 assert_eq!(set.len(), 8, "{set:?}");
686 }
687 #[test]
688 fn cell_correctness() {
689 for x in 0..4 {
690 for y in 0..2 {
691 assert_eq!(
692 Cell::from_position(Vector2::new(x, y)).0,
693 Vector2::new(0, 0),
694 "{x}, {y}"
695 );
696 }
697 }
698 for x in 4..8 {
699 for y in -2..0 {
700 assert_eq!(
701 Cell::from_position(Vector2::new(x, y)).0,
702 Vector2::new(4, -2),
703 "{x}, {y}"
704 );
705 }
706 }
707 }
708 #[test]
709 fn cell_from_position_and_back() {
710 for x in -3..4 {
711 for y in -2..4 {
712 let p = Vector2::new(x, y);
713 assert_eq!(
714 Cell::from_position(p).into_position(),
715 p,
716 "Cell: {:?}",
717 Cell::from_position(p)
718 );
719 }
720 }
721 }
722}