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
|
||||
// c d
|
||||
|
||||
struct matrix2d {
|
||||
[[deprecated("Use Mat2")]]
|
||||
float a,b,c,d;
|
||||
};
|
||||
[[deprecated("Use Mat2")]]
|
||||
typedef struct matrix2d matrix2d;
|
||||
|
||||
// column vector
|
||||
struct vector2d {
|
||||
[[deprecated("Use Vec2")]]
|
||||
float x,y;
|
||||
};
|
||||
[[deprecated("Use Vec2")]]
|
||||
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);
|
||||
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);
|
||||
|
||||
[[deprecated("Use Vec2::operator+")]]
|
||||
vector2d v2d_add(vector2d v1, vector2d v2);
|
||||
[[deprecated("Use Vec2::operator-")]]
|
||||
vector2d v2d_sub(vector2d v1, vector2d v2);
|
||||
|
||||
[[deprecated("Use Mat2")]]
|
||||
matrix2d m2d_new(float me0, float me1, float me2, float me3);
|
||||
[[deprecated("Use Vec2")]]
|
||||
vector2d v2d_new(float x, float y);
|
||||
|
||||
[[deprecated("Use Vec2::Zero")]]
|
||||
extern vector2d v2d_zero;
|
||||
[[deprecated("Use Mat2::Identity")]]
|
||||
extern matrix2d m2d_identity;
|
||||
|
||||
class ByteString;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <common/Vec2.h>
|
||||
|
||||
constexpr int MENUSIZE = 40;
|
||||
constexpr int BARSIZE = 17;
|
||||
@ -8,18 +9,23 @@ constexpr float M_GRAV = 6.67300e-1f;
|
||||
|
||||
//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag
|
||||
constexpr int CELL = 4;
|
||||
constexpr int XCELLS = 153;
|
||||
constexpr int YCELLS = 96;
|
||||
constexpr Vec2<int> CELLS = Vec2(153, 96);
|
||||
constexpr Vec2<int> RES = CELLS * CELL;
|
||||
|
||||
constexpr int XCELLS = CELLS.X;
|
||||
constexpr int YCELLS = CELLS.Y;
|
||||
constexpr int NCELL = XCELLS * YCELLS;
|
||||
constexpr int XRES = XCELLS * CELL;
|
||||
constexpr int YRES = YCELLS * CELL;
|
||||
constexpr int XRES = RES.X;
|
||||
constexpr int YRES = RES.Y;
|
||||
constexpr int NPART = XRES * YRES;
|
||||
|
||||
constexpr int XCNTR = XRES / 2;
|
||||
constexpr int YCNTR = YRES / 2;
|
||||
|
||||
constexpr int WINDOWW = XRES + BARSIZE;
|
||||
constexpr int WINDOWH = YRES + MENUSIZE;
|
||||
constexpr Vec2<int> WINDOW = RES + Vec2(BARSIZE, MENUSIZE);
|
||||
|
||||
constexpr int WINDOWW = WINDOW.X;
|
||||
constexpr int WINDOWH = WINDOW.Y;
|
||||
|
||||
constexpr int MAXSIGNS = 16;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "common/Plane.h"
|
||||
#include "common/String.h"
|
||||
#include "simulation/Sign.h"
|
||||
#include "simulation/Particle.h"
|
||||
@ -51,26 +52,27 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<class Item>
|
||||
struct Plane
|
||||
template<typename Item>
|
||||
struct [[deprecated("Use PlaneAdapter<std::vector>")]] Plane: PlaneAdapter<std::vector<Item>>
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
std::vector<Item> items;
|
||||
// invariant: items.size() == width * height
|
||||
|
||||
[[deprecated("Use operator[](Vec2)")]]
|
||||
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
|
||||
{
|
||||
return &items[y * width];
|
||||
return &*PlaneAdapter<std::vector<Item>>::RowIterator(Vec2(0, y));
|
||||
}
|
||||
|
||||
[[deprecated("Use PlaneAdapter<std::vector>")]]
|
||||
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
|
||||
|
||||
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 PIXELSIZE = 4;
|
||||
[[deprecated("Use 0x######_rgb .Pack()")]]
|
||||
constexpr pixel PIXPACK(int x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
[[deprecated("Use RGB(...).Pack()")]]
|
||||
constexpr pixel PIXRGB(int r, int g, int b)
|
||||
{
|
||||
return (r << 16) | (g << 8) | b;
|
||||
@ -24,3 +34,104 @@ constexpr int PIXB(pixel x)
|
||||
{
|
||||
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
|
||||
|
||||
#include "common/Vec2.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using Point = Vec2<int>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user