src/geometry.h

Sat, 08 Apr 2023 12:55:11 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Sat, 08 Apr 2023 12:55:11 +0300
changeset 343
4a82990affd5
parent 322
a39f454a3d7f
child 369
57de8fab2237
permissions
-rw-r--r--

Fix BFC formatting not working due to being evaluated after comment format

#pragma once
#include <QPolygonF>
#include <glm/glm.hpp>
#include <optional>

enum Winding
{
	NoWinding,
	Anticlockwise,
	Clockwise,
};

//! \brief XOR operator for winding
constexpr Winding operator^(Winding one, Winding other)
{
	if (one == NoWinding or other == NoWinding) {
		return NoWinding;
	}
	else {
		const int xored = static_cast<int>(one) ^ static_cast<int>(other);
		return static_cast<Winding>(xored);
	}
}

constexpr Winding& operator^=(Winding& one, Winding other)
{
	one = one ^ other;
	return one;
}

struct Plane
{
	glm::vec3 normal;
	glm::vec3 anchor;
};

template<int N, typename T = float, glm::qualifier Q = glm::defaultp>
struct Line
{
	glm::vec<N, T, Q> direction;
	glm::vec<N, T, Q> anchor;
};

template<int N, typename T = float, glm::qualifier Q = glm::defaultp>
struct Ray
{
	glm::vec<N, T, Q> direction;
	glm::vec<N, T, Q> anchor;
};

inline const glm::vec3 origin = {0, 0, 0};
inline const Plane XY = {{0, 0, 1}, origin};
inline const Plane XZ = {{0, 1, 0}, origin};
inline const Plane YZ = {{1, 0, 0}, origin};

struct LineSegment
{
	glm::vec3 p1, p2;
};
struct Triangle
{
	glm::vec3 p1, p2, p3;
};
struct Quadrilateral
{
	glm::vec3 p1, p2, p3, p4;
};
struct ConditionalEdge
{
	glm::vec3 p1, p2;
	glm::vec3 c1, c2;
};
struct LineSegment2D
{
	glm::vec2 p1, p2;
};

// get polygon type from amount of points
template<int N>
struct PolygonType {};
template<>
struct PolygonType<2> { using type = LineSegment; };
template<>
struct PolygonType<3> { using type = Triangle; };
template<>
struct PolygonType<4> { using type = Quadrilateral; };
template<int N>
using Polygon = typename PolygonType<N>::type;

/**
 * @brief Computes a line from two points
 * @param point_1
 * @param point_2
 * @return line
 */
template<int N, typename T, glm::qualifier Q>
Line<N, T, Q> lineFromPoints(const glm::vec<N, T, Q>& point_1, const glm::vec<N, T, Q>& point_2)
{
	return {point_2 - point_1, point_1};
}

template<int N, typename T, glm::qualifier Q>
Ray<N, T, Q> rayFromPoints(const glm::vec<N, T, Q>& point_1, const glm::vec<N, T, Q>& point_2)
{
	return {point_2 - point_1, point_1};
}

template<int N, typename T, glm::qualifier Q>
Line<N, T, Q> rayToLine(const Ray<N, T, Q>& ray)
{
	return {ray.direction, ray.anchor};
}

enum class RectangleSide
{
	Top,
	Left,
	Bottom,
	Right
};

struct PointOnRectagle
{
	glm::vec2 position;
	RectangleSide side;
};

std::optional<glm::vec2> lineLineIntersection(const Line<2>& line_1, const Line<2>& line_2);
std::optional<glm::vec2> rayLineSegmentIntersection(const Ray<2>& ray, const LineSegment2D& line);
std::optional<PointOnRectagle> rayRectangleIntersection(const Ray<2>& ray, const QRectF& rectangle);
Plane planeFromTriangle(const Triangle& triangle);
glm::vec3 normalVector(const Triangle& triangle);
std::optional<glm::vec3> linePlaneIntersection(
	const Line<3>& line,
	const Plane& plane, const float epsilon = 1e-6f);
glm::vec3 scalingVector(const glm::mat4 matrix);
LineSegment2D top(const QRectF& rectangle);
LineSegment2D bottom(const QRectF& rectangle);
LineSegment2D left(const QRectF& rectangle);
LineSegment2D right(const QRectF& rectangle);
bool isConvex(const Quadrilateral& quad);
bool isConvex(const std::vector<glm::vec3>& polygon);
Winding winding(const QPolygonF& polygon);
struct ScalingExtract
{
	glm::vec3 scaling;
	glm::mat4 unscaled;
};
ScalingExtract extractScaling(const glm::mat4& matrix);

struct NPolygon
{
	std::vector<glm::vec3> points;
};

inline constexpr bool isclose(const glm::vec3& a, const glm::vec3& b)
{
	return qFuzzyCompare(a.x, b.x)
		and qFuzzyCompare(a.y, b.y)
		and qFuzzyCompare(a.z, b.z);
}

struct CircleF
{
	QPointF center;
	qreal radius;
};

/**
 * @brief Inscribes a circle
 * @param circle
 * @return a QRectF that inscribes the specified circle
 */
inline constexpr QRectF inscribe(const CircleF& circle)
{
	return {
		circle.center.x() - circle.radius,
		circle.center.y() - circle.radius,
		circle.radius * 2,
		circle.radius * 2
	};
}

struct BezierCurve
{
	glm::vec3 points[4];
	const glm::vec3& operator[](int x) const
	{
		Q_ASSERT(x >= 0 and x < 4);
		return this->points[x];
	}
	glm::vec3& operator[](int x)
	{
		Q_ASSERT(x >= 0 and x < 4);
		return this->points[x];
	}
};

glm::vec3 pointOnCurve(const BezierCurve& curve, float t);
glm::vec3 derivativeOnCurve(const BezierCurve& curve, float t);

template<typename Iter>
std::optional<glm::mat3> calculateNormal(Iter&& begin, Iter&& end)
{
	const long int n = end - begin;
	std::optional<glm::mat3> result;
	for (long int i = 0; i < n; ++i) {
		const glm::vec3& v1 = *(begin + (i + n - 1) % n);
		const glm::vec3& v2 = *(begin + i);
		const glm::vec3& v3 = *(begin + (i + 1) % n);
		const glm::vec3 xvec = v1 - v2;
		const glm::vec3 yvec = v3 - v2;
		constexpr float threshold = 1e-6f;
		if (glm::length(xvec) > threshold and glm::length(yvec) > threshold) {
			const glm::vec3 zvec = glm::cross(glm::normalize(xvec), glm::normalize(yvec));
			if (glm::length(zvec) > threshold) {
				result = {xvec, yvec, zvec};
				break;
			}
		}
	}
	return result;
}

constexpr QPointF vecToQPoint(const glm::vec2& a)
{
	return {a.x, a.y};
}

mercurial