1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use de_types::projection::ToFlat;
use glam::{IVec2, Vec2};
use parry3d::bounding_volume::Aabb;

use crate::TILE_SIZE;

/// Iterable rectangular range of tiles.
///
/// The tiles are iterated row-by-row, for example: (1, 1) -> (2, 1) -> (1, 2)
/// -> (2, 2).
pub(super) struct TileRange {
    a: IVec2,
    b: IVec2,
    x: i32,
    y: i32,
    exhausted: bool,
}

impl TileRange {
    /// Creates minimum tile range covers a given AABB.
    ///
    /// Tiles are assumed to be topologically closed. In other words, both
    /// touching and intersecting tiles are included in the range.
    pub(super) fn from_aabb(aabb: &Aabb) -> Self {
        let aabb = aabb.to_flat();
        let min_flat: Vec2 = aabb.mins.into();
        let max_flat: Vec2 = aabb.maxs.into();
        let start = (min_flat / TILE_SIZE).floor().as_ivec2();
        let stop = (max_flat / TILE_SIZE).floor().as_ivec2();
        Self::new(start, stop)
    }

    /// # Arguments
    ///
    /// * `a` - inclusive range start.
    ///
    /// * `b` - inclusive range end.
    pub(super) fn new(a: IVec2, b: IVec2) -> Self {
        Self {
            a,
            b,
            x: a.x,
            y: a.y,
            exhausted: a.cmpgt(b).any(),
        }
    }

    /// Returns true if the given point is not contained in the tile range.
    pub(super) fn excludes(&self, point: IVec2) -> bool {
        self.a.cmpgt(point).any() || self.b.cmplt(point).any()
    }

    /// Returns intersecting tile range. The result might be empty.
    pub(super) fn intersection(&self, other: &TileRange) -> TileRange {
        Self::new(self.a.max(other.a), self.b.min(other.b))
    }
}

impl PartialEq for TileRange {
    fn eq(&self, other: &Self) -> bool {
        self.a == other.a && self.b == other.b
    }
}

impl Eq for TileRange {}

impl Iterator for TileRange {
    type Item = IVec2;

    fn next(&mut self) -> Option<IVec2> {
        if self.exhausted {
            return None;
        }

        let next = Some(IVec2::new(self.x, self.y));
        if self.x == self.b.x {
            if self.y == self.b.y {
                self.exhausted = true;
            } else {
                self.x = self.a.x;
                self.y += 1;
            }
        } else {
            self.x += 1;
        }
        next
    }
}