From 53b6c935d3e7ba8e84cfa8ea49b37dc8da76bad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Lescaudey=20de=20Maneville?= Date: Fri, 20 Dec 2024 11:38:44 +0100 Subject: [PATCH 1/3] feat: Orientation based offset coordinates conversion methods --- CHANGELOG.md | 4 +++ src/conversions.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 424b4c5..40ed301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +* Added new offset coordinates conversion methods using a `HexOrientation` (#189) + * `Hex::to_even_offset` and `Hex::from_even_offset` + * `Hex::to_odd_offset` and `Hex::from_odd_offset` + ## 0.19.1 * Fix: `Hex::rectiline_to` not reaching the `end` parameter (#188) diff --git a/src/conversions.rs b/src/conversions.rs index 6c96f3a..126499a 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -1,4 +1,4 @@ -use crate::Hex; +use crate::{Hex, HexOrientation}; /// Layout mode for [doubled] coordinates conversion. See /// [`Hex::to_doubled_coordinates`] and [`Hex::from_doubled_coordinates`]. @@ -51,6 +51,14 @@ impl Hex { /// /// The coordinates are returned as `[COLUMN, ROW]` /// + /// # Note: + /// + /// For most use cases using [`OffsetHexMode`] directly is not relevant as + /// the offset coordinate conversion would depend on the [`HexOrientation`]. + /// For this cases prefer + /// - [`Hex::to_even_offset`] + /// - [`Hex::to_odd_offset`] + /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset #[inline] #[must_use] @@ -63,6 +71,30 @@ impl Hex { } } + /// Converts `self` to even [offset] coordinates according to the given `orientation`. + /// + /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset + #[inline] + #[must_use] + pub const fn to_even_offset(self, orientation: HexOrientation) -> [i32; 2] { + match orientation { + HexOrientation::Pointy => self.to_offset_coordinates(OffsetHexMode::EvenRows), + HexOrientation::Flat => self.to_offset_coordinates(OffsetHexMode::EvenColumns), + } + } + + /// Converts `self` to odd [offset] coordinates according to the given `orientation`. + /// + /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset + #[inline] + #[must_use] + pub const fn to_odd_offset(self, orientation: HexOrientation) -> [i32; 2] { + match orientation { + HexOrientation::Pointy => self.to_offset_coordinates(OffsetHexMode::OddRows), + HexOrientation::Flat => self.to_offset_coordinates(OffsetHexMode::OddColumns), + } + } + /// Converts `self` to [hexmod] coordinates according to the given `range` /// /// [hexmod]: https://observablehq.com/@sanderevers/hexmod-representation @@ -126,6 +158,34 @@ impl Hex { OffsetHexMode::OddRows => Self::new(col - (row - (row & 1)) / 2, row), } } + + /// Converts even [offset] to [axial] coordinates according to the given `orientation`. + /// + /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset + /// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial + #[inline] + #[must_use] + pub const fn from_even_offset(coord: [i32; 2], orientation: HexOrientation) -> Self { + match orientation { + HexOrientation::Pointy => Self::from_offset_coordinates(coord, OffsetHexMode::EvenRows), + HexOrientation::Flat => { + Self::from_offset_coordinates(coord, OffsetHexMode::EvenColumns) + } + } + } + + /// Converts odd [offset] to [axial] coordinates according to the given `orientation`. + /// + /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset + /// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial + #[inline] + #[must_use] + pub const fn from_odd_offset(coord: [i32; 2], orientation: HexOrientation) -> Self { + match orientation { + HexOrientation::Pointy => Self::from_offset_coordinates(coord, OffsetHexMode::OddRows), + HexOrientation::Flat => Self::from_offset_coordinates(coord, OffsetHexMode::OddColumns), + } + } } #[cfg(test)] @@ -159,6 +219,28 @@ mod tests { } } + #[test] + fn even_offset_coordinates() { + for hex in Hex::ZERO.range(20) { + for orientation in [HexOrientation::Flat, HexOrientation::Pointy] { + let offset = hex.to_even_offset(orientation); + let converted = Hex::from_even_offset(offset, orientation); + assert_eq!(converted, hex); + } + } + } + + #[test] + fn odd_offset_coordinates() { + for hex in Hex::ZERO.range(20) { + for orientation in [HexOrientation::Flat, HexOrientation::Pointy] { + let offset = hex.to_odd_offset(orientation); + let converted = Hex::from_odd_offset(offset, orientation); + assert_eq!(converted, hex); + } + } + } + #[test] fn hexmod_coordinates() { let range = 20; From 0ab312b10458d4c39ccb9cebaafbe634155c03de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Lescaudey=20de=20Maneville?= Date: Fri, 20 Dec 2024 11:40:17 +0100 Subject: [PATCH 2/3] fmt --- src/conversions.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/conversions.rs b/src/conversions.rs index 126499a..369edb3 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -71,7 +71,8 @@ impl Hex { } } - /// Converts `self` to even [offset] coordinates according to the given `orientation`. + /// Converts `self` to even [offset] coordinates according to the given + /// `orientation`. /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset #[inline] @@ -83,7 +84,8 @@ impl Hex { } } - /// Converts `self` to odd [offset] coordinates according to the given `orientation`. + /// Converts `self` to odd [offset] coordinates according to the given + /// `orientation`. /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset #[inline] @@ -159,7 +161,8 @@ impl Hex { } } - /// Converts even [offset] to [axial] coordinates according to the given `orientation`. + /// Converts even [offset] to [axial] coordinates according to the given + /// `orientation`. /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset /// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial @@ -174,7 +177,8 @@ impl Hex { } } - /// Converts odd [offset] to [axial] coordinates according to the given `orientation`. + /// Converts odd [offset] to [axial] coordinates according to the given + /// `orientation`. /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset /// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial From 5a1bbc456668229f3d6ff2229ad859dfa9fede43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Lescaudey=20de=20Maneville?= Date: Fri, 20 Dec 2024 15:10:14 +0100 Subject: [PATCH 3/3] Rework the enum directly --- CHANGELOG.md | 6 +- src/conversions.rs | 174 +++++++++++++++------------------------------ 2 files changed, 59 insertions(+), 121 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40ed301..90fcd4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ ## [Unreleased] -* Added new offset coordinates conversion methods using a `HexOrientation` (#189) - * `Hex::to_even_offset` and `Hex::from_even_offset` - * `Hex::to_odd_offset` and `Hex::from_odd_offset` +* (**BREAKING**) `HexOffsetMode` now has only 2 variants `Even` and `Odd` +* (**BREAKING**) `Hex::to_offset_coordinates` and `Hex::from_offset_coordinates` + take a new `HexOrientation` parameter (#189) ## 0.19.1 diff --git a/src/conversions.rs b/src/conversions.rs index 369edb3..31c907d 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -18,18 +18,19 @@ pub enum DoubledHexMode { /// [`Hex::to_offset_coordinates`] and [`Hex::from_offset_coordinates`]. /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum OffsetHexMode { - /// Vertical layout, shoves even columns down - EvenColumns, - /// Vertical layout, shoves odd columns down - OddColumns, - /// Horizontal layout, shoves even rows right - EvenRows, - #[default] - /// Horizontal layout, shoves odd rows right - OddRows, + /// Depending on the orientation: + /// + /// * Flat: Vertical layout, shoves even columns down + /// * Pointy: Horizontal layout, shoves even rows right + Even, + /// Depending on the orientation: + /// + /// * Flat: Vertical layout, shoves odd columns down + /// * Pointy: Horizontal layout, shoves odd rows right + Odd, } impl Hex { @@ -47,53 +48,32 @@ impl Hex { } } - /// Converts `self` to [offset] coordinates according to the given `mode`. + /// Converts `self` to [offset] coordinates according to the given `mode` + /// and `orientation` /// /// The coordinates are returned as `[COLUMN, ROW]` /// - /// # Note: - /// - /// For most use cases using [`OffsetHexMode`] directly is not relevant as - /// the offset coordinate conversion would depend on the [`HexOrientation`]. - /// For this cases prefer - /// - [`Hex::to_even_offset`] - /// - [`Hex::to_odd_offset`] - /// - /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset - #[inline] - #[must_use] - pub const fn to_offset_coordinates(self, mode: OffsetHexMode) -> [i32; 2] { - match mode { - OffsetHexMode::EvenColumns => [self.x, self.y + (self.x + (self.x & 1)) / 2], - OffsetHexMode::OddColumns => [self.x, self.y + (self.x - (self.x & 1)) / 2], - OffsetHexMode::EvenRows => [self.x + (self.y + (self.y & 1)) / 2, self.y], - OffsetHexMode::OddRows => [self.x + (self.y - (self.y & 1)) / 2, self.y], - } - } - - /// Converts `self` to even [offset] coordinates according to the given - /// `orientation`. - /// - /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset - #[inline] - #[must_use] - pub const fn to_even_offset(self, orientation: HexOrientation) -> [i32; 2] { - match orientation { - HexOrientation::Pointy => self.to_offset_coordinates(OffsetHexMode::EvenRows), - HexOrientation::Flat => self.to_offset_coordinates(OffsetHexMode::EvenColumns), - } - } - - /// Converts `self` to odd [offset] coordinates according to the given - /// `orientation`. - /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset #[inline] #[must_use] - pub const fn to_odd_offset(self, orientation: HexOrientation) -> [i32; 2] { - match orientation { - HexOrientation::Pointy => self.to_offset_coordinates(OffsetHexMode::OddRows), - HexOrientation::Flat => self.to_offset_coordinates(OffsetHexMode::OddColumns), + pub const fn to_offset_coordinates( + self, + mode: OffsetHexMode, + orientation: HexOrientation, + ) -> [i32; 2] { + match (mode, orientation) { + (OffsetHexMode::Even, HexOrientation::Flat) => { + [self.x, self.y + (self.x + (self.x & 1)) / 2] + } + (OffsetHexMode::Even, HexOrientation::Pointy) => { + [self.x + (self.y + (self.y & 1)) / 2, self.y] + } + (OffsetHexMode::Odd, HexOrientation::Flat) => { + [self.x, self.y + (self.x - (self.x & 1)) / 2] + } + (OffsetHexMode::Odd, HexOrientation::Pointy) => { + [self.x + (self.y - (self.y & 1)) / 2, self.y] + } } } @@ -146,48 +126,31 @@ impl Hex { } } - /// Converts [offset] to [axial] coordinates according to the given `mode`. - /// - /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset - /// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial - #[inline] - #[must_use] - pub const fn from_offset_coordinates([col, row]: [i32; 2], mode: OffsetHexMode) -> Self { - match mode { - OffsetHexMode::EvenColumns => Self::new(col, row - (col + (col & 1)) / 2), - OffsetHexMode::OddColumns => Self::new(col, row - (col - (col & 1)) / 2), - OffsetHexMode::EvenRows => Self::new(col - (row + (row & 1)) / 2, row), - OffsetHexMode::OddRows => Self::new(col - (row - (row & 1)) / 2, row), - } - } - - /// Converts even [offset] to [axial] coordinates according to the given - /// `orientation`. + /// Converts [offset] to [axial] coordinates according to the given `mode` + /// and `orientation` /// /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset /// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial #[inline] #[must_use] - pub const fn from_even_offset(coord: [i32; 2], orientation: HexOrientation) -> Self { - match orientation { - HexOrientation::Pointy => Self::from_offset_coordinates(coord, OffsetHexMode::EvenRows), - HexOrientation::Flat => { - Self::from_offset_coordinates(coord, OffsetHexMode::EvenColumns) + pub const fn from_offset_coordinates( + [col, row]: [i32; 2], + mode: OffsetHexMode, + orientation: HexOrientation, + ) -> Self { + match (mode, orientation) { + (OffsetHexMode::Even, HexOrientation::Flat) => { + Self::new(col, row - (col + (col & 1)) / 2) + } + (OffsetHexMode::Odd, HexOrientation::Flat) => { + Self::new(col, row - (col - (col & 1)) / 2) + } + (OffsetHexMode::Even, HexOrientation::Pointy) => { + Self::new(col - (row + (row & 1)) / 2, row) + } + (OffsetHexMode::Odd, HexOrientation::Pointy) => { + Self::new(col - (row - (row & 1)) / 2, row) } - } - } - - /// Converts odd [offset] to [axial] coordinates according to the given - /// `orientation`. - /// - /// [offset]: https://www.redblobgames.com/grids/hexagons/#coordinates-offset - /// [axial]: https://www.redblobgames.com/grids/hexagons/#coordinates-axial - #[inline] - #[must_use] - pub const fn from_odd_offset(coord: [i32; 2], orientation: HexOrientation) -> Self { - match orientation { - HexOrientation::Pointy => Self::from_offset_coordinates(coord, OffsetHexMode::OddRows), - HexOrientation::Flat => Self::from_offset_coordinates(coord, OffsetHexMode::OddColumns), } } } @@ -210,37 +173,12 @@ mod tests { #[test] fn offset_coordinates() { for hex in Hex::ZERO.range(20) { - for mode in [ - OffsetHexMode::OddRows, - OffsetHexMode::OddColumns, - OffsetHexMode::EvenColumns, - OffsetHexMode::EvenRows, - ] { - let offset = hex.to_offset_coordinates(mode); - let converted = Hex::from_offset_coordinates(offset, mode); - assert_eq!(converted, hex); - } - } - } - - #[test] - fn even_offset_coordinates() { - for hex in Hex::ZERO.range(20) { - for orientation in [HexOrientation::Flat, HexOrientation::Pointy] { - let offset = hex.to_even_offset(orientation); - let converted = Hex::from_even_offset(offset, orientation); - assert_eq!(converted, hex); - } - } - } - - #[test] - fn odd_offset_coordinates() { - for hex in Hex::ZERO.range(20) { - for orientation in [HexOrientation::Flat, HexOrientation::Pointy] { - let offset = hex.to_odd_offset(orientation); - let converted = Hex::from_odd_offset(offset, orientation); - assert_eq!(converted, hex); + for mode in [OffsetHexMode::Even, OffsetHexMode::Odd] { + for orientation in [HexOrientation::Flat, HexOrientation::Pointy] { + let offset = hex.to_offset_coordinates(mode, orientation); + let converted = Hex::from_offset_coordinates(offset, mode, orientation); + assert_eq!(converted, hex); + } } } }