From 6464d0e75c6887d775898da4d8cf6135d3e1fd6a Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Sun, 27 Nov 2022 14:58:11 +0000
Subject: [PATCH] some of the more useful hex-grid features from HexBoard

---
 include/fggl/grid/hexagon.hpp | 143 ++++++++++++++++++++++++++++++++++
 include/fggl/math/fmath.hpp   |   8 ++
 2 files changed, 151 insertions(+)
 create mode 100644 include/fggl/grid/hexagon.hpp

diff --git a/include/fggl/grid/hexagon.hpp b/include/fggl/grid/hexagon.hpp
new file mode 100644
index 0000000..be21107
--- /dev/null
+++ b/include/fggl/grid/hexagon.hpp
@@ -0,0 +1,143 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/11/22.
+//
+
+#ifndef FGGL_GRID_HEX_HPP
+#define FGGL_GRID_HEX_HPP
+
+#include <array>
+#include <vector>
+#include <cmath>
+
+#include <fggl/math/fmath.hpp>
+
+/**
+ * Hexagonal Grid Implementation.
+ * Based largely off Amit's incredible grid documentation.
+ */
+namespace fggl::grid {
+
+	enum class HexDirPointy {
+		RIGHT = 0, TOP_RIGHT = 1, TOP_LEFT = 2, LEFT = 3, BOTTOM_LEFT = 4, BOTTOM_RIGHT = 5
+	};
+
+	enum class HexDirFlat {
+		BOTTOM_RIGHT = 0, TOP_RIGHT = 1, TOP = 2, TOP_LEFT = 3, BOTTOM_LEFT = 4, BOTTOM = 5
+	};
+
+	constexpr std::array< std::array<int, 2>, 6> HEX_DIRECTIONS {{
+		{1, 0}, {1, -1}, {0, 1},
+		{-1, 0}, {-1, 1}, {0, 1}
+	}};
+
+	constexpr std::array< std::array<int, 2>, 6> HEX_DIAGONALS {{
+																	{2, -1}, {+1, -2}, {-1, -1},
+																	{-2, 1}, {-1, 2}, {1, 1}
+																}};
+
+	template<typename T>
+	struct HexPointT {
+
+		constexpr HexPointT(T q, T r) : m_pos({q, r}) {}
+		constexpr explicit HexPointT(const std::array<int, 2>& pos) : m_pos(pos[0], pos[1]) {}
+
+		inline T q() const {
+			return m_pos[0];
+		}
+
+		inline T r() const {
+			return m_pos[1];
+		}
+
+		inline T s() const {
+			return -m_pos[0]-m_pos[1];
+		}
+
+		inline HexPointT neighbour(HexDirPointy dir) {
+			return this + HexPointT<T>(HEX_DIRECTIONS[(int)dir]);
+		}
+
+		inline HexPointT neighbour(HexDirFlat dir) {
+			return this + HexPointT<T>(HEX_DIAGONALS[(int)dir]);
+		}
+
+		HexPointT operator+(const HexPointT<T>& other) const {
+			return { m_pos[0] + other.m_pos[0], m_pos[1] + other.m_pos[1] };
+		}
+
+		HexPointT operator-(const HexPointT<T>& other) const {
+			return { m_pos[0] - other.m_pos[0], m_pos[1] - other.m_pos[1] };
+		}
+
+		T distance(const HexPointT& other) const {
+			auto vec = *this - other;
+			return (
+				::abs(vec.q())
+				+ ::abs(vec.q() - vec.r())
+				+ ::abs(vec.r()) / 2
+				);
+		}
+
+		std::vector<HexPointT<T>> hexesInRange(int range) const {
+			std::vector<HexPointT<T>> results;
+			for ( auto q = -range; q <= range; ++q ) {
+				auto stopCount = std::min(range, -q+range);
+				for ( auto r = std::max(-range, -q-range); r <= stopCount; ++r ) {
+					results.push_back( this + HexPointT<T>(q, r) );
+				}
+			}
+			return results;
+		}
+
+		private:
+			std::array<T, 2> m_pos;
+	};
+
+	using FloatHex = HexPointT<float>;
+	using IntHex = HexPointT<int>;
+
+	template<typename T>
+	constexpr FloatHex hexLerp(const HexPointT<T>& start, const HexPointT<T>& end, float t) {
+		return {
+			 math::lerp(start.q(), end.q(), t),
+			 math::lerp(start.r(), end.r(), t)
+		};
+	}
+
+	constexpr IntHex round(const FloatHex& hex) {
+		// see https://observablehq.com/@jrus/hexround for original JS implementation
+		float xGrid = std::round( hex.r() );
+		float yGrid = std::round( hex.q() );
+		float x = hex.q() - xGrid;
+		float y = hex.r() - yGrid;
+		auto dx = std::round(x + 0.5F*y) * (x*x >= y*y);
+		auto dy = std::round(y + 0.5F*x) * (x*x < y*y);
+		return { (int)(xGrid + dx), (int)(yGrid + dy) };
+	}
+
+	std::vector<IntHex> lineTo(const IntHex& start, const IntHex& end) {
+		int distance = start.distance(end);
+		std::vector<IntHex> line;
+		for (auto i=0; i < distance; ++i) {
+			line.push_back( round(hexLerp(start, end, 1.0F/distance * i)) );
+		}
+		return line;
+	}
+
+} // namespace fggl::grid
+
+#endif //FGGL_GRID_HEX_HPP
diff --git a/include/fggl/math/fmath.hpp b/include/fggl/math/fmath.hpp
index 8deee12..f6683b6 100644
--- a/include/fggl/math/fmath.hpp
+++ b/include/fggl/math/fmath.hpp
@@ -84,6 +84,14 @@ namespace fggl::math {
 		return valueOrMin > max ? max : valueOrMin;
 	}
 
+	constexpr float lerpImprecise(float start, float end, float t) {
+		return start + t * (end - start);
+	}
+
+	constexpr float lerp(float start, float end, float t) {
+		return (1 - t) * start + t * end;
+	}
+
 } // namespace fggl::math
 
 
-- 
GitLab