Skip to content

omath::Vector3 — 3D vector (C++20/23)

Header: your project’s vector3.hpp Namespace: omath Template: template<class Type> requires std::is_arithmetic_v<Type> Depends on: omath::Vector2<Type> (base class), omath::Angle (for angle_between) C++: uses std::expectedC++23 recommended (or a backport)

Vector3<Type> extends Vector2<Type> with a z component and 3D operations: arithmetic, geometry (dot, cross, distance), normalization, angle-between with robust error signaling, hashing (for float) and std::formatter support.


Quick start

#include "vector3.hpp"
using omath::Vector3;

using Vec3f = Vector3<float>;

Vec3f a{3, 4, 0};
Vec3f b{1, 2, 2};

auto d     = a.distance_to(b);         // Euclidean distance
auto dot   = a.dot(b);                 // 3*1 + 4*2 + 0*2 = 11
auto cr    = a.cross(b);               // (8, -6, 2)
auto len   = a.length();               // hypot(x,y,z)
auto unit  = a.normalized();           // safe normalize (returns a if length==0)

if (auto ang = a.angle_between(b)) {
  float deg = ang->as_degrees();       // [0, 180], clamped
} else {
  // vectors have zero length -> no defined angle
}

Data members

Type x{0};  // inherited from Vector2<Type>
Type y{0};  // inherited from Vector2<Type>
Type z{0};

Constructors

constexpr Vector3() noexcept;
constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept;

Equality & ordering

constexpr bool operator==(const Vector3&) const noexcept; // component-wise
constexpr bool operator!=(const Vector3&) const noexcept;

bool operator< (const Vector3&) const noexcept; // compare by length()
bool operator> (const Vector3&) const noexcept;
bool operator<=(const Vector3&) const noexcept;
bool operator>=(const Vector3&) const noexcept;

Note: Ordering uses magnitude, not lexicographic order.


Arithmetic (mutating)

Component-wise with another vector:

Vector3& operator+=(const Vector3&) noexcept;
Vector3& operator-=(const Vector3&) noexcept;
Vector3& operator*=(const Vector3&) noexcept;  // Hadamard product
Vector3& operator/=(const Vector3&) noexcept;

With a scalar:

Vector3& operator*=(const Type& v) noexcept;
Vector3& operator/=(const Type& v) noexcept;
Vector3& operator+=(const Type& v) noexcept;
Vector3& operator-=(const Type& v) noexcept;

Arithmetic (non-mutating)

constexpr Vector3 operator-()                     const noexcept;
constexpr Vector3 operator+(const Vector3&)       const noexcept;
constexpr Vector3 operator-(const Vector3&)       const noexcept;
constexpr Vector3 operator*(const Vector3&)       const noexcept; // Hadamard
constexpr Vector3 operator/(const Vector3&)       const noexcept; // Hadamard
constexpr Vector3 operator*(const Type& scalar)   const noexcept;
constexpr Vector3 operator/(const Type& scalar)   const noexcept;

Geometry & helpers

// Distances & lengths
Type        distance_to(const Vector3&) const;          // sqrt of squared distance
constexpr Type distance_to_sqr(const Vector3&) const noexcept;
#ifndef _MSC_VER
constexpr Type length()     const;                       // hypot(x,y,z)
constexpr Type length_2d()  const;                       // 2D length from base
constexpr Vector3 normalized() const;                    // returns *this if length==0
#else
Type        length()     const noexcept;
Type        length_2d()  const noexcept;
Vector3     normalized() const noexcept;
#endif
constexpr Type length_sqr() const noexcept;

// Products
constexpr Type   dot(const Vector3&)   const noexcept;
constexpr Vector3 cross(const Vector3&) const noexcept;  // right-handed

// Sums & tuples
constexpr Type sum()      const noexcept;  // x + y + z
constexpr Type sum_2d()   const noexcept;  // x + y
constexpr auto as_tuple() const noexcept -> std::tuple<Type,Type,Type>;

// Utilities
Vector3& abs() noexcept; // component-wise absolute

Angles & orthogonality

enum class Vector3Error { IMPOSSIBLE_BETWEEN_ANGLE };

// Angle in degrees, clamped to [0,180]. Error if any vector has zero length.
std::expected<
  omath::Angle<float, 0.f, 180.f, AngleFlags::Clamped>,
  Vector3Error
> angle_between(const Vector3& other) const noexcept;

bool is_perpendicular(const Vector3& other) const noexcept; // true if angle == 90°

Hashing & formatting

  • Hash (for Vector3<float>)

cpp template<> struct std::hash<omath::Vector3<float>> { std::size_t operator()(const omath::Vector3<float>&) const noexcept; };

Example:

cpp std::unordered_map<omath::Vector3<float>, int> counts; counts[{1.f, 2.f, 3.f}] = 7;

  • std::formatter (all character types)

cpp template<class Type> struct std::formatter<omath::Vector3<Type>>; // prints "[x, y, z]"


Error handling

  • angle_between() returns std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE) if either vector length is zero.
  • Other operations are total for arithmetic Type (no throwing behavior in this class).

Examples

Cross product & perpendicular check

omath::Vector3<float> x{1,0,0}, y{0,1,0};
auto z = x.cross(y);                // (0,0,1)
bool perp = x.is_perpendicular(y);  // true

Safe normalization and angle

omath::Vector3<float> u{0,0,0}, v{1,1,0};
auto nu = u.normalized();           // returns {0,0,0}
if (auto ang = u.angle_between(v)) {
  // won't happen: u has zero length → error
} else {
  // handle degenerate case
}

Hadamard vs scalar multiply

omath::Vector3<float> a{2,3,4}, b{5,6,7};
auto h = a * b;   // (10, 18, 28) component-wise
auto s = a * 2.f; // (4, 6, 8)    scalar

API summary (signatures)

// Ctors
constexpr Vector3() noexcept;
constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept;

// Equality & ordering
constexpr bool operator==(const Vector3&) const noexcept;
constexpr bool operator!=(const Vector3&) const noexcept;
bool operator< (const Vector3&) const noexcept;
bool operator> (const Vector3&) const noexcept;
bool operator<=(const Vector3&) const noexcept;
bool operator>=(const Vector3&) const noexcept;

// Mutating arithmetic
Vector3& operator+=(const Vector3&) noexcept;
Vector3& operator-=(const Vector3&) noexcept;
Vector3& operator*=(const Vector3&) noexcept;
Vector3& operator/=(const Vector3&) noexcept;
Vector3& operator*=(const Type&)   noexcept;
Vector3& operator/=(const Type&)   noexcept;
Vector3& operator+=(const Type&)   noexcept;
Vector3& operator-=(const Type&)   noexcept;

// Non-mutating arithmetic
constexpr Vector3 operator-() const noexcept;
constexpr Vector3 operator+(const Vector3&) const noexcept;
constexpr Vector3 operator-(const Vector3&) const noexcept;
constexpr Vector3 operator*(const Vector3&) const noexcept;
constexpr Vector3 operator/(const Vector3&) const noexcept;
constexpr Vector3 operator*(const Type&)   const noexcept;
constexpr Vector3 operator/(const Type&)   const noexcept;

// Geometry
Type        distance_to(const Vector3&) const;
constexpr Type distance_to_sqr(const Vector3&) const noexcept;
#ifndef _MSC_VER
constexpr Type   length() const;
constexpr Type   length_2d() const;
constexpr Vector3 normalized() const;
#else
Type        length() const noexcept;
Type        length_2d() const noexcept;
Vector3     normalized() const noexcept;
#endif
constexpr Type length_sqr() const noexcept;
constexpr Type dot(const Vector3&) const noexcept;
constexpr Vector3 cross(const Vector3&) const noexcept;

Vector3&    abs() noexcept;
constexpr Type sum()    const noexcept;
constexpr Type sum_2d() const noexcept;
constexpr auto as_tuple() const noexcept -> std::tuple<Type,Type,Type>;

// Angles
std::expected<omath::Angle<float,0.f,180.f,AngleFlags::Clamped>, omath::Vector3Error>
angle_between(const Vector3&) const noexcept;
bool is_perpendicular(const Vector3&) const noexcept;

// Hash (float) and formatter specializations provided below the class

Notes

  • Inherits all public API of Vector2<Type> (including x, y, many operators, and helpers used internally).
  • normalized() returns the original vector if its length is zero (no NaNs).
  • cross() uses the standard right-handed definition.
  • length()/normalized() are constexpr on non-MSVC; MSVC builds provide noexcept runtime versions.

See Also


Last updated: 1 Nov 2025