blob: 9bb04b45fa39f4dc767106fbff462ad747920eef [file] [log] [blame] [edit]
#ifndef _RIVE_AABB_HPP_
#define _RIVE_AABB_HPP_
#include "rive/span.hpp"
#include "rive/math/vec2d.hpp"
#include <limits>
namespace rive
{
template <typename T> struct TAABB
{
int32_t left, top, right, bottom;
constexpr T width() const { return right - left; }
constexpr T height() const { return bottom - top; }
constexpr bool empty() const { return left >= right || top >= bottom; }
TAABB inset(T dx, T dy) const
{
return {left + dx, top + dy, right - dx, bottom - dy};
}
TAABB outset(T dx, T dy) const { return inset(-dx, -dy); }
TAABB offset(T dx, T dy) const
{
return {left + dx, top + dy, right + dx, bottom + dy};
}
TAABB join(TAABB b) const
{
return {std::min(left, b.left),
std::min(top, b.top),
std::max(right, b.right),
std::max(bottom, b.bottom)};
}
TAABB intersect(TAABB b) const
{
return {std::max(left, b.left),
std::max(top, b.top),
std::min(right, b.right),
std::min(bottom, b.bottom)};
}
bool operator==(const TAABB& o) const
{
return left == o.left && top == o.top && right == o.right &&
bottom == o.bottom;
}
bool operator!=(const TAABB& o) const { return !(*this == o); }
bool contains(const TAABB& rhs) const
{
return left <= rhs.left && top <= rhs.top && right >= rhs.right &&
bottom >= rhs.bottom;
}
static TAABB MakeWH(T width, T height) { return {0, 0, width, height}; }
};
using IAABB = TAABB<int32_t>;
class AABB
{
public:
float minX, minY, maxX, maxY;
AABB() : minX(0), minY(0), maxX(0), maxY(0) {}
AABB(const Vec2D& min, const Vec2D& max) :
minX(min.x), minY(min.y), maxX(max.x), maxY(max.y)
{}
static AABB fromLTWH(float x, float y, float width, float height)
{
return {x, y, x + width, y + height};
}
AABB(float minX, float minY, float maxX, float maxY) :
minX(minX), minY(minY), maxX(maxX), maxY(maxY)
{}
AABB(const IAABB& o) :
AABB((float)o.left, (float)o.top, (float)o.right, (float)o.bottom)
{}
AABB(Span<Vec2D>); // computes the union of all points, or 0,0,0,0
bool operator==(const AABB& o) const
{
return minX == o.minX && minY == o.minY && maxX == o.maxX &&
maxY == o.maxY;
}
bool operator!=(const AABB& o) const { return !(*this == o); }
inline float left() const { return minX; }
inline float top() const { return minY; }
inline float right() const { return maxX; }
inline float bottom() const { return maxY; }
Vec2D min() const { return Vec2D(minX, minY); }
Vec2D max() const { return Vec2D(maxX, maxY); }
float width() const { return maxX - minX; }
float height() const { return maxY - minY; }
Vec2D size() const { return {width(), height()}; }
Vec2D center() const
{
return {(minX + maxX) * 0.5f, (minY + maxY) * 0.5f};
}
bool isEmptyOrNaN() const
{
// Use "inverse" logic so we return true if either of the comparisons
// fail due to a NaN.
return !(width() > 0 && height() > 0);
}
AABB pad(float amount) const { return outset(amount, amount); }
AABB inset(float dx, float dy) const
{
AABB r = {minX + dx, minY + dy, maxX - dx, maxY - dy};
assert(r.width() >= 0);
assert(r.height() >= 0);
return r;
}
AABB outset(float dx, float dy) const { return inset(-dx, -dy); }
AABB offset(float dx, float dy) const
{
return {minX + dx, minY + dy, maxX + dx, maxY + dy};
}
IAABB round() const;
IAABB roundOut()
const; // Rounds out to integer bounds that fully contain the rectangle.
///
/// Initialize an AABB to values that represent an invalid/collapsed
/// AABB that can then expand to points that are added to it.
///
inline static AABB forExpansion()
{
return AABB(std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(),
-std::numeric_limits<float>::max(),
-std::numeric_limits<float>::max());
}
///
/// Grow the AABB to fit the point.
///
static void expandTo(AABB& out, const Vec2D& point);
static void expandTo(AABB& out, float x, float y);
/// Join two AABBs.
static void join(AABB& out, const AABB& a, const AABB& b);
void expand(const AABB& other) { join(*this, *this, other); }
Vec2D factorFrom(Vec2D point) const
{
return Vec2D(
width() == 0.0f ? 0.0f : (point.x - left()) * 2.0f / width() - 1.0f,
(height() == 0.0f ? 0.0f : point.y - top()) * 2.0f / height() -
1.0f);
}
bool contains(Vec2D position) const;
Vec2D operator[](size_t index) const
{
switch (index)
{
case 0:
return Vec2D(minX, minY);
case 1:
return Vec2D(maxX, maxY);
default:
RIVE_UNREACHABLE();
}
}
};
} // namespace rive
#endif