omath::Triangle — Simple 3D triangle utility
Header: your project’s
triangle.hppNamespace:omathDepends on:omath::Vector3<float>(fromvector3.hpp)
A tiny helper around three Vector3<float> vertices with convenience methods for normals, edge vectors/lengths, a right-angle test (at v2), and the triangle centroid.
Note on the template parameter
The class is declared as
template<class Vector> class Triangle, but the stored vertices are concretelyVector3<float>. In practice this type is currently fixed toVector3<float>. You can ignore the template parameter or refactor to storeVectorif you intend true genericity.
Vertex layout & naming
v1
|\
| \
a | \ hypot = |v1 - v3|
| \
v2 -- v3
b
a = |v1 - v2| (side_a_length)
b = |v3 - v2| (side_b_length)
side_a_vector()=v1 - v2(points from v2 → v1)side_b_vector()=v3 - v2(points from v2 → v3)- Right-angle check uses
a² + b² ≈ hypot²with an epsilon of1e-4.
Quick start
#include "triangle.hpp"
using omath::Vector3;
using omath::Triangle;
Triangle<void> tri( // template arg unused; any placeholder ok
Vector3<float>{0,0,0}, // v1
Vector3<float>{0,0,1}, // v2 (right angle is tested at v2)
Vector3<float>{1,0,1} // v3
);
auto n = tri.calculate_normal(); // unit normal (right-handed: (v3-v2) × (v1-v2))
float a = tri.side_a_length(); // |v1 - v2|
float b = tri.side_b_length(); // |v3 - v2|
float hyp = tri.hypot(); // |v1 - v3|
bool rect = tri.is_rectangular(); // true if ~right triangle at v2
auto C = tri.mid_point(); // centroid (average of v1,v2,v3)
Data members
Vector3<float> m_vertex1; // v1
Vector3<float> m_vertex2; // v2 (the corner tested by is_rectangular)
Vector3<float> m_vertex3; // v3
Constructors
constexpr Triangle() = default;
constexpr Triangle(const Vector3<float>& v1,
const Vector3<float>& v2,
const Vector3<float>& v3);
Methods
// Normal (unit) using right-handed cross product:
// n = (v3 - v2) × (v1 - v2), then normalized()
[[nodiscard]] constexpr Vector3<float> calculate_normal() const;
// Edge lengths with the naming from the diagram
[[nodiscard]] float side_a_length() const; // |v1 - v2|
[[nodiscard]] float side_b_length() const; // |v3 - v2|
// Edge vectors (from v2 to the other vertex)
[[nodiscard]] constexpr Vector3<float> side_a_vector() const; // v1 - v2
[[nodiscard]] constexpr Vector3<float> side_b_vector() const; // v3 - v2
// Hypotenuse length between v1 and v3
[[nodiscard]] constexpr float hypot() const; // |v1 - v3|
// Right-triangle check at vertex v2 (Pythagoras with epsilon 1e-4)
[[nodiscard]] constexpr bool is_rectangular() const;
// Centroid of the triangle (average of the 3 vertices)
[[nodiscard]] constexpr Vector3<float> mid_point() const; // actually the centroid
Notes & edge cases
- Normal direction follows the right-hand rule for the ordered vertices
{v2 → v3} × {v2 → v1}. Swap vertex order to flip the normal. - Degenerate triangles (collinear or overlapping vertices) yield a zero vector normal (since
normalized()of the zero vector returns the zero vector in your math types). mid_point()is the centroid, not the midpoint of any single edge. If you need the midpoint of edgev1–v2, use(m_vertex1 + m_vertex2) * 0.5f.
Examples
Area and plane from existing API
const auto a = tri.side_a_vector();
const auto b = tri.side_b_vector();
const auto n = b.cross(a); // unnormalized normal
float area = 0.5f * n.length(); // triangle area
// Plane equation n̂·(x - v2) = 0
auto nhat = n.length() > 0 ? n / n.length() : n;
float d = -nhat.dot(tri.m_vertex2);
Project a point onto the triangle’s plane
Vector3<float> p{0.3f, 1.0f, 0.7f};
auto n = tri.calculate_normal();
float t = n.dot(tri.m_vertex2 - p); // signed distance along normal
auto projected = p + n * t; // on-plane projection
API summary (signatures)
class Triangle final {
public:
constexpr Triangle();
constexpr Triangle(const Vector3<float>& v1,
const Vector3<float>& v2,
const Vector3<float>& v3);
Vector3<float> m_vertex1, m_vertex2, m_vertex3;
[[nodiscard]] constexpr Vector3<float> calculate_normal() const;
[[nodiscard]] float side_a_length() const;
[[nodiscard]] float side_b_length() const;
[[nodiscard]] constexpr Vector3<float> side_a_vector() const;
[[nodiscard]] constexpr Vector3<float> side_b_vector() const;
[[nodiscard]] constexpr float hypot() const;
[[nodiscard]] constexpr bool is_rectangular() const;
[[nodiscard]] constexpr Vector3<float> mid_point() const;
};
Suggestions (optional improvements)
- If generic vectors are intended, store
Vector m_vertex*;and constrainVectorto the required ops (-,cross,normalized,distance_to,+,/). - Consider renaming
mid_point()→centroid()to avoid ambiguity. - Expose an
area()helper and (optionally) a barycentric coordinate routine if you plan to use this in rasterization or intersection tests.
Last updated: 31 Oct 2025