You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
4.4 KiB
194 lines
4.4 KiB
#pragma once |
|
|
|
#include <cmath> |
|
#ifdef BUILD_TESTING |
|
#include <ostream> |
|
#endif |
|
|
|
#include "engine/direction.hpp" |
|
#include "engine/displacement.hpp" |
|
#include "utils/stdcompat/abs.hpp" |
|
#include "utils/stdcompat/algorithm.hpp" |
|
|
|
namespace devilution { |
|
|
|
struct Point { |
|
int x; |
|
int y; |
|
|
|
Point() = default; |
|
|
|
constexpr Point(int x, int y) |
|
: x(x) |
|
, y(y) |
|
{ |
|
} |
|
|
|
constexpr bool operator==(const Point &other) const |
|
{ |
|
return x == other.x && y == other.y; |
|
} |
|
|
|
constexpr bool operator!=(const Point &other) const |
|
{ |
|
return !(*this == other); |
|
} |
|
|
|
constexpr Point &operator+=(const Displacement &displacement) |
|
{ |
|
x += displacement.deltaX; |
|
y += displacement.deltaY; |
|
return *this; |
|
} |
|
|
|
constexpr Point &operator+=(Direction direction) |
|
{ |
|
return (*this) += Displacement(direction); |
|
} |
|
|
|
constexpr Point &operator-=(const Displacement &displacement) |
|
{ |
|
x -= displacement.deltaX; |
|
y -= displacement.deltaY; |
|
return *this; |
|
} |
|
|
|
constexpr Point &operator*=(const float factor) |
|
{ |
|
x = static_cast<int>(x * factor); |
|
y = static_cast<int>(y * factor); |
|
return *this; |
|
} |
|
|
|
constexpr Point &operator*=(const int factor) |
|
{ |
|
x *= factor; |
|
y *= factor; |
|
return *this; |
|
} |
|
|
|
constexpr friend Point operator+(Point a, Displacement displacement) |
|
{ |
|
a += displacement; |
|
return a; |
|
} |
|
|
|
constexpr friend Point operator+(Point a, Direction direction) |
|
{ |
|
a += direction; |
|
return a; |
|
} |
|
|
|
constexpr friend Displacement operator-(Point a, const Point &b) |
|
{ |
|
return { a.x - b.x, a.y - b.y }; |
|
} |
|
|
|
constexpr friend Point operator-(const Point &a) |
|
{ |
|
return { -a.x, -a.y }; |
|
} |
|
|
|
constexpr friend Point operator-(Point a, Displacement displacement) |
|
{ |
|
a -= displacement; |
|
return a; |
|
} |
|
|
|
constexpr friend Point operator*(Point a, const float factor) |
|
{ |
|
a *= factor; |
|
return a; |
|
} |
|
|
|
constexpr friend Point operator*(Point a, const int factor) |
|
{ |
|
a *= factor; |
|
return a; |
|
} |
|
|
|
/** |
|
* @brief Fast approximate distance between two points, using only integer arithmetic, with less than ~5% error |
|
* @param other Pointer to which we want the distance |
|
* @return Magnitude of vector this -> other |
|
*/ |
|
|
|
constexpr int ApproxDistance(Point other) const |
|
{ |
|
Displacement offset = abs(other - *this); |
|
auto minMax = std::minmax(offset.deltaX, offset.deltaY); |
|
int min = minMax.first; |
|
int max = minMax.second; |
|
|
|
int approx = max * 1007 + min * 441; |
|
if (max < (min * 16)) |
|
approx -= max * 40; |
|
|
|
return (approx + 512) / 1024; |
|
} |
|
|
|
/** |
|
* @brief Calculates the exact distance between two points (as accurate as the closest integer representation) |
|
* |
|
* In practice it is likely that ApproxDistance gives the same result, especially for nearby points. |
|
* @param other Point to which we want the distance |
|
* @return Exact magnitude of vector this -> other |
|
*/ |
|
int ExactDistance(Point other) const |
|
{ |
|
auto vector = *this - other; // No need to call abs() as we square the values anyway |
|
|
|
// Casting multiplication operands to a wide type to address overflow warnings |
|
return static_cast<int>(std::sqrt(static_cast<int64_t>(vector.deltaX) * vector.deltaX + static_cast<int64_t>(vector.deltaY) * vector.deltaY)); |
|
} |
|
|
|
constexpr friend Point abs(Point a) |
|
{ |
|
return { abs(a.x), abs(a.y) }; |
|
} |
|
|
|
constexpr int ManhattanDistance(Point other) const |
|
{ |
|
Displacement offset = abs(*this - other); |
|
|
|
return offset.deltaX + offset.deltaY; |
|
} |
|
|
|
constexpr int WalkingDistance(Point other) const |
|
{ |
|
Displacement offset = abs(*this - other); |
|
|
|
return std::max<int>(offset.deltaX, offset.deltaY); |
|
} |
|
|
|
/** |
|
* @brief Converts a coordinate in megatiles to the northmost of the 4 corresponding world tiles |
|
*/ |
|
constexpr Point megaToWorld() const |
|
{ |
|
return { 16 + 2 * x, 16 + 2 * y }; |
|
} |
|
|
|
/** |
|
* @brief Converts a coordinate in world tiles back to the corresponding megatile |
|
*/ |
|
constexpr Point worldToMega() const |
|
{ |
|
return { (x - 16) / 2, (y - 16) / 2 }; |
|
} |
|
|
|
#ifdef BUILD_TESTING |
|
/** |
|
* @brief Format points nicely in test failure messages |
|
* @param stream output stream, expected to have overloads for int and char* |
|
* @param point Object to display |
|
* @return the stream, to allow chaining |
|
*/ |
|
friend std::ostream &operator<<(std::ostream &stream, const Point &point) |
|
{ |
|
return stream << "(x: " << point.x << ", y: " << point.y << ")"; |
|
} |
|
#endif |
|
}; |
|
|
|
} // namespace devilution
|
|
|