aboutsummaryrefslogtreecommitdiff
path: root/source-common/Rect.hpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-05-25 22:30:59 -0700
committerrtk0c <[email protected]>2022-05-25 22:30:59 -0700
commit8fc3192da5ae3ac24511ad32088d669c799b6ddb (patch)
treee8288e736427dd758986860e0956286969d60f7b /source-common/Rect.hpp
parentd18a28a9659092952aef70a30a47726e7c16d31a (diff)
Changeset: 39 Move more things to source-common/
Diffstat (limited to 'source-common/Rect.hpp')
-rw-r--r--source-common/Rect.hpp164
1 files changed, 164 insertions, 0 deletions
diff --git a/source-common/Rect.hpp b/source-common/Rect.hpp
new file mode 100644
index 0000000..89d9b01
--- /dev/null
+++ b/source-common/Rect.hpp
@@ -0,0 +1,164 @@
+#pragma once
+
+#include <glm/glm.hpp>
+
+/// Rect is a rectangle representation based on a point and a dimensions, in television coordinate space
+/// (x increases from left to right, y increases from top to bottom).
+template <class T>
+class Rect {
+public:
+ using ScalarType = T;
+ using VectorType = glm::vec<2, T>;
+
+public:
+ T x;
+ T y;
+ T width;
+ T height;
+
+public:
+ Rect()
+ : x{ 0 }, y{ 0 }, width{ 0 }, height{ 0 } {
+ }
+
+ Rect(T x, T y, T width, T height)
+ : x{ x }, y{ y }, width{ width }, height{ height } {
+ }
+
+ Rect(VectorType pos, VectorType size)
+ : x{ pos.x }
+ , y{ pos.y }
+ , width{ size.x }
+ , height{ size.y } {
+ }
+
+ T x0() const { return x; }
+ T y0() const { return y; }
+ T x1() const { return x + width; }
+ T y1() const { return y + height; }
+
+ VectorType TopLeft() const {
+ return VectorType{ x, y };
+ }
+
+ VectorType TopRight() const {
+ return VectorType{ x + width, y };
+ }
+
+ VectorType BottomLeft() const {
+ return VectorType{ x, y + height };
+ }
+
+ VectorType BottomRight() const {
+ return VectorType{ x + width, y + height };
+ }
+
+ VectorType Center() const {
+ return TopLeft() + VectorType{ width / 2, height / 2 };
+ }
+
+ VectorType Dimensions() const {
+ return VectorType{ width, height };
+ }
+
+ VectorType Extents() const {
+ return VectorType{ width / 2, height / 2 };
+ }
+
+ /// Assumes `bySize * 2` is smaller than both `width` and `height` (does not produce a negative-dimension rectangle).
+ Rect Shrink(T bySize) const {
+ T two = bySize * 2;
+ return Rect{ x + bySize, y + bySize, width - two, height - two };
+ }
+
+ Rect Shrink(T left, T top, T right, T bottom) const {
+ return Rect{
+ x + left,
+ y + top,
+ width - left - right,
+ height - top - bottom,
+ };
+ }
+
+ Rect Expand(T bySize) const {
+ T two = bySize * 2;
+ return Rect{ x - bySize, y - bySize, width + two, height + two };
+ }
+
+ Rect Expand(T left, T top, T right, T bottom) const {
+ return Rect{
+ x - left,
+ y - top,
+ width + left + right,
+ height + top + bottom,
+ };
+ }
+
+ bool Contains(VectorType point) const {
+ return point.x >= x &&
+ point.y >= y &&
+ point.x < x + width &&
+ point.y < y + height;
+ }
+
+ bool Intersects(const Rect& that) const {
+ bool xBetween = x > that.x0() && x < that.x1();
+ bool yBetween = y > that.y0() && y < that.y1();
+ return xBetween && yBetween;
+ }
+
+ // Write min()/max() tenary by hand so that we don't have to include <algorithm>
+ // This file is practically going to be included in every file in this project
+
+ static Rect Intersection(const Rect& a, const Rect& b) {
+ auto x0 = a.x0() > b.x0() ? a.x0() : b.x0(); // Max
+ auto y0 = a.y0() > b.y0() ? a.y0() : b.y0(); // Max
+ auto x1 = a.x1() < b.x1() ? a.x1() : b.x1(); // Min
+ auto y1 = a.y1() < b.y1() ? a.y1() : b.y1(); // Min
+ auto width = x1 - x0;
+ auto height = y1 - x0;
+ return Rect{ x0, y0, width, height };
+ }
+
+ static Rect Union(const Rect& a, const Rect& b) {
+ auto x0 = a.x0() < b.x0() ? a.x0() : b.x0(); // Min
+ auto y0 = a.y0() < b.y0() ? a.y0() : b.y0(); // Min
+ auto x1 = a.x1() > b.x1() ? a.x1() : b.x1(); // Max
+ auto y1 = a.y1() > b.y1() ? a.y1() : b.y1(); // Max
+ auto width = x1 - x0;
+ auto height = y1 - x0;
+ return Rect{ x0, y0, width, height };
+ }
+
+ friend bool operator==(const Rect<T>&, const Rect<T>&) = default;
+
+ Rect operator+(glm::vec<2, T> offset) const {
+ return { x + offset.x, y + offset.y, width, height };
+ }
+
+ Rect operator-(glm::vec<2, T> offset) const {
+ return { x - offset.x, y - offset.y, width, height };
+ }
+
+ Rect& operator+=(glm::vec<2, T> offset) {
+ x += offset.x;
+ y += offset.y;
+ return *this;
+ }
+
+ Rect& operator-=(glm::vec<2, T> offset) {
+ x -= offset.x;
+ y -= offset.y;
+ return *this;
+ }
+
+ template <class TTarget>
+ Rect<TTarget> Cast() const {
+ return {
+ static_cast<TTarget>(x),
+ static_cast<TTarget>(y),
+ static_cast<TTarget>(width),
+ static_cast<TTarget>(height),
+ };
+ }
+};