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; } };