#pragma once #include "vec2.h" #include template T _clip(T v, T s, T x1, T x2, T w) { // -_- return (T)(w > 0 ? (double)v+(((double)x2 - (double)x1)/(double)w)*(double)s : (double)v); } //Generic rectangle template struct rect { vec2 topLeft; vec2 botRight; rect() {} rect(vec2 a, vec2 b) : topLeft(a), botRight(b) {} rect(T x1, T y1, T x2, T y2) : topLeft(x1, y1), botRight(x2, y2) {} template rect(const rect& other) { topLeft.x = (T)other.topLeft.x; topLeft.y = (T)other.topLeft.y; botRight.x = (T)other.botRight.x; botRight.y = (T)other.botRight.y; } static rect area(T x, T y, T w, T h) { return rect(x, y, x+w, y+h); } static rect area(const vec2 pos, const vec2 size) { return rect(pos.x, pos.y, pos.x+size.width, pos.y+size.height); } static rect centered(const vec2 around, const vec2 size) { return area(around.x - size.x/2, around.y - size.y/2, size.width, size.height); } static rect centered(const rect& within, const vec2 size) { return centered( vec2((within.topLeft.x + within.botRight.x) / 2, (within.topLeft.y + within.botRight.y) / 2), size); } bool operator==(const rect& other) { return topLeft == other.topLeft && botRight == other.botRight; } rect& operator=(const rect& other) { topLeft = other.topLeft; botRight = other.botRight; return *this; } rect& operator+=(const vec2& other) { topLeft += other; botRight += other; return *this; } rect operator+(const vec2& other) const { return rect(topLeft+other, botRight+other); } rect& operator-=(const vec2& other) { topLeft -= other; botRight -= other; return *this; } rect operator-(const vec2& other) const { return rect(topLeft-other, botRight-other); } vec2 getSize() const { return botRight - topLeft; } T getWidth() const { return botRight.x - topLeft.x; } T getHeight() const { return botRight.y - topLeft.y; } vec2 getBotLeft() const { return vec2(topLeft.x, botRight.y); } vec2 getTopRight() const { return vec2(botRight.x, topLeft.y); } rect interpolate(const rect& other, double pct) const { return rect( topLeft.x + (T)((double)(other.topLeft.x - topLeft.x) * pct), topLeft.y + (T)((double)(other.topLeft.y - topLeft.y) * pct), botRight.x + (T)((double)(other.botRight.x - botRight.x) * pct), botRight.y + (T)((double)(other.botRight.y - botRight.y) * pct) ); } vec2 getCenter() const { return vec2( topLeft.x + (botRight.x - topLeft.x) / 2, topLeft.y + (botRight.y - topLeft.y) / 2 ); } float distanceTo(const vec2& pos) { if(pos.x < topLeft.x) { if(pos.y < topLeft.y) { //Distance to top left corner float xdist = float(topLeft.x - pos.x); float ydist = float(topLeft.y - pos.y); return sqrt(xdist*xdist + ydist*ydist); } else if(pos.y > botRight.y) { //Distance to bottom left corner float xdist = float(topLeft.x - pos.x); float ydist = float(botRight.y - pos.y); return sqrt(xdist*xdist + ydist*ydist); } else { //Distance to left edge float xdist = float(topLeft.x - pos.x); return xdist; } } else if(pos.x > botRight.x) { if(pos.y < topLeft.y) { //Distance to top right corner float xdist = float(botRight.x - pos.x); float ydist = float(topLeft.y - pos.y); return sqrt(xdist*xdist + ydist*ydist); } else if(pos.y > botRight.y) { //Distance to bottom right corner float xdist = float(botRight.x - pos.x); float ydist = float(botRight.y - pos.y); return sqrt(xdist*xdist + ydist*ydist); } else { //Distance to right edge float xdist = float(pos.x - botRight.x); return xdist; } } else { if(pos.y < topLeft.y) { //Distance to top edge float ydist = float(topLeft.y - pos.y); return ydist; } else if(pos.y > botRight.y) { //Distance to bottom edge float ydist = float(pos.y - botRight.y); return ydist; } else { //Inside rectangle return 0.f; } } } bool isWithin(const vec2& pos) const { return pos.x >= topLeft.x && pos.y >= topLeft.y && pos.x < botRight.x && pos.y < botRight.y; } bool isRectInside(const rect& other) const { return ((other.topLeft.x >= topLeft.x && other.topLeft.x < botRight.x) && (other.botRight.x >= topLeft.x && other.botRight.x < botRight.x)) && ((other.topLeft.y >= topLeft.y && other.topLeft.y < botRight.y) && (other.botRight.y >= topLeft.y && other.botRight.y < botRight.y)); } bool overlaps(const rect& other) const { return topLeft.x < other.botRight.x && botRight.x > other.topLeft.x && topLeft.y < other.botRight.y && botRight.y > other.topLeft.y; } bool empty() const { return botRight.x == topLeft.x && botRight.y == topLeft.y; } rect padded(T padding) const { return rect(topLeft.x + padding, topLeft.y + padding, botRight.x - padding, botRight.y - padding); } rect padded(T horiz, T vert) const { return rect(topLeft.x + horiz, topLeft.y + vert, botRight.x - horiz, botRight.y - vert); } rect padded(T x1, T y1, T x2, T y2) const { return rect(topLeft.x + x1, topLeft.y + y1, botRight.x - x2, botRight.y - y2); } rect resized(T w = 0, T h = 0, double horizAlign = 0.0, double vertAlign = 0.0) const { rect result = *this; if(w != 0) { double width = getWidth(); double diff = (width - w); result.topLeft.x += (T)(diff * horizAlign); result.botRight.x -= (T)(diff * (1.0 - horizAlign)); } if(h != 0) { double height = getHeight(); double diff = (height - h); result.topLeft.y += (T)(diff * vertAlign); result.botRight.y -= (T)(diff * (1.0 - vertAlign)); } return result; } rect aspectAligned(double aspect, double horizAlign = 0.5, double vertAlign = 0.5) { double height = getHeight(); double width = getWidth(); double aspectWidth = height * aspect; double aspectHeight = width / aspect; rect result = *this; if(aspectWidth < width) { double diff = (width - aspectWidth); result.topLeft.x += (T)(diff * horizAlign); result.botRight.x -= (T)(diff * (1.0 - horizAlign)); } else if(aspectHeight < height) { double diff = (height - aspectHeight); result.topLeft.y += (T)(diff * vertAlign); result.botRight.y -= (T)(diff * (1.0 - vertAlign)); } return result; } rect clipAgainst(const rect& other) const { return rect( std::max(topLeft.x, other.topLeft.x), std::max(topLeft.y, other.topLeft.y), std::min(botRight.x, other.botRight.x), std::min(botRight.y, other.botRight.y)); } rect clipProportional(const rect& from, const rect& to) const { vec2 size = getSize(); vec2 otherSize = from.getSize(); return rect( _clip(topLeft.x, size.width, from.topLeft.x, to.topLeft.x, otherSize.width), _clip(topLeft.y, size.height, from.topLeft.y, to.topLeft.y, otherSize.height), _clip(botRight.x, size.width, from.botRight.x, to.botRight.x, otherSize.width), _clip(botRight.y, size.height, from.botRight.y, to.botRight.y, otherSize.height)); } }; typedef rect recti; typedef rect rectf; typedef rect rectd; //Relative position specifier enum RelativePositionType { RPT_Left, RPT_Right, RPT_Top = RPT_Left, RPT_Bottom = RPT_Right, }; template struct relpos { RelativePositionType type; T pos; double percent; relpos() : type(RPT_Left), pos(0), percent(0.0) { } void set(RelativePositionType Type, T Pos, double Percent) { type = Type; pos = Pos; percent = Percent; } void setOffset(T value) { pos = value; } void setPercentage(double value) { percent = value; } T evaluate(T from, T to) const { T rp; switch(type) { default: case RPT_Left: rp = from + pos + (T)((double)(to - from) * percent); break; case RPT_Right: rp = to - pos - (T)((double)(to - from) * percent); break; } return rp; } }; typedef relpos relposi; typedef relpos relposf; typedef relpos relposd; //Relative position rectangle template struct relrect { relpos left, top; relpos right, bottom; relrect() { right.type = RPT_Right; bottom.type = RPT_Bottom; } recti evaluate(const recti& pos) const { recti out; out.topLeft.x = left.evaluate(pos.topLeft.x, pos.botRight.x); out.topLeft.y = top.evaluate(pos.topLeft.y, pos.botRight.y); out.botRight.x = right.evaluate(pos.topLeft.x, pos.botRight.x); out.botRight.y = bottom.evaluate(pos.topLeft.y, pos.botRight.y); return out; } }; typedef relrect relrecti; typedef relrect relrectf; typedef relrect relrectd;