Add new Vec2, Mat2, RGB, RGBA classes and deprecate some old functions
This commit is contained in:
parent
a2c7242c7c
commit
a0a9ad0abd
17
src/Misc.h
17
src/Misc.h
@ -59,30 +59,41 @@ void membwand(void * dest, void * src, size_t destsize, size_t srcsize);
|
|||||||
|
|
||||||
// a b
|
// a b
|
||||||
// c d
|
// c d
|
||||||
|
|
||||||
struct matrix2d {
|
struct matrix2d {
|
||||||
|
[[deprecated("Use Mat2")]]
|
||||||
float a,b,c,d;
|
float a,b,c,d;
|
||||||
};
|
};
|
||||||
|
[[deprecated("Use Mat2")]]
|
||||||
typedef struct matrix2d matrix2d;
|
typedef struct matrix2d matrix2d;
|
||||||
|
|
||||||
// column vector
|
// column vector
|
||||||
struct vector2d {
|
struct vector2d {
|
||||||
|
[[deprecated("Use Vec2")]]
|
||||||
float x,y;
|
float x,y;
|
||||||
};
|
};
|
||||||
|
[[deprecated("Use Vec2")]]
|
||||||
typedef struct vector2d vector2d;
|
typedef struct vector2d vector2d;
|
||||||
|
|
||||||
matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2);
|
//matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2);
|
||||||
|
[[deprecated("Use Mat2::operator*(Vec2)")]]
|
||||||
vector2d m2d_multiply_v2d(matrix2d m, vector2d v);
|
vector2d m2d_multiply_v2d(matrix2d m, vector2d v);
|
||||||
matrix2d m2d_multiply_float(matrix2d m, float s);
|
//matrix2d m2d_multiply_float(matrix2d m, float s);
|
||||||
|
[[deprecated("Use Vec2<float>::operator*(float)")]]
|
||||||
vector2d v2d_multiply_float(vector2d v, float s);
|
vector2d v2d_multiply_float(vector2d v, float s);
|
||||||
|
|
||||||
|
[[deprecated("Use Vec2::operator+")]]
|
||||||
vector2d v2d_add(vector2d v1, vector2d v2);
|
vector2d v2d_add(vector2d v1, vector2d v2);
|
||||||
|
[[deprecated("Use Vec2::operator-")]]
|
||||||
vector2d v2d_sub(vector2d v1, vector2d v2);
|
vector2d v2d_sub(vector2d v1, vector2d v2);
|
||||||
|
|
||||||
|
[[deprecated("Use Mat2")]]
|
||||||
matrix2d m2d_new(float me0, float me1, float me2, float me3);
|
matrix2d m2d_new(float me0, float me1, float me2, float me3);
|
||||||
|
[[deprecated("Use Vec2")]]
|
||||||
vector2d v2d_new(float x, float y);
|
vector2d v2d_new(float x, float y);
|
||||||
|
|
||||||
|
[[deprecated("Use Vec2::Zero")]]
|
||||||
extern vector2d v2d_zero;
|
extern vector2d v2d_zero;
|
||||||
|
[[deprecated("Use Mat2::Identity")]]
|
||||||
extern matrix2d m2d_identity;
|
extern matrix2d m2d_identity;
|
||||||
|
|
||||||
class ByteString;
|
class ByteString;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <common/Vec2.h>
|
||||||
|
|
||||||
constexpr int MENUSIZE = 40;
|
constexpr int MENUSIZE = 40;
|
||||||
constexpr int BARSIZE = 17;
|
constexpr int BARSIZE = 17;
|
||||||
@ -7,19 +8,24 @@ constexpr int BARSIZE = 17;
|
|||||||
constexpr float M_GRAV = 6.67300e-1f;
|
constexpr float M_GRAV = 6.67300e-1f;
|
||||||
|
|
||||||
//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag
|
//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag
|
||||||
constexpr int CELL = 4;
|
constexpr int CELL = 4;
|
||||||
constexpr int XCELLS = 153;
|
constexpr Vec2<int> CELLS = Vec2(153, 96);
|
||||||
constexpr int YCELLS = 96;
|
constexpr Vec2<int> RES = CELLS * CELL;
|
||||||
|
|
||||||
|
constexpr int XCELLS = CELLS.X;
|
||||||
|
constexpr int YCELLS = CELLS.Y;
|
||||||
constexpr int NCELL = XCELLS * YCELLS;
|
constexpr int NCELL = XCELLS * YCELLS;
|
||||||
constexpr int XRES = XCELLS * CELL;
|
constexpr int XRES = RES.X;
|
||||||
constexpr int YRES = YCELLS * CELL;
|
constexpr int YRES = RES.Y;
|
||||||
constexpr int NPART = XRES * YRES;
|
constexpr int NPART = XRES * YRES;
|
||||||
|
|
||||||
constexpr int XCNTR = XRES / 2;
|
constexpr int XCNTR = XRES / 2;
|
||||||
constexpr int YCNTR = YRES / 2;
|
constexpr int YCNTR = YRES / 2;
|
||||||
|
|
||||||
constexpr int WINDOWW = XRES + BARSIZE;
|
constexpr Vec2<int> WINDOW = RES + Vec2(BARSIZE, MENUSIZE);
|
||||||
constexpr int WINDOWH = YRES + MENUSIZE;
|
|
||||||
|
constexpr int WINDOWW = WINDOW.X;
|
||||||
|
constexpr int WINDOWH = WINDOW.Y;
|
||||||
|
|
||||||
constexpr int MAXSIGNS = 16;
|
constexpr int MAXSIGNS = 16;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "common/Plane.h"
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "simulation/Sign.h"
|
#include "simulation/Sign.h"
|
||||||
#include "simulation/Particle.h"
|
#include "simulation/Particle.h"
|
||||||
@ -51,26 +52,27 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Item>
|
template<typename Item>
|
||||||
struct Plane
|
struct [[deprecated("Use PlaneAdapter<std::vector>")]] Plane: PlaneAdapter<std::vector<Item>>
|
||||||
{
|
{
|
||||||
int width = 0;
|
[[deprecated("Use operator[](Vec2)")]]
|
||||||
int height = 0;
|
|
||||||
std::vector<Item> items;
|
|
||||||
// invariant: items.size() == width * height
|
|
||||||
|
|
||||||
Item *operator [](int y)
|
Item *operator [](int y)
|
||||||
{
|
{
|
||||||
return &items[y * width];
|
return &*PlaneAdapter<std::vector<Item>>::RowIterator(Vec2(0, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[deprecated("Use operator[](Vec2)")]]
|
||||||
const Item *operator [](int y) const
|
const Item *operator [](int y) const
|
||||||
{
|
{
|
||||||
return &items[y * width];
|
return &*PlaneAdapter<std::vector<Item>>::RowIterator(Vec2(0, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[deprecated("Use PlaneAdapter<std::vector>")]]
|
||||||
Plane() = default;
|
Plane() = default;
|
||||||
Plane(int newWidth, int newHeight, Item defaultVal) : width(newWidth), height(newHeight), items(width * height, defaultVal)
|
|
||||||
|
[[deprecated("Use PlaneAdapter<std::vector>")]]
|
||||||
|
Plane(int newWidth, int newHeight, Item defaultVal):
|
||||||
|
PlaneAdapter<std::vector<Item>>(Vec2(newWidth, newHeight), defaultVal)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
126
src/common/Plane.h
Normal file
126
src/common/Plane.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "common/Vec2.h"
|
||||||
|
|
||||||
|
constexpr size_t DynamicExtent = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
|
template<size_t Extent>
|
||||||
|
struct extentStorage
|
||||||
|
{
|
||||||
|
constexpr extentStorage(size_t)
|
||||||
|
{}
|
||||||
|
|
||||||
|
constexpr size_t getExtent() const
|
||||||
|
{
|
||||||
|
return Extent;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct extentStorage<DynamicExtent>
|
||||||
|
{
|
||||||
|
size_t extent;
|
||||||
|
|
||||||
|
constexpr extentStorage(size_t extent):
|
||||||
|
extent(extent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
constexpr size_t getExtent() const
|
||||||
|
{
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t Extent>
|
||||||
|
struct xExtent: extentStorage<Extent>
|
||||||
|
{
|
||||||
|
using extentStorage<Extent>::extentStorage;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t Extent>
|
||||||
|
struct yExtent: extentStorage<Extent>
|
||||||
|
{
|
||||||
|
using extentStorage<Extent>::extentStorage;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A class that contains some container T and lets you index into it as if it
|
||||||
|
// were a 2D array of size Width x Height, in row-major order.
|
||||||
|
template<typename T, size_t Width = DynamicExtent, size_t Height = DynamicExtent>
|
||||||
|
class PlaneAdapter: xExtent<Width>, yExtent<Height>
|
||||||
|
{
|
||||||
|
using value_type = std::remove_reference_t<decltype(std::declval<T>()[0])>;
|
||||||
|
using iterator = decltype(std::begin(std::declval<T &>()));
|
||||||
|
using const_iterator = decltype(std::begin(std::declval<T const &>()));
|
||||||
|
|
||||||
|
size_t getWidth() const
|
||||||
|
{
|
||||||
|
return xExtent<Width>::getExtent();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getHeight() const
|
||||||
|
{
|
||||||
|
return yExtent<Height>::getExtent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
T Base;
|
||||||
|
|
||||||
|
PlaneAdapter():
|
||||||
|
xExtent<Width>(0),
|
||||||
|
yExtent<Height>(0),
|
||||||
|
Base()
|
||||||
|
{}
|
||||||
|
|
||||||
|
PlaneAdapter(PlaneAdapter const &) = default;
|
||||||
|
|
||||||
|
PlaneAdapter(PlaneAdapter &&) = default;
|
||||||
|
|
||||||
|
PlaneAdapter &operator=(PlaneAdapter const &) = default;
|
||||||
|
|
||||||
|
PlaneAdapter &operator=(PlaneAdapter &&) = default;
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
PlaneAdapter(Vec2<int> size, Args&&... args):
|
||||||
|
xExtent<Width>(size.X),
|
||||||
|
yExtent<Height>(size.Y),
|
||||||
|
Base(getWidth() * getHeight(), std::forward<Args>(args)...)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Vec2<int> Size() const
|
||||||
|
{
|
||||||
|
return Vec2<int>(getWidth(), getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator RowIterator(Vec2<int> p)
|
||||||
|
{
|
||||||
|
return std::begin(Base) + (p.X + p.Y * getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator RowIterator(Vec2<int> p) const
|
||||||
|
{
|
||||||
|
return std::begin(Base) + (p.X + p.Y * getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type *data()
|
||||||
|
{
|
||||||
|
return std::data(Base);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type const *data() const
|
||||||
|
{
|
||||||
|
return std::data(Base);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type &operator[](Vec2<int> p)
|
||||||
|
{
|
||||||
|
return Base[p.X + p.Y * getWidth()];
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type const &operator[](Vec2<int> p) const
|
||||||
|
{
|
||||||
|
return Base[p.X + p.Y * getWidth()];
|
||||||
|
}
|
||||||
|
};
|
164
src/common/RasterGeometry.h
Normal file
164
src/common/RasterGeometry.h
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#include "common/Vec2.h"
|
||||||
|
|
||||||
|
// Draw a line from the origin on the ZW plane, assuming abs(dw) <= dz
|
||||||
|
template<bool Ortho, typename F>
|
||||||
|
void rasterizeLineZW(int dz, int dw, F f)
|
||||||
|
{
|
||||||
|
const int incW = dw >= 0 ? 1 : -1;
|
||||||
|
int w = 0, err = 0;
|
||||||
|
for (int z = 0; z <= dz; z++)
|
||||||
|
{
|
||||||
|
f(z, w);
|
||||||
|
|
||||||
|
// err / (2 * dz) is the difference between the integer w and the
|
||||||
|
// (potentially non-integer) value z * dw / dz that would like w to be.
|
||||||
|
// When the difference becomes too large, we can increment w.
|
||||||
|
err += 2 * dw * incW;
|
||||||
|
if (err >= dz)
|
||||||
|
{
|
||||||
|
w += incW;
|
||||||
|
err -= 2 * dz;
|
||||||
|
if (Ortho && z < dz)
|
||||||
|
f(z, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call f for every point on the rasterization of a line between p1 and p2.
|
||||||
|
// Ortho makes the resulting line orthogonally connected.
|
||||||
|
template<bool Ortho, typename F>
|
||||||
|
void RasterizeLine(Vec2<int> p1, Vec2<int> p2, F f)
|
||||||
|
{
|
||||||
|
if(std::abs(p1.X - p2.X) >= std::abs(p1.Y - p2.Y))
|
||||||
|
{
|
||||||
|
// If it's more wide than tall, map Z to X and W to Y
|
||||||
|
auto source = p1.X < p2.X ? p1 : p2;
|
||||||
|
auto delta = p1.X < p2.X ? p2 - p1 : p1 - p2;
|
||||||
|
rasterizeLineZW<Ortho>(delta.X, delta.Y, [source, f](int z, int w) { f(source + Vec2(z, w)); });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If it's more tall than wide, map Z to Y and W to X
|
||||||
|
auto source = p1.Y < p2.Y ? p1 : p2;
|
||||||
|
auto delta = p1.Y < p2.Y ? p2 - p1 : p1 - p2;
|
||||||
|
rasterizeLineZW<Ortho>(delta.Y, delta.X, [source, f](int z, int w) { f(source + Vec2(w, z)); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void rasterizeEllipseQuadrant(Vec2<float> radiusSquared, F f)
|
||||||
|
{
|
||||||
|
// An ellipse is a region of points (x, y) such that
|
||||||
|
// (x / rx)^2 + (y / ry)^2 <= 1, which can be rewritten as
|
||||||
|
// x^2 * ry^2 + y^2 * rx^2 <= ry^2 * rx^2,
|
||||||
|
// except, if rx == 0, then an additional constraint abs(y) <= ry must be
|
||||||
|
// added, and same for ry.
|
||||||
|
// The code below ensures 0 <= x <= rx and 0 <= y <= ry + 1.
|
||||||
|
// A false positive for y > ry can only happen if rx == 0 and does not
|
||||||
|
// affect the outcome
|
||||||
|
auto inEllipse = [=](int x, int y)
|
||||||
|
{
|
||||||
|
return y * y * radiusSquared.X + x * x * radiusSquared.Y <= radiusSquared.X * radiusSquared.Y;
|
||||||
|
};
|
||||||
|
// Focusing on the bottom right quadrant, in every row we find the range of
|
||||||
|
// points inside the ellipse, and within those, the range of points on the
|
||||||
|
// boundary.
|
||||||
|
int x = int(std::floor(std::sqrt(radiusSquared.X)));
|
||||||
|
int maxY = int(std::floor(std::sqrt(radiusSquared.Y)));
|
||||||
|
for (int y = 0; y <= maxY; y++)
|
||||||
|
{
|
||||||
|
// At the start of each iteration, (x, y) is on the boundary,
|
||||||
|
// i.e. the range of points inside the ellipse is [0, x]
|
||||||
|
if (inEllipse(x, y + 1))
|
||||||
|
{
|
||||||
|
// If the point below is inside, x is the only boundary point
|
||||||
|
f(x, x, y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, all points whose below point is outside -- are on the boundary
|
||||||
|
int xStart = x;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
x--;
|
||||||
|
} while (x >= 0 && !inEllipse(x, y + 1));
|
||||||
|
f(x + 1, xStart, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call f for every point on the rasterized boundary of the ellipse with the
|
||||||
|
// indicated radius.
|
||||||
|
// In some situations we may want the radius to be the square root of an
|
||||||
|
// integer, so passing the radius squared allows for exact calculation.
|
||||||
|
// In some situations we may want the radius to be a half-integer, and floating
|
||||||
|
// point arithmetic is still exact for half-integers (really, 1/16ths), which is
|
||||||
|
// why we pass this as a float.
|
||||||
|
template<typename F>
|
||||||
|
void RasterizeEllipsePoints(Vec2<float> radiusSquared, F f)
|
||||||
|
{
|
||||||
|
rasterizeEllipseQuadrant(radiusSquared, [f](int x1, int x2, int y)
|
||||||
|
{
|
||||||
|
for (int x = x1; x <= x2; x++)
|
||||||
|
{
|
||||||
|
f(Vec2(x, y));
|
||||||
|
if (x) f(Vec2(-x, y));
|
||||||
|
if (y) f(Vec2(x, -y));
|
||||||
|
if (x && y) f(Vec2(-x, -y));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call f for every point inside the ellipse with the indicated radius.
|
||||||
|
template<typename F>
|
||||||
|
void RasterizeEllipseRows(Vec2<float> radiusSquared, F f)
|
||||||
|
{
|
||||||
|
rasterizeEllipseQuadrant(radiusSquared, [f](int _, int xLim, int y)
|
||||||
|
{
|
||||||
|
f(xLim, y);
|
||||||
|
if (y) f(xLim, -y);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call f for every point on the boundary of the indicated rectangle (so that
|
||||||
|
// TopLeft and BottomRight are both corners).
|
||||||
|
template<typename F>
|
||||||
|
void RasterizeRect(Rect<int> rect, F f)
|
||||||
|
{
|
||||||
|
for (int x = rect.TopLeft.X; x <= rect.BottomRight.X; x++)
|
||||||
|
f(Vec2(x, rect.TopLeft.Y));
|
||||||
|
|
||||||
|
if (rect.TopLeft.Y != rect.BottomRight.Y)
|
||||||
|
for (int x = rect.TopLeft.X; x <= rect.BottomRight.X; x++)
|
||||||
|
f(Vec2(x, rect.BottomRight.Y));
|
||||||
|
|
||||||
|
// corners already drawn
|
||||||
|
for (int y = rect.TopLeft.Y + 1; y <= rect.BottomRight.Y - 1; y++)
|
||||||
|
f(Vec2(rect.TopLeft.X, y));
|
||||||
|
|
||||||
|
if (rect.TopLeft.X != rect.BottomRight.X)
|
||||||
|
for (int y = rect.TopLeft.Y + 1; y <= rect.BottomRight.Y - 1; y++)
|
||||||
|
f(Vec2(rect.BottomRight.X, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call f for every point on the dotted boundary of the indicated rectangle.
|
||||||
|
template<typename F>
|
||||||
|
void RasterizeDottedRect(Rect<int> rect, F f)
|
||||||
|
{
|
||||||
|
for (int x = rect.TopLeft.X; x <= rect.BottomRight.X; x += 2)
|
||||||
|
f(Vec2(x, rect.TopLeft.Y));
|
||||||
|
|
||||||
|
int bottomOff = (rect.BottomRight.Y - rect.TopLeft.Y) % 2;
|
||||||
|
if (rect.TopLeft.Y != rect.BottomRight.Y)
|
||||||
|
for (int x = rect.TopLeft.X + bottomOff; x <= rect.BottomRight.X; x += 2)
|
||||||
|
f(Vec2(x, rect.BottomRight.Y));
|
||||||
|
|
||||||
|
// corners already drawn
|
||||||
|
for (int y = rect.TopLeft.Y + 1 + 1; y <= rect.BottomRight.Y - 1; y += 2)
|
||||||
|
f(Vec2(rect.TopLeft.X, y));
|
||||||
|
|
||||||
|
int leftOff = (rect.BottomRight.X - rect.TopLeft.X + 1) % 2;
|
||||||
|
if (rect.TopLeft.X != rect.BottomRight.X)
|
||||||
|
for (int y = rect.TopLeft.Y + 1 + leftOff; y <= rect.BottomRight.Y - 1; y += 2)
|
||||||
|
f(Vec2(rect.BottomRight.X, y));
|
||||||
|
}
|
429
src/common/Vec2.h
Normal file
429
src/common/Vec2.h
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iterator>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||||
|
struct Rect;
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||||
|
struct Vec2
|
||||||
|
{
|
||||||
|
T X, Y;
|
||||||
|
|
||||||
|
constexpr Vec2(T x, T y):
|
||||||
|
X(x),
|
||||||
|
Y(y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S, typename = std::enable_if_t<std::is_constructible_v<T, S>>>
|
||||||
|
constexpr explicit Vec2(Vec2<S> other):
|
||||||
|
X(other.X),
|
||||||
|
Y(other.Y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(Vec2<T> other) const
|
||||||
|
{
|
||||||
|
return X == other.X && Y == other.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator!=(Vec2<T> other) const
|
||||||
|
{
|
||||||
|
return X != other.X || Y != other.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
constexpr Vec2<decltype(std::declval<T>() + std::declval<S>())> operator+(Vec2<S> other) const
|
||||||
|
{
|
||||||
|
return Vec2<decltype(std::declval<T>() + std::declval<S>())>(X + other.X, Y + other.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
[[deprecated("Use operator+(Vec2)")]]
|
||||||
|
constexpr Vec2<decltype(std::declval<T>() + std::declval<S>())> operator+(S other) const
|
||||||
|
{
|
||||||
|
return Vec2<decltype(std::declval<T>() + std::declval<S>())>(X + other, Y + other);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Vec2<T> operator-() const
|
||||||
|
{
|
||||||
|
return Vec2<T>(-X, -Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
constexpr Vec2<decltype(std::declval<T>() - std::declval<S>())> operator-(Vec2<S> other) const
|
||||||
|
{
|
||||||
|
return Vec2<decltype(std::declval<T>() - std::declval<S>())>(X - other.X, Y - other.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
[[deprecated("Use operator-(Vec2)")]]
|
||||||
|
constexpr Vec2<decltype(std::declval<T>() - std::declval<S>())> operator-(S other) const
|
||||||
|
{
|
||||||
|
return Vec2<decltype(std::declval<T>() - std::declval<S>())>(X - other, Y - other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S, typename = std::enable_if_t<std::is_arithmetic_v<S>>>
|
||||||
|
constexpr Vec2<decltype(std::declval<T>() * std::declval<S>())> operator*(S other) const
|
||||||
|
{
|
||||||
|
return Vec2<decltype(std::declval<T>() * std::declval<S>())>(X * other, Y * other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S, typename = std::enable_if_t<std::is_arithmetic_v<S>>>
|
||||||
|
constexpr Vec2<decltype(std::declval<T>() / std::declval<S>())> operator/(S other) const
|
||||||
|
{
|
||||||
|
return Vec2<decltype(std::declval<T>() / std::declval<S>())>(X / other, Y / other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
constexpr Vec2<T> &operator+=(Vec2<S> other)
|
||||||
|
{
|
||||||
|
return *this = *this + other;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
constexpr Vec2<T> &operator-=(Vec2<S> other)
|
||||||
|
{
|
||||||
|
return *this = *this - other;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
constexpr Vec2<T> &operator*=(Vec2<S> other)
|
||||||
|
{
|
||||||
|
return *this = *this * other;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
constexpr Vec2<T> &operator/=(Vec2<S> other)
|
||||||
|
{
|
||||||
|
return *this = *this / other;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round towards -infinity
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_floating_point_v<S>>>
|
||||||
|
Vec2<T> Floor() const
|
||||||
|
{
|
||||||
|
return Vec2<T>(std::floor(X), std::floor(Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round towards nearest integer, halfpoints towards -infinity
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_floating_point_v<S>>>
|
||||||
|
Vec2<T> Round() const
|
||||||
|
{
|
||||||
|
return (*this + Vec2<T>(0.5, 0.5)).Floor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a rectangle starting at origin, whose dimensions match this vector
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_integral_v<S>>>
|
||||||
|
constexpr inline Rect<T> OriginRect() const
|
||||||
|
{
|
||||||
|
return RectSized(Vec2<T>(0, 0), *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vec2<T> const Zero;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename V>
|
||||||
|
Vec2<T> const Vec2<T, V>::Zero = Vec2<T>(0, 0);
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||||
|
struct Mat2
|
||||||
|
{
|
||||||
|
// ⎛A B⎞
|
||||||
|
// ⎝C D⎠, acting on column vectors
|
||||||
|
T A, B, C, D;
|
||||||
|
|
||||||
|
constexpr Mat2(T a, T b, T c, T d):
|
||||||
|
A(a),
|
||||||
|
B(b),
|
||||||
|
C(c),
|
||||||
|
D(d)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(Mat2<T> other) const
|
||||||
|
{
|
||||||
|
return A == other.A && B == other.B && C == other.C && D == other.D;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator!=(Mat2<T> other) const
|
||||||
|
{
|
||||||
|
return A != other.A || B != other.B || C != other.C || D != other.D;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
constexpr Vec2<decltype(std::declval<T>() * std::declval<S>())> operator*(Vec2<S> vec) const
|
||||||
|
{
|
||||||
|
return Vec2<decltype(std::declval<T>() * std::declval<S>())>(A * vec.X + B * vec.Y, C * vec.X + D * vec.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mat2<T> const Identity, MirrorX, MirrorY, CCW;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename V>
|
||||||
|
Mat2<T> const Mat2<T, V>::Identity = Mat2<T>(1, 0, 0, 1);
|
||||||
|
template<typename T, typename V>
|
||||||
|
Mat2<T> const Mat2<T, V>::MirrorX = Mat2<T>(-1, 0, 0, 1);
|
||||||
|
template<typename T, typename V>
|
||||||
|
Mat2<T> const Mat2<T, V>::MirrorY = Mat2<T>(1, 0, 0, -1);
|
||||||
|
template<typename T, typename V>
|
||||||
|
Mat2<T> const Mat2<T, V>::CCW = Mat2<T>(0, 1, -1, 0); // reminder: the Y axis points down
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||||
|
constexpr static inline Rect<T> RectBetween(Vec2<T>, Vec2<T>);
|
||||||
|
|
||||||
|
enum IterationDirection
|
||||||
|
{
|
||||||
|
TOP_TO_BOTTOM,
|
||||||
|
BOTTOM_TO_TOP,
|
||||||
|
LEFT_TO_RIGHT,
|
||||||
|
RIGHT_TO_LEFT,
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename>
|
||||||
|
struct Rect
|
||||||
|
{
|
||||||
|
// Inclusive
|
||||||
|
Vec2<T> TopLeft, BottomRight;
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr Rect(Vec2<T> topLeft, Vec2<T> bottomRight):
|
||||||
|
TopLeft(topLeft),
|
||||||
|
BottomRight(bottomRight)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
friend constexpr Rect<T> RectBetween<T>(Vec2<T>, Vec2<T>);
|
||||||
|
|
||||||
|
struct end_sentinel
|
||||||
|
{};
|
||||||
|
|
||||||
|
template<IterationDirection D1, IterationDirection D2>
|
||||||
|
struct range_row_major
|
||||||
|
{
|
||||||
|
static_assert(D1 == TOP_TO_BOTTOM || D1 == BOTTOM_TO_TOP);
|
||||||
|
static_assert(D2 == LEFT_TO_RIGHT || D2 == RIGHT_TO_LEFT);
|
||||||
|
T left, top, right, bottom;
|
||||||
|
|
||||||
|
struct iterator
|
||||||
|
{
|
||||||
|
T x, y;
|
||||||
|
T const first_x, last_x, end_y;
|
||||||
|
|
||||||
|
iterator &operator++()
|
||||||
|
{
|
||||||
|
if (x == last_x)
|
||||||
|
{
|
||||||
|
x = first_x;
|
||||||
|
if constexpr (D1 == TOP_TO_BOTTOM)
|
||||||
|
y++;
|
||||||
|
else
|
||||||
|
y--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if constexpr (D2 == LEFT_TO_RIGHT)
|
||||||
|
x++;
|
||||||
|
else
|
||||||
|
x--;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2<T> operator*() const
|
||||||
|
{
|
||||||
|
return Vec2<T>(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(end_sentinel) const
|
||||||
|
{
|
||||||
|
if constexpr (D1 == TOP_TO_BOTTOM)
|
||||||
|
return y < end_y;
|
||||||
|
else
|
||||||
|
return y > end_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
using difference_type = void;
|
||||||
|
using value_type = Vec2<T>;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const
|
||||||
|
{
|
||||||
|
T first_x = D2 == LEFT_TO_RIGHT ? left : right;
|
||||||
|
T last_x = D2 == LEFT_TO_RIGHT ? right : left;
|
||||||
|
T first_y = D1 == TOP_TO_BOTTOM ? top : bottom;
|
||||||
|
T end_y = D1 == TOP_TO_BOTTOM ? bottom + 1 : top - 1;
|
||||||
|
return iterator{first_x, right >= left ? first_y : end_y, first_x, last_x, end_y};
|
||||||
|
}
|
||||||
|
|
||||||
|
end_sentinel end() const
|
||||||
|
{
|
||||||
|
return end_sentinel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<IterationDirection D1, IterationDirection D2>
|
||||||
|
struct range_column_major
|
||||||
|
{
|
||||||
|
static_assert(D1 == LEFT_TO_RIGHT || D1 == RIGHT_TO_LEFT);
|
||||||
|
static_assert(D2 == TOP_TO_BOTTOM || D2 == BOTTOM_TO_TOP);
|
||||||
|
T left, top, right, bottom;
|
||||||
|
|
||||||
|
struct iterator
|
||||||
|
{
|
||||||
|
T x, y;
|
||||||
|
T const first_y, last_y, end_x;
|
||||||
|
|
||||||
|
iterator &operator++()
|
||||||
|
{
|
||||||
|
if (y == last_y)
|
||||||
|
{
|
||||||
|
y = first_y;
|
||||||
|
if constexpr (D1 == LEFT_TO_RIGHT)
|
||||||
|
x++;
|
||||||
|
else
|
||||||
|
x--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if constexpr (D2 == TOP_TO_BOTTOM)
|
||||||
|
y++;
|
||||||
|
else
|
||||||
|
y--;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2<T> operator*() const
|
||||||
|
{
|
||||||
|
return Vec2<T>(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(end_sentinel) const
|
||||||
|
{
|
||||||
|
if constexpr (D1 == LEFT_TO_RIGHT)
|
||||||
|
return x < end_x;
|
||||||
|
else
|
||||||
|
return x > end_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
using difference_type = void;
|
||||||
|
using value_type = Vec2<T>;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const
|
||||||
|
{
|
||||||
|
T first_y = D2 == TOP_TO_BOTTOM ? top : bottom;
|
||||||
|
T last_y = D2 == TOP_TO_BOTTOM ? bottom : top;
|
||||||
|
T first_x = D1 == LEFT_TO_RIGHT ? left : right;
|
||||||
|
T end_x = D1 == LEFT_TO_RIGHT ? right + 1 : left - 1;
|
||||||
|
return iterator{bottom >= top ? first_x : end_x, first_y, first_y, last_y, end_x};
|
||||||
|
}
|
||||||
|
|
||||||
|
end_sentinel end() const
|
||||||
|
{
|
||||||
|
return end_sentinel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr operator bool() const
|
||||||
|
{
|
||||||
|
return BottomRight.X >= TopLeft.X || BottomRight.Y >= TopLeft.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the smallest rectangle that contains both input rectangles,
|
||||||
|
// **assuming neither are empty**
|
||||||
|
Rect<T> operator|(Rect<T> other) const
|
||||||
|
{
|
||||||
|
return Rect<T>(
|
||||||
|
Vec2<T>(std::min(TopLeft.X, other.TopLeft.X), std::min(TopLeft.Y, other.TopLeft.Y)),
|
||||||
|
Vec2<T>(std::max(BottomRight.X, other.BottomRight.X), std::max(BottomRight.Y, other.BottomRight.Y))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the intersection of two rectangles (possibly empty)
|
||||||
|
Rect<T> operator&(Rect<T> other) const
|
||||||
|
{
|
||||||
|
return Rect<T>(
|
||||||
|
Vec2<T>(std::max(TopLeft.X, other.TopLeft.X), std::max(TopLeft.Y, other.TopLeft.Y)),
|
||||||
|
Vec2<T>(std::min(BottomRight.X, other.BottomRight.X), std::min(BottomRight.Y, other.BottomRight.Y))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Rect<T> &operator|=(Rect<T> other)
|
||||||
|
{
|
||||||
|
return *this = *this | other;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Rect<T> &operator&=(Rect<T> other)
|
||||||
|
{
|
||||||
|
return *this = *this & other;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Contains(Vec2<T> point) const
|
||||||
|
{
|
||||||
|
return point.X >= TopLeft.X && point.X <= BottomRight.X && point.Y >= TopLeft.Y && point.Y <= BottomRight.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_integral_v<S>>>
|
||||||
|
inline Vec2<T> Size() const
|
||||||
|
{
|
||||||
|
return BottomRight - TopLeft + Vec2<T>(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<IterationDirection D1, IterationDirection D2, typename S = T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||||
|
constexpr auto Range() const
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
((D1 == TOP_TO_BOTTOM || D1 == BOTTOM_TO_TOP) && (D2 == LEFT_TO_RIGHT || D2 == RIGHT_TO_LEFT)) ||
|
||||||
|
((D1 == LEFT_TO_RIGHT || D1 == RIGHT_TO_LEFT) && (D2 == TOP_TO_BOTTOM || D2 == BOTTOM_TO_TOP)),
|
||||||
|
"Must include exactly 1 of TOP_TO_BOTTOM/BOTTOM_TO_TOP and exactly 1 of LEFT_TO_RIGHT/RIGHT_TO_LEFT"
|
||||||
|
);
|
||||||
|
if constexpr (D1 == TOP_TO_BOTTOM || D1 == BOTTOM_TO_TOP)
|
||||||
|
{
|
||||||
|
return range_row_major<D1, D2>{TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return range_column_major<D1, D2>{TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use when the order isn't important
|
||||||
|
constexpr typename range_row_major<TOP_TO_BOTTOM, LEFT_TO_RIGHT>::iterator begin() const
|
||||||
|
{
|
||||||
|
return Range<TOP_TO_BOTTOM, LEFT_TO_RIGHT>().begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr end_sentinel end() const
|
||||||
|
{
|
||||||
|
return end_sentinel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename>
|
||||||
|
constexpr inline Rect<T> RectBetween(Vec2<T> topLeft, Vec2<T> bottomRight)
|
||||||
|
{
|
||||||
|
return Rect<T>(topLeft, bottomRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||||
|
constexpr inline Rect<T> RectAt(Vec2<T> pos)
|
||||||
|
{
|
||||||
|
return RectBetween<T>(pos, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||||
|
constexpr inline Rect<T> RectSized(Vec2<T> topLeft, Vec2<T> dimen)
|
||||||
|
{
|
||||||
|
return RectBetween<T>(topLeft, topLeft + dimen - Vec2<T>(1, 1));
|
||||||
|
}
|
@ -1,13 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
typedef unsigned int pixel;
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
// This is always packed with the least significant byte being blue,
|
||||||
|
// then green, then red, then 0.
|
||||||
|
typedef uint32_t pixel;
|
||||||
|
|
||||||
constexpr int PIXELCHANNELS = 3;
|
constexpr int PIXELCHANNELS = 3;
|
||||||
constexpr int PIXELSIZE = 4;
|
constexpr int PIXELSIZE = 4;
|
||||||
|
[[deprecated("Use 0x######_rgb .Pack()")]]
|
||||||
constexpr pixel PIXPACK(int x)
|
constexpr pixel PIXPACK(int x)
|
||||||
{
|
{
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
[[deprecated("Use RGB(...).Pack()")]]
|
||||||
constexpr pixel PIXRGB(int r, int g, int b)
|
constexpr pixel PIXRGB(int r, int g, int b)
|
||||||
{
|
{
|
||||||
return (r << 16) | (g << 8) | b;
|
return (r << 16) | (g << 8) | b;
|
||||||
@ -24,3 +34,104 @@ constexpr int PIXB(pixel x)
|
|||||||
{
|
{
|
||||||
return x & 0xFF;
|
return x & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||||
|
struct RGBA;
|
||||||
|
|
||||||
|
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
||||||
|
struct alignas(alignof(uint32_t) > alignof(T) ? alignof(uint32_t) : alignof(T)) RGB
|
||||||
|
{
|
||||||
|
T Blue, Green, Red;
|
||||||
|
|
||||||
|
constexpr RGB(T r, T g, T b):
|
||||||
|
Blue(b),
|
||||||
|
Green(g),
|
||||||
|
Red(r)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S> // Avoid referring to the non-intuitive order of components
|
||||||
|
RGB(std::initializer_list<S>) = delete;
|
||||||
|
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_same_v<S, uint8_t>>>
|
||||||
|
RGB<T> Blend(RGBA<T> other) const
|
||||||
|
{
|
||||||
|
if (other.Alpha == 0xFF)
|
||||||
|
return other.NoAlpha();
|
||||||
|
return RGB<T>(
|
||||||
|
// Technically should divide by 0xFF, but >> 8 is close enough
|
||||||
|
(other.Alpha * other.Red + (0xFF - other.Alpha) * Red ) >> 8,
|
||||||
|
(other.Alpha * other.Green + (0xFF - other.Alpha) * Green) >> 8,
|
||||||
|
(other.Alpha * other.Blue + (0xFF - other.Alpha) * Blue ) >> 8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_same_v<S, uint8_t>>>
|
||||||
|
RGB<T> Add(RGBA<T> other) const
|
||||||
|
{
|
||||||
|
return RGB<T>(
|
||||||
|
std::min(0xFF, (other.Alpha * other.Red + 0xFF * Red ) >> 8),
|
||||||
|
std::min(0xFF, (other.Alpha * other.Green + 0xFF * Green) >> 8),
|
||||||
|
std::min(0xFF, (other.Alpha * other.Blue + 0xFF * Blue ) >> 8)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_same_v<S, uint8_t>>>
|
||||||
|
RGB<T> Inverse() const
|
||||||
|
{
|
||||||
|
return RGB<T>(0xFF - Red, 0xFF - Green, 0xFF - Blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr RGBA<T> WithAlpha(T a) const
|
||||||
|
{
|
||||||
|
return RGBA<T>(Red, Green, Blue, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_same_v<S, uint8_t>>>
|
||||||
|
pixel Pack() const
|
||||||
|
{
|
||||||
|
return Red << 16 | Green << 8 | Blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_same_v<S, uint8_t>>>
|
||||||
|
constexpr static RGB<T> Unpack(pixel px)
|
||||||
|
{
|
||||||
|
return RGB<T>(px >> 16, px >> 8, px);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr inline RGB<uint8_t> operator ""_rgb(unsigned long long value)
|
||||||
|
{
|
||||||
|
return RGB<uint8_t>::Unpack(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename>
|
||||||
|
struct alignas(alignof(uint32_t) > alignof(T) ? alignof(uint32_t) : alignof(T)) RGBA
|
||||||
|
{
|
||||||
|
T Blue, Green, Red, Alpha;
|
||||||
|
|
||||||
|
constexpr RGBA(T r, T g, T b, T a):
|
||||||
|
Blue(b),
|
||||||
|
Green(g),
|
||||||
|
Red(r),
|
||||||
|
Alpha(a)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S = T, typename = std::enable_if_t<std::is_same_v<S, uint8_t>>>
|
||||||
|
RGBA(T r, T g, T b):
|
||||||
|
Blue(b),
|
||||||
|
Green(g),
|
||||||
|
Red(r),
|
||||||
|
Alpha(0xFF)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S> // Avoid referring to the non-intuitive order of components
|
||||||
|
RGBA(std::initializer_list<S>) = delete;
|
||||||
|
|
||||||
|
RGB<T> NoAlpha() const
|
||||||
|
{
|
||||||
|
return RGB<T>(Red, Green, Blue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,142 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/Vec2.h"
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
|
using Point = Vec2<int>;
|
||||||
//Lightweight 2D Int32/Float32 Point struct for UI
|
|
||||||
struct Point
|
|
||||||
{
|
|
||||||
using POINT_T = int;
|
|
||||||
|
|
||||||
POINT_T X;
|
|
||||||
POINT_T Y;
|
|
||||||
|
|
||||||
Point(POINT_T x, POINT_T y)
|
|
||||||
: X(x)
|
|
||||||
, Y(y)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator - () const
|
|
||||||
{
|
|
||||||
return Point(-X, -Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator + (const Point& v) const
|
|
||||||
{
|
|
||||||
return Point(X + v.X, Y + v.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator + (const int v) const
|
|
||||||
{
|
|
||||||
return Point(X + v, Y + v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator - (const Point& v) const
|
|
||||||
{
|
|
||||||
return Point(X - v.X, Y - v.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator - (const int v) const
|
|
||||||
{
|
|
||||||
return Point(X - v, Y - v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator * (const Point& v) const
|
|
||||||
{
|
|
||||||
return Point(X * v.X, Y * v.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator * (int v) const
|
|
||||||
{
|
|
||||||
return Point(X * static_cast<POINT_T>(v), Y * static_cast<POINT_T>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator * (float v) const
|
|
||||||
{
|
|
||||||
return Point(X * static_cast<POINT_T>(v), Y * static_cast<POINT_T>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator / (const Point& v) const
|
|
||||||
{
|
|
||||||
return Point(X / v.X, Y / v.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator / (int v) const
|
|
||||||
{
|
|
||||||
return Point(X / static_cast<POINT_T>(v), Y / static_cast<POINT_T>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator / (float v) const
|
|
||||||
{
|
|
||||||
return Point(X / static_cast<POINT_T>(v), Y / static_cast<POINT_T>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator += (const Point& v)
|
|
||||||
{
|
|
||||||
X += v.X;
|
|
||||||
Y += v.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator -= (const Point& v)
|
|
||||||
{
|
|
||||||
X -= v.X;
|
|
||||||
Y -= v.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator *= (const Point& v)
|
|
||||||
{
|
|
||||||
X *= v.X;
|
|
||||||
Y *= v.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator *= (int v)
|
|
||||||
{
|
|
||||||
X *= static_cast<POINT_T>(v);
|
|
||||||
Y *= static_cast<POINT_T>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator *= (float v)
|
|
||||||
{
|
|
||||||
X *= static_cast<POINT_T>(v);
|
|
||||||
Y *= static_cast<POINT_T>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator /= (const Point& v)
|
|
||||||
{
|
|
||||||
X /= v.X;
|
|
||||||
Y /= v.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator /= (int v)
|
|
||||||
{
|
|
||||||
X /= static_cast<POINT_T>(v);
|
|
||||||
Y /= static_cast<POINT_T>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator /= (float v)
|
|
||||||
{
|
|
||||||
X /= static_cast<POINT_T>(v);
|
|
||||||
Y /= static_cast<POINT_T>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator == (const Point& v) const
|
|
||||||
{
|
|
||||||
return (X == v.X && Y == v.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator != (const Point& v) const
|
|
||||||
{
|
|
||||||
return (X != v.X || Y != v.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point operator = (const Point& v)
|
|
||||||
{
|
|
||||||
X = v.X;
|
|
||||||
Y = v.Y;
|
|
||||||
return Point(X, Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user