generated from tipragot/rust
Remake utils for hexagon grids #55
|
@ -1,8 +1,8 @@
|
|||
//! All functions related to calculations in a hexagonal grid.
|
||||
|
||||
use std::{collections::HashSet, ops::{
|
||||
use std::ops::{
|
||||
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
|
||||
}};
|
||||
};
|
||||
|
||||
use paste::paste;
|
||||
|
||||
|
@ -65,6 +65,12 @@ pub trait Number:
|
|||
|
||||
/// Converts an `f32` to `Self`.
|
||||
fn from_f32(value: f32) -> Self;
|
||||
|
||||
/// Converts `self` to an `isize`.
|
||||
fn to_isize(self) -> isize;
|
||||
|
||||
/// Converts an `isize` to `Self`.
|
||||
fn from_isize(value: isize) -> Self;
|
||||
}
|
||||
|
||||
/// Implements the `Number` trait for the given types.
|
||||
|
@ -93,6 +99,14 @@ macro_rules! number_impl {
|
|||
value as $t
|
||||
}
|
||||
|
||||
fn to_isize(self) -> isize {
|
||||
self as isize
|
||||
}
|
||||
|
||||
fn from_isize(value: isize) -> Self {
|
||||
value as $t
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
)*}};
|
||||
|
@ -104,6 +118,8 @@ number_impl! {
|
|||
}
|
||||
|
||||
/// Represents a position in a hexagonal grid.
|
||||
/// We use the axial coordinate system explained in this
|
||||
/// [documentation](https://www.redblobgames.com/grids/hexagons/#coordinates).
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct HexPosition<T: Number>(pub T, pub T);
|
||||
|
||||
|
@ -130,7 +146,16 @@ pub enum HexDirection {
|
|||
}
|
||||
|
||||
impl HexDirection {
|
||||
/// Returns the vector of the direction.
|
||||
/// Returns the vector ([HexPosition]) of the direction.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use border_wars::map::hex::{HexDirection, HexPosition};
|
||||
///
|
||||
/// let direction = HexDirection::Right;
|
||||
/// assert_eq!(direction.to_vector(), HexPosition(1, 0));
|
||||
/// ```
|
||||
pub const fn to_vector<T: Number>(self) -> HexPosition<T> {
|
||||
match self {
|
||||
Self::Right => HexPosition(T::ONE, T::ZERO),
|
||||
|
@ -211,6 +236,7 @@ impl<T: Number> Iterator for HexSpiral<T> {
|
|||
type Item = HexPosition<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// The origin of the spiral.
|
||||
if self.index == 0 {
|
||||
self.index += 1;
|
||||
return Some(self.origin);
|
||||
|
@ -229,12 +255,24 @@ impl<T: Number> Iterator for HexSpiral<T> {
|
|||
}
|
||||
|
||||
impl<T: Number> HexPosition<T> {
|
||||
/// Creates a new hexagonal position.
|
||||
pub const fn new(x: T, y: T) -> Self {
|
||||
Self(x, y)
|
||||
}
|
||||
|
||||
/// Returns the pixel coordinates of the hexagonal position.
|
||||
/// Converts the current [HexPosition] into a pixel coordinate.
|
||||
/// Input: The size of the hexagon in pixels (witdh, height).
|
||||
///
|
||||
/// If you want to learn more about pixel coordinates conversion,
|
||||
/// you can check the
|
||||
/// [documentation](https://www.redblobgames.com/grids/hexagons/#hex-to-pixel).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use border_wars::map::hex::HexPosition;
|
||||
///
|
||||
/// let position = HexPosition(1, 0);
|
||||
/// assert_eq!(
|
||||
/// position.to_pixel_coordinates((1.0, 1.0)),
|
||||
/// (3f32.sqrt(), 0.0)
|
||||
/// );
|
||||
/// ```
|
||||
pub fn to_pixel_coordinates(&self, size: (f32, f32)) -> (f32, f32) {
|
||||
(
|
||||
size.0
|
||||
|
@ -245,23 +283,79 @@ impl<T: Number> HexPosition<T> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Returns the distance between two hexagonal positions.
|
||||
/// Returns the distance between two [HexPosition]s.
|
||||
///
|
||||
/// # How it works
|
||||
///
|
||||
/// In the hexagonal grid, using the
|
||||
/// [cube coordinate system](https://www.redblobgames.com/grids/hexagons/#coordinates),
|
||||
/// it's akin to a cube in 3D space.
|
||||
/// The Manhattan distance between two positions is equal to half of
|
||||
/// the sum of abs(dx) + abs(dy) + abs(dz).
|
||||
/// However, in hexagonal grids, z is defined as -q - r.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use border_wars::map::hex::HexPosition;
|
||||
///
|
||||
/// let a = HexPosition(0, 0);
|
||||
/// let b = HexPosition(1, 1);
|
||||
///
|
||||
/// assert_eq!(a.distance(b), 2);
|
||||
/// ```
|
||||
pub fn distance(self, other: Self) -> T {
|
||||
let Self(x, y) = self - other;
|
||||
x.abs() + y.abs() + (x + y).abs() / T::TWO
|
||||
}
|
||||
|
||||
pub fn range(&self, range: T) -> HashSet<Self> {
|
||||
let mut result_positions = HashSet::new();
|
||||
for q in num::range_inclusive(-range, range) {
|
||||
for r in num::range_inclusive((-range, -q - range), min(range, -q + range)) {
|
||||
result_positions.insert(Self { q, r });
|
||||
/// Returns all positions within a given `range` from the current
|
||||
/// `HexPosition`.
|
||||
///
|
||||
/// This function iterates over the possible q and r values within the
|
||||
/// specified range.
|
||||
/// Note that the original position is also returned.
|
||||
///
|
||||
/// For more details, refer to: https://www.redblobgames.com/grids/hexagons/#range
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use border_wars::map::hex::HexPosition;
|
||||
///
|
||||
/// let position = HexPosition(0, 0);
|
||||
/// let range = 1;
|
||||
///
|
||||
/// let positions = position.range(range);
|
||||
///
|
||||
/// assert_eq!(positions.len(), 7);
|
||||
/// ```
|
||||
pub fn range(&self, range: isize) -> Vec<Self> {
|
||||
let mut result_positions = Vec::new();
|
||||
for q in -range..=range {
|
||||
for r in Number::min(-range, -q - range)..=Number::min(range, -q + range) {
|
||||
result_positions.push(Self(T::from_isize(q), T::from_isize(r)));
|
||||
}
|
||||
}
|
||||
result_positions
|
||||
}
|
||||
|
||||
/// Returns the hexagonal ring of the given radius.
|
||||
/// If you want to learn more about hexagonal grids, check the
|
||||
/// [documentation](https://www.redblobgames.com/grids/hexagons/#rings)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use border_wars::map::hex::HexPosition;
|
||||
///
|
||||
/// let position = HexPosition(0, 0);
|
||||
/// let radius = 1;
|
||||
///
|
||||
/// for ring_position in position.ring(radius) {
|
||||
/// println!("{:?}", ring_position);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn ring(self, radius: usize) -> HexRing<T> {
|
||||
HexRing {
|
||||
current: self + HexDirection::DownLeft.to_vector() * T::from_usize(radius),
|
||||
|
@ -272,6 +366,21 @@ impl<T: Number> HexPosition<T> {
|
|||
}
|
||||
|
||||
/// Returns the hexagonal spiral of the given radius.
|
||||
/// If you want to learn more about hexagonal grids, check the
|
||||
/// [documentation](https://www.redblobgames.com/grids/hexagons/#rings-spiral)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use border_wars::map::hex::HexPosition;
|
||||
///
|
||||
/// let position = HexPosition(0, 0);
|
||||
/// let radius = 1;
|
||||
///
|
||||
/// for spiral_position in position.spiral(radius) {
|
||||
/// println!("{:?}", spiral_position);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn spiral(self, radius: usize) -> HexSpiral<T> {
|
||||
HexSpiral {
|
||||
origin: self,
|
||||
|
|
Loading…
Reference in a new issue