bestagon 0.9.0

An engine for discrete stuff in hexagonal grids
Documentation
use std::hash::Hash;

#[cfg(feature = "bevy")]
use bevy_ecs::prelude::Component;
#[cfg(feature = "bevy")]
use bevy_reflect::Reflect;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// A node is shared by three edges, and by three hexes. It is the point that
/// connects them.
pub trait Node: Into<NodeAx> + From<NodeAx> + Hash + Eq + Copy {}

#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash, Default)]
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NodeAx {
  pub q: isize,
  pub r: isize,
}

impl NodeAx {
  pub fn new(q: isize, r: isize) -> Self {
    NodeAx { q, r }
  }
}

impl Node for NodeAx {}

#[derive(PartialEq, Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FNodeAx {
  pub q: f32,
  pub r: f32,
}

impl FNodeAx {
  pub fn new(q: f32, r: f32) -> Self {
    FNodeAx { q, r }
  }
}

impl From<&NodeAx> for FNodeAx {
  fn from(value: &NodeAx) -> Self {
    FNodeAx::new(value.q as f32, value.r as f32)
  }
}

#[cfg(test)]
mod test {
  use super::*;
  use crate::{edge::*, hex::*, hex_range::*, neighbours::*};
  use std::collections::HashSet;

  #[test]
  fn neighbouring_edges_share_node_cases() {
    for hex in HexRange::sphere(&HexAx::new(0, 0), 2).compute::<HexAx>() {
      for e in hex.edges() {
        verify_neighbouring_edge_share_node(&e);
      }
    }
  }

  fn verify_neighbouring_edge_share_node(e: &EdgeAx) {
    let my_nodes = e.nodes().into_iter().collect::<HashSet<NodeAx>>();
    for n in e.edges() {
      assert_eq!(
        my_nodes
          .intersection(&n.nodes().collect::<HashSet<NodeAx>>())
          .collect::<HashSet<&NodeAx>>()
          .len(),
        1
      );
    }
  }

  #[test]
  fn hex_has_6_nodes_which_each_have_3_hexes_including_the_aforementioned_hex() {
    for hex in HexRange::sphere(&HexAx::new(0, 0), 3).compute::<HexAx>() {
      let nodes = hex.nodes();
      assert_eq!(nodes.count(), 6);
      for node in hex.nodes() {
        let hexes: HashSet<HexAx> = node.hexes().collect();
        assert_eq!(hexes.len(), 3);
        assert!(hexes.contains(&hex));
      }
    }
  }

  #[test]
  fn hex_nodes_each_touch_2_edges_that_touch_the_aforementioned_hex() {
    for hex in HexRange::sphere(&HexAx::new(0, 0), 3).compute::<HexAx>() {
      let hex_edges: HashSet<EdgeAx> = hex.edges().collect();
      let nodes = hex.nodes();
      for node in nodes {
        let edges: HashSet<EdgeAx> = node.edges().collect();
        assert_eq!(edges.len(), 3);
        assert_eq!(
          edges
            .intersection(&hex_edges)
            .collect::<HashSet<&EdgeAx>>()
            .len(),
          2
        );
      }
    }
  }
}