From 5100104dce2a39bff960656d4e2e422de90b77a9 Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Wed, 20 Apr 2022 08:06:25 +0100
Subject: [PATCH] some math library additions

---
 fggl/math/shapes.cpp         | 178 +++++++++++++++++++++++++++++
 include/fggl/math/fmath.hpp  |  49 ++++++++
 include/fggl/math/imath.hpp  |  61 ++++++++++
 include/fggl/math/shapes.hpp | 210 +++++++++++++++++++++++++++++++++++
 4 files changed, 498 insertions(+)
 create mode 100644 fggl/math/shapes.cpp
 create mode 100644 include/fggl/math/fmath.hpp
 create mode 100644 include/fggl/math/imath.hpp
 create mode 100644 include/fggl/math/shapes.hpp

diff --git a/fggl/math/shapes.cpp b/fggl/math/shapes.cpp
new file mode 100644
index 0000000..b67f6f5
--- /dev/null
+++ b/fggl/math/shapes.cpp
@@ -0,0 +1,178 @@
+/*
+ * ${license.title}
+ * Copyright (C) 2022 ${license.owner}
+ * ${license.mailto}
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "fggl/math/shapes.hpp"
+#include <vector>
+
+namespace fggl::math::phs3d {
+
+	void AABB::emtpy() {
+		min = {FLT_MAX, FLT_MAX, FLT_MAX};
+		max = { - FLT_MAX, - FLT_MAX, -FLT_MAX};
+	}
+
+	void AABB::add(const math::vec3 &p) {
+		if ( p.x < min.x ) min.x = p.x;
+		if ( p.x > max.x ) max.x = p.x;
+		if ( p.y < min.y ) min.y = p.y;
+		if ( p.y > min.y ) max.y = p.y;
+		if ( p.z < min.z ) min.z = p.z;
+		if ( p.z > max.z ) max.z = p.z;
+	}
+
+	AABB AABB::fromPoints(const std::vector<math::vec3> &points) {
+		AABB box;
+		for (const auto& point : points) {
+			box.add(point);
+		}
+		return box;
+	}
+
+	void AABB::set(const AABB &other, const math::mat4 &m) {
+		// TODO this needs testing, I'm not sure if our vectors are oriented the same as 9.4.4
+		min = max = m[3]; // should be the translation component of the matrix
+
+		// this feels like something that should be vectorizable...
+		for ( int i = 0; i < 3; i++) {
+			for (int j = 0; j < 3; j++) {
+				if ( m[i][j] > 0.0f ) {
+					min[j] += m[i][j] * other.min[j];
+					max[j] += m[i][j] * other.max[j];
+				} else {
+					min[j] += m[i][j] * other.max[j];
+					max[j] += m[i][j] * other.min[j];
+				}
+			}
+		}
+	}
+
+	Plane Plane::fromPoints(const math::vec3 p1, const math::vec3 p2, const math::vec3 p3) {
+		const auto e3 = p2 - p1;
+		const auto e1 = p3 - p2;
+		auto normal = glm::normalize(glm::cross(e3, e1));
+		auto d = glm::dot(p2, normal);
+		return {normal, d};
+	}
+
+	static math::vec3 bestFitNormal(const std::vector<math::vec3>& points) {
+		assert(!points.empty());
+
+		math::vec3 result;
+
+		math::vec3 p = points.back();
+		for ( std::size_t i = 0; i < points.size(); ++i ){
+			math::vec3 c = points[i];
+
+			result.x += (p.z + c.z) * (p.y - c.y);
+			result.y += (p.x + c.x) * (p.z - c.z);
+			result.z += (p.y + c.y) - (p.x - c.x);
+
+			p = c;
+		}
+
+		return glm::normalize(result);
+	};
+
+	static float bestFitD(const std::vector<math::vec3>& points, glm::vec3 normal) {
+		math::vec3 sum;
+		for (auto& point : points) {
+			sum += point;
+		}
+		sum *= 1.0F/points.size();
+		return glm::dot( sum, normal );
+	}
+
+	const char X = 0;
+	const char Y = 1;
+	const char Z = 2;
+
+	struct bary_axis {
+		const char a;
+		const char b;
+	};
+
+	static bary_axis baryCalcAxis(const math::vec3& normal) {
+		if ( (fabs(normal.x) >= fabs(normal.y)) && (fabs(normal.x) >= fabs(normal.z))) {
+			// discard x
+			return {Y, Z};
+		} else if ( fabs(normal.y) >= fabs(normal.z) ) {
+			// discard y
+			return {Z, X};
+		} else {
+			// discard z
+			return {X, Y};
+		}
+	}
+
+	bool Triangle::CartToBarycentric(const math::vec3& cart, Barycentric& outVal) {
+		// everything is const because I'm paying the compiler is smarter than me...
+
+		const auto d1 = v[1] - v[0];
+		const auto d2 = v[2] - v[1];
+		const auto n = glm::cross(d1, d2);
+
+		// 0 = x, 1 = y, 2 = z
+		const auto ax = baryCalcAxis(n);
+
+		// first axis
+		const float u1 = v[0][ax.a] - v[2][ax.a];
+		const float u2 = v[1][ax.a] - v[2][ax.a];
+		const float u3 = cart[ax.a] - v[0][ax.a];
+		const float u4 = cart[ax.a] - v[2][ax.a];
+
+		// second axis
+		const float v1 = v[0][ax.b] - v[2][ax.b];
+		const float v2 = v[1][ax.b] - v[2][ax.b];
+		const float v3 = cart[ax.b] - v[0][ax.b];
+		const float v4 = cart[ax.b] - v[2][ax.b];
+
+		const float denom = v1*u2 - v2*u1;
+		if ( denom == 0.0f) {
+			return false;
+		}
+
+		// finally, we can work it out
+		const float oneOverDenom = 1.0f / denom;
+		outVal.b[0] = (v4*u2 - v2*u4) * oneOverDenom;
+		outVal.b[1] = (v1*u3 - v3*u1) * oneOverDenom;
+		outVal.b[2] = 1.0f - outVal.b[0] - outVal.b[1];
+		return true;
+	}
+
+	bool Triangle::CartToBarycentric2(const math::vec3& cart, Barycentric& outVal) {
+		const auto e1 = v[2] - v[1];
+		const auto e2 = v[0] - v[2];
+		const auto e3 = v[1] - v[0];
+
+		const auto d1 = cart - v[0];
+		const auto d2 = cart - v[1];
+		const auto d3 = cart - v[2];
+
+		const auto normal = glm::normalize(glm::cross(e1, e2));
+		const auto denom = glm::dot(glm::cross(e1, e2), normal);
+		assert( denom != 0.0f);
+
+		outVal.b[0] = glm::dot(glm::cross(e1, d3), normal) / denom;
+		outVal.b[1] = glm::dot(glm::cross(e2, d1), normal) / denom;
+		outVal.b[2] = glm::dot(glm::cross(e3, d2), normal) / denom;
+		return true;
+	}
+
+} // namespace fggl::math::phys3d
\ No newline at end of file
diff --git a/include/fggl/math/fmath.hpp b/include/fggl/math/fmath.hpp
new file mode 100644
index 0000000..265e58b
--- /dev/null
+++ b/include/fggl/math/fmath.hpp
@@ -0,0 +1,49 @@
+/*
+ * ${license.title}
+ * Copyright (C) 2022 ${license.owner}
+ * ${license.mailto}
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef FGGL_MATH_FMATH_H
+#define FGGL_MATH_FMATH_H
+
+#include <cmath>
+
+namespace fggl::math {
+
+	// wrap value in range [0, max)
+	inline float wrap(float value, float max) {
+		return fmod(max + fmod(value, max), max);
+	}
+
+	// wrap value in range [min, max)
+	inline float wrap(float value, float min, float max) {
+		if ( min > max ){ std::swap(min, max); };
+		value -= min;
+		float rangeSize = max - min;
+		return value - (rangeSize* std::floor(value/rangeSize)) + min;
+	}
+
+	// if the value is out of bounds, return that bound
+	inline float clamp(float value, float min, float max) {
+		const float t = value < min ? min : value;
+		return t > max ? max : t;
+	}
+
+} // namespace fggl::math
+
+#endif //FGGL_MATH_FMATH_H
diff --git a/include/fggl/math/imath.hpp b/include/fggl/math/imath.hpp
new file mode 100644
index 0000000..ae0fe4f
--- /dev/null
+++ b/include/fggl/math/imath.hpp
@@ -0,0 +1,61 @@
+/*
+ * ${license.title}
+ * Copyright (C) 2022 ${license.owner}
+ * ${license.mailto}
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+//
+// Created by webpigeon on 20/04/22.
+//
+
+#ifndef FGGL_MATH_IMATH_HPP
+#define FGGL_MATH_IMATH_HPP
+
+namespace fggl::math {
+
+	// wrap value in range [0, max)
+	inline int wrap(int value, int max) {
+		if ( value < 0 ) return (n-1)-(-1-value) % n;
+		if ( value >= max ) return value % max;
+		return value;
+	}
+
+	// wrap value in range [min, max)
+	inline int wrap(int value, int const min, int const max) {
+		int range = max - min + 1;
+		if ( value < min) {
+			value += range * ( (min - value) / range + 1 );
+		}
+		return min + (value - min) % range;
+	}
+
+	inline
+	int clamp(int value, int min, int max) {
+		const int i = value > max ? max : value;
+		return i < min ? min : value;
+	}
+
+	template<typename T>
+	inline
+	T clampT(T value, T min, T max) {
+		const T i = value > max ? max : value;
+		return i < min ? min : value;
+	}
+
+} // namespace fggl::math
+
+#endif //FGGL_MATH_IMATH_HPP
diff --git a/include/fggl/math/shapes.hpp b/include/fggl/math/shapes.hpp
new file mode 100644
index 0000000..1a5c712
--- /dev/null
+++ b/include/fggl/math/shapes.hpp
@@ -0,0 +1,210 @@
+/*
+ * ${license.title}
+ * Copyright (C) 2022 ${license.owner}
+ * ${license.mailto}
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef FGGL_MATH_PHYS3D_SHAPES_HPP
+#define FGGL_MATH_PHYS3D_SHAPES_HPP
+
+#include "fggl/math/types.hpp"
+#include <vector>
+#include <array>
+
+// based on https://gamemath.com/book/geomprims.html
+namespace fggl::math::phs3d {
+
+	struct Line {
+		math::vec2 normal;
+		float d;
+	};
+
+	struct LineSegment {
+		math::vec3 start;
+		math::vec3 end;
+	};
+
+	struct Ray {
+		math::vec3 start;
+		math::vec3 length;
+
+		inline math::vec3 end() {
+			return start + length;
+		}
+
+		static Ray fromPos(math::vec3 start, math::vec3 end);
+	};
+
+	// nice bounding shapes
+
+	struct Sphere {
+		math::vec3 center;
+		float radius;
+
+		float volume() const;
+		float area() const;
+	};
+
+	struct AABB {
+		math::vec3 min;
+		math::vec3 max;
+
+		void add(const math::vec3& p);
+		void emtpy();
+		void set(const AABB& other, const math::mat4& m);
+
+		inline math::vec3 center() const {
+			return (min + max) / 2.0F;
+		}
+
+		inline math::vec3 size() const {
+			return max - min;
+		}
+
+		inline math::vec3 radius() const {
+			return max - center();
+		}
+
+		static AABB fromPoints(const std::vector<math::vec3>& points);
+	};
+
+	struct Plane {
+		glm::vec3 normal;
+		float d; // distance to origin
+
+		inline bool contains(const math::vec3& point) {
+			return glm::dot(point, normal) == d;
+		}
+
+		inline float distance(const math::vec3& q) {
+			return glm::dot(q, normal) - d;
+		}
+
+		static Plane fromPoints(const math::vec3, const math::vec3, const math::vec3);
+		static Plane bestFit(const std::vector<math::vec3>& points);
+	};
+
+	struct Barycentric {
+
+		std::array<float, 3> b;
+
+		bool inTriangle() {
+			return 0 <= b[0] && b[0] <= 1 &&
+				   0 <= b[1] && b[1] <= 1 &&
+				   0 <= b[2] && b[2] <= 1;
+		}
+
+		inline bool isLegal() const {
+			return (b[0] + b[1] + b[2]) == 1.0f;
+		}
+	};
+
+	constexpr float THIRD = 1.0F/3.0F;
+	const Barycentric cGrav = { {THIRD, THIRD, THIRD}};
+
+	struct Triangle {
+		std::array<math::vec3, 3> v;
+
+		inline float l1() const {
+			return length(2, 1);
+		}
+
+		inline float l2() const {
+			return length(0, 2);
+		}
+
+		inline float l3() const {
+			return length(1, 0);
+		}
+
+		inline math::vec3 edge(int v1, int v2) const {
+			return v[v1] - v[v2];
+		}
+
+		inline float length(int a, int b) const {
+			return glm::length( edge(a, b));
+		}
+
+		inline float perimeter() const {
+			return length(3, 2) + length(1,3) + length(2, 1);
+		}
+
+		inline float area() const {
+			return glm::length(glm::cross(edge(3, 2), edge(1, 3))) / 2;
+		}
+
+		inline math::vec3 cGrav() const {
+			return (v[0] + v[1] + v[2]) / 3.0f;
+		}
+
+		inline math::vec3 inCenter() const {
+			auto p = perimeter();
+			Barycentric bs{
+				l1() / p,
+				l2() / p,
+				l3() / p
+			};
+			return BarycentricToCart(bs);
+		}
+
+		inline float centerRadius() const {
+			return area() / perimeter();
+		}
+
+		// circle that passes through all three points = cirumcenter + circumraidus
+		inline math::vec3 circumcenter() {
+			// d1 = - e2 DOT e3
+			// d2 = - e3 DOT e1
+			// d3 = - e1 DOT e2
+			// C1 = d2 * d3
+			// C2 = d3 * d1
+			// C3 = d1 * d2
+			// c = c1 + c2 + c3
+			// c_2 = 2 * c
+			// bary{
+			//   (c2 + c3 ) / c_2
+			//   (c3 + c1 ) / c_2
+			//   (c1 + c2 ) / c_2
+			// return BarycentricToCart(bary)
+		}
+
+		inline float circumradius() {
+			// d1 = - e2 DOT e3
+			// d2 = - e3 DOT e1
+			// d3 = - e1 DOT e2
+			// C1 = d2 * d3
+			// C2 = d3 * d1
+			// C3 = d1 * d2
+			// c = c1 + c2 + c3
+			// c_2 = 2 * c
+			// return sqrt( (d1+d2) * (d2+d3) * ( d3 + d1 ) / c ) / 2
+		}
+
+		bool CartToBarycentric(const math::vec3& cart, Barycentric& outVal);
+		bool CartToBarycentric2(const math::vec3& cart, Barycentric& outVal);
+
+		math::vec3 BarycentricToCart(const Barycentric& p) const {
+			return v[0] * p.b[0] +
+				   v[1] * p.b[1] +
+				   v[2] * p.b[2];
+		}
+	};
+
+} // namespace fggl::math::phys3d
+
+
+#endif //FGGL_MATH_PHYS3D_SHAPES_HPP
-- 
GitLab