From 37f23842295a01631b99f16d85186796d5622aa8 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 9 Nov 2023 22:46:57 -0800 Subject: [PATCH] Add srb2::math, fixed-point, vector, line and slope formula classes - srb2::math::Fixed - Operator overloads for FixedMul and FixedDiv - Implicit conversion between fixed_t, Fixed and floating-point types - srb2::math::Vec2 - Template to any type - Operator overloads for arithmetic operations - Convertible between different types - srb2::math::LineSegment - Template to any type - Holds two Vec2 instances - Sorting methods and vertical/horizontal test - srb2::math::LineEquation - Slope formula from LineSegment - y method to find y from x - Intersect algorithm - Fixed-point specialization to avoid overflows - srb2::math::LineEquationX - Inherits LineEquation - x method to find x from y --- src/math/fixed.hpp | 107 +++++++++++++++++++++++++++++++++++++ src/math/line_equation.hpp | 102 +++++++++++++++++++++++++++++++++++ src/math/line_segment.hpp | 43 +++++++++++++++ src/math/traits.hpp | 34 ++++++++++++ src/math/vec.hpp | 84 +++++++++++++++++++++++++++++ 5 files changed, 370 insertions(+) create mode 100644 src/math/fixed.hpp create mode 100644 src/math/line_equation.hpp create mode 100644 src/math/line_segment.hpp create mode 100644 src/math/traits.hpp create mode 100644 src/math/vec.hpp diff --git a/src/math/fixed.hpp b/src/math/fixed.hpp new file mode 100644 index 000000000..0e5cae4c1 --- /dev/null +++ b/src/math/fixed.hpp @@ -0,0 +1,107 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_fixed_hpp +#define math_fixed_hpp + +#include + +#include "traits.hpp" + +#include "../m_fixed.h" + +namespace srb2::math +{ + +struct Fixed +{ + static Fixed copysign(fixed_t x, fixed_t y) { return (x < 0) != (y < 0) ? -x : x; } + static Fixed hypot(fixed_t x, fixed_t y) { return FixedHypot(x, y); } + + constexpr Fixed() : val_(0) {} + constexpr Fixed(fixed_t val) : val_(val) {} + + template , bool> = true> + Fixed(T val) : val_(FloatToFixed(val)) {} + + Fixed(const Fixed& b) = default; + Fixed& operator=(const Fixed& b) = default; + + fixed_t value() const { return val_; } + int sign() const { return val_ < 0 ? -1 : 1; } + + operator fixed_t() const { return val_; } + explicit operator float() const { return FixedToFloat(val_); } + + Fixed& operator+=(const Fixed& b) + { + val_ += b.val_; + return *this; + } + + Fixed& operator-=(const Fixed& b) + { + val_ -= b.val_; + return *this; + } + + Fixed& operator*=(const Fixed& b) + { + val_ = FixedMul(val_, b.val_); + return *this; + } + + Fixed& operator/=(const Fixed& b) + { + val_ = FixedDiv(val_, b.val_); + return *this; + } + + Fixed operator-() const { return -val_; } + +#define X(op) \ + template \ + Fixed operator op(const T& b) const { return val_ op b; } \ + Fixed operator op(const Fixed& b) const \ + { \ + Fixed f{val_};\ + f op##= b;\ + return f;\ + } \ + template \ + Fixed& operator op##=(const T& b) \ + { \ + val_ op##= b; \ + return *this; \ + } + + X(+) + X(-) + X(*) + X(/) + +#undef X + +private: + fixed_t val_; +}; + +template <> +struct Traits +{ + static constexpr Fixed kZero = 0; + static constexpr Fixed kUnit = FRACUNIT; + + static constexpr auto copysign = Fixed::copysign; + static constexpr auto hypot = Fixed::hypot; +}; + +}; // namespace srb2::math + +#endif/*math_fixed_hpp*/ diff --git a/src/math/line_equation.hpp b/src/math/line_equation.hpp new file mode 100644 index 000000000..ec4b69d52 --- /dev/null +++ b/src/math/line_equation.hpp @@ -0,0 +1,102 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_line_equation_hpp +#define math_line_equation_hpp + +#include "fixed.hpp" +#include "line_segment.hpp" +#include "vec.hpp" + +namespace srb2::math +{ + +template +struct LineEquation +{ + using vec2 = Vec2; + using line_segment = LineSegment; + + // Fixed-point: shift value by this amount during + // multiplications and divisions to avoid overflows. + static constexpr std::enable_if_t, fixed_t> kF = 1024; // fixed_t, not Fixed + + LineEquation() {} + LineEquation(const vec2& p, const vec2& d) : d_(d), m_(d.y / d.x), b_(p.y - (p.x * m())) {} + LineEquation(const line_segment& l) : LineEquation(l.a, l.b - l.a) {} + + const vec2& d() const { return d_; } + T m() const { return m_; } + T b() const { return b_; } + T y(T x) const { return (m() * x) + b(); } + + vec2 intersect(const LineEquation& q) const + { + T x = (b() - q.b()) / (q.m() - m()); + return {x, y(x)}; + } + +protected: + vec2 d_{}; + T m_{}, b_{}; +}; + +template <> +inline LineEquation::LineEquation(const vec2& p, const vec2& d) : + d_(d), m_((d.y / d.x) / kF), b_((p.y / kF) - (p.x * m_)) +{ +} + +template <> +inline Fixed LineEquation::m() const +{ + return m_ * kF; +} + +template <> +inline Fixed LineEquation::b() const +{ + return b_ * kF; +} + +template <> +inline Fixed LineEquation::y(Fixed x) const +{ + return ((m_ * x) + b_) * kF; +} + +template <> +inline LineEquation::vec2 LineEquation::intersect(const LineEquation& q) const +{ + Fixed x = ((b_ - q.b_) / ((q.m_ - m_) * kF)) * kF; + return {x, y(x)}; +} + +template +struct LineEquationX : LineEquation +{ + T x(T y) const { return (y - LineEquation::b()) / LineEquation::m(); } +}; + +template <> +struct LineEquationX : LineEquation +{ + LineEquationX() {} + LineEquationX(const vec2& p, const vec2& d) : LineEquation(p, d), w_((d.x / d.y) / kF), a_((p.x / kF) - (p.y * w_)) {} + LineEquationX(const line_segment& l) : LineEquationX(l.a, l.b - l.a) {} + + Fixed x(Fixed y) const { return ((w_ * y) + a_) * kF; } + +protected: + Fixed w_{}, a_{}; +}; + +}; // namespace srb2::math + +#endif/*math_line_equation_hpp*/ diff --git a/src/math/line_segment.hpp b/src/math/line_segment.hpp new file mode 100644 index 000000000..0094acf60 --- /dev/null +++ b/src/math/line_segment.hpp @@ -0,0 +1,43 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_line_segment_hpp +#define math_line_segment_hpp + +#include +#include + +#include "vec.hpp" + +namespace srb2::math +{ + +template +struct LineSegment +{ + using vec2 = Vec2; + using view = std::pair; + + vec2 a, b; + + LineSegment(vec2 a_, vec2 b_) : a(a_), b(b_) {} + + template + LineSegment(const LineSegment& b) : LineSegment(b.a, b.b) {} + + bool horizontal() const { return a.y == b.y; } + bool vertical() const { return a.x == b.x; } + + view by_x() const { return std::minmax(a, b, [](auto& a, auto& b) { return a.x < b.x; }); } + view by_y() const { return std::minmax(a, b, [](auto& a, auto& b) { return a.y < b.y; }); } +}; + +}; // namespace srb2 + +#endif/*math_line_segment_hpp*/ diff --git a/src/math/traits.hpp b/src/math/traits.hpp new file mode 100644 index 000000000..fbb6aaa5b --- /dev/null +++ b/src/math/traits.hpp @@ -0,0 +1,34 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_traits_hpp +#define math_traits_hpp + +#include +#include + +namespace srb2::math +{ + +template +struct Traits; + +template +struct Traits>> +{ + static constexpr T kZero = 0.0; + static constexpr T kUnit = 1.0; + + static T copysign(T x, T y) { return std::copysign(x, y); } + static T hypot(T x, T y) { return std::hypot(x, y); } +}; + +}; // namespace srb2::math + +#endif/*math_traits_hpp*/ diff --git a/src/math/vec.hpp b/src/math/vec.hpp new file mode 100644 index 000000000..670a3677c --- /dev/null +++ b/src/math/vec.hpp @@ -0,0 +1,84 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_vec_hpp +#define math_vec_hpp + +#include + +#include "traits.hpp" + +namespace srb2::math +{ + +template +struct Vec2 +{ + T x, y; + + Vec2() : x{}, y{} {} + Vec2(T x_, T y_) : x(x_), y(y_) {} + Vec2(T z) : x(z), y(z) {} + + template + Vec2(const Vec2& b) : Vec2(b.x, b.y) {} + + T magnitude() const { return Traits::hypot(x, y); } + Vec2 normal() const { return {-y, x}; } + +#define X(op) \ + Vec2& operator op##=(const Vec2& b) \ + { \ + x op##= b.x; \ + y op##= b.y; \ + return *this; \ + } \ + Vec2 operator op(const Vec2& b) const { return Vec2(x op b.x, y op b.y); } \ + + X(+) + X(-) + X(*) + X(/) + +#undef X + + Vec2 operator-() const { return Vec2(-x, -y); } +}; + +template +struct is_vec2 : std::false_type {}; + +template +struct is_vec2> : std::true_type {}; + +template +inline constexpr bool is_vec2_v = is_vec2::value; + +#define X(op) \ + template , bool> = true> \ + Vec2 operator op(const T& a, const Vec2& b) \ + { \ + return Vec2 {a} op Vec2 {b}; \ + } \ + template , bool> = true> \ + Vec2 operator op(const Vec2& a, const U& b) \ + { \ + return Vec2 {a} op Vec2 {b}; \ + } \ + +X(+) +X(-) +X(*) +X(/) + +#undef X + +}; // namespace srb2::math + +#endif/*math_vec_hpp*/