Skip to content

Commit

Permalink
Fractional hex on HexLayout (#141)
Browse files Browse the repository at this point in the history
> Closes #138 
> Supercedes #140 

Re implementation of #140 by @msparkles 

I added CHANGELOG entries and some code clarifications.

## Work done

Added the capacity to use *fractional axial coordinates* as `Vec2` in
`HexLayout` methods:

* Added `HexLayout::fract_hex_to_world_pos` method
* Added `HexLayout::world_pos_to_fract_hex` method 

To avoid duplication and multi-method chaining I encapsulated the matrix
computations in:

* Added `HexOrientationData::forward` method
* Added `HexOrientationData::inverse` method

I also added these utility methods:

* Added `Hex::to_array_f32` utility method
* Added `Hex::to_cubic_array_f32` utility method
  • Loading branch information
ManevilleF authored Jan 24, 2024
1 parent f42a378 commit 6a4bbd3
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 14 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,33 @@

## [Unreleased]

### algorithms

* (**BREAKING**) `a_star` `cost` function parameter now takes two adjacent `Hex`
nodes instead of one, allowing for more use cases (#130, #128)

### Dependencies

* Bumped `bevy_inspector_egui` dependency (#129)

### Examples

* Added a `sprite_sheet` bevy example (#135)

### Additions

* Added `HexLayout::rect_size` method (#135)
* Added `ColumnMeshBuilder::center_aligned` option (#139)
* Added `PlaneMeshBuilder::center_aligned` option (#139)
* Added `Hex::to_array_f32` utility method (#141)
* Added `Hex::to_cubic_array_f32` utility method (#141)
* Added `HexLayout::fract_hex_to_world_pos` method (#141, #138, #140)
* Added `HexLayout::world_pos_to_fract_hex` method (#141, #138, #140)
* Added `HexOrientationData::forward` method (#141)
* Added `HexOrientationData::inverse` method (#141)

### Deprecation

* Deprecated `MeshInfo::hexagonal_plane` in favor of `PlaneMeshBuilder` (#139)

## 0.12.0
Expand Down
18 changes: 17 additions & 1 deletion src/hex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,15 @@ impl Hex {

#[inline]
#[must_use]
/// Converts `self` to cubic coordinates an array as `[x, y, z]`
#[allow(clippy::cast_precision_loss)]
/// Converts `self` to an [`f32`] array as `[x, y]`
pub const fn to_array_f32(self) -> [f32; 2] {
[self.x as f32, self.y as f32]
}

#[inline]
#[must_use]
/// Converts `self` to cubic coordinates array as `[x, y, z]`
///
/// # Example
///
Expand All @@ -283,6 +291,14 @@ impl Hex {
[self.x, self.y, self.z()]
}

#[inline]
#[must_use]
#[allow(clippy::cast_precision_loss)]
/// Converts `self` to cubic [`f32`] coordinates array as `[x, y, z]`
pub const fn to_cubic_array_f32(self) -> [f32; 3] {
[self.x as f32, self.y as f32, self.z() as f32]
}

/// Creates a [`Hex`] from the first 2 values in `slice`.
///
/// # Panics
Expand Down
40 changes: 27 additions & 13 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,32 +53,46 @@ pub struct HexLayout {

impl HexLayout {
#[must_use]
#[inline]
/// Computes hexagonal coordinates `hex` into world/pixel coordinates
pub fn hex_to_world_pos(&self, hex: Hex) -> Vec2 {
self.hex_to_center_aligned_world_pos(hex) + self.origin
}

#[allow(clippy::cast_precision_loss)]
#[must_use]
#[inline]
/// Computes hexagonal coordinates `hex` into world/pixel coordinates but
/// ignoring [`HexLayout::origin`]
pub(crate) fn hex_to_center_aligned_world_pos(&self, hex: Hex) -> Vec2 {
let matrix = self.orientation.forward_matrix;
Vec2::new(
matrix[0].mul_add(hex.x() as f32, matrix[1] * hex.y() as f32),
matrix[2].mul_add(hex.x() as f32, matrix[3] * hex.y() as f32),
) * self.hex_size
* self.axis_scale()
let [x, y] = self.orientation.forward(hex.to_array_f32());
Vec2::new(x, y) * self.hex_size * self.axis_scale()
}

#[must_use]
#[inline]
/// Computes fractional hexagonal coordinates `hex` into world/pixel
/// coordinates
pub fn fract_hex_to_world_pos(&self, hex: Vec2) -> Vec2 {
let [x, y] = self.orientation.forward(hex.to_array());
Vec2::new(x, y) * self.hex_size * self.axis_scale() + self.origin
}

#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
#[must_use]
#[inline]
/// Computes world/pixel coordinates `pos` into hexagonal coordinates
pub fn world_pos_to_hex(&self, pos: Vec2) -> Hex {
let matrix = self.orientation.inverse_matrix;
let p = self.world_pos_to_fract_hex(pos).to_array();
Hex::round(p)
}

#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
#[must_use]
/// Computes world/pixel coordinates `pos` into fractional hexagonal
/// coordinates
pub fn world_pos_to_fract_hex(&self, pos: Vec2) -> Vec2 {
let point = (pos - self.origin) * self.axis_scale() / self.hex_size;
Hex::round([
matrix[0].mul_add(point.x, matrix[1] * point.y),
matrix[2].mul_add(point.x, matrix[3] * point.y),
])
let [x, y] = self.orientation.inverse(point.to_array());
Vec2::new(x, y)
}

#[must_use]
Expand Down
26 changes: 26 additions & 0 deletions src/orientation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,29 @@ impl Deref for HexOrientation {
self.orientation_data()
}
}

impl HexOrientationData {
#[must_use]
#[inline]
/// Applies `matrix` to a point defined by `x` and `y`
fn matrix_op(matrix: [f32; 4], [x, y]: [f32; 2]) -> [f32; 2] {
[
matrix[0].mul_add(x, matrix[1] * y),
matrix[2].mul_add(x, matrix[3] * y),
]
}

#[must_use]
#[inline]
/// Applies the orientation `forward_matrix` to a point `p`
pub fn forward(&self, p: [f32; 2]) -> [f32; 2] {
Self::matrix_op(self.forward_matrix, p)
}

#[must_use]
#[inline]
/// Applies the orientation `inverse_matrix` to a point `p`
pub fn inverse(&self, p: [f32; 2]) -> [f32; 2] {
Self::matrix_op(self.inverse_matrix, p)
}
}

0 comments on commit 6a4bbd3

Please sign in to comment.