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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! Offset Coordinates

use super::*;

//////////////////////////////////////////////////////////////////////////////
// Primary Structure
//////////////////////////////////////////////////////////////////////////////

/// `Offset` coordinates treat the `HexGrid` as a square grid with offsetting
/// indentations on the rows/columns.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Offset {
    pub col: i32,
    pub row: i32,
}

//////////////////////////////////////////////////////////////////////////////
// Traits: From & Into
//////////////////////////////////////////////////////////////////////////////

/// Create an `Offset` from an `(i32, i32)`.
impl From<(i32, i32)> for Offset {
    fn from((col, row): (i32, i32)) -> Self {
        Self {
            col: col,
            row: row,
        }
    }
}

/// Create an `Offset` from a `MultiCoord`.
///
/// # Panics
///
/// Panics when given a non-`Offset` `MultiCoord`.
impl From<MultiCoord> for Offset {
    fn from(coord: MultiCoord) -> Self {
        if coord.sys == CoordSys::Offset {
            Offset { col: coord.a, row: coord.b }
        } else {
            panic!("{:?} is not an Offset coordinate", coord)
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////////////

impl Offset {
    //////////////////////////////////
    // Constants
    //////////////////////////////////

    /// `Offset` coordinate origin of (0, 0).
    pub const ORIGIN: Offset = Offset { col: 0, row: 0 };

    //////////////////////////////////
    // Initialization
    //////////////////////////////////

    /// Create an `Offset` from two `i32` values.
    ///
    /// # Examples
    ///
    /// ```
    /// use chickenwire::coordinate::offset::Offset;
    ///
    /// assert_eq!(
    ///     Offset::from_coords(1, 2),
    ///     Offset { col: 1, row: 2 }
    /// );
    /// ```
    pub fn from_coords(col: i32, row: i32) -> Self {
        Self::from((col, row))
    }

    //////////////////////////////////
    // Conversion
    //////////////////////////////////

    /// Converts an `Offset` to a `Cube`, assuming the `HexGrid` has
    /// `Parity::Odd` and `Tilt::Flat` parameters.
    pub fn oflat_to_cube(self) -> Cube {
        let x = self.col;
        let z = self.row - (self.col - (self.col & 1)) / 2;
        let y = 0 - x - z;

        Cube::force_from_coords(x, y, z)
    }

    /// Converts an `Offset` to a `Cube`, assuming the `HexGrid` has
    /// `Parity::Even` and `Tilt::Flat` parameters.
    pub fn eflat_to_cube(self) -> Cube {
        let x = self.col;
        let z = self.row - (self.col + (self.col & 1)) / 2;
        let y = 0 - x - z;

        Cube::force_from_coords(x, y, z)
    }

    /// Converts an `Offset` to a `Cube`, assuming the `HexGrid` has
    /// `Parity::Odd` and `Tilt::Sharp` parameters.
    pub fn osharp_to_cube(self) -> Cube {
        let x = self.col - (self.row - (self.row & 1)) / 2;
        let z = self.row;
        let y = 0 - x - z;

        Cube::force_from_coords(x, y, z)
    }

    /// Converts an `Offset` to a `Cube`, assuming the `HexGrid` has
    /// `Parity::Even` and `Tilt::Sharp` parameters.
    pub fn esharp_to_cube(self) -> Cube {
        let x = self.col - (self.row + (self.row & 1)) / 2;
        let z = self.row;
        let y = 0 - x - z;

        Cube::force_from_coords(x, y, z)
    }

    //////////////////////////////////
    // Neighbors
    //////////////////////////////////

    fn offset_map(
        self,
        offsets: [[[i32; 2]; 6]; 2],
        parity_check: i32
    ) -> Vec<Self> {
        let mut neighbors = Vec::new();

        for side in 0..6 {
            let parity: usize = (parity_check & 1) as usize;
            let offset_pair = offsets[parity][side];

            let col = self.col + offset_pair[0];
            let row = self.row + offset_pair[1];

            neighbors.push(Offset { col: col, row: row });
        }

        neighbors
    }

    /// Calculates the `Offset` coordinates of the hexes surrounding the
    /// calling instance, in the context of a `HexGrid` with `Parity::Odd` and
    /// `Tilt::Flat` parameters.
    pub fn oflat_neighbors(self) -> Vec<Self> {
        let offsets = [
            [[1, -1], [1, 0], [0, 1], [-1, 0], [-1, -1], [0, -1]],
            [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [0, -1]],
        ];

        self.offset_map(offsets, self.col)
    }

    /// Calculates the `Offset` coordinates of the hexes surrounding the
    /// calling instance, in the context of a `HexGrid` with `Parity::Even`
    /// and `Tilt::Flat` parameters.
    pub fn eflat_neighbors(self) -> Vec<Self> {
        let offsets = [
            [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [0, -1]],
            [[1, -1], [1, 0], [0, 1], [-1, 0], [-1, -1], [0, -1]],
        ];

        self.offset_map(offsets, self.col)
    }

    /// Calculates the `Offset` coordinates of the hexes surrounding the
    /// calling instance, in the context of a `HexGrid` with `Parity::Odd` and
    /// `Tilt::Sharp` parameters.
    pub fn osharp_neighbors(self) -> Vec<Self> {
        let offsets = [
            [[0, -1], [1, 0], [0, 1], [-1, 1], [-1, 0], [-1, -1]],
            [[1, -1], [1, 0], [1, 1], [0, 1], [-1, 0], [0, -1]],
        ];

        self.offset_map(offsets, self.row)
    }

    /// Calculates the `Offset` coordinates of the hexes surrounding the
    /// calling instance, in the context of a `HexGrid` with `Parity::Even`
    /// and `Tilt::Sharp` parameters.
    pub fn esharp_neighbors(self) -> Vec<Self> {
        let offsets = [
            [[1, -1], [1, 0], [1, 1], [0, 1], [-1, 0], [0, -1]],
            [[0, -1], [1, 0], [0, 1], [-1, 1], [-1, 0], [-1, -1]],
        ];

        self.offset_map(offsets, self.row)
    }

    //////////////////////////////////
    // Distances
    //////////////////////////////////

    /// Calculates the distance between two `Offset` coordinates.
    pub fn dist(self, other: Self) -> i32 {
        self.eflat_to_cube().dist(other.eflat_to_cube())
    }
}