2017-08-28 01:47:38 +02:00
|
|
|
#ifndef RECT3I_H
|
|
|
|
#define RECT3I_H
|
|
|
|
|
|
|
|
#include "vector3i.h"
|
2019-04-24 02:07:06 +01:00
|
|
|
#include <core/variant.h>
|
2017-08-28 01:47:38 +02:00
|
|
|
|
2019-09-03 22:54:40 +01:00
|
|
|
// TODO Could be renamed to something more sensical, like Box3i
|
2017-08-28 01:47:38 +02:00
|
|
|
class Rect3i {
|
|
|
|
|
|
|
|
public:
|
|
|
|
Vector3i pos;
|
|
|
|
Vector3i size;
|
|
|
|
|
|
|
|
Rect3i() {}
|
|
|
|
|
2019-04-24 01:29:47 +01:00
|
|
|
Rect3i(Vector3i p_pos, Vector3i p_size) :
|
|
|
|
pos(p_pos),
|
|
|
|
size(p_size) {}
|
2017-08-28 01:47:38 +02:00
|
|
|
|
2019-08-14 20:34:06 +01:00
|
|
|
Rect3i(int ox, int oy, int oz, int sx, int sy, int sz) :
|
|
|
|
pos(ox, oy, oz),
|
2021-05-07 18:38:02 +01:00
|
|
|
size(sx, sy, sz) {}
|
2019-08-14 20:34:06 +01:00
|
|
|
|
2019-04-24 01:29:47 +01:00
|
|
|
Rect3i(const Rect3i &other) :
|
|
|
|
pos(other.pos),
|
|
|
|
size(other.size) {}
|
2017-08-28 01:47:38 +02:00
|
|
|
|
|
|
|
static inline Rect3i from_center_extents(Vector3i center, Vector3i extents) {
|
2019-04-24 01:29:47 +01:00
|
|
|
return Rect3i(center - extents, 2 * extents);
|
2017-08-28 01:47:38 +02:00
|
|
|
}
|
|
|
|
|
2020-09-13 22:36:28 +01:00
|
|
|
// max is exclusive
|
2019-09-03 22:54:40 +01:00
|
|
|
static inline Rect3i from_min_max(Vector3i p_min, Vector3i p_max) {
|
|
|
|
return Rect3i(p_min, p_max - p_min);
|
|
|
|
}
|
|
|
|
|
2017-08-28 01:47:38 +02:00
|
|
|
static inline Rect3i get_bounding_box(Rect3i a, Rect3i b) {
|
|
|
|
Rect3i box;
|
|
|
|
|
|
|
|
box.pos.x = MIN(a.pos.x, b.pos.x);
|
|
|
|
box.pos.y = MIN(a.pos.y, b.pos.y);
|
|
|
|
box.pos.z = MIN(a.pos.z, b.pos.z);
|
|
|
|
|
|
|
|
Vector3i max_a = a.pos + a.size;
|
|
|
|
Vector3i max_b = b.pos + b.size;
|
|
|
|
|
|
|
|
box.size.x = MAX(max_a.x, max_b.x) - box.pos.x;
|
|
|
|
box.size.y = MAX(max_a.y, max_b.y) - box.pos.y;
|
|
|
|
box.size.z = MAX(max_a.z, max_b.z) - box.pos.z;
|
|
|
|
|
|
|
|
return box;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool inline contains(Vector3i p_pos) const {
|
2020-08-10 19:03:01 +01:00
|
|
|
const Vector3i end = pos + size;
|
2019-04-24 01:29:47 +01:00
|
|
|
return p_pos.x >= pos.x &&
|
|
|
|
p_pos.y >= pos.y &&
|
|
|
|
p_pos.z >= pos.z &&
|
|
|
|
p_pos.x < end.x &&
|
|
|
|
p_pos.y < end.y &&
|
|
|
|
p_pos.z < end.z;
|
2017-08-28 01:47:38 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 19:03:01 +01:00
|
|
|
bool inline contains(const Rect3i other) const {
|
|
|
|
const Vector3i other_end = other.pos + other.size;
|
|
|
|
const Vector3i end = pos + size;
|
|
|
|
return other.pos.x >= pos.x &&
|
|
|
|
other.pos.y >= pos.y &&
|
|
|
|
other.pos.z >= pos.z &&
|
|
|
|
other_end.x < end.x &&
|
|
|
|
other_end.y < end.y &&
|
|
|
|
other_end.z < end.z;
|
|
|
|
}
|
|
|
|
|
2017-08-28 01:47:38 +02:00
|
|
|
String to_string() const {
|
|
|
|
return String("(o:{0}, s:{1})").format(varray(pos.to_vec3(), size.to_vec3()));
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:55:02 +01:00
|
|
|
bool intersects(Rect3i other) const {
|
2019-09-01 16:10:01 +01:00
|
|
|
if (pos.x > other.pos.x + other.size.x) {
|
2018-10-04 23:13:03 +01:00
|
|
|
return false;
|
2019-09-01 16:10:01 +01:00
|
|
|
}
|
|
|
|
if (pos.y > other.pos.y + other.size.y) {
|
2018-10-04 23:13:03 +01:00
|
|
|
return false;
|
2019-09-01 16:10:01 +01:00
|
|
|
}
|
|
|
|
if (pos.z > other.pos.z + other.size.z) {
|
2018-10-04 23:13:03 +01:00
|
|
|
return false;
|
2019-09-01 16:10:01 +01:00
|
|
|
}
|
|
|
|
if (other.pos.x > pos.x + size.x) {
|
2018-10-04 23:13:03 +01:00
|
|
|
return false;
|
2019-09-01 16:10:01 +01:00
|
|
|
}
|
|
|
|
if (other.pos.y > pos.y + size.y) {
|
2018-10-04 23:13:03 +01:00
|
|
|
return false;
|
2019-09-01 16:10:01 +01:00
|
|
|
}
|
|
|
|
if (other.pos.z > pos.z + size.z) {
|
2018-10-04 23:13:03 +01:00
|
|
|
return false;
|
2019-09-01 16:10:01 +01:00
|
|
|
}
|
2018-10-04 23:13:03 +01:00
|
|
|
return true;
|
|
|
|
}
|
2019-08-29 22:55:02 +01:00
|
|
|
|
|
|
|
struct NoAction {
|
|
|
|
inline void operator()(const Vector3i pos) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename A>
|
2019-09-01 16:10:01 +01:00
|
|
|
inline void for_each_cell(A a) const {
|
2019-08-29 22:55:02 +01:00
|
|
|
Vector3i max = pos + size;
|
|
|
|
Vector3i p;
|
|
|
|
for (p.z = pos.z; p.z < max.z; ++p.z) {
|
|
|
|
for (p.y = pos.y; p.y < max.y; ++p.y) {
|
|
|
|
for (p.x = pos.x; p.x < max.x; ++p.x) {
|
|
|
|
a(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
template <typename A>
|
|
|
|
inline void for_each_cell_zxy(A a) const {
|
|
|
|
Vector3i max = pos + size;
|
|
|
|
Vector3i p;
|
|
|
|
for (p.z = pos.z; p.z < max.z; ++p.z) {
|
|
|
|
for (p.x = pos.x; p.x < max.x; ++p.x) {
|
|
|
|
for (p.y = pos.y; p.y < max.y; ++p.y) {
|
|
|
|
a(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-15 20:35:31 +01:00
|
|
|
// Returns true if all cells of the box comply with the given predicate on their position.
|
2019-09-06 23:24:56 +01:00
|
|
|
template <typename A>
|
|
|
|
inline bool all_cells_match(A a) const {
|
|
|
|
Vector3i max = pos + size;
|
|
|
|
Vector3i p;
|
|
|
|
for (p.z = pos.z; p.z < max.z; ++p.z) {
|
|
|
|
for (p.y = pos.y; p.y < max.y; ++p.y) {
|
|
|
|
for (p.x = pos.x; p.x < max.x; ++p.x) {
|
|
|
|
if (!a(p)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-04-15 20:35:31 +01:00
|
|
|
// Subtracts another box from the current box,
|
|
|
|
// then execute a function on a set of boxes representing the remaining area.
|
|
|
|
//
|
|
|
|
// For example, seen from 2D, a possible result would be:
|
|
|
|
//
|
|
|
|
// o-----------o o-----o-----o
|
|
|
|
// | A | | C1 | C2 |
|
|
|
|
// | o-----+---o | o-----o
|
|
|
|
// | | | | A - B => | |
|
|
|
|
// o-----+-----o | o-----o
|
|
|
|
// | B |
|
|
|
|
// o---------o
|
|
|
|
//
|
2019-09-01 16:10:01 +01:00
|
|
|
template <typename A>
|
2019-09-03 22:54:40 +01:00
|
|
|
void difference(const Rect3i &b, A action) const {
|
2019-09-02 20:08:33 +01:00
|
|
|
if (!intersects(b)) {
|
|
|
|
action(*this);
|
2019-09-01 16:10:01 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2019-09-02 20:08:33 +01:00
|
|
|
Rect3i a = *this;
|
|
|
|
|
2019-09-01 16:10:01 +01:00
|
|
|
Vector3i a_min = a.pos;
|
|
|
|
Vector3i b_min = b.pos;
|
|
|
|
Vector3i a_max = a.pos + a.size;
|
|
|
|
Vector3i b_max = b.pos + b.size;
|
|
|
|
|
|
|
|
if (a_min.x < b_min.x) {
|
|
|
|
Vector3i a_rect_size(b_min.x - a_min.x, a.size.y, a.size.z);
|
|
|
|
action(Rect3i(a_min, a_rect_size));
|
|
|
|
a_min.x = b_min.x;
|
|
|
|
a.pos.x = b.pos.x;
|
|
|
|
a.size.x = a_max.x - a_min.x;
|
|
|
|
}
|
|
|
|
if (a_min.y < b_min.y) {
|
|
|
|
Vector3i a_rect_size(a.size.x, b_min.y - a_min.y, a.size.z);
|
|
|
|
action(Rect3i(a_min, a_rect_size));
|
|
|
|
a_min.y = b_min.y;
|
|
|
|
a.pos.y = b.pos.y;
|
|
|
|
a.size.y = a_max.y - a_min.y;
|
|
|
|
}
|
|
|
|
if (a_min.z < b_min.z) {
|
|
|
|
Vector3i a_rect_size(a.size.x, a.size.y, b_min.z - a_min.z);
|
|
|
|
action(Rect3i(a_min, a_rect_size));
|
|
|
|
a_min.z = b_min.z;
|
|
|
|
a.pos.z = b.pos.z;
|
|
|
|
a.size.z = a_max.z - a_min.z;
|
|
|
|
}
|
2019-08-29 22:55:02 +01:00
|
|
|
|
2019-09-01 16:10:01 +01:00
|
|
|
if (a_max.x > b_max.x) {
|
|
|
|
Vector3i a_rect_pos(b_max.x, a_min.y, a_min.z);
|
|
|
|
Vector3i a_rect_size(a_max.x - b_max.x, a.size.y, a.size.z);
|
|
|
|
action(Rect3i(a_rect_pos, a_rect_size));
|
|
|
|
a_max.x = b_max.x;
|
|
|
|
a.size.x = a_max.x - a_min.x;
|
|
|
|
}
|
|
|
|
if (a_max.y > b_max.y) {
|
|
|
|
Vector3i a_rect_pos(a_min.x, b_max.y, a_min.z);
|
|
|
|
Vector3i a_rect_size(a.size.x, a_max.y - b_max.y, a.size.z);
|
|
|
|
action(Rect3i(a_rect_pos, a_rect_size));
|
|
|
|
a_max.y = b_max.y;
|
|
|
|
a.size.y = a_max.y - a_min.y;
|
|
|
|
}
|
|
|
|
if (a_max.z > b_max.z) {
|
|
|
|
Vector3i a_rect_pos(a_min.x, a_min.y, b_max.z);
|
|
|
|
Vector3i a_rect_size(a.size.x, a.size.y, a_max.z - b_max.z);
|
|
|
|
action(Rect3i(a_rect_pos, a_rect_size));
|
|
|
|
}
|
|
|
|
}
|
2019-09-03 22:54:40 +01:00
|
|
|
|
2021-04-15 20:35:31 +01:00
|
|
|
// Calls a function on all side cell positions belonging to the box.
|
|
|
|
// This function was implemented with no particular order in mind.
|
2021-04-03 20:39:37 +01:00
|
|
|
template <typename F>
|
|
|
|
void for_inner_outline(F f) const {
|
|
|
|
// o-------o
|
|
|
|
// /| /|
|
|
|
|
// / | / |
|
|
|
|
// o--+----o |
|
|
|
|
// | o----|--o
|
|
|
|
// | / | / y z
|
|
|
|
// |/ |/ |/
|
|
|
|
// o-------o o---x
|
|
|
|
|
|
|
|
Vector3i min_pos = pos;
|
|
|
|
Vector3i max_pos = pos + size;
|
|
|
|
|
|
|
|
// Top and bottom
|
|
|
|
for (int z = min_pos.z; z < max_pos.z; ++z) {
|
|
|
|
for (int x = min_pos.x; x < max_pos.x; ++x) {
|
|
|
|
f(Vector3i(x, min_pos.y, z));
|
|
|
|
f(Vector3i(x, max_pos.y - 1, z));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exclude top and bottom cells from the sides we'll iterate next
|
|
|
|
++min_pos.y;
|
|
|
|
--max_pos.y;
|
|
|
|
|
|
|
|
// Z sides
|
|
|
|
for (int x = min_pos.x; x < max_pos.x; ++x) {
|
|
|
|
for (int y = min_pos.y; y < max_pos.y; ++y) {
|
|
|
|
f(Vector3i(x, y, min_pos.z));
|
|
|
|
f(Vector3i(x, y, max_pos.z - 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exclude cells belonging to edges of Z sides we did before
|
|
|
|
++min_pos.z;
|
|
|
|
--max_pos.z;
|
|
|
|
|
|
|
|
// X sides
|
|
|
|
for (int z = min_pos.z; z < max_pos.z; ++z) {
|
|
|
|
for (int y = min_pos.y; y < max_pos.y; ++y) {
|
|
|
|
f(Vector3i(min_pos.x, y, z));
|
|
|
|
f(Vector3i(max_pos.x - 1, y, z));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 22:54:40 +01:00
|
|
|
inline Rect3i padded(int m) const {
|
|
|
|
return Rect3i(
|
|
|
|
pos.x - m,
|
|
|
|
pos.y - m,
|
|
|
|
pos.z - m,
|
|
|
|
size.x + 2 * m,
|
|
|
|
size.y + 2 * m,
|
|
|
|
size.z + 2 * m);
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
// Converts the rectangle into a coordinate system of higher step size,
|
|
|
|
// rounding outwards of the area covered by the original rectangle if divided coordinates have remainders.
|
2019-09-03 22:54:40 +01:00
|
|
|
inline Rect3i downscaled(int step_size) const {
|
|
|
|
Rect3i o;
|
2020-10-24 00:08:14 +01:00
|
|
|
o.pos = pos.floordiv(step_size);
|
2021-04-03 20:39:37 +01:00
|
|
|
// TODO Is that ceildiv?
|
2020-10-24 00:08:14 +01:00
|
|
|
Vector3i max_pos = (pos + size - Vector3i(1)).floordiv(step_size);
|
2019-09-03 22:54:40 +01:00
|
|
|
o.size = max_pos - o.pos + Vector3i(1);
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2021-04-03 20:39:37 +01:00
|
|
|
// Converts the rectangle into a coordinate system of higher step size,
|
|
|
|
// rounding inwards of the area covered by the original rectangle if divided coordinates have remainders.
|
|
|
|
// This is such that the result is included in the original rectangle (assuming a common coordinate system).
|
|
|
|
// The result can be an empty rectangle.
|
|
|
|
inline Rect3i downscaled_inner(int step_size) const {
|
|
|
|
return Rect3i::from_min_max(pos.ceildiv(step_size), (pos + size).floordiv(step_size));
|
|
|
|
}
|
|
|
|
|
2020-01-21 20:00:17 +00:00
|
|
|
static inline void clip_range(int &pos, int &size, int lim_pos, int lim_size) {
|
|
|
|
int max_pos = pos + size;
|
|
|
|
int lim_max_pos = lim_pos + lim_size;
|
|
|
|
|
|
|
|
pos = CLAMP(pos, lim_pos, lim_max_pos);
|
|
|
|
max_pos = CLAMP(max_pos, lim_pos, lim_max_pos);
|
|
|
|
|
|
|
|
size = max_pos - pos;
|
|
|
|
if (size < 0) {
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 22:54:40 +01:00
|
|
|
inline void clip(const Rect3i lim) {
|
2020-01-21 20:00:17 +00:00
|
|
|
clip_range(pos.x, size.x, lim.pos.x, lim.size.x);
|
|
|
|
clip_range(pos.y, size.y, lim.pos.y, lim.size.y);
|
|
|
|
clip_range(pos.z, size.z, lim.pos.z, lim.size.z);
|
2019-09-03 22:54:40 +01:00
|
|
|
}
|
2019-12-14 21:34:41 +00:00
|
|
|
|
2020-08-10 19:03:01 +01:00
|
|
|
inline Rect3i clipped(const Rect3i lim) const {
|
|
|
|
Rect3i copy(*this);
|
|
|
|
copy.clip(lim);
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
2019-12-14 21:34:41 +00:00
|
|
|
inline bool encloses(const Rect3i &other) const {
|
|
|
|
return pos.x <= other.pos.x &&
|
|
|
|
pos.y <= other.pos.y &&
|
|
|
|
pos.x + size.x >= other.pos.x + other.size.x &&
|
|
|
|
pos.y + size.y >= other.pos.y + other.size.y;
|
|
|
|
}
|
2020-10-22 22:43:31 +01:00
|
|
|
|
|
|
|
inline Rect3i snapped(int step) const {
|
2021-03-12 23:33:29 +00:00
|
|
|
Rect3i r = downscaled(step);
|
|
|
|
r.pos *= step;
|
|
|
|
r.size *= step;
|
|
|
|
return r;
|
2020-10-22 22:43:31 +01:00
|
|
|
}
|
2017-08-28 01:47:38 +02:00
|
|
|
};
|
|
|
|
|
2019-04-24 01:29:47 +01:00
|
|
|
inline bool operator!=(const Rect3i &a, const Rect3i &b) {
|
2017-08-28 01:47:38 +02:00
|
|
|
return a.pos != b.pos || a.size != b.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // RECT3I_H
|