generated from tipragot/rust
Remake utils for hexagon grids #55
|
@ -1,8 +1,8 @@
|
||||||
//! All functions related to calculations in a hexagonal grid.
|
//! 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,
|
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
|
||||||
}};
|
};
|
||||||
|
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
|
|
||||||
|
@ -65,6 +65,12 @@ pub trait Number:
|
||||||
|
|
||||||
/// Converts an `f32` to `Self`.
|
/// Converts an `f32` to `Self`.
|
||||||
fn from_f32(value: f32) -> 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.
|
/// Implements the `Number` trait for the given types.
|
||||||
|
@ -93,6 +99,14 @@ macro_rules! number_impl {
|
||||||
value as $t
|
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.
|
/// 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)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct HexPosition<T: Number>(pub T, pub T);
|
pub struct HexPosition<T: Number>(pub T, pub T);
|
||||||
|
|
||||||
|
@ -130,7 +146,16 @@ pub enum HexDirection {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
pub const fn to_vector<T: Number>(self) -> HexPosition<T> {
|
||||||
match self {
|
match self {
|
||||||
Self::Right => HexPosition(T::ONE, T::ZERO),
|
Self::Right => HexPosition(T::ONE, T::ZERO),
|
||||||
|
@ -211,6 +236,7 @@ impl<T: Number> Iterator for HexSpiral<T> {
|
||||||
type Item = HexPosition<T>;
|
type Item = HexPosition<T>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// The origin of the spiral.
|
||||||
if self.index == 0 {
|
if self.index == 0 {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
return Some(self.origin);
|
return Some(self.origin);
|
||||||
|
@ -229,12 +255,24 @@ impl<T: Number> Iterator for HexSpiral<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Number> HexPosition<T> {
|
impl<T: Number> HexPosition<T> {
|
||||||
/// Creates a new hexagonal position.
|
/// Converts the current [HexPosition] into a pixel coordinate.
|
||||||
pub const fn new(x: T, y: T) -> Self {
|
/// Input: The size of the hexagon in pixels (witdh, height).
|
||||||
Self(x, y)
|
///
|
||||||
}
|
/// If you want to learn more about pixel coordinates conversion,
|
||||||
|
/// you can check the
|
||||||
/// Returns the pixel coordinates of the hexagonal position.
|
/// [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) {
|
pub fn to_pixel_coordinates(&self, size: (f32, f32)) -> (f32, f32) {
|
||||||
(
|
(
|
||||||
size.0
|
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 {
|
pub fn distance(self, other: Self) -> T {
|
||||||
let Self(x, y) = self - other;
|
let Self(x, y) = self - other;
|
||||||
x.abs() + y.abs() + (x + y).abs() / T::TWO
|
x.abs() + y.abs() + (x + y).abs() / T::TWO
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range(&self, range: T) -> HashSet<Self> {
|
/// Returns all positions within a given `range` from the current
|
||||||
let mut result_positions = HashSet::new();
|
/// `HexPosition`.
|
||||||
for q in num::range_inclusive(-range, range) {
|
///
|
||||||
for r in num::range_inclusive((-range, -q - range), min(range, -q + range)) {
|
/// This function iterates over the possible q and r values within the
|
||||||
result_positions.insert(Self { q, r });
|
/// 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
|
result_positions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the hexagonal ring of the given radius.
|
/// 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> {
|
pub fn ring(self, radius: usize) -> HexRing<T> {
|
||||||
HexRing {
|
HexRing {
|
||||||
current: self + HexDirection::DownLeft.to_vector() * T::from_usize(radius),
|
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.
|
/// 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> {
|
pub fn spiral(self, radius: usize) -> HexSpiral<T> {
|
||||||
HexSpiral {
|
HexSpiral {
|
||||||
origin: self,
|
origin: self,
|
||||||
|
|
Loading…
Reference in a new issue