// HARFANG(R) Copyright (C) 2021 Emmanuel Julien, NWNC HARFANG. Released under GPL/LGPL/Commercial Licence, see licence.txt for details. #pragma once #include "foundation/math.h" #include "foundation/vector2.h" namespace hg { /// Rectangle 2D template struct Rect { Rect() = default; Rect(T x_, T y_) : sx(x_), sy(y_), ex(x_), ey(y_) {} Rect(T sx_, T sy_, T ex_, T ey_) : sx(sx_), sy(sy_), ex(ex_), ey(ey_) {} T sx, sy, ex, ey; }; using iRect = Rect; using fRect = Rect; // template T GetX(const Rect &r) { return r.sx; } template T GetY(const Rect &r) { return r.sy; } template void SetX(Rect &r, T x) { const auto w = GetWidth(r); r.sx = x; r.ex = x + w; } template void SetY(Rect &r, T y) { const auto h = GetHeight(r); r.sy = y; r.ey = y + h; } template Rect Reorder(const Rect &r) { return {r.sx < r.ex ? r.sx : r.ex, r.sy < r.ey ? r.sy : r.ey, r.sx > r.ex ? r.sx : r.ex, r.sy > r.ey ? r.sy : r.ey}; } template T GetWidth(const Rect &r) { return r.ex - r.sx; } template T GetHeight(const Rect &r) { return r.ey - r.sy; } template void SetWidth(Rect &r, T w) { r.ex = r.sx + w; } template void SetHeight(Rect &r, T h) { r.ey = r.sy + h; } template tVec2 GetSize(const Rect &r) { return {r.ex - r.sx, r.ey - r.sy}; } // template Rect operator*(const Rect &r, T v) { return {r.sx * v, r.sy * v, r.ex * v, r.ey * v}; } template Rect operator*(T v, const Rect &r) { return r * v; } template Rect operator/(const Rect &r, T v) { return {r.sx / v, r.sy / v, r.ex / v, r.ey / v}; } template bool operator==(const Rect &a, const Rect &b) { return a.sx == b.sx && a.sy == b.sy && a.ex == b.ex && a.ey == b.ey; } template bool operator!=(const Rect &a, const Rect &b) { return a.sx != b.sx || a.sy != b.sy || a.ex != b.ex || a.ey != b.ey; } template bool Inside(const Rect &r, const O &o) { return T(o.x) > r.sx && T(o.y) > r.sy && T(o.x) < r.ex && T(o.y) < r.ey; } /// Return whether `a` fits in `b`. template bool FitsInside(const Rect &a, const Rect &b) { return GetWidth(a) <= GetWidth(b) && GetHeight(a) <= GetHeight(b); } /// Return `true` if rect `a` intersects rect `b`. template bool Intersects(const Rect &a, const Rect &b) { return !(a.ex < b.sx || a.ey < b.sy || a.sx > b.ex || a.sy > b.ey); } template Rect Intersection(const Rect &a, const Rect &b) { const auto _sx = Max(a.sx, b.sx), _sy = Max(a.sy, b.sy), _ex = Min(a.ex, b.ex), _ey = Min(a.ey, b.ey); const auto n_sx = Min(_sx, _ex), n_sy = Min(_sy, _ey), n_ex = Max(_sx, _ex), n_ey = Max(_sy, _ey); return {n_sx, n_sy, n_ex, n_ey}; } template Rect Union(const Rect &a, const Rect &b) { return {Min(a.sx, b.sx), Min(a.sy, b.sy), Max(a.ex, b.ex), Max(a.ey, b.ey)}; } /// Grow a rectangle by the specified amount of units. /// @see Crop. template Rect Grow(const Rect &r, T border) { return {r.sx - border, r.sy - border, r.ex + border, r.ey + border}; } /// Crop a rectangle. Remove the specified amount of units on each side of the rectangle. /// @see Grow. template Rect Crop(const Rect &r, T left, T top, T right, T bottom) { return {r.sx + left, r.sy + top, r.ex - right, r.ey - bottom}; } template bool Clamp(const Rect &r, O &o) { if (Inside(r, o)) return false; // no clamp o.x = decltype(o.x)(Clamp(T(o.x), r.sx, r.ex)); o.y = decltype(o.y)(Clamp(T(o.y), r.sy, r.ey)); return true; } // template Rect Offset(const Rect &r, T x, T y) { return {r.sx + x, r.sy + y, r.ex + x, r.ey + y}; } template Rect OffsetX(const Rect &r, T x) { return {r.sx + x, r.sy, r.ex + x, r.ey}; } template Rect OffsetY(const Rect &r, T y) { return {r.sx, r.sy + y, r.ex, r.ey + y}; } template Rect MakeRectFromWidthHeight(T x, T y, T w, T h) { return {x, y, x + w, y + h}; } // template Rect FitRectByWidth(const Rect &src, const Rect &fit_to) { Rect out; out.sx = src.sx; out.ex = src.ex; const auto dy = (GetHeight(fit_to) * GetWidth(src)) / GetWidth(fit_to) / 2; out.sy = (src.sy + src.ey) / 2 - dy; out.ey = (src.sy + src.ey) / 2 + dy; return out; } template Rect FitRectByHeight(const Rect &src, const Rect &fit_to) { Rect out; out.sy = src.sy; out.ey = src.ey; const auto dx = (GetWidth(fit_to) * GetHeight(src)) / GetHeight(fit_to) / 2; out.sx = (src.sx + src.ex) / 2 - dx; out.ex = (src.sx + src.ex) / 2 + dx; return out; } template Rect FitRectByAspectRatio(const Rect &src, const Rect &fit_to) { const auto dst_ar = float(GetWidth(fit_to)) / GetHeight(fit_to), src_ar = float(GetWidth(src)) / GetHeight(src); return src_ar > dst_ar ? FitRectByHeight(src, fit_to) : FitRectByWidth(src, fit_to); } // template Rect AlignTop(const Rect &r, T top) { return {r.sx, top, r.ex, top + GetHeight(r)}; } template Rect AlignBottom(const Rect &r, T bottom) { return {r.sx, bottom - GetHeight(r), r.ex, bottom}; } template Rect AlignLeft(const Rect &r, T left) { return {left, r.sy, left + GetWidth(r), r.ey}; } template Rect AlignRight(const Rect &r, T right) { return {right - GetWidth(r), r.sy, right, r.ey}; } // template fRect ToFloatRect(const Rect &r) { return {float(r.sx), float(r.sy), float(r.ex), float(r.ey)}; } template iRect ToIntRect(const Rect &r) { return {int(r.sx), int(r.sy), int(r.ex), int(r.ey)}; } } // namespace hg