hexx/hex/
mod.rs

1#![allow(clippy::inline_always)]
2/// Type conversions
3mod convert;
4/// Hexagonal grid utilities, like edge and vertices
5#[cfg(feature = "grid")]
6pub mod grid;
7/// Traits implementations
8mod impls;
9/// Iterator tools module
10mod iter;
11/// Hex ring utils
12mod rings;
13/// swizzle utils
14mod siwzzle;
15#[cfg(test)]
16mod tests;
17
18pub(crate) use iter::ExactSizeHexIterator;
19pub use iter::HexIterExt;
20
21use crate::{DirectionWay, EdgeDirection, VertexDirection};
22use glam::{IVec2, IVec3, Vec2};
23#[cfg(feature = "grid")]
24pub use grid::{GridEdge, GridVertex};
25use std::{
26    cmp::{max, min},
27    fmt::Debug,
28};
29
30/// Hexagonal [axial] coordinates
31///
32/// # Why Axial ?
33///
34/// Axial coordinates allow to compute and use *cubic* coordinates with less
35/// storage, and allow:
36/// - Vector operations
37/// - Rotations
38/// - Symmetry
39/// - Simple algorithms
40///
41/// when *offset* and *doubled* coordinates don't. Furthermore, it makes the
42/// [`Hex`] behave like classic 2D coordinates ([`IVec2`]) and therefore more
43/// user friendly.
44///
45/// Check out this [comparison] article for more information.
46///
47/// # Conversions
48///
49///  * Cubic: use [`Self::z`] to compute the third axis
50///  * Offset: use [`Self::from_offset_coordinates`] and
51///    [`Self::to_offset_coordinates`]
52///  * Doubled: use [`Self::from_doubled_coordinates`] and
53///    [`Self::to_doubled_coordinates`]
54///
55/// [comparison]: https://www.redblobgames.com/grids/hexagons/#coordinates-comparison
56/// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial
57#[derive(Copy, Clone, Default, Eq, PartialEq)]
58#[cfg_attr(not(target_arch = "spirv"), derive(Hash))]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[cfg_attr(feature = "packed", repr(C))]
61#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
62pub struct Hex {
63    /// `x` axial coordinate (sometimes called `q` or `i`)
64    pub x: i32,
65    /// `y` axial coordinate (sometimes called `r` or `j`)
66    pub y: i32,
67}
68
69#[inline(always)]
70#[must_use]
71/// Instantiates a new hexagon from axial coordinates
72///
73/// # Example
74///
75/// ```rust
76/// # use hexx::*;
77/// let coord = hex(3, 5);
78/// assert_eq!(coord.x, 3);
79/// assert_eq!(coord.y, 5);
80/// assert_eq!(coord.z(), -3 - 5);
81/// ```
82pub const fn hex(x: i32, y: i32) -> Hex {
83    Hex::new(x, y)
84}
85
86impl Hex {
87    /// (0, 0)
88    pub const ORIGIN: Self = Self::ZERO;
89    /// (0, 0)
90    pub const ZERO: Self = Self::new(0, 0);
91    /// (1, 1)
92    pub const ONE: Self = Self::new(1, 1);
93    /// (-1, -1)
94    pub const NEG_ONE: Self = Self::new(-1, -1);
95
96    /// +X (Q) (1, 0)
97    pub const X: Self = Self::new(1, 0);
98    /// -X (-Q) (-1, 0)
99    pub const NEG_X: Self = Self::new(-1, 0);
100    /// +Y (R) (0, 1)
101    pub const Y: Self = Self::new(0, 1);
102    /// -Y (-R) (0, -1)
103    pub const NEG_Y: Self = Self::new(0, -1);
104
105    /// Unit vectors that increase the X axis in clockwise order
106    pub const INCR_X: [Self; 2] = [Self::new(1, 0), Self::new(1, -1)];
107    /// Unit vectors that increase the Y axis in clockwise order
108    pub const INCR_Y: [Self; 2] = [Self::new(0, 1), Self::new(-1, 1)];
109    /// Unit vectors that increase the Z axis in clockwise order
110    pub const INCR_Z: [Self; 2] = [Self::new(-1, 0), Self::new(0, -1)];
111
112    /// Unit vectors that decrease the X axis in clockwise order
113    pub const DECR_X: [Self; 2] = [Self::new(-1, 0), Self::new(-1, 1)];
114    /// Unit vectors that decrease the Y axis in clockwise order
115    pub const DECR_Y: [Self; 2] = [Self::new(0, -1), Self::new(1, -1)];
116    /// Unit vectors that decrease the Z axis in clockwise order
117    pub const DECR_Z: [Self; 2] = [Self::new(1, 0), Self::new(0, 1)];
118
119    /// Hexagon edge neighbor coordinates array, following [`EdgeDirection`]
120    /// order
121    ///
122    /// ```txt
123    ///       Z    ___   -Y
124    ///           /   \
125    ///       +--+  4  +--+
126    ///      / 3  \___/  5 \
127    ///      \    /   \    /
128    ///  -X   +--+     +--+    X
129    ///      /    \___/    \
130    ///      \ 2  /   \  0 /
131    ///       +--+  1  +--+
132    ///           \___/
133    ///       Y           -Z
134    /// ```
135    ///
136    /// Cubic coordinates:
137    ///
138    /// ```txt
139    ///              (0, -1, 1)
140    ///                  ___
141    ///                 /   \
142    ///  (-1, 0, 1) +--+  4  +--+ (1, -1, 0)
143    ///            / 3  \___/  5 \
144    ///            \    /   \    /
145    ///             +--+     +--+
146    ///            /    \___/    \
147    /// (-1, 1, 0) \ 2  /   \  0 / (1, 0, -1)
148    ///             +--+  1  +--+
149    ///                 \___/
150    ///               (0, 1, -1)
151    /// ```
152    pub const NEIGHBORS_COORDS: [Self; 6] = [
153        Self::new(1, 0),
154        Self::new(0, 1),
155        Self::new(-1, 1),
156        Self::new(-1, 0),
157        Self::new(0, -1),
158        Self::new(1, -1),
159    ];
160
161    /// Hexagon diagonal neighbor coordinates array, following
162    /// [`VertexDirection`] order
163    ///
164    /// ```txt
165    ///       Z           -Y
166    ///           \___/
167    ///      \ 4  /   \ 5  /
168    ///       +--+     +--+
169    ///    __/    \___/    \__
170    ///      \    /   \    /
171    /// -X 3  +--+     +--+  0   X
172    ///    __/    \___/    \__
173    ///      \    /   \    /
174    ///       +--+     +--+
175    ///      / 2  \___/  1 \
176    ///
177    ///       Y           -Z
178    /// ```
179    pub const DIAGONAL_COORDS: [Self; 6] = [
180        Self::new(2, -1),
181        Self::new(1, 1),
182        Self::new(-1, 2),
183        Self::new(-2, 1),
184        Self::new(-1, -1),
185        Self::new(1, -2),
186    ];
187
188    #[inline(always)]
189    #[must_use]
190    /// Instantiates a new hexagon from axial coordinates
191    ///
192    /// # Example
193    ///
194    /// ```rust
195    /// # use hexx::*;
196    /// let coord = Hex::new(3, 5);
197    /// assert_eq!(coord.x, 3);
198    /// assert_eq!(coord.y, 5);
199    /// assert_eq!(coord.z(), -3 - 5);
200    /// ```
201    pub const fn new(x: i32, y: i32) -> Self {
202        Self { x, y }
203    }
204
205    #[inline]
206    #[must_use]
207    /// Instantiates a new hexagon with all coordinates set to `v`
208    ///
209    /// # Example
210    ///
211    /// ```rust
212    /// # use hexx::*;
213    /// let coord = Hex::splat(3);
214    /// assert_eq!(coord.x, 3);
215    /// assert_eq!(coord.y, 3);
216    /// assert_eq!(coord.z(), -3 - 3);
217    /// ```
218    pub const fn splat(v: i32) -> Self {
219        Self { x: v, y: v }
220    }
221
222    #[inline]
223    #[must_use]
224    /// Instantiates new hexagonal coordinates in cubic space
225    ///
226    /// # Panics
227    ///
228    /// Will panic if the coordinates are invalid, meaning that the sum of
229    /// coordinates is not equal to zero
230    ///
231    /// # Example
232    ///
233    /// ```rust
234    /// # use hexx::*;
235    /// let coord = Hex::new_cubic(3, 5, -8);
236    /// assert_eq!(coord.x, 3);
237    /// assert_eq!(coord.y, 5);
238    /// assert_eq!(coord.z(), -8);
239    /// ```
240    pub const fn new_cubic(x: i32, y: i32, z: i32) -> Self {
241        assert!(x + y + z == 0);
242        Self { x, y }
243    }
244
245    #[inline]
246    #[must_use]
247    #[doc(alias = "q")]
248    /// `x` coordinate (sometimes called `q` or `i`)
249    pub const fn x(self) -> i32 {
250        self.x
251    }
252
253    #[inline]
254    #[must_use]
255    #[doc(alias = "r")]
256    /// `y` coordinate (sometimes called `r` or `j`)
257    pub const fn y(self) -> i32 {
258        self.y
259    }
260
261    #[inline]
262    #[must_use]
263    #[doc(alias = "s")]
264    /// `z` coordinate (sometimes called `s` or `k`).
265    ///
266    /// This cubic space coordinate is computed as `-x - y`
267    pub const fn z(self) -> i32 {
268        -self.x - self.y
269    }
270
271    #[inline]
272    #[must_use]
273    /// Creates a [`Hex`] from an array
274    ///
275    /// # Example
276    ///
277    /// ```rust
278    /// # use hexx::*;
279    /// let p = Hex::from_array([3, 5]);
280    /// assert_eq!(p.x, 3);
281    /// assert_eq!(p.y, 5);
282    /// ```
283    pub const fn from_array([x, y]: [i32; 2]) -> Self {
284        Self::new(x, y)
285    }
286
287    #[inline]
288    #[must_use]
289    /// Converts `self` to an array as `[x, y]`
290    ///
291    /// # Example
292    ///
293    /// ```rust
294    /// # use hexx::*;
295    /// let coord = Hex::new(3, 5);
296    /// let [x, y] = coord.to_array();
297    /// assert_eq!(x, 3);
298    /// assert_eq!(y, 5);
299    /// ```
300    pub const fn to_array(self) -> [i32; 2] {
301        [self.x, self.y]
302    }
303
304    #[inline]
305    #[must_use]
306    #[allow(clippy::cast_precision_loss)]
307    /// Converts `self` to an [`f32`] array as `[x, y]`
308    pub const fn to_array_f32(self) -> [f32; 2] {
309        [self.x as f32, self.y as f32]
310    }
311
312    #[inline]
313    #[must_use]
314    /// Converts `self` to cubic coordinates array as `[x, y, z]`
315    ///
316    /// # Example
317    ///
318    /// ```rust
319    /// # use hexx::*;
320    /// let coord = Hex::new(3, 5);
321    /// let [x, y, z] = coord.to_cubic_array();
322    /// assert_eq!(x, 3);
323    /// assert_eq!(y, 5);
324    /// assert_eq!(z, -3 - 5);
325    /// ```
326    pub const fn to_cubic_array(self) -> [i32; 3] {
327        [self.x, self.y, self.z()]
328    }
329
330    #[inline]
331    #[must_use]
332    #[allow(clippy::cast_precision_loss)]
333    /// Converts `self` to cubic [`f32`] coordinates array as `[x, y, z]`
334    pub const fn to_cubic_array_f32(self) -> [f32; 3] {
335        [self.x as f32, self.y as f32, self.z() as f32]
336    }
337
338    /// Creates a [`Hex`] from the first 2 values in `slice`.
339    ///
340    /// # Panics
341    ///
342    /// Panics if `slice` is less than 2 elements long.
343    #[inline]
344    #[must_use]
345    pub const fn from_slice(slice: &[i32]) -> Self {
346        Self::new(slice[0], slice[1])
347    }
348
349    /// Writes the elements of `self` to the first 2 elements in `slice`.
350    ///
351    /// # Panics
352    ///
353    /// Panics if `slice` is less than 2 elements long.
354    #[inline]
355    pub fn write_to_slice(self, slice: &mut [i32]) {
356        slice[0] = self.x;
357        slice[1] = self.y;
358    }
359
360    #[must_use]
361    #[inline]
362    /// Converts `self` to an [`IVec2`].
363    /// This operation is a direct mapping of coordinates, no hex to square
364    /// coordinates are performed. To convert hex coordinates to world space
365    /// use [`HexLayout`]
366    ///
367    /// [`HexLayout`]: crate::HexLayout
368    pub const fn as_ivec2(self) -> IVec2 {
369        IVec2 {
370            x: self.x,
371            y: self.y,
372        }
373    }
374
375    #[must_use]
376    #[inline]
377    #[doc(alias = "as_cubic")]
378    /// Converts `self` to an [`IVec3`] using cubic coordinates.
379    /// This operation is a direct mapping of coordinates.
380    /// To convert hex coordinates to world space use [`HexLayout`]
381    ///
382    /// [`HexLayout`]: crate::HexLayout
383    pub const fn as_ivec3(self) -> IVec3 {
384        IVec3 {
385            x: self.x,
386            y: self.y,
387            z: self.z(),
388        }
389    }
390
391    #[allow(clippy::cast_precision_loss)]
392    #[must_use]
393    #[inline]
394    /// Converts `self` to a [`Vec2`].
395    /// This operation is a direct mapping of coordinates.
396    /// To convert hex coordinates to world space use [`HexLayout`]
397    ///
398    /// [`HexLayout`]: crate::HexLayout
399    pub const fn as_vec2(self) -> Vec2 {
400        Vec2 {
401            x: self.x as f32,
402            y: self.y as f32,
403        }
404    }
405
406    #[inline]
407    #[must_use]
408    /// Negates the coordinate, giving its reflection (symmetry) around the
409    /// origin.
410    ///
411    /// [`Hex`] implements [`Neg`] (`-` operator) but this method is `const`.
412    ///
413    /// [`Neg`]: std::ops::Neg
414    pub const fn const_neg(self) -> Self {
415        Self {
416            x: -self.x,
417            y: -self.y,
418        }
419    }
420
421    #[inline]
422    #[must_use]
423    /// adds `self` and `other`.
424    ///
425    /// [`Hex`] implements [`Add`] (`+` operator) but this method is `const`.
426    ///
427    /// [`Add`]: std::ops::Add
428    pub const fn const_add(self, other: Self) -> Self {
429        Self {
430            x: self.x + other.x,
431            y: self.y + other.y,
432        }
433    }
434
435    #[inline]
436    #[must_use]
437    /// substracts `self` and `rhs`.
438    ///
439    /// [`Hex`] implements [`Sub`] (`-` operator) but this method is `const`.
440    ///
441    /// [`Sub`]: std::ops::Sub
442    pub const fn const_sub(self, rhs: Self) -> Self {
443        Self {
444            x: self.x - rhs.x,
445            y: self.y - rhs.y,
446        }
447    }
448
449    #[inline]
450    #[must_use]
451    #[allow(clippy::cast_possible_truncation)]
452    /// Rounds floating point coordinates to [`Hex`].
453    /// This method is used for operations like multiplications and divisions
454    /// with floating point numbers.
455    /// See the original author Jacob Rus's [article](https://observablehq.com/@jrus/hexround) for
456    /// more details
457    ///
458    /// # Example
459    ///
460    /// ```rust
461    /// # use hexx::*;
462    /// let point = [0.6, 10.2];
463    /// let coord = Hex::round(point);
464    /// assert_eq!(coord.x, 1);
465    /// assert_eq!(coord.y, 10);
466    /// ```
467    pub fn round([mut x, mut y]: [f32; 2]) -> Self {
468        let [mut x_r, mut y_r] = [x.round(), y.round()];
469        x -= x_r;
470        y -= y_r;
471        if x.abs() >= y.abs() {
472            x_r += 0.5_f32.mul_add(y, x).round();
473        } else {
474            y_r += 0.5_f32.mul_add(x, y).round();
475        }
476        Self::new(x_r as i32, y_r as i32)
477    }
478
479    #[inline]
480    #[must_use]
481    /// Computes the absolute value of `self`
482    ///
483    /// # Example
484    ///
485    /// ```rust
486    /// # use hexx::*;
487    /// let coord = Hex::new(-1, -32).abs();
488    /// assert_eq!(coord.x, 1);
489    /// assert_eq!(coord.y, 32);
490    /// ```
491    pub const fn abs(self) -> Self {
492        Self {
493            x: self.x.abs(),
494            y: self.y.abs(),
495        }
496    }
497    /// Returns a vector containing the minimum values for each element of
498    /// `self` and `rhs`.
499    ///
500    /// In other words this computes `[self.x.min(rhs.x), self.y.min(rhs.y),
501    /// ..]`.
502    #[inline]
503    #[must_use]
504    pub fn min(self, rhs: Self) -> Self {
505        Self {
506            x: self.x.min(rhs.x),
507            y: self.y.min(rhs.y),
508        }
509    }
510
511    /// Returns a vector containing the maximum values for each element of
512    /// `self` and `rhs`.
513    ///
514    /// In other words this computes `[self.x.max(rhs.x), self.y.max(rhs.y),
515    /// ..]`.
516    #[inline]
517    #[must_use]
518    pub fn max(self, rhs: Self) -> Self {
519        Self {
520            x: self.x.max(rhs.x),
521            y: self.y.max(rhs.y),
522        }
523    }
524
525    /// Computes the dot product of `self` and `rhs`.
526    #[inline]
527    #[must_use]
528    pub const fn dot(self, rhs: Self) -> i32 {
529        (self.x * rhs.x) + (self.y * rhs.y)
530    }
531
532    #[inline]
533    #[must_use]
534    /// Returns a [`Hex`] with elements representing the sign of `self`.
535    ///
536    ///  - `0` if the number is zero
537    ///  - `1` if the number is positive
538    ///  - `-1` if the number is negative
539    pub const fn signum(self) -> Self {
540        Self {
541            x: self.x.signum(),
542            y: self.y.signum(),
543        }
544    }
545
546    #[inline]
547    #[must_use]
548    #[doc(alias = "magnitude")]
549    /// Computes coordinates length as a signed integer.
550    /// The length of a [`Hex`] coordinate is equal to its distance from the
551    /// origin.
552    ///
553    /// See [`Self::ulength`] for the unsigned version
554    ///
555    /// # Example
556    /// ```rust
557    /// # use hexx::*;
558    /// let coord = Hex::new(10, 0);
559    /// assert_eq!(coord.length(), 10);
560    /// ```
561    pub const fn length(self) -> i32 {
562        let [x, y, z] = [self.x.abs(), self.y.abs(), self.z().abs()];
563        if x >= y && x >= z {
564            x
565        } else if y >= x && y >= z {
566            y
567        } else {
568            z
569        }
570    }
571
572    #[inline]
573    #[must_use]
574    #[doc(alias = "unsigned_length")]
575    /// Computes coordinates length as an unsigned integer
576    /// The length of a [`Hex`] coordinate is equal to its distance from the
577    /// origin.
578    ///
579    /// See [`Self::length`] for the signed version
580    ///
581    /// # Example
582    /// ```rust
583    /// # use hexx::*;
584    /// let coord = Hex::new(10, 0);
585    /// assert_eq!(coord.ulength(), 10);
586    /// ```
587    pub const fn ulength(self) -> u32 {
588        let [x, y, z] = [
589            self.x.unsigned_abs(),
590            self.y.unsigned_abs(),
591            self.z().unsigned_abs(),
592        ];
593        if x >= y && x >= z {
594            x
595        } else if y >= x && y >= z {
596            y
597        } else {
598            z
599        }
600    }
601
602    #[inline]
603    #[must_use]
604    /// Computes the distance from `self` to `rhs` in hexagonal space as a
605    /// signed integer
606    ///
607    /// See [`Self::unsigned_distance_to`] for the unsigned version
608    pub const fn distance_to(self, rhs: Self) -> i32 {
609        self.const_sub(rhs).length()
610    }
611
612    #[inline]
613    #[must_use]
614    /// Computes the distance from `self` to `rhs` in hexagonal space as an
615    /// unsigned integer
616    ///
617    /// See [`Self::distance_to`] for the signed version
618    pub const fn unsigned_distance_to(self, rhs: Self) -> u32 {
619        self.const_sub(rhs).ulength()
620    }
621
622    #[inline]
623    #[must_use]
624    /// Retrieves the hexagonal neighbor coordinates matching the given
625    /// `direction`
626    pub const fn neighbor_coord(direction: EdgeDirection) -> Self {
627        direction.into_hex()
628    }
629
630    #[inline]
631    #[must_use]
632    /// Retrieves the diagonal neighbor coordinates matching the given
633    /// `direction`
634    pub const fn diagonal_neighbor_coord(direction: VertexDirection) -> Self {
635        direction.into_hex()
636    }
637
638    pub(crate) const fn add_dir(self, direction: EdgeDirection) -> Self {
639        self.const_add(Self::neighbor_coord(direction))
640    }
641
642    pub(crate) const fn add_diag_dir(self, direction: VertexDirection) -> Self {
643        self.const_add(Self::diagonal_neighbor_coord(direction))
644    }
645
646    #[inline]
647    #[must_use]
648    /// Retrieves the neighbor coordinates matching the given `direction`
649    ///
650    /// # Example
651    ///
652    /// ```rust
653    /// # use hexx::*;
654    /// let coord = Hex::new(10, 5);
655    /// let bottom = coord.neighbor(EdgeDirection::FLAT_BOTTOM);
656    /// assert_eq!(bottom, Hex::new(10, 6));
657    /// ```
658    pub const fn neighbor(self, direction: EdgeDirection) -> Self {
659        self.const_add(Self::neighbor_coord(direction))
660    }
661
662    #[inline]
663    #[must_use]
664    /// Retrieves the diagonal neighbor coordinates matching the given
665    /// `direction`
666    ///
667    /// # Example
668    ///
669    /// ```rust
670    /// # use hexx::*;
671    /// let coord = Hex::new(10, 5);
672    /// let bottom = coord.diagonal_neighbor(VertexDirection::FLAT_RIGHT);
673    /// assert_eq!(bottom, Hex::new(12, 4));
674    /// ```
675    pub const fn diagonal_neighbor(self, direction: VertexDirection) -> Self {
676        self.const_add(Self::diagonal_neighbor_coord(direction))
677    }
678
679    #[inline]
680    #[must_use]
681    /// Retrieves the direction of the given neighbor. Will return `None` if
682    /// `other` is not a neighbor of `self`
683    ///
684    /// # Example
685    ///
686    /// ```rust
687    /// # use hexx::*;
688    /// let coord = Hex::new(10, 5);
689    /// let bottom = coord.neighbor(EdgeDirection::FLAT_BOTTOM);
690    /// let dir = coord.neighbor_direction(bottom).unwrap();
691    /// assert_eq!(dir, EdgeDirection::FLAT_BOTTOM);
692    /// ```
693    pub fn neighbor_direction(self, other: Self) -> Option<EdgeDirection> {
694        EdgeDirection::iter().find(|&dir| self.neighbor(dir) == other)
695    }
696
697    #[must_use]
698    /// Find in which [`VertexDirection`] wedge `rhs` is relative to `self`.
699    ///
700    /// > This method can be innaccurate in case of a *tie* between directions,
701    /// > prefer using [`Self::diagonal_way_to`] instead
702    pub fn main_diagonal_to(self, rhs: Self) -> VertexDirection {
703        self.diagonal_way_to(rhs).unwrap()
704    }
705
706    #[must_use]
707    /// Find in which [`VertexDirection`] wedge `rhs` is relative to `self`
708    pub fn diagonal_way_to(self, rhs: Self) -> DirectionWay<VertexDirection> {
709        let [x, y, z] = (rhs - self).to_cubic_array();
710        let [xa, ya, za] = [x.abs(), y.abs(), z.abs()];
711        match xa.max(ya).max(za) {
712            v if v == xa => {
713                DirectionWay::way_from(x < 0, xa == ya, xa == za, VertexDirection::FLAT_RIGHT)
714            }
715            v if v == ya => {
716                DirectionWay::way_from(y < 0, ya == za, ya == xa, VertexDirection::FLAT_BOTTOM_LEFT)
717            }
718            _ => DirectionWay::way_from(z < 0, za == xa, za == ya, VertexDirection::FLAT_TOP_LEFT),
719        }
720    }
721
722    /// Find in which [`EdgeDirection`] wedge `rhs` is relative to `self`
723    ///
724    /// > This method can be innaccurate in case of a *tie* between directions,
725    /// > prefer using [`Self::way_to`] for accuracy
726    #[must_use]
727    pub fn main_direction_to(self, rhs: Self) -> EdgeDirection {
728        self.way_to(rhs).unwrap()
729    }
730
731    #[must_use]
732    /// Find in which [`EdgeDirection`] wedge `rhs` is relative to `self`
733    pub fn way_to(self, rhs: Self) -> DirectionWay<EdgeDirection> {
734        let [x, y, z] = (rhs - self).to_cubic_array();
735        let [x, y, z] = [y - x, z - y, x - z];
736        let [xa, ya, za] = [x.abs(), y.abs(), z.abs()];
737        match xa.max(ya).max(za) {
738            v if v == xa => {
739                DirectionWay::way_from(x < 0, xa == ya, xa == za, EdgeDirection::FLAT_BOTTOM_LEFT)
740            }
741            v if v == ya => {
742                DirectionWay::way_from(y < 0, ya == za, ya == xa, EdgeDirection::FLAT_TOP)
743            }
744            _ => {
745                DirectionWay::way_from(z < 0, za == xa, za == ya, EdgeDirection::FLAT_BOTTOM_RIGHT)
746            }
747        }
748    }
749
750    #[inline]
751    #[must_use]
752    /// Retrieves all 6 neighbor coordinates around `self`
753    pub fn all_neighbors(self) -> [Self; 6] {
754        Self::NEIGHBORS_COORDS.map(|n| self.const_add(n))
755    }
756
757    #[inline]
758    #[must_use]
759    /// Retrieves all 6 neighbor diagonal coordinates around `self`
760    pub fn all_diagonals(self) -> [Self; 6] {
761        Self::DIAGONAL_COORDS.map(|n| self.const_add(n))
762    }
763
764    #[inline]
765    #[must_use]
766    #[doc(alias = "ccw")]
767    /// Rotates `self` around [`Hex::ZERO`] counter clockwise (by -60 degrees)
768    ///
769    /// # Example
770    ///
771    /// ```rust
772    /// # use hexx::*;
773    ///
774    /// let p = Hex::new(1, 2);
775    /// assert_eq!(p.counter_clockwise(), Hex::new(3, -1));
776    /// ```
777    pub const fn counter_clockwise(self) -> Self {
778        Self::new(-self.z(), -self.x)
779    }
780
781    #[inline]
782    #[must_use]
783    /// Rotates `self` around `center` counter clockwise (by -60 degrees)
784    pub const fn ccw_around(self, center: Self) -> Self {
785        self.const_sub(center).counter_clockwise().const_add(center)
786    }
787
788    #[inline]
789    #[must_use]
790    /// Rotates `self` around [`Hex::ZERO`] counter clockwise by `m` (by `-60 *
791    /// m` degrees)
792    pub const fn rotate_ccw(self, m: u32) -> Self {
793        match m % 6 {
794            1 => self.counter_clockwise(),
795            2 => self.counter_clockwise().counter_clockwise(),
796            3 => self.const_neg(),
797            4 => self.clockwise().clockwise(),
798            5 => self.clockwise(),
799            _ => self,
800        }
801    }
802
803    #[inline]
804    #[must_use]
805    /// Rotates `self` around `center` counter clockwise by `m` (by `-60 * m`
806    /// degrees)
807    pub const fn rotate_ccw_around(self, center: Self, m: u32) -> Self {
808        self.const_sub(center).rotate_ccw(m).const_add(center)
809    }
810
811    #[inline]
812    #[must_use]
813    #[doc(alias = "cw")]
814    /// Rotates `self` around [`Hex::ZERO`] clockwise (by 60 degrees)
815    ///
816    /// # Example
817    ///
818    /// ```rust
819    /// # use hexx::*;
820    ///
821    /// let p = Hex::new(1, 2);
822    /// assert_eq!(p.clockwise(), Hex::new(-2, 3));
823    /// ```
824    pub const fn clockwise(self) -> Self {
825        Self::new(-self.y, -self.z())
826    }
827
828    #[inline]
829    #[must_use]
830    /// Rotates `self` around `center` clockwise (by 60 degrees)
831    pub const fn cw_around(self, center: Self) -> Self {
832        self.const_sub(center).clockwise().const_add(center)
833    }
834
835    #[inline]
836    #[must_use]
837    /// Rotates `self` around [`Hex::ZERO`] clockwise by `m` (by `60 * m`
838    /// degrees)
839    pub const fn rotate_cw(self, m: u32) -> Self {
840        match m % 6 {
841            1 => self.clockwise(),
842            2 => self.clockwise().clockwise(),
843            3 => self.const_neg(),
844            4 => self.counter_clockwise().counter_clockwise(),
845            5 => self.counter_clockwise(),
846            _ => self,
847        }
848    }
849
850    #[inline]
851    #[must_use]
852    /// Rotates `self` around `center` clockwise by `m` (by `60 * m` degrees)
853    pub const fn rotate_cw_around(self, center: Self, m: u32) -> Self {
854        self.const_sub(center).rotate_cw(m).const_add(center)
855    }
856
857    #[inline]
858    #[must_use]
859    #[doc(alias = "reflect_q")]
860    /// Computes the reflection of `self` accross the `x` axis
861    pub const fn reflect_x(self) -> Self {
862        Self::new(self.x, self.z())
863    }
864
865    #[inline]
866    #[must_use]
867    #[doc(alias = "reflect_r")]
868    /// Computes the reflection of `self` accross the `y` axis
869    pub const fn reflect_y(self) -> Self {
870        Self::new(self.z(), self.y)
871    }
872
873    #[inline]
874    #[must_use]
875    #[doc(alias = "reflect_s")]
876    /// Computes the reflection of `self` accross the `z` axis
877    pub const fn reflect_z(self) -> Self {
878        Self::new(self.y, self.x)
879    }
880
881    #[allow(clippy::cast_precision_loss)]
882    #[must_use]
883    /// Computes all coordinates in a line from `self` to `other`.
884    ///
885    /// # Example
886    /// ```rust
887    /// # use hexx::*;
888    /// let start = Hex::ZERO;
889    /// let end = Hex::new(5, 0);
890    ///
891    /// let line = start.line_to(end);
892    /// assert_eq!(line.len(), 6);
893    /// let line: Vec<Hex> = line.collect();
894    /// assert_eq!(line.len(), 6);
895    /// ````
896    pub fn line_to(self, other: Self) -> impl ExactSizeIterator<Item = Self> {
897        let distance = self.unsigned_distance_to(other);
898        let dist = distance.max(1) as f32;
899        let [a, b]: [Vec2; 2] = [self.as_vec2(), other.as_vec2()];
900        ExactSizeHexIterator {
901            iter: (0..=distance).map(move |step| a.lerp(b, step as f32 / dist).into()),
902            count: distance as usize + 1,
903        }
904    }
905
906    #[allow(clippy::cast_sign_loss)]
907    #[must_use]
908    /// Computes all coordinate in a two segment rectiline path from `self` to
909    /// `other`
910    ///
911    /// # Arguments
912    ///
913    /// * `other` - The destination coordinate
914    /// * `clockwise` - If set to `true` the line paths will be clockwise
915    ///
916    /// # Example
917    /// ```rust
918    /// # use hexx::*;
919    /// let start = Hex::ZERO;
920    /// let end = Hex::new(5, 0);
921    ///
922    /// let line = start.rectiline_to(end, true);
923    /// assert_eq!(line.len(), 6);
924    /// let line: Vec<Hex> = line.collect();
925    /// assert_eq!(line.len(), 6);
926    /// assert_eq!(line[0], start);
927    /// assert_eq!(line[5], end);
928    /// ````
929    pub fn rectiline_to(self, other: Self, clockwise: bool) -> impl ExactSizeIterator<Item = Self> {
930        let delta = other.const_sub(self);
931        let count = delta.length();
932        let mut dirs = self.main_diagonal_to(other).edge_directions();
933        if !clockwise {
934            dirs.rotate_left(1);
935        }
936        // The two directions to apply
937        let [dir_a, dir_b] = dirs;
938        // The amount of `da` is the distance between `delta` and the full projection of
939        // `db`
940        let proj_b = dir_b * count;
941        let ca = proj_b.distance_to(delta);
942
943        let iter = std::iter::once(self).chain((0..count).scan(self, move |p, i| {
944            if i < ca {
945                *p += dir_a;
946            } else {
947                *p += dir_b;
948            }
949            Some(*p)
950        }));
951        ExactSizeHexIterator {
952            iter,
953            count: (count + 1) as usize,
954        }
955    }
956
957    /// Performs a linear interpolation between `self` and `rhs` based on the
958    /// value `s`.
959    ///
960    /// When `s` is `0.0`, the result will be equal to `self`.  When `s` is
961    /// `1.0`, the result will be equal to `rhs`. When `s` is outside of
962    /// range `[0, 1]`, the result is linearly extrapolated.
963    #[doc(alias = "mix")]
964    #[inline]
965    #[must_use]
966    pub fn lerp(self, rhs: Self, s: f32) -> Self {
967        let [start, end]: [Vec2; 2] = [self.as_vec2(), rhs.as_vec2()];
968        start.lerp(end, s).into()
969    }
970
971    #[allow(clippy::cast_possible_wrap)]
972    #[must_use]
973    /// Retrieves all [`Hex`] around `self` in a given `range`.
974    /// The number of returned coordinates is equal to `Hex::range_count(range)`
975    ///
976    /// > See also [`Hex::xrange`] to retrieve all coordinates excluding `self`
977    ///
978    /// # Example
979    ///
980    /// ```rust
981    /// # use hexx::*;
982    /// let coord = hex(12, 34);
983    /// assert_eq!(coord.range(0).len(), 1);
984    /// assert_eq!(coord.range(1).len(), 7);
985    /// ```
986    pub fn range(self, range: u32) -> impl ExactSizeIterator<Item = Self> {
987        let radius = range as i32;
988        ExactSizeHexIterator {
989            iter: (-radius..=radius).flat_map(move |x| {
990                let y_min = max(-radius, -x - radius);
991                let y_max = min(radius, radius - x);
992                (y_min..=y_max).map(move |y| self.const_add(Self::new(x, y)))
993            }),
994            count: Self::range_count(range) as usize,
995        }
996    }
997
998    #[allow(clippy::cast_possible_wrap)]
999    #[doc(alias = "excluding_range")]
1000    #[must_use]
1001    /// Retrieves all [`Hex`] around `self` in a given `range` except `self`.
1002    /// The number of returned coordinates is equal to
1003    /// `Hex::range_count(range) - 1`
1004    ///
1005    /// > See also [`Hex::range`] to retrieve all coordinates including `self`
1006    ///
1007    /// # Example
1008    ///
1009    /// ```rust
1010    /// # use hexx::*;
1011    /// let coord = hex(12, 34);
1012    /// assert_eq!(coord.xrange(0).len(), 0);
1013    /// assert_eq!(coord.xrange(1).len(), 6);
1014    /// ```
1015    pub fn xrange(self, range: u32) -> impl ExactSizeIterator<Item = Self> {
1016        let iter = self.range(range);
1017        ExactSizeHexIterator {
1018            count: iter.len().saturating_sub(1),
1019            iter: iter.filter(move |h| *h != self),
1020        }
1021    }
1022
1023    /// Computes the coordinate of a lower resolution hexagon containing `self`
1024    /// of a given `radius`.
1025    /// The lower resolution coordinate can be considered *parent* of
1026    /// the contained higher resolution coordinates.
1027    /// The `radius` can be thought of as a *chunk size*, as if the grid was
1028    /// split in hexagonal chunks of that radius. The returned value are the
1029    /// coordinates of that chunk, in its own coordinates system.
1030    ///
1031    /// See the [source] documentation for more information
1032    ///
1033    /// > See also [`Self::to_higher_res`] and [`Self::to_local`]
1034    ///
1035    /// # Example
1036    ///
1037    /// ```rust
1038    /// # use hexx::*;
1039    ///
1040    /// // We define a coordinate
1041    /// let coord = hex(23, 45);
1042    /// // We take its *parent* in a coordinate system of size 5
1043    /// let parent = coord.to_lower_res(5);
1044    /// // We can then retrieve the center of that parent in the same system as `coord`
1045    /// let center = parent.to_higher_res(5);
1046    /// // Therefore the distance between the parent center and `coord` should be lower than 5
1047    /// assert!(coord.distance_to(center) <= 5);
1048    /// ```
1049    ///
1050    /// [source]: https://observablehq.com/@sanderevers/hexagon-tiling-of-an-hexagonal-grid
1051    #[must_use]
1052    #[allow(
1053        clippy::cast_possible_wrap,
1054        clippy::cast_precision_loss,
1055        clippy::cast_possible_truncation
1056    )]
1057    #[doc(alias = "downscale")]
1058    pub fn to_lower_res(self, radius: u32) -> Self {
1059        let [x, y, z] = self.to_cubic_array();
1060        let area = Self::range_count(radius) as f32;
1061        let shift = Self::shift(radius) as i32;
1062        let [x, y, z] = [
1063            ((y + shift * x) as f32 / area).floor() as i32,
1064            ((z + shift * y) as f32 / area).floor() as i32,
1065            ((x + shift * z) as f32 / area).floor() as i32,
1066        ];
1067        let [x, y] = [
1068            ((1 + x - y) as f32 / 3.0).floor() as i32,
1069            ((1 + y - z) as f32 / 3.0).floor() as i32,
1070            // ((1 + z - x) as f32 / 3.0).floor() as i32, -- z
1071        ];
1072        // debug_assert_eq!(z, -x - y);
1073        Self::new(x, y)
1074    }
1075
1076    /// Computes the center coordinates of `self` in a higher resolution system
1077    /// of a given `radius`.
1078    /// The higher resolution coordinate can be considered as a *child* of
1079    /// `self` as it is contained by it in a lower resolution coordinates
1080    /// system. The `radius` can be thought of as a *chunk size*, as if the
1081    /// grid was split in hexagonal chunks of that radius. The returned
1082    /// value are the coordinates of the center that chunk, in a higher
1083    /// resolution coordinates system.
1084    ///
1085    /// See the [source] documentation for more information
1086    ///
1087    /// > See also [`Self::to_lower_res`] and [`Self::to_local`]
1088    ///
1089    /// # Example
1090    ///
1091    /// ```rust
1092    /// # use hexx::*;
1093    ///
1094    /// // We define a coordinate
1095    /// let coord = hex(23, 45);
1096    /// // We take its *parent* in a coordinate system of size 5
1097    /// let parent = coord.to_lower_res(5);
1098    /// // We can then retrieve the center of that parent in the same system as `coord`
1099    /// let center = parent.to_higher_res(5);
1100    /// // Therefore the distance between the parent center and `coord` should be lower than 5
1101    /// assert!(coord.distance_to(center) <= 5);
1102    /// ```
1103    ///
1104    /// [source]: https://observablehq.com/@sanderevers/hexagon-tiling-of-an-hexagonal-grid
1105    #[must_use]
1106    #[allow(clippy::cast_possible_wrap)]
1107    #[doc(alias = "upscale")]
1108    pub const fn to_higher_res(self, radius: u32) -> Self {
1109        let range = radius as i32;
1110        let [x, y, z] = self.to_cubic_array();
1111        Self::new(x * (range + 1) - range * z, y * (range + 1) - range * x)
1112    }
1113
1114    /// Computes the local coordinates of `self` in a lower resolution
1115    /// coordinates system relative to its containing *parent* hexagon
1116    ///
1117    ///
1118    /// See the [source] documentation for more information
1119    ///
1120    /// > See also [`Self::to_lower_res`] and [`Self::to_local`]
1121    ///
1122    /// # Example
1123    ///
1124    /// ```rust
1125    /// # use hexx::*;
1126    ///
1127    /// // We define a coordinate
1128    /// let coord = hex(23, 45);
1129    /// // We can then retrieve the center of that hexagon in a higher res of size 5
1130    /// let center = coord.to_higher_res(5);
1131    /// // Therefore, the local coordinates of `center` relative to `coord` should be zero
1132    /// assert_eq!(center.to_local(5), Hex::ZERO);
1133    /// ```
1134    ///
1135    /// [source]: https://observablehq.com/@sanderevers/hexagon-tiling-of-an-hexagonal-grid
1136    #[must_use]
1137    pub fn to_local(self, radius: u32) -> Self {
1138        let upscale = self.to_lower_res(radius);
1139        let center = upscale.to_higher_res(radius);
1140        self.const_sub(center)
1141    }
1142
1143    #[inline]
1144    #[must_use]
1145    /// Counts how many coordinates there are in the given `range`
1146    ///
1147    /// # Example
1148    ///
1149    /// ```rust
1150    /// # use hexx::*;
1151    /// assert_eq!(Hex::range_count(15), 721);
1152    /// assert_eq!(Hex::range_count(0), 1);
1153    /// ```
1154    pub const fn range_count(range: u32) -> u32 {
1155        3 * range * (range + 1) + 1
1156    }
1157
1158    /// Shift constant used for [hexmod] operations
1159    ///
1160    /// [hexmod]: https://observablehq.com/@sanderevers/hexmod-representation
1161    #[inline]
1162    #[must_use]
1163    pub(crate) const fn shift(range: u32) -> u32 {
1164        3 * range + 2
1165    }
1166
1167    #[must_use]
1168    /// Wraps `self` in an hex range around the origin ([`Hex::ZERO`]).
1169    /// this allows for seamless *wraparound* hexagonal maps.
1170    /// See this [article] for more information.
1171    ///
1172    /// Use [`HexBounds`] for custom wrapping
1173    ///
1174    /// [`HexBounds`]: crate::HexBounds
1175    /// [article]: https://www.redblobgames.com/grids/hexagons/#wraparound
1176    pub fn wrap_in_range(self, range: u32) -> Self {
1177        self.to_local(range)
1178    }
1179}
1180
1181#[cfg(not(target_arch = "spirv"))]
1182impl Debug for Hex {
1183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1184        f.debug_struct("Hex")
1185            .field("x", &self.x)
1186            .field("y", &self.y)
1187            .field("z", &self.z())
1188            .finish()
1189    }
1190}