hex_grid 0.2.1

A library to easily work with 2d hex grids of arbitrary shapes
Documentation
use std::ops::{Add, Sub, Neg};

pub use super::offset::*;
use PixelOrientation;
use HexSize;

pub const CENTER: Coordinate = Coordinate { x: 0, y: 0 };

#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
pub struct Coordinate {
    pub x: i32,
    pub y: i32,
}

impl Coordinate {
    pub fn z(&self) -> i32 {
        Self::calculate_3rd((self.x, self.y))
    }

    fn x32(&self) -> f32 {
        self.x as f32
    }
    fn y32(&self) -> f32 {
        self.y as f32
    }

    pub fn to_pixel(&self, hex_size: HexSize, pixel_orientation: PixelOrientation) -> (f32, f32) {
        let px = hex_size.width() * (self.x32() + self.y32() / 2.0);
        let py = hex_size.height() * -0.75 * self.y32();
        pixel_orientation.orient((px, py))
    }

    pub fn from_pixel((px, py): (f32, f32), hex_size: HexSize, pixel_orientation: PixelOrientation) -> Coordinate {
        let (px, py) = pixel_orientation.orient((px, py));
        let hy = py / (hex_size.height() * -0.75);
        let hx = px / hex_size.width() - hy / 2.0;
        Self::round_to_nearest((hx, hy))
    }

    fn round_to_nearest((x, y): (f32, f32)) -> Coordinate {
        let z = Self::calculate_3rd((x, y));

        let mut rx = x.round();
        let mut ry = y.round();
        let rz = z.round();

        let delta_x = (rx - x).abs();
        let delta_y = (ry - y).abs();
        let delta_z = (rz - z).abs();

        if delta_x > delta_y && delta_x > delta_z {
            rx = Self::calculate_3rd((ry, rz));
        } else if delta_y > delta_z {
            ry = Self::calculate_3rd((rx, rz));
        }
        Coordinate {
            x: rx as i32,
            y: ry as i32,
        }
    }

    fn calculate_3rd<T: Neg<Output=T> + Sub<Output=T>>((x, y): (T, T)) -> T {
        -x - y
    }
}

impl From<(i32, i32)> for Coordinate {
    fn from((x, y): (i32, i32)) -> Self {
        Coordinate { x, y }
    }
}

impl<O: Into<Offset>> Add<O> for Coordinate {
    type Output = Coordinate;

    fn add(self, offset: O) -> Coordinate {
        let offset = offset.into();
        Coordinate {
            x: self.x + offset.x,
            y: self.y + offset.y,
        }
    }
}

impl Add<Vec<Offset>> for Coordinate {
    type Output = Vec<Coordinate>;

    fn add(self, offsets: Vec<Offset>) -> Self::Output {
        offsets.iter().map(|offset| *offset + self).collect()
    }
}

impl Add<Coordinate> for Vec<Offset> {
    type Output = Vec<Coordinate>;

    fn add(self, coord: Coordinate) -> Self::Output {
        coord + self
    }
}

impl<C: Into<Coordinate>> Sub<C> for Coordinate {
    type Output = Offset;

    fn sub(self, other: C) -> Offset {
        let other = other.into();
        Offset {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

#[test]
fn test_coord_plus_offset_list() {
    let coord = CENTER + UP_RIGHT;
    let offsets = vec!(LEFT, ZERO_OFFSET, RIGHT);
    let coords = coord + offsets.clone();
    assert_eq!(coords, vec!(coord + LEFT, coord + ZERO_OFFSET, RIGHT + coord));
    assert_eq!(coord + offsets.clone(), offsets.clone() + coord);
}

#[test]
fn test_sub() {
    let offset = (CENTER + RIGHT) - (0, 0);
    assert_eq!(offset, Offset::from((1, 0)));
}

#[test]
fn test_to_pixel() {
    let hex_size = HexSize::from_regular_height(1.0);
    let pixel_orientation = PixelOrientation { right_increasing: true, up_increasing: true };

    assert_eq!(CENTER.to_pixel(hex_size, pixel_orientation), (0.0, 0.0));
    assert_eq!((CENTER + RIGHT).to_pixel(hex_size, pixel_orientation), ((0.8660254, 0.0)));
    assert_eq!((CENTER + LEFT).to_pixel(hex_size, pixel_orientation), ((-0.8660254, 0.0)));
    assert_eq!((CENTER + UP_RIGHT).to_pixel(hex_size, pixel_orientation), ((0.4330127, 0.75)));
    assert_eq!((CENTER + DOWN_LEFT * 2).to_pixel(hex_size, pixel_orientation), ((-0.8660254, -1.5)));
}

#[test]
fn test_from_pixel() {
    let hex_size = HexSize::from_regular_height(1.0);
    let pixel_orientation = PixelOrientation { right_increasing: true, up_increasing: true };

    assert_eq!(Coordinate::from_pixel((0.0, 0.0), hex_size, pixel_orientation), CENTER);
    assert_eq!(Coordinate::from_pixel((0.8660254, 0.0), hex_size, pixel_orientation), CENTER + RIGHT);
    assert_eq!(Coordinate::from_pixel((-0.8660254, 0.0), hex_size, pixel_orientation), CENTER + LEFT);
    assert_eq!(Coordinate::from_pixel((0.4330127, 0.75), hex_size, pixel_orientation), CENTER + UP_RIGHT);
    assert_eq!(Coordinate::from_pixel((-0.8660254, -1.5), hex_size, pixel_orientation), CENTER + DOWN_LEFT * 2);
}