Files
2018-07-17 14:15:37 +02:00

347 lines
8.5 KiB
C++

#pragma once
#include "vec2.h"
#include <algorithm>
template<class T>
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<class T>
struct rect {
vec2<T> topLeft;
vec2<T> botRight;
rect() {}
rect(vec2<T> a, vec2<T> b) :
topLeft(a), botRight(b) {}
rect(T x1, T y1, T x2, T y2) :
topLeft(x1, y1), botRight(x2, y2) {}
template<class Q>
rect(const rect<Q>& 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<T> area(T x, T y, T w, T h) {
return rect<T>(x, y, x+w, y+h);
}
static rect<T> area(const vec2<T> pos, const vec2<T> size) {
return rect<T>(pos.x, pos.y, pos.x+size.width, pos.y+size.height);
}
static rect<T> centered(const vec2<T> around, const vec2<T> size) {
return area(around.x - size.x/2, around.y - size.y/2, size.width, size.height);
}
static rect<T> centered(const rect<T>& within, const vec2<T> size) {
return centered( vec2<T>((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<T>& other) {
topLeft += other;
botRight += other;
return *this;
}
rect operator+(const vec2<T>& other) const {
return rect(topLeft+other, botRight+other);
}
rect& operator-=(const vec2<T>& other) {
topLeft -= other;
botRight -= other;
return *this;
}
rect operator-(const vec2<T>& other) const {
return rect(topLeft-other, botRight-other);
}
vec2<T> getSize() const {
return botRight - topLeft;
}
T getWidth() const {
return botRight.x - topLeft.x;
}
T getHeight() const {
return botRight.y - topLeft.y;
}
vec2<T> getBotLeft() const {
return vec2<T>(topLeft.x, botRight.y);
}
vec2<T> getTopRight() const {
return vec2<T>(botRight.x, topLeft.y);
}
rect<T> interpolate(const rect<T>& other, double pct) const {
return rect<T>(
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<T> getCenter() const {
return vec2<T>(
topLeft.x + (botRight.x - topLeft.x) / 2,
topLeft.y + (botRight.y - topLeft.y) / 2
);
}
float distanceTo(const vec2<T>& 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<T>& pos) const {
return pos.x >= topLeft.x && pos.y >= topLeft.y
&& pos.x < botRight.x && pos.y < botRight.y;
}
bool isRectInside(const rect<T>& 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<T>& 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<T> padded(T padding) const {
return rect<T>(topLeft.x + padding, topLeft.y + padding,
botRight.x - padding, botRight.y - padding);
}
rect<T> padded(T horiz, T vert) const {
return rect<T>(topLeft.x + horiz, topLeft.y + vert,
botRight.x - horiz, botRight.y - vert);
}
rect<T> padded(T x1, T y1, T x2, T y2) const {
return rect<T>(topLeft.x + x1, topLeft.y + y1,
botRight.x - x2, botRight.y - y2);
}
rect<T> resized(T w = 0, T h = 0, double horizAlign = 0.0, double vertAlign = 0.0) const {
rect<T> 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<T> 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<T> 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<T> clipAgainst(const rect<T>& other) const {
return rect<T>(
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<T> clipProportional(const rect<T>& from, const rect<T>& to) const {
vec2<T> size = getSize();
vec2<T> otherSize = from.getSize();
return rect<T>(
_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<int> recti;
typedef rect<float> rectf;
typedef rect<double> rectd;
//Relative position specifier
enum RelativePositionType {
RPT_Left,
RPT_Right,
RPT_Top = RPT_Left,
RPT_Bottom = RPT_Right,
};
template<class T>
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<int> relposi;
typedef relpos<float> relposf;
typedef relpos<double> relposd;
//Relative position rectangle
template<class T>
struct relrect {
relpos<T> left, top;
relpos<T> 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<int> relrecti;
typedef relrect<float> relrectf;
typedef relrect<double> relrectd;