Changeset View
Changeset View
Standalone View
Standalone View
0ad/source/maths/FixedVector2D.h
Show All 15 Lines | |||||
*/ | */ | ||||
#ifndef INCLUDED_FIXED_VECTOR2D | #ifndef INCLUDED_FIXED_VECTOR2D | ||||
#define INCLUDED_FIXED_VECTOR2D | #define INCLUDED_FIXED_VECTOR2D | ||||
#include "maths/Fixed.h" | #include "maths/Fixed.h" | ||||
#include "maths/Sqrt.h" | #include "maths/Sqrt.h" | ||||
class CFixedVector2D | class alignas(16) CFixedVector2D | ||||
{ | { | ||||
public: | |||||
fixed X, Y; | fixed X, Y; | ||||
mutable fixed L; | |||||
public: | |||||
///constructors, etc. | |||||
CFixedVector2D() { } | CFixedVector2D() { } | ||||
CFixedVector2D(fixed X, fixed Y) : X(X), Y(Y) { } | CFixedVector2D(fixed X, fixed Y) : X(X), Y(Y) | ||||
{ | |||||
MarkLengthUpdateIsRequired(); | |||||
} | |||||
//virtual ~CFixedVector2D() { } //do not inherit with data augmentation | |||||
///get functions | |||||
fixed const & getX() const { return X; } | |||||
fixed const & getY() const { return Y; } | |||||
///Not quite "set functions"; "get non-const ref" functions to minimize changes | |||||
fixed& Xref() | |||||
{ | |||||
MarkLengthUpdateIsRequired(); | |||||
return X; | |||||
} | |||||
fixed& Yref() | |||||
{ | |||||
MarkLengthUpdateIsRequired(); | |||||
return Y; | |||||
} | |||||
wraitii: We're using c++17 now, so you can `alignas` to specify this: https://docs.microsoft.com/fr… | |||||
/// Vector equality | /// Vector equality | ||||
bool operator==(const CFixedVector2D& v) const | bool operator==(const CFixedVector2D& v) const | ||||
{ | { | ||||
return (X == v.X && Y == v.Y); | return (X == v.X && Y == v.Y); | ||||
} | } | ||||
/// Vector inequality | /// Vector inequality | ||||
bool operator!=(const CFixedVector2D& v) const | bool operator!=(const CFixedVector2D& v) const | ||||
Show All 18 Lines | public: | ||||
{ | { | ||||
return CFixedVector2D(-X, -Y); | return CFixedVector2D(-X, -Y); | ||||
} | } | ||||
/// Vector addition | /// Vector addition | ||||
CFixedVector2D& operator+=(const CFixedVector2D& v) | CFixedVector2D& operator+=(const CFixedVector2D& v) | ||||
{ | { | ||||
*this = *this + v; | *this = *this + v; | ||||
MarkLengthUpdateIsRequired(); | |||||
return *this; | return *this; | ||||
} | } | ||||
/// Vector subtraction | /// Vector subtraction | ||||
CFixedVector2D& operator-=(const CFixedVector2D& v) | CFixedVector2D& operator-=(const CFixedVector2D& v) | ||||
{ | { | ||||
*this = *this - v; | *this = *this - v; | ||||
MarkLengthUpdateIsRequired(); | |||||
return *this; | return *this; | ||||
} | } | ||||
/// Scalar multiplication by an integer | /// Scalar multiplication by an integer | ||||
CFixedVector2D operator*(int n) const | CFixedVector2D operator*(int n) const | ||||
{ | { | ||||
return CFixedVector2D(X*n, Y*n); | return CFixedVector2D(X*n, Y*n); | ||||
} | } | ||||
/// Scalar division by an integer. Must not have n == 0. | /// Scalar division by an integer. Must not have n == 0. | ||||
CFixedVector2D operator/(int n) const | CFixedVector2D operator/(int n) const | ||||
{ | { | ||||
return CFixedVector2D(X/n, Y/n); | return CFixedVector2D(X/n, Y/n); | ||||
} | } | ||||
/** | /** | ||||
* Multiply by a CFixed. Likely to overflow if both numbers are large, | * Multiply by a CFixed. Likely to overflow if both numbers are large, | ||||
* so we use an ugly name instead of operator* to make it obvious. | * so we use an ugly name instead of operator* to make it obvious. | ||||
*/ | */ | ||||
CFixedVector2D Multiply(fixed n) const | CFixedVector2D Multiply(fixed n) const | ||||
{ | { | ||||
return CFixedVector2D(X.Multiply(n), Y.Multiply(n)); | return CFixedVector2D(X.Multiply(n), Y.Multiply(n)); | ||||
} | } | ||||
/** | private: | ||||
* Returns the length of the vector. | |||||
* Will not overflow if the result can be represented as type 'fixed'. | void UnconditionallyUpdateLength() const //L is mutable | ||||
*/ | |||||
fixed Length() const | |||||
{ | { | ||||
// Do intermediate calculations with 64-bit ints to avoid overflows | // Do intermediate calculations with 64-bit ints to avoid overflows | ||||
u64 xx = SQUARE_U64_FIXED(X); | u64 xx = SQUARE_U64_FIXED(X); | ||||
u64 yy = SQUARE_U64_FIXED(Y); | u64 yy = SQUARE_U64_FIXED(Y); | ||||
u64 d2 = xx + yy; | u64 d2 = xx + yy; | ||||
CheckUnsignedAdditionOverflow(d2, xx, L"Overflow in CFixedVector2D::Length() part 1") | CheckUnsignedAdditionOverflow(d2, xx, L"Overflow in CFixedVector2D::Length() part 1") | ||||
u32 d = isqrt64(d2); | u32 d = isqrt64(d2); | ||||
CheckU32CastOverflow(d, i32, L"Overflow in CFixedVector2D::Length() part 2") | CheckU32CastOverflow(d, i32, L"Overflow in CFixedVector2D::Length() part 2") | ||||
fixed r; | |||||
r.SetInternalValue(static_cast<i32>(d)); | |||||
return r; | |||||
} | |||||
/** | L.SetInternalValue(static_cast<i32>(d)); //Store the length in L | ||||
* Returns -1, 0, +1 depending on whether length is less/equal/greater | } | ||||
* than the argument. | void ConditionallyUpdateLength() const | ||||
* Avoids sqrting and overflowing. | |||||
*/ | |||||
int CompareLength(fixed cmp) const | |||||
{ | { | ||||
u64 d2 = SQUARE_U64_FIXED(X) + SQUARE_U64_FIXED(Y); // d2 <= 2^63 (no overflow) | if( L.IsZero() && !(X.IsZero() && Y.IsZero()) ) | ||||
u64 cmpSquared = SQUARE_U64_FIXED(cmp); | UnconditionallyUpdateLength(); | ||||
} | |||||
void MarkLengthUpdateIsRequired() const //L is mutable | |||||
{ | |||||
L.SetInternalValue(0); | |||||
} | |||||
if (d2 < cmpSquared) | public: | ||||
return -1; | |||||
if (d2 > cmpSquared) | fixed const & Length() const | ||||
return +1; | { | ||||
ConditionallyUpdateLength(); | |||||
return L; | |||||
} | |||||
return 0; | int CompareLength(fixed cmp) const | ||||
{ | |||||
ConditionallyUpdateLength(); | |||||
int result = 0; | |||||
if (L < cmp) result = -1; | |||||
if (cmp < L) result = 1; | |||||
return result; | |||||
} | } | ||||
/** | /** | ||||
* Same as above, but avoids squaring the compared value. | * Like CompareLength(), but avoids square-rooting the compared value. | ||||
* The argument must be the result of an SQUARE_U64_FIXED operation. | * The argument must be the result of an SQUARE_U64_FIXED operation. | ||||
*/ | */ | ||||
int CompareLengthSquared(u64 cmpSquared) const | int CompareLengthSquared(u64 cmpSquared) const | ||||
{ | { | ||||
u64 d2 = SQUARE_U64_FIXED(X) + SQUARE_U64_FIXED(Y); // d2 <= 2^63 (no overflow) | u64 d2 = SQUARE_U64_FIXED(X) + SQUARE_U64_FIXED(Y); // d2 <= 2^63 (no overflow) | ||||
if (d2 < cmpSquared) | if (d2 < cmpSquared) | ||||
return -1; | return -1; | ||||
if (d2 > cmpSquared) | if (d2 > cmpSquared) | ||||
return +1; | return +1; | ||||
return 0; | return 0; | ||||
} | } | ||||
/** | |||||
* Returns -1, 0, +1 depending on whether length is less/equal/greater | |||||
* than the argument's length. | |||||
* Avoids sqrting and overflowing. | |||||
*/ | |||||
int CompareLength(const CFixedVector2D& other) const | int CompareLength(const CFixedVector2D& other) const | ||||
{ | { | ||||
u64 d2 = SQUARE_U64_FIXED(X) + SQUARE_U64_FIXED(Y); | ConditionallyUpdateLength(); | ||||
u64 od2 = SQUARE_U64_FIXED(other.X) + SQUARE_U64_FIXED(other.Y); | other.ConditionallyUpdateLength(); | ||||
int result = 0; | |||||
if (d2 < od2) | if ( L < other.L ) result = -1; | ||||
return -1; | if ( other.L < L ) result = 1; | ||||
return result; | |||||
if (d2 > od2) | |||||
return +1; | |||||
return 0; | |||||
} | } | ||||
bool IsZero() const | bool IsZero() const | ||||
{ | { | ||||
return X.IsZero() && Y.IsZero(); | return X.IsZero() && Y.IsZero(); | ||||
} | } | ||||
/** | /** | ||||
* Normalize the vector so that length is close to 1. | * Normalize the vector so that length is close to 1. | ||||
* If length is 0, does nothing. | * If length is 0, does nothing. | ||||
*/ | */ | ||||
void Normalize() | void Normalize() | ||||
{ | { | ||||
if (!IsZero()) | ConditionallyUpdateLength(); | ||||
if (!L.IsZero()) | |||||
{ | { | ||||
fixed l = Length(); | X = X / L; | ||||
X = X / l; | Y = Y / L; | ||||
Y = Y / l; | UnconditionallyUpdateLength(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Normalize the vector so that length is close to n. | * Normalize the vector so that length is close to n. | ||||
* If length is 0, does nothing. | * If length is 0, does nothing. | ||||
*/ | */ | ||||
void Normalize(fixed n) | void Normalize(fixed n) | ||||
{ | { | ||||
fixed l = Length(); | ConditionallyUpdateLength(); | ||||
if (!l.IsZero()) | if (!L.IsZero()) | ||||
{ | { | ||||
X = X.MulDiv(n, l); | X = X.MulDiv(n, L); | ||||
Y = Y.MulDiv(n, l); | Y = Y.MulDiv(n, L); | ||||
UnconditionallyUpdateLength(); //L will be pretty close to n | |||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Compute the dot product of this vector with another. | * Compute the dot product of this vector with another. | ||||
* Likely to overflow if both vectors are large-ish (around the 200 range). | * Likely to overflow if both vectors are large-ish (around the 200 range). | ||||
*/ | */ | ||||
fixed Dot(const CFixedVector2D& v) const | fixed Dot(const CFixedVector2D& v) const | ||||
Show All 40 Lines |
Wildfire Games · Phabricator
We're using c++17 now, so you can alignas to specify this: https://docs.microsoft.com/fr-fr/cpp/cpp/alignment-cpp-declarations?view=msvc-160