[286] | 1 | // This file is a part of Framsticks SDK. http://www.framsticks.com/ |
---|
[1327] | 2 | // Copyright (C) 1999-2024 Maciej Komosinski and Szymon Ulatowski. |
---|
[286] | 3 | // See LICENSE.txt for details. |
---|
[122] | 4 | |
---|
[109] | 5 | #ifndef _2D_H_ |
---|
| 6 | #define _2D_H_ |
---|
| 7 | |
---|
| 8 | #include <math.h> |
---|
[1130] | 9 | #include <algorithm> |
---|
[109] | 10 | |
---|
| 11 | template <typename T> class XY |
---|
| 12 | { |
---|
| 13 | public: |
---|
[848] | 14 | T x, y; |
---|
| 15 | XY() {} |
---|
| 16 | XY(T _x, T _y) :x(_x), y(_y) {} |
---|
[909] | 17 | template <typename Q> XY(const Q &other) : x(other.x), y(other.y) {} |
---|
| 18 | template <typename Q> const XY &operator=(const Q &other) { x = other.x; y = other.y; return *this; } |
---|
| 19 | template <typename Q> const XY operator()(const Q &other) { return XY(other.x, other.y); } |
---|
| 20 | XY operator+(const XY &p) const { return XY(x + p.x, y + p.y); } |
---|
| 21 | XY operator-(const XY &p) const { return XY(x - p.x, y - p.y); } |
---|
| 22 | XY operator+=(const XY &p) { x += p.x; y += p.y; return *this; } |
---|
| 23 | XY operator-=(const XY &p) { x -= p.x; y -= p.y; return *this; } |
---|
[848] | 24 | XY operator-() const { return XY(-x, -y); } |
---|
| 25 | // allows float operations on ints |
---|
| 26 | template <typename Q> XY operator*=(Q q) { x *= q; y *= q; return *this; } |
---|
| 27 | template <typename Q> XY operator/=(Q q) { x /= q; y /= q; return *this; } |
---|
[856] | 28 | template <typename Q> XY operator/(Q q) const { return XY(x / q, y / q); } |
---|
[909] | 29 | template <typename Q> XY operator*(Q q) const { return XY(q * x, q * y); } |
---|
| 30 | XY operator*=(const XY &q) { x *= q.x; y *= q.y; return *this; } |
---|
| 31 | XY operator/=(const XY &q) { x /= q.x; y /= q.y; return *this; } |
---|
| 32 | XY operator*(const XY &q) const { return XY(x * q.x, y * q.y); } |
---|
| 33 | XY operator/(const XY &q) const { return XY(x / q.x, y / q.y); } |
---|
[848] | 34 | void set(T _x, T _y) { x = _x; y = _y; } |
---|
| 35 | void add(T _x, T _y) { x += _x; y += _y; } |
---|
| 36 | void sub(T _x, T _y) { x -= _x; y -= _y; } |
---|
[909] | 37 | bool operator==(const XY &p) const { return (fabs(double(x - p.x)) < 1e-20) && (fabs(double(y - p.y)) < 1e-20); } |
---|
| 38 | bool operator!=(const XY &p) const { return !operator==(p); } |
---|
[1028] | 39 | T distanceTo(const XY &p) const { return sqrt(distanceToSq(p)); } |
---|
| 40 | T distanceToSq(const XY &p) const { return double((p.x - x) * (p.x - x) + (p.y - y) * (p.y - y)); } |
---|
[909] | 41 | T length() const { return sqrt(x * x + y * y); } |
---|
[848] | 42 | T lengthSq() const { return x * x + y * y; } |
---|
[909] | 43 | T dotProduct(const XY &v) const { return x * v.x + y * v.y; } |
---|
| 44 | T crossProduct(const XY &v) const { return x * v.y - y * v.x; } |
---|
[848] | 45 | void normalize() { operator/=(length()); } // length becomes 1 |
---|
[909] | 46 | static XY average(const XY &v1, const XY &v2) { return XY((v1.x + v2.x) * 0.5, (v1.y + v2.y) * 0.5); } |
---|
[848] | 47 | double getDirection() const { return atan2(y, x); } |
---|
[1327] | 48 | float aspect() const { return float(x) / float(y); } |
---|
[909] | 49 | static XY interpolate(const XY &v1, const XY &v2, double t) { return universal_lerp(v1, v2, t); } |
---|
[848] | 50 | XY toInt() const { return XY(int(x), int(y)); } |
---|
| 51 | XY transpose() const { return XY(y, x); } |
---|
[909] | 52 | static const XY &zero() { static XY t(0, 0); return t; } |
---|
| 53 | static const XY &one() { static XY t(1, 1); return t; } |
---|
[109] | 54 | }; |
---|
| 55 | |
---|
[848] | 56 | //specialized: int equality not using fabs() |
---|
[909] | 57 | template<> inline bool XY<int>::operator==(const XY<int> &p) const { return (x == p.x) && (y == p.y); } |
---|
[109] | 58 | |
---|
[1130] | 59 | template <typename T> XY<T> xymin(const XY<T> &a, const XY<T> &b) { return XY<T>(std::min(a.x, b.x), std::min(a.y, b.y)); } |
---|
| 60 | template <typename T> XY<T> xymax(const XY<T> &a, const XY<T> &b) { return XY<T>(std::max(a.x, b.x), std::max(a.y, b.y)); } |
---|
[848] | 61 | |
---|
[766] | 62 | template <typename T> |
---|
| 63 | class XYMargin |
---|
| 64 | { |
---|
[848] | 65 | public: |
---|
| 66 | XYMargin(T x = 0) :left(x), top(x), right(x), bottom(x) {} |
---|
| 67 | XYMargin(T l, T t, T r, T b) :left(l), top(t), right(r), bottom(b) {} |
---|
| 68 | T left, top, right, bottom; |
---|
| 69 | void operator=(T x) { left = top = right = bottom = x; } |
---|
| 70 | XYMargin operator-() const { return XYMargin(-left, -top, -right, -bottom); } |
---|
[1327] | 71 | XYMargin operator+(const XYMargin<T> &m) const { return XYMargin(left + m.left, top + m.top, right + m.right, bottom + m.bottom); } |
---|
[848] | 72 | void operator=(const XYMargin<T> &other) { left = other.left; top = other.top; right = other.right; bottom = other.bottom; } |
---|
| 73 | T horizontal() const { return left + right; } |
---|
| 74 | T vertical() const { return top + bottom; } |
---|
| 75 | bool operator==(const XYMargin &other) const { return left == other.left && top == other.top && right == other.right && bottom == other.bottom; } |
---|
[1130] | 76 | XYMargin normalized() const { return XYMargin(std::max(left, T(0)), std::max(top, T(0)), std::max(right, T(0)), std::max(bottom, T(0))); } |
---|
[766] | 77 | }; |
---|
[109] | 78 | |
---|
| 79 | template <typename T> |
---|
| 80 | class XYRect |
---|
| 81 | { |
---|
| 82 | public: |
---|
[848] | 83 | XY<T> p, size; |
---|
| 84 | XYRect() {} |
---|
[909] | 85 | XYRect(const XY<T> &p1, const XY<T> &s) :p(p1), size(s) {} |
---|
| 86 | template <typename Q> XYRect(const Q &other) : p(other.p), size(other.size) {} |
---|
[848] | 87 | XYRect(T _x, T _y, T _w, T _h) :p(_x, _y), size(_w, _h) {} |
---|
[909] | 88 | static XYRect<T> centeredAt(const XY<T> &p, XY<T> s) { return XYRect<T>(p - s * 0.5, s); } |
---|
[109] | 89 | |
---|
[848] | 90 | bool isEmpty() const { return (size.x < 0) || (size.y < 0); } |
---|
| 91 | XYRect toInt() const { return XYRect(int(p.x), int(p.y), int(p.x + size.x) - int(p.x), int(p.y + size.y) - int(p.y)); } |
---|
[909] | 92 | bool operator==(const XYRect &r) const { return (p == r.p) && (size == r.size); } |
---|
[1284] | 93 | bool operator!=(const XYRect &r) const { return !operator==(r); } |
---|
[909] | 94 | template <typename Q> const XYRect &operator=(const Q &other) { p = other.p; size = other.size; return *this; } |
---|
[109] | 95 | |
---|
[866] | 96 | T right() const { return p.x + size.x; } |
---|
| 97 | T bottom() const { return p.y + size.y; } |
---|
| 98 | T top() const { return p.y; } |
---|
| 99 | T left() const { return p.x; } |
---|
| 100 | XY<T> center() const { return p + size / 2; } |
---|
[909] | 101 | const XY<T> &topLeft() const { return p; } |
---|
[866] | 102 | XY<T> bottomRight() const { return p + size; } |
---|
| 103 | XY<T> topRight() const { return XY<T>(p.x + size.x, p.y); } |
---|
| 104 | XY<T> bottomLeft() const { return XY<T>(p.x, p.y + size.y); } |
---|
| 105 | |
---|
[909] | 106 | T area() const { return size.x * size.y; } |
---|
[856] | 107 | |
---|
[909] | 108 | bool intersects(const XYRect &r) const |
---|
[848] | 109 | { |
---|
| 110 | if (r.p.x >= (p.x + size.x)) return false; |
---|
| 111 | if (r.p.y >= (p.y + size.y)) return false; |
---|
| 112 | if ((r.p.x + r.size.x) <= p.x) return false; |
---|
| 113 | if ((r.p.y + r.size.y) <= p.y) return false; |
---|
| 114 | return true; |
---|
| 115 | } |
---|
[109] | 116 | |
---|
[909] | 117 | bool contains(const XY<T> &n) const |
---|
[848] | 118 | { |
---|
| 119 | if (n.x < p.x) return false; |
---|
| 120 | if (n.x > (p.x + size.x)) return false; |
---|
| 121 | if (n.y < p.y) return false; |
---|
| 122 | if (n.y > (p.y + size.y)) return false; |
---|
| 123 | return true; |
---|
| 124 | } |
---|
[109] | 125 | |
---|
[909] | 126 | bool contains(const XYRect &r) const |
---|
[856] | 127 | { |
---|
[866] | 128 | return contains(r.p) && contains(r.p + r.size); |
---|
[856] | 129 | } |
---|
[866] | 130 | |
---|
[909] | 131 | void add(const XY<T> &n) |
---|
[848] | 132 | { |
---|
| 133 | if (n.x < p.x) { size.x += p.x - n.x; p.x = n.x; } |
---|
| 134 | else if (n.x > (p.x + size.x)) size.x = n.x - p.x; |
---|
| 135 | if (n.y < p.y) { size.y += p.y - n.y; p.y = n.y; } |
---|
| 136 | else if (n.y > (p.y + size.y)) size.y = n.y - p.y; |
---|
| 137 | } |
---|
[109] | 138 | |
---|
[909] | 139 | XYRect extendBy(const XY<T> &border_size) const |
---|
[848] | 140 | { |
---|
| 141 | return XYRect(p - border_size, size + border_size * 2); |
---|
| 142 | } |
---|
[109] | 143 | |
---|
[909] | 144 | XYRect shrinkBy(const XY<T> &border_size) const |
---|
[848] | 145 | { |
---|
| 146 | return XYRect(p + border_size, size - border_size * 2); |
---|
| 147 | } |
---|
[766] | 148 | |
---|
[909] | 149 | XYRect extendBy(const XYMargin<T> &m) const |
---|
[848] | 150 | { |
---|
| 151 | return XYRect(p.x - m.left, p.y - m.top, size.x + m.horizontal(), size.y + m.vertical()); |
---|
| 152 | } |
---|
[766] | 153 | |
---|
[909] | 154 | XYRect shrinkBy(const XYMargin<T> &m) const |
---|
[848] | 155 | { |
---|
| 156 | return XYRect(p.x + m.left, p.y + m.top, size.x - m.horizontal(), size.y - m.vertical()); |
---|
| 157 | } |
---|
[766] | 158 | |
---|
[848] | 159 | XYMargin<T> marginTowards(const XYRect &r) const |
---|
| 160 | { |
---|
| 161 | return XYMargin<T>(r.p.x - p.x, r.p.y - p.y, |
---|
| 162 | (p.x + size.x) - (r.p.x + r.size.x), (p.y + size.y) - (r.p.y + r.size.y)); |
---|
| 163 | } |
---|
[766] | 164 | |
---|
[1130] | 165 | XYRect fitAspect(float aspect) const ///< place a new rectangle having 'aspect' inside the rectangle |
---|
[905] | 166 | { |
---|
| 167 | XYRect r; |
---|
[909] | 168 | r.size = size; |
---|
| 169 | if (size.x < size.y * aspect) |
---|
| 170 | r.size.y = r.size.x / aspect; |
---|
[905] | 171 | else |
---|
[909] | 172 | r.size.x = r.size.y * aspect; |
---|
| 173 | r.p = p + (size - r.size) * 0.5; |
---|
| 174 | return r; |
---|
[905] | 175 | } |
---|
[909] | 176 | |
---|
[1274] | 177 | XYRect fillAspect(float aspect) |
---|
| 178 | { |
---|
| 179 | XYRect r; |
---|
| 180 | r.size = size; |
---|
| 181 | if (size.x < size.y * aspect) |
---|
| 182 | r.size.x = r.size.y * aspect; |
---|
| 183 | else |
---|
| 184 | r.size.y = r.size.x / aspect; |
---|
| 185 | r.p = p + (size - r.size) * 0.5; |
---|
| 186 | return r; |
---|
| 187 | } |
---|
[1327] | 188 | |
---|
[909] | 189 | XYRect intersection(const XYRect &r) const |
---|
[848] | 190 | { |
---|
| 191 | XYRect i; |
---|
| 192 | XY<T> p2 = p + size; |
---|
| 193 | XY<T> rp2 = r.p + r.size; |
---|
[1130] | 194 | i.p.x = std::max(p.x, r.p.x); |
---|
| 195 | i.p.y = std::max(p.y, r.p.y); |
---|
| 196 | i.size.x = std::min(p2.x, rp2.x) - i.p.x; |
---|
| 197 | i.size.y = std::min(p2.y, rp2.y) - i.p.y; |
---|
[848] | 198 | return i; |
---|
| 199 | } |
---|
[109] | 200 | |
---|
[909] | 201 | XYRect extensionContaining(const XY<T> &p) const |
---|
[885] | 202 | { |
---|
[909] | 203 | XY<T> p1 = xymin(topLeft(), p); |
---|
| 204 | XY<T> p2 = xymax(bottomRight(), p); |
---|
| 205 | return XYRect(p1, p2 - p1); |
---|
| 206 | } |
---|
| 207 | |
---|
| 208 | XYRect extensionContaining(const XYRect &r) const |
---|
| 209 | { |
---|
[885] | 210 | XY<T> p1 = xymin(topLeft(), r.topLeft()); |
---|
| 211 | XY<T> p2 = xymax(bottomRight(), r.bottomRight()); |
---|
| 212 | return XYRect(p1, p2 - p1); |
---|
| 213 | } |
---|
| 214 | |
---|
[909] | 215 | XYRect translation(const XY<T> &t) const |
---|
[848] | 216 | { |
---|
| 217 | return XYRect(p + t, size); |
---|
| 218 | } |
---|
[766] | 219 | |
---|
[909] | 220 | T distanceTo(const XY<T> &n) const |
---|
[848] | 221 | { |
---|
| 222 | XY<T> tp = n; |
---|
| 223 | if (n.x < p.x) tp.x = p.x; else if (n.x >= (p.x + size.x)) tp.x = p.x + size.x; |
---|
| 224 | if (n.y < p.y) tp.y = p.y; else if (n.y >= (p.y + size.y)) tp.y = p.y + size.y; |
---|
| 225 | return tp.distanceTo(n); |
---|
| 226 | } |
---|
[109] | 227 | |
---|
[909] | 228 | T distanceTo(const XYRect<T> &r) const |
---|
[856] | 229 | { |
---|
[866] | 230 | bool r_above = (r.bottom() <= top()); |
---|
| 231 | bool r_below = (r.top() >= bottom()); |
---|
| 232 | bool r_left = (r.right() <= left()); |
---|
| 233 | bool r_right = (r.left() >= right()); |
---|
[856] | 234 | |
---|
[866] | 235 | if (r_above) |
---|
[856] | 236 | { |
---|
[866] | 237 | if (r_left) return r.bottomRight().distanceTo(topLeft()); |
---|
| 238 | else if (r_right) return r.bottomLeft().distanceTo(topRight()); |
---|
| 239 | else return top() - r.bottom(); |
---|
[856] | 240 | } |
---|
[866] | 241 | else if (r_below) |
---|
[856] | 242 | { |
---|
[866] | 243 | if (r_left) return r.topRight().distanceTo(bottomLeft()); |
---|
| 244 | else if (r_right) return r.topLeft().distanceTo(bottomRight()); |
---|
| 245 | else return r.top() - bottom(); |
---|
[856] | 246 | } |
---|
[866] | 247 | else if (r_left) |
---|
[856] | 248 | { |
---|
[866] | 249 | return left() - r.right(); |
---|
[856] | 250 | } |
---|
[866] | 251 | else if (r_right) |
---|
[856] | 252 | { |
---|
[866] | 253 | return r.left() - right(); |
---|
[856] | 254 | } |
---|
[866] | 255 | else |
---|
| 256 | return 0; //intersection |
---|
[856] | 257 | } |
---|
| 258 | |
---|
[909] | 259 | static const XYRect &zero() { static XYRect t(0, 0, 0, 0); return t; } |
---|
| 260 | static const XYRect &one() { static XYRect t(0, 0, 1, 1); return t; } |
---|
[109] | 261 | }; |
---|
| 262 | |
---|
[766] | 263 | typedef XY<int> IntXY; |
---|
| 264 | typedef XYRect<int> IntRect; |
---|
[109] | 265 | |
---|
[866] | 266 | typedef XY<float> FloatXY; |
---|
| 267 | typedef XYRect<float> FloatRect; |
---|
| 268 | |
---|
[109] | 269 | #endif |
---|