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
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::prelude::BaseMap;
use bracket_geometry::prelude::Point3;

/// Implement these for handling conversion to/from 2D coordinates (they are separate, because you might
/// want Dwarf Fortress style 3D!)
pub trait Algorithm3D: BaseMap {
    /// Convert a Point (x/y) to an array index. Defaults to a Z, Y, X striding.
    #[allow(clippy::cast_sign_loss)]
    fn point3d_to_index(&self, pt: Point3) -> usize {
        let bounds = self.dimensions();
        ((pt.z * (bounds.x * bounds.y)) + (pt.y * bounds.x) + pt.x) as usize
    }

    /// Convert an array index to a point.
    #[allow(clippy::cast_possible_wrap)]
    #[allow(clippy::cast_possible_truncation)]
    fn index_to_point3d(&self, idx: usize) -> Point3 {
        let mut my_idx = idx as i32;
        let bounds = self.dimensions();
        let z = my_idx / (bounds.x * bounds.y);
        my_idx -= z * bounds.x * bounds.y;

        let y = my_idx / bounds.x;
        my_idx -= y * bounds.x;

        let x = my_idx;
        Point3::new(x, y, z)
    }

    /// Dimensions
    fn dimensions(&self) -> Point3 {
        panic!("You must either define the dimensions function (trait Algorithm3D) on your map, or define the various point3d_to_index and index_to_point3d functions.");
    }

    // Optional - check that an x/y/z coordinate is within the map bounds. If not provided,
    // it falls back to using the map's dimensions from that trait implementation. Most of
    // the time, that's what you want.
    fn in_bounds(&self, pos: Point3) -> bool {
        let bounds = self.dimensions();
        pos.x > 0 && pos.x < bounds.x && pos.y > 0 && pos.y < bounds.y && pos.z > 0 && pos.z < bounds.z
    }
}

#[cfg(test)]
mod tests {
    use crate::prelude::{Algorithm3D, BaseMap};
    use bracket_geometry::prelude::Point3;

    #[test]
    // Tests that we make an RGB triplet at defaults and it is black.
    #[should_panic]
    fn test_unimplemented_dimensions() {
        struct TestMap{};
        impl BaseMap for TestMap {}
        impl Algorithm3D for TestMap{}

        let map = TestMap{};
        assert!(map.in_bounds(Point3::new(1,1,1)));
    }

    #[test]
    fn test_in_bounds() {
        struct TestMap{};
        impl BaseMap for TestMap {}
        impl Algorithm3D for TestMap{
            fn dimensions(&self) -> Point3 {
                Point3::new(2, 2, 2)
            }
        }

        let map = TestMap{};
        assert!(map.in_bounds(Point3::new(1,1,1)));
        assert!(!map.in_bounds(Point3::new(3,3,3)));
    }

    #[test]
    fn test_point3d_to_index() {
        struct TestMap{};
        impl BaseMap for TestMap {}
        impl Algorithm3D for TestMap{
            fn dimensions(&self) -> Point3 {
                Point3::new(10, 10, 10)
            }
        }

        let map = TestMap{};
        assert!(map.point3d_to_index(Point3::new(0,0,0)) == 0);
        assert!(map.point3d_to_index(Point3::new(1,0,0)) == 1);
        assert!(map.point3d_to_index(Point3::new(0,1,0)) == 10);
        assert!(map.point3d_to_index(Point3::new(9,9,0)) == 99);
        assert!(map.point3d_to_index(Point3::new(9,9,9)) == 999);
    }

    #[test]
    fn test_index_to_point3d() {
        struct TestMap{};
        impl BaseMap for TestMap {}
        impl Algorithm3D for TestMap{
            fn dimensions(&self) -> Point3 {
                Point3::new(10, 10, 10)
            }
        }

        let map = TestMap{};
        let mut x = 0;
        let mut y = 0;
        let mut z: i32 = 0;
        for i in 0..1000 {
            assert!(map.index_to_point3d(i) == Point3::new(x, y, z));

            x += 1;
            if x > 9 {
                x = 0;
                y += 1;
            }
            if y > 9 {
                y = 0;
                z += 1;
            }
        }
    }
}