diff --git a/inc/Base.h b/inc/Base.h
index 1451dbc..88ce23f 100644
--- a/inc/Base.h
+++ b/inc/Base.h
@@ -251,64 +251,87 @@ struct GroupNode
bool Needed;
};
-
-
+///
+/// A rectangle with uint8 coordinates.
+///
+/// Has an assumed but not fully enforced invariant where the 1st coordinates
+/// must have values equal or lower than the 2nd coordinates.
+///
struct Rect
{
uint8 x1{}, x2{}, y1{}, y2{};
Rect() = default;
- Rect(uint8 _x1, uint8 _y1, uint8 _x2, uint8 _y2)
+ /// Initialize a Rect respecting the invariant.
+ Rect(uint8 _x1, uint8 _y1, uint8 _x2, uint8 _y2) noexcept
: x1{min(_x1, _x2)}, y1{min(_y1, _y2)},
x2{max(_x1, _x2)}, y2{max(_y1, _y2)} {}
- void Set(uint8 _x1, uint8 _y1, uint8 _x2, uint8 _y2)
- { x1 = min(_x1,_x2); y1 = min(_y1,_y2);
- x2 = max(_x1,_x2); y2 = max(_y1,_y2); }
- Rect PlaceWithin(uint8 sx, uint8 sy) const
- {
- Rect r{};
- if (sx>=(x2-x1))
- { r.x1 = x1+1; r.x2 = x2-1; }
+ /// Set this Rect's values respecting the invariant.
+ void Set(uint8 _x1, uint8 _y1, uint8 _x2, uint8 _y2) noexcept
+ { *this = Rect{_x1, _y1, _x2, _y2}; }
+ ///
+ /// Return a rect randomly placed within this rect with the desired size.
+ /// The returned rect will not touch the borders of this rect (inclusive).
+ /// The desired `width` and `height` will be clipped if they are too big to fit.
+ ///
+ Rect PlaceWithin(uint8 width, uint8 height) const
+ {
+ Rect inner{};
+ if (width>=(x2-x1))
+ {
+ inner.x1 = x1+1;
+ inner.x2 = x2-1;
+ }
else
{
- r.x1 = x1 + 1 + random(max(0,((x2-x1)-2)-sx));
- r.x2 = r.x1 + sx;
+ inner.x1 = x1 + 1 + random(max(0,((x2-x1)-2)-width));
+ inner.x2 = inner.x1 + width;
+ }
+ if (height >= (y2-y1))
+ {
+ inner.y1 = y1+1;
+ inner.y2 = y2-1;
}
- if (sy >= (y2-y1))
- { r.y1 = y1+1; r.y2 = y2-1; }
else
{
- r.y1 = y1 + 1 + random(max(0,((y2-y1)-2)-sy));
- r.y2 = r.y1 + sy;
+ inner.y1 = y1 + 1 + random(max(0,((y2-y1)-2)-height));
+ inner.y2 = inner.y1 + height;
}
- return r;
+ return inner;
}
- Rect PlaceWithinSafely(uint8 sx, uint8 sy) const
- {
- Rect r{};
- r.x1 = x1 + random(max(0,((x2-x1)-1)-sx));
- r.x2 = r.x1 + sx;
- r.y1 = y1 + random(max(0,((y2-y1)-1)-sy));
- r.y2 = r.y1 + sy;
-
- r.x1 = max(r.x1, x1 + 2);
- r.y1 = max(r.y1, y1 + 2);
- r.x2 = min(r.x2, x2 - 2);
- r.y2 = min(r.y2, y2 - 2);
- return r;
+ ///
+ /// Same as PlaceWithin but with 2 units of border padding instead of 1.
+ /// Likely to clip the desired sizes when placed near the border.
+ ///
+ Rect PlaceWithinSafely(uint8 width, uint8 height) const
+ {
+ Rect inner{};
+ inner.x1 = x1 + random(max(0,((x2-x1)-1)-width));
+ inner.x2 = inner.x1 + width;
+ inner.y1 = y1 + random(max(0,((y2-y1)-1)-height));
+ inner.y2 = inner.y1 + height;
+
+ inner.x1 = max(inner.x1, x1 + 2);
+ inner.y1 = max(inner.y1, y1 + 2);
+ inner.x2 = min(inner.x2, x2 - 2);
+ inner.y2 = min(inner.y2, y2 - 2);
+ return inner;
}
- bool Within(uint8 x, uint8 y) const
+ /// Return true if `x` and `y` are within this rect (inclusive).
+ bool Within(uint8 x, uint8 y) const noexcept
{ return (x >= x1 && x <= x2 && y >= y1 && y <= y2); }
- uint16 Volume() const
+ /// Return the area of this rect (exclusive).
+ uint16 Volume() const noexcept
{ return (x2 - x1) * (y2 - y1); }
- bool Overlaps(const Rect& r) const
+ /// Return true if this rect overlaps with `other`.
+ bool Overlaps(const Rect& other) const noexcept
{
- if (((x1 >= r.x1) == (x2 <= r.x2)) &&
- ((y1 >= r.y1) == (y2 <= r.y2)))
+ if (((x1 >= other.x1) == (x2 <= other.x2)) &&
+ ((y1 >= other.y1) == (y2 <= other.y2)))
return false;
- if (Within(r.x1,r.y1)) return true;
- if (Within(r.x1,r.y2)) return true;
- if (Within(r.x2,r.y1)) return true;
- if (Within(r.x2,r.y2)) return true;
+ if (Within(other.x1,other.y1)) return true;
+ if (Within(other.x1,other.y2)) return true;
+ if (Within(other.x2,other.y1)) return true;
+ if (Within(other.x2,other.y2)) return true;
return false;
}
};