mirror of
https://github.com/slendidev/smath.git
synced 2026-03-17 02:26:50 +02:00
Add .clang-format and format codebase
Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
51
.clang-format
Normal file
51
.clang-format
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: ForIndentation
|
||||||
|
|
||||||
|
ColumnLimit: 80
|
||||||
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
AlignOperands: false
|
||||||
|
BreakBeforeBinaryOperators: All
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: true
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
AllowShortBlocksOnASingleLine: Empty
|
||||||
|
SpaceInEmptyBlock: true
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
IndentRequiresClause: false
|
||||||
|
RequiresClausePosition: OwnLine
|
||||||
|
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReferenceAlignment: Right
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
|
||||||
|
SortIncludes: CaseSensitive
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
AlignEscapedNewlines: DontAlign
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
...
|
||||||
@@ -13,202 +13,216 @@
|
|||||||
#include <smath.hpp>
|
#include <smath.hpp>
|
||||||
using smath::Vec2;
|
using smath::Vec2;
|
||||||
|
|
||||||
enum Color : uint8_t {
|
enum Color : uint8_t
|
||||||
CLR_NONE = 0, // default
|
{
|
||||||
CLR_AXES = 90, // bright black (gray)
|
CLR_NONE = 0, // default
|
||||||
CLR_A = 32, // green
|
CLR_AXES = 90, // bright black (gray)
|
||||||
CLR_B = 33, // yellow
|
CLR_A = 32, // green
|
||||||
CLR_P = 35, // magenta
|
CLR_B = 33, // yellow
|
||||||
CLR_DOT = 36 // cyan
|
CLR_P = 35, // magenta
|
||||||
|
CLR_DOT = 36 // cyan
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Cell {
|
struct Cell
|
||||||
char ch{' '};
|
{
|
||||||
uint8_t color{CLR_NONE};
|
char ch { ' ' };
|
||||||
int prio{0};
|
uint8_t color { CLR_NONE };
|
||||||
|
int prio { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Canvas {
|
struct Canvas
|
||||||
int w, h;
|
{
|
||||||
std::vector<Cell> pix;
|
int w, h;
|
||||||
Canvas(int W, int H) : w(W), h(H), pix(W * H) {}
|
std::vector<Cell> pix;
|
||||||
|
Canvas(int W, int H) : w(W), h(H), pix(W * H) { }
|
||||||
|
|
||||||
void put(int x, int y, char c, int prio, uint8_t color) {
|
void put(int x, int y, char c, int prio, uint8_t color)
|
||||||
if (x < 0 || x >= w || y < 0 || y >= h)
|
{
|
||||||
return;
|
if (x < 0 || x >= w || y < 0 || y >= h)
|
||||||
Cell &cell = pix[y * w + x];
|
return;
|
||||||
if (prio >= cell.prio) {
|
Cell &cell = pix[y * w + x];
|
||||||
cell.ch = c;
|
if (prio >= cell.prio) {
|
||||||
cell.prio = prio;
|
cell.ch = c;
|
||||||
cell.color = color;
|
cell.prio = prio;
|
||||||
}
|
cell.color = color;
|
||||||
}
|
}
|
||||||
void hline(int y, char c, int prio, uint8_t color) {
|
}
|
||||||
for (int x = 0; x < w; ++x)
|
void hline(int y, char c, int prio, uint8_t color)
|
||||||
put(x, y, c, prio, color);
|
{
|
||||||
}
|
for (int x = 0; x < w; ++x)
|
||||||
void vline(int x, char c, int prio, uint8_t color) {
|
put(x, y, c, prio, color);
|
||||||
for (int y = 0; y < h; ++y)
|
}
|
||||||
put(x, y, c, prio, color);
|
void vline(int x, char c, int prio, uint8_t color)
|
||||||
}
|
{
|
||||||
|
for (int y = 0; y < h; ++y)
|
||||||
|
put(x, y, c, prio, color);
|
||||||
|
}
|
||||||
|
|
||||||
void line_dir(int x0, int y0, int x1, int y1, int prio, uint8_t color) {
|
void line_dir(int x0, int y0, int x1, int y1, int prio, uint8_t color)
|
||||||
int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
{
|
||||||
int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||||
int err = dx + dy;
|
int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||||
|
int err = dx + dy;
|
||||||
|
|
||||||
int px = x0, py = y0;
|
int px = x0, py = y0;
|
||||||
put(px, py, '+', prio, color);
|
put(px, py, '+', prio, color);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (px == x1 && py == y1)
|
if (px == x1 && py == y1)
|
||||||
break;
|
break;
|
||||||
int e2 = 2 * err;
|
int e2 = 2 * err;
|
||||||
int nx = px, ny = py;
|
int nx = px, ny = py;
|
||||||
if (e2 >= dy) {
|
if (e2 >= dy) {
|
||||||
err += dy;
|
err += dy;
|
||||||
nx += sx;
|
nx += sx;
|
||||||
}
|
}
|
||||||
if (e2 <= dx) {
|
if (e2 <= dx) {
|
||||||
err += dx;
|
err += dx;
|
||||||
ny += sy;
|
ny += sy;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sdx = nx - px;
|
int sdx = nx - px;
|
||||||
int sdy = ny - py;
|
int sdy = ny - py;
|
||||||
int adx = std::abs(sdx);
|
int adx = std::abs(sdx);
|
||||||
int ady = std::abs(sdy);
|
int ady = std::abs(sdy);
|
||||||
char g;
|
char g;
|
||||||
if (adx >= 2 * ady)
|
if (adx >= 2 * ady)
|
||||||
g = '-';
|
g = '-';
|
||||||
else if (ady >= 2 * adx)
|
else if (ady >= 2 * adx)
|
||||||
g = '|';
|
g = '|';
|
||||||
else
|
else
|
||||||
g = ((sdx > 0 && sdy > 0) || (sdx < 0 && sdy < 0)) ? '\\' : '/';
|
g = ((sdx > 0 && sdy > 0) || (sdx < 0 && sdy < 0)) ? '\\' : '/';
|
||||||
|
|
||||||
put(nx, ny, g, prio, color);
|
put(nx, ny, g, prio, color);
|
||||||
px = nx;
|
px = nx;
|
||||||
py = ny;
|
py = ny;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() const {
|
void flush() const
|
||||||
uint8_t cur = 255;
|
{
|
||||||
for (int y = 0; y < h; ++y) {
|
uint8_t cur = 255;
|
||||||
for (int x = 0; x < w; ++x) {
|
for (int y = 0; y < h; ++y) {
|
||||||
const Cell &c = pix[y * w + x];
|
for (int x = 0; x < w; ++x) {
|
||||||
if (c.color != cur) {
|
const Cell &c = pix[y * w + x];
|
||||||
if (c.color == CLR_NONE)
|
if (c.color != cur) {
|
||||||
std::print("\x1b[0m");
|
if (c.color == CLR_NONE)
|
||||||
else
|
std::print("\x1b[0m");
|
||||||
std::print("\x1b[{}m", c.color);
|
else
|
||||||
cur = c.color;
|
std::print("\x1b[{}m", c.color);
|
||||||
}
|
cur = c.color;
|
||||||
std::print("{}", c.ch);
|
}
|
||||||
}
|
std::print("{}", c.ch);
|
||||||
std::println("");
|
}
|
||||||
}
|
std::println("");
|
||||||
std::print("\x1b[0m");
|
}
|
||||||
}
|
std::print("\x1b[0m");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Mapper {
|
struct Mapper
|
||||||
int W, H;
|
{
|
||||||
double scale;
|
int W, H;
|
||||||
int cx, cy;
|
double scale;
|
||||||
Mapper(int w, int h, double s) : W(w), H(h), scale(s), cx(w / 2), cy(h / 2) {}
|
int cx, cy;
|
||||||
std::pair<int, int> map(Vec2 v) const {
|
Mapper(int w, int h, double s) : W(w), H(h), scale(s), cx(w / 2), cy(h / 2)
|
||||||
int x = cx + (int)std::llround(v.x() * scale);
|
{ }
|
||||||
int y = cy - (int)std::llround(v.y() * scale);
|
std::pair<int, int> map(Vec2 v) const
|
||||||
return {x, y};
|
{
|
||||||
}
|
int x = cx + (int)std::llround(v.x() * scale);
|
||||||
|
int y = cy - (int)std::llround(v.y() * scale);
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main()
|
||||||
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
if (h != INVALID_HANDLE_VALUE) {
|
if (h != INVALID_HANDLE_VALUE) {
|
||||||
DWORD m;
|
DWORD m;
|
||||||
if (GetConsoleMode(h, &m))
|
if (GetConsoleMode(h, &m))
|
||||||
SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::print("Enter vector a (ax ay): ");
|
std::print("Enter vector a (ax ay): ");
|
||||||
double ax, ay;
|
double ax, ay;
|
||||||
if (!(std::cin >> ax >> ay))
|
if (!(std::cin >> ax >> ay))
|
||||||
return 0;
|
return 0;
|
||||||
std::print("Enter vector b (bx by): ");
|
std::print("Enter vector b (bx by): ");
|
||||||
double bx, by;
|
double bx, by;
|
||||||
if (!(std::cin >> bx >> by))
|
if (!(std::cin >> bx >> by))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Vec2 a{(float)ax, (float)ay};
|
Vec2 a { (float)ax, (float)ay };
|
||||||
Vec2 b{(float)bx, (float)by};
|
Vec2 b { (float)bx, (float)by };
|
||||||
Vec2 p = a.project_onto(b);
|
Vec2 p = a.project_onto(b);
|
||||||
|
|
||||||
std::println("\nResults:");
|
std::println("\nResults:");
|
||||||
std::println("a = {}", a);
|
std::println("a = {}", a);
|
||||||
std::println("b = {}", b);
|
std::println("b = {}", b);
|
||||||
std::println("proj_b(a) = p = {}", p);
|
std::println("proj_b(a) = p = {}", p);
|
||||||
std::println("|a|={} |b|={} |p|={}", a.magnitude(), b.magnitude(),
|
std::println(
|
||||||
p.magnitude());
|
"|a|={} |b|={} |p|={}", a.magnitude(), b.magnitude(), p.magnitude());
|
||||||
double dot = a.dot(b);
|
double dot = a.dot(b);
|
||||||
double ang = (a.magnitude() > 0 && b.magnitude() > 0)
|
double ang = (a.magnitude() > 0 && b.magnitude() > 0)
|
||||||
? std::acos(std::clamp(dot / (a.magnitude() * b.magnitude()),
|
? std::acos(
|
||||||
-1.0, 1.0)) *
|
std::clamp(dot / (a.magnitude() * b.magnitude()), -1.0, 1.0))
|
||||||
180.0 / M_PI
|
* 180.0 / M_PI
|
||||||
: 0.0;
|
: 0.0;
|
||||||
std::println("a·b={} angle(a,b)={} deg\n", dot, ang);
|
std::println("a·b={} angle(a,b)={} deg\n", dot, ang);
|
||||||
|
|
||||||
const int W = 73, H = 33;
|
const int W = 73, H = 33;
|
||||||
double maxr = std::max({(double)a.magnitude(), (double)b.magnitude(),
|
double maxr = std::max({ (double)a.magnitude(),
|
||||||
(double)p.magnitude(), 1.0});
|
(double)b.magnitude(),
|
||||||
double usable = 0.45 * std::min(W, H);
|
(double)p.magnitude(),
|
||||||
double scale = usable / maxr;
|
1.0 });
|
||||||
|
double usable = 0.45 * std::min(W, H);
|
||||||
|
double scale = usable / maxr;
|
||||||
|
|
||||||
Canvas cvs(W, H);
|
Canvas cvs(W, H);
|
||||||
Mapper mp(W, H, scale);
|
Mapper mp(W, H, scale);
|
||||||
|
|
||||||
int pr_axes = 1;
|
int pr_axes = 1;
|
||||||
auto [cx, cy] = mp.map({0, 0});
|
auto [cx, cy] = mp.map({ 0, 0 });
|
||||||
cvs.hline(H / 2, '-', pr_axes, CLR_AXES);
|
cvs.hline(H / 2, '-', pr_axes, CLR_AXES);
|
||||||
cvs.vline(W / 2, '|', pr_axes, CLR_AXES);
|
cvs.vline(W / 2, '|', pr_axes, CLR_AXES);
|
||||||
cvs.put(W / 2, H / 2, 'O', pr_axes + 1, CLR_AXES);
|
cvs.put(W / 2, H / 2, 'O', pr_axes + 1, CLR_AXES);
|
||||||
|
|
||||||
auto [ox, oy] = mp.map({0, 0});
|
auto [ox, oy] = mp.map({ 0, 0 });
|
||||||
auto [ax1, ay1] = mp.map(a);
|
auto [ax1, ay1] = mp.map(a);
|
||||||
auto [bx1, by1] = mp.map(b);
|
auto [bx1, by1] = mp.map(b);
|
||||||
auto [px1, py1] = mp.map(p);
|
auto [px1, py1] = mp.map(p);
|
||||||
|
|
||||||
int pr_a = 3, pr_b = 3, pr_p = 4;
|
int pr_a = 3, pr_b = 3, pr_p = 4;
|
||||||
|
|
||||||
cvs.line_dir(ox, oy, ax1, ay1, pr_a, CLR_A);
|
cvs.line_dir(ox, oy, ax1, ay1, pr_a, CLR_A);
|
||||||
cvs.line_dir(ox, oy, bx1, by1, pr_b, CLR_B);
|
cvs.line_dir(ox, oy, bx1, by1, pr_b, CLR_B);
|
||||||
cvs.line_dir(ox, oy, px1, py1, pr_p, CLR_P);
|
cvs.line_dir(ox, oy, px1, py1, pr_p, CLR_P);
|
||||||
|
|
||||||
if (!a.approx_equal(p) && b.magnitude() > 0) {
|
if (!a.approx_equal(p) && b.magnitude() > 0) {
|
||||||
int steps = std::max(std::abs(ax1 - px1), std::abs(ay1 - py1));
|
int steps = std::max(std::abs(ax1 - px1), std::abs(ay1 - py1));
|
||||||
for (int i = 0; i <= steps; ++i) {
|
for (int i = 0; i <= steps; ++i) {
|
||||||
double t = steps ? double(i) / steps : 0.0;
|
double t = steps ? double(i) / steps : 0.0;
|
||||||
int x = (int)std::llround(px1 + t * (ax1 - px1));
|
int x = (int)std::llround(px1 + t * (ax1 - px1));
|
||||||
int y = (int)std::llround(py1 + t * (ay1 - py1));
|
int y = (int)std::llround(py1 + t * (ay1 - py1));
|
||||||
if (i % 2 == 0)
|
if (i % 2 == 0)
|
||||||
cvs.put(x, y, '.', 2, CLR_DOT);
|
cvs.put(x, y, '.', 2, CLR_DOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto place_label = [&](Vec2 v, const char *txt, uint8_t c, int pr) {
|
auto place_label = [&](Vec2 v, const char *txt, uint8_t c, int pr) {
|
||||||
auto tip = mp.map(v * 1.05f);
|
auto tip = mp.map(v * 1.05f);
|
||||||
for (int i = 0; txt[i]; ++i)
|
for (int i = 0; txt[i]; ++i)
|
||||||
cvs.put(tip.first + i, tip.second, txt[i], pr, c);
|
cvs.put(tip.first + i, tip.second, txt[i], pr, c);
|
||||||
};
|
};
|
||||||
place_label(a, "A", CLR_A, pr_a + 1);
|
place_label(a, "A", CLR_A, pr_a + 1);
|
||||||
place_label(b, "B", CLR_B, pr_b + 1);
|
place_label(b, "B", CLR_B, pr_b + 1);
|
||||||
place_label(p, "P", CLR_P, pr_p + 1);
|
place_label(p, "P", CLR_P, pr_p + 1);
|
||||||
|
|
||||||
std::println("Legend: axes(gray), a(green), b(yellow), p=proj(magenta), "
|
std::println("Legend: axes(gray), a(green), b(yellow), p=proj(magenta), "
|
||||||
"dotted=cross-perp\n");
|
"dotted=cross-perp\n");
|
||||||
cvs.flush();
|
cvs.flush();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,20 +24,21 @@
|
|||||||
|
|
||||||
#include <smath.hpp>
|
#include <smath.hpp>
|
||||||
|
|
||||||
auto main() -> int {
|
auto main() -> int
|
||||||
using namespace smath;
|
{
|
||||||
|
using namespace smath;
|
||||||
|
|
||||||
Vec2d point;
|
Vec2d point;
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 rng{rd()};
|
std::mt19937 rng { rd() };
|
||||||
std::uniform_real_distribution<> dis(-5, 5);
|
std::uniform_real_distribution<> dis(-5, 5);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
do {
|
do {
|
||||||
Vec2d add{dis(rng), dis(rng)};
|
Vec2d add { dis(rng), dis(rng) };
|
||||||
auto const n = point + add;
|
auto const n = point + add;
|
||||||
std::println("{}: {:.2f} + {:.2f} -> {:.2f}", i, point, add, n);
|
std::println("{}: {:.2f} + {:.2f} -> {:.2f}", i, point, add, n);
|
||||||
point = n;
|
point = n;
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
} while (i < 15);
|
} while (i < 15);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,51 +19,52 @@
|
|||||||
|
|
||||||
#include <smath.hpp>
|
#include <smath.hpp>
|
||||||
|
|
||||||
int main() {
|
int main()
|
||||||
using namespace smath;
|
{
|
||||||
Vec3 v{1, 2, 3};
|
using namespace smath;
|
||||||
std::println("v: {}", v);
|
Vec3 v { 1, 2, 3 };
|
||||||
auto v2 = swizzle<"zyx">(v);
|
std::println("v: {}", v);
|
||||||
std::println("v2: {}", v2);
|
auto v2 = swizzle<"zyx">(v);
|
||||||
std::println("+: {}", v + v2);
|
std::println("v2: {}", v2);
|
||||||
std::println("-: {}", v - v2);
|
std::println("+: {}", v + v2);
|
||||||
std::println("*: {}", v * v2);
|
std::println("-: {}", v - v2);
|
||||||
std::println("/: {}", v / v2);
|
std::println("*: {}", v * v2);
|
||||||
std::println("dot: {}", v.dot(v2));
|
std::println("/: {}", v / v2);
|
||||||
std::println("rrggbb: {}", swizzle<"rrggbb">(v));
|
std::println("dot: {}", v.dot(v2));
|
||||||
std::println("Magnitude: {}", v.magnitude());
|
std::println("rrggbb: {}", swizzle<"rrggbb">(v));
|
||||||
std::println("Normalized: {}", v.normalized());
|
std::println("Magnitude: {}", v.magnitude());
|
||||||
std::println("(alias) Unit: {}", v.unit());
|
std::println("Normalized: {}", v.normalized());
|
||||||
std::println("(alias) Normalize: {}", v.normalize());
|
std::println("(alias) Unit: {}", v.unit());
|
||||||
std::println("(alias) Length: {}", v.length());
|
std::println("(alias) Normalize: {}", v.normalize());
|
||||||
std::println("std::get<1>(v): {}", std::get<1>(v));
|
std::println("(alias) Length: {}", v.length());
|
||||||
auto [x, y, z] = v;
|
std::println("std::get<1>(v): {}", std::get<1>(v));
|
||||||
std::println("Bindings: [{}, {}, {}]", x, y, z);
|
auto [x, y, z] = v;
|
||||||
float x1{}, y1{}, z1{};
|
std::println("Bindings: [{}, {}, {}]", x, y, z);
|
||||||
v.unpack(x1, y1, z1);
|
float x1 {}, y1 {}, z1 {};
|
||||||
std::println("Unpacked: {}, {}, {}", x1, y1, z1);
|
v.unpack(x1, y1, z1);
|
||||||
|
std::println("Unpacked: {}, {}, {}", x1, y1, z1);
|
||||||
|
|
||||||
// Let's mix and match!
|
// Let's mix and match!
|
||||||
Vec<6> v3(v, 7, swizzle<"zy">(v2));
|
Vec<6> v3(v, 7, swizzle<"zy">(v2));
|
||||||
std::println("{{v, 7, XZ(v2)}}: {}", v3);
|
std::println("{{v, 7, XZ(v2)}}: {}", v3);
|
||||||
|
|
||||||
// Scalar operations
|
// Scalar operations
|
||||||
std::println("v + 3: {}", v + 3);
|
std::println("v + 3: {}", v + 3);
|
||||||
std::println("v - 3: {}", v - 3);
|
std::println("v - 3: {}", v - 3);
|
||||||
std::println("v * 3: {}", v * 3);
|
std::println("v * 3: {}", v * 3);
|
||||||
std::println("v / 3: {}", v / 3);
|
std::println("v / 3: {}", v / 3);
|
||||||
|
|
||||||
std::println("3 + v: {}", 3 + v);
|
std::println("3 + v: {}", 3 + v);
|
||||||
std::println("3 - v: {}", 3 - v);
|
std::println("3 - v: {}", 3 - v);
|
||||||
std::println("3 * v: {}", 3 * v);
|
std::println("3 * v: {}", 3 * v);
|
||||||
std::println("3 / v: {}", 3 / v);
|
std::println("3 / v: {}", 3 / v);
|
||||||
|
|
||||||
// Casting
|
// Casting
|
||||||
auto v4 = static_cast<Vec3d>(v);
|
auto v4 = static_cast<Vec3d>(v);
|
||||||
#ifdef SMATH_IMPLICIT_CONVERSIONS
|
#ifdef SMATH_IMPLICIT_CONVERSIONS
|
||||||
Vec3d v5 = v;
|
Vec3d v5 = v;
|
||||||
#else
|
#else
|
||||||
Vec3d v5 = static_cast<Vec3d>(v);
|
Vec3d v5 = static_cast<Vec3d>(v);
|
||||||
#endif // SMATH_IMPLICIT_CONVERSIONS
|
#endif // SMATH_IMPLICIT_CONVERSIONS
|
||||||
std::println("Are v4 and v5 same? {}", v4 == v5);
|
std::println("Are v4 and v5 same? {}", v4 == v5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,20 +32,23 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#ifndef SMATH_ANGLE_UNIT
|
#ifndef SMATH_ANGLE_UNIT
|
||||||
# define SMATH_ANGLE_UNIT rad
|
#define SMATH_ANGLE_UNIT rad
|
||||||
#endif // SMATH_ANGLE_UNIT
|
#endif // SMATH_ANGLE_UNIT
|
||||||
|
|
||||||
namespace smath {
|
namespace smath
|
||||||
|
{
|
||||||
|
|
||||||
template<std::size_t N, typename T>
|
template<std::size_t N, typename T>
|
||||||
requires std::is_arithmetic_v<T> struct Vec;
|
requires std::is_arithmetic_v<T>
|
||||||
|
struct Vec;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
#define SMATH_STR(x) #x
|
#define SMATH_STR(x) #x
|
||||||
#define SMATH_XSTR(x) SMATH_STR(x)
|
#define SMATH_XSTR(x) SMATH_STR(x)
|
||||||
|
|
||||||
consteval bool streq(const char *a, const char *b)
|
consteval auto streq(const char *a, const char *b) -> bool
|
||||||
{
|
{
|
||||||
for (;; ++a, ++b) {
|
for (;; ++a, ++b) {
|
||||||
if (*a != *b)
|
if (*a != *b)
|
||||||
@@ -55,13 +58,14 @@ consteval bool streq(const char *a, const char *b)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class AngularUnit {
|
enum class AngularUnit
|
||||||
|
{
|
||||||
Radians,
|
Radians,
|
||||||
Degrees,
|
Degrees,
|
||||||
Turns,
|
Turns,
|
||||||
};
|
};
|
||||||
|
|
||||||
consteval std::optional<AngularUnit> parse_unit(char const *s)
|
consteval auto parse_unit(char const *s) -> std::optional<AngularUnit>
|
||||||
{
|
{
|
||||||
if (streq(s, "rad"))
|
if (streq(s, "rad"))
|
||||||
return AngularUnit::Radians;
|
return AngularUnit::Radians;
|
||||||
@@ -76,7 +80,8 @@ constexpr auto SMATH_ANGLE_UNIT_ID = parse_unit(SMATH_XSTR(SMATH_ANGLE_UNIT));
|
|||||||
static_assert(SMATH_ANGLE_UNIT_ID != std::nullopt,
|
static_assert(SMATH_ANGLE_UNIT_ID != std::nullopt,
|
||||||
"Invalid SMATH_ANGLE_UNIT. Should be rad, deg, or turns.");
|
"Invalid SMATH_ANGLE_UNIT. Should be rad, deg, or turns.");
|
||||||
|
|
||||||
template<std::size_t N> struct FixedString {
|
template<std::size_t N> struct FixedString
|
||||||
|
{
|
||||||
char data[N] {};
|
char data[N] {};
|
||||||
static constexpr std::size_t size = N - 1;
|
static constexpr std::size_t size = N - 1;
|
||||||
constexpr FixedString(char const (&s)[N])
|
constexpr FixedString(char const (&s)[N])
|
||||||
@@ -86,8 +91,10 @@ template<std::size_t N> struct FixedString {
|
|||||||
}
|
}
|
||||||
constexpr char operator[](std::size_t i) const { return data[i]; }
|
constexpr char operator[](std::size_t i) const { return data[i]; }
|
||||||
};
|
};
|
||||||
template<class X> struct is_Vec : std::false_type { };
|
template<class X> struct is_Vec : std::false_type
|
||||||
template<std::size_t M, class U> struct is_Vec<Vec<M, U>> : std::true_type { };
|
{ };
|
||||||
|
template<std::size_t M, class U> struct is_Vec<Vec<M, U>> : std::true_type
|
||||||
|
{ };
|
||||||
template<class X>
|
template<class X>
|
||||||
inline constexpr bool is_Vec_v = is_Vec<std::remove_cvref_t<X>>::value;
|
inline constexpr bool is_Vec_v = is_Vec<std::remove_cvref_t<X>>::value;
|
||||||
template<class X>
|
template<class X>
|
||||||
@@ -95,7 +102,8 @@ inline constexpr bool is_scalar_v
|
|||||||
= std::is_arithmetic_v<std::remove_cvref_t<X>>;
|
= std::is_arithmetic_v<std::remove_cvref_t<X>>;
|
||||||
template<class X> struct Vec_size;
|
template<class X> struct Vec_size;
|
||||||
template<std::size_t M, class U>
|
template<std::size_t M, class U>
|
||||||
struct Vec_size<Vec<M, U>> : std::integral_constant<std::size_t, M> { };
|
struct Vec_size<Vec<M, U>> : std::integral_constant<std::size_t, M>
|
||||||
|
{ };
|
||||||
|
|
||||||
template<class T> constexpr auto pack_unorm8(T v) -> std::uint8_t
|
template<class T> constexpr auto pack_unorm8(T v) -> std::uint8_t
|
||||||
{
|
{
|
||||||
@@ -144,9 +152,11 @@ template<class T> constexpr auto unpack_snorm8(std::int8_t b) -> T
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template<std::size_t N, typename T = float>
|
template<std::size_t N, typename T = float>
|
||||||
requires std::is_arithmetic_v<T> struct Vec : std::array<T, N> {
|
requires std::is_arithmetic_v<T>
|
||||||
|
struct Vec : std::array<T, N>
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
template<class X> static consteval std::size_t extent()
|
template<class X> static consteval auto extent() -> std::size_t
|
||||||
{
|
{
|
||||||
if constexpr (detail::is_Vec_v<X>)
|
if constexpr (detail::is_Vec_v<X>)
|
||||||
return detail::Vec_size<std::remove_cvref_t<X>>::value;
|
return detail::Vec_size<std::remove_cvref_t<X>>::value;
|
||||||
@@ -155,7 +165,7 @@ private:
|
|||||||
else
|
else
|
||||||
return 0; // Should be unreachable
|
return 0; // Should be unreachable
|
||||||
}
|
}
|
||||||
template<class... Args> static consteval std::size_t total_extent()
|
template<class... Args> static consteval auto total_extent() -> std::size_t
|
||||||
{
|
{
|
||||||
return (extent<Args>() + ... + 0);
|
return (extent<Args>() + ... + 0);
|
||||||
}
|
}
|
||||||
@@ -188,12 +198,9 @@ public:
|
|||||||
// NOTE: This can (probably) be improved with C++26 reflection in the
|
// NOTE: This can (probably) be improved with C++26 reflection in the
|
||||||
// future.
|
// future.
|
||||||
#define VEC_ACC(component, req, idx) \
|
#define VEC_ACC(component, req, idx) \
|
||||||
constexpr auto component() noexcept -> T & \
|
constexpr auto component() noexcept -> T &requires(N >= req) { \
|
||||||
requires(N >= req) \
|
|
||||||
{ \
|
|
||||||
return (*this)[idx]; \
|
return (*this)[idx]; \
|
||||||
} \
|
} constexpr auto component() const->T const & \
|
||||||
constexpr auto component() const -> T const & \
|
|
||||||
requires(N >= req) \
|
requires(N >= req) \
|
||||||
{ \
|
{ \
|
||||||
return (*this)[idx]; \
|
return (*this)[idx]; \
|
||||||
@@ -225,7 +232,8 @@ public:
|
|||||||
((args = (*this)[Is]), ...);
|
((args = (*this)[Is]), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class... Args> constexpr void unpack(Args &...args) noexcept
|
template<class... Args>
|
||||||
|
constexpr auto unpack(Args &...args) noexcept -> void
|
||||||
{
|
{
|
||||||
unpack_impl(std::index_sequence_for<Args...> {}, args...);
|
unpack_impl(std::index_sequence_for<Args...> {}, args...);
|
||||||
}
|
}
|
||||||
@@ -284,13 +292,13 @@ public:
|
|||||||
VEC_OP(/)
|
VEC_OP(/)
|
||||||
#undef VEC_OP
|
#undef VEC_OP
|
||||||
#define VEC_OP_ASSIGN(sym) \
|
#define VEC_OP_ASSIGN(sym) \
|
||||||
constexpr Vec &operator sym## = (Vec const &rhs) noexcept \
|
constexpr Vec &operator sym##=(Vec const &rhs) noexcept \
|
||||||
{ \
|
{ \
|
||||||
for (std::size_t i = 0; i < N; ++i) \
|
for (std::size_t i = 0; i < N; ++i) \
|
||||||
(*this)[i] sym## = rhs[i]; \
|
(*this)[i] sym## = rhs[i]; \
|
||||||
return *this; \
|
return *this; \
|
||||||
} \
|
} \
|
||||||
constexpr Vec &operator sym## = (T const &s) noexcept \
|
constexpr Vec &operator sym##=(T const &s) noexcept \
|
||||||
{ \
|
{ \
|
||||||
for (std::size_t i = 0; i < N; ++i) \
|
for (std::size_t i = 0; i < N; ++i) \
|
||||||
(*this)[i] sym## = s; \
|
(*this)[i] sym## = s; \
|
||||||
@@ -393,11 +401,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename U = T>
|
template<typename U = T>
|
||||||
requires(N == 3) constexpr auto cross(Vec const &r) const noexcept -> Vec
|
requires(N == 3)
|
||||||
|
constexpr auto cross(Vec const &r) const noexcept -> Vec
|
||||||
{
|
{
|
||||||
return { (*this)[1] * r[2] - (*this)[2] * r[1],
|
return {
|
||||||
|
(*this)[1] * r[2] - (*this)[2] * r[1],
|
||||||
(*this)[2] * r[0] - (*this)[0] * r[2],
|
(*this)[2] * r[0] - (*this)[0] * r[2],
|
||||||
(*this)[0] * r[1] - (*this)[1] * r[0] };
|
(*this)[0] * r[1] - (*this)[1] * r[0],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto distance(Vec const &r) const noexcept -> T
|
constexpr auto distance(Vec const &r) const noexcept -> T
|
||||||
@@ -425,8 +436,8 @@ public:
|
|||||||
|
|
||||||
template<class U>
|
template<class U>
|
||||||
requires(std::is_arithmetic_v<U> && N >= 1)
|
requires(std::is_arithmetic_v<U> && N >= 1)
|
||||||
constexpr explicit(!std::is_convertible_v<T, U>)
|
constexpr explicit(
|
||||||
operator Vec<N, U>() const noexcept
|
!std::is_convertible_v<T, U>) operator Vec<N, U>() const noexcept
|
||||||
{
|
{
|
||||||
Vec<N, U> r {};
|
Vec<N, U> r {};
|
||||||
for (std::size_t i = 0; i < N; ++i)
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
@@ -444,26 +455,26 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr void fill_one(std::size_t &i, T const &v) noexcept
|
constexpr auto fill_one(std::size_t &i, T const &v) noexcept -> void
|
||||||
{
|
{
|
||||||
(*this)[i++] = v;
|
(*this)[i++] = v;
|
||||||
}
|
}
|
||||||
#ifdef SMATH_IMPLICIT_CONVERSIONS
|
#ifdef SMATH_IMPLICIT_CONVERSIONS
|
||||||
template<class U>
|
template<class U>
|
||||||
requires std::is_arithmetic_v<U> && (!std::is_same_v<U, T>)constexpr void
|
requires std::is_arithmetic_v<U> && (!std::is_same_v<U, T>)
|
||||||
fill_one(std::size_t &i, const U &v) noexcept
|
constexpr auto fill_one(std::size_t &i, const U &v) noexcept -> void
|
||||||
{
|
{
|
||||||
(*this)[i++] = static_cast<T>(v);
|
(*this)[i++] = static_cast<T>(v);
|
||||||
}
|
}
|
||||||
template<std::size_t M, class U>
|
template<std::size_t M, class U>
|
||||||
constexpr void fill_one(std::size_t &i, Vec<M, U> const &v) noexcept
|
constexpr auto fill_one(std::size_t &i, Vec<M, U> const &v) noexcept -> void
|
||||||
{
|
{
|
||||||
for (std::size_t k = 0; k < M; ++k)
|
for (std::size_t k = 0; k < M; ++k)
|
||||||
(*this)[i++] = static_cast<T>(v[k]);
|
(*this)[i++] = static_cast<T>(v[k]);
|
||||||
}
|
}
|
||||||
#endif // SMATH_IMPLICIT_CONVERSIONS
|
#endif // SMATH_IMPLICIT_CONVERSIONS
|
||||||
template<std::size_t M>
|
template<std::size_t M>
|
||||||
constexpr void fill_one(std::size_t &i, const Vec<M, T> &v) noexcept
|
constexpr auto fill_one(std::size_t &i, const Vec<M, T> &v) noexcept -> void
|
||||||
{
|
{
|
||||||
for (std::size_t k = 0; k < M; ++k)
|
for (std::size_t k = 0; k < M; ++k)
|
||||||
(*this)[i++] = static_cast<T>(v[k]);
|
(*this)[i++] = static_cast<T>(v[k]);
|
||||||
@@ -497,7 +508,8 @@ template<std::size_t N, typename T = float>
|
|||||||
requires std::is_arithmetic_v<T>
|
requires std::is_arithmetic_v<T>
|
||||||
using VecOrScalar = std::conditional_t<N == 1, T, Vec<N, T>>;
|
using VecOrScalar = std::conditional_t<N == 1, T, Vec<N, T>>;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
consteval auto char_to_idx(char c) -> std::size_t
|
consteval auto char_to_idx(char c) -> std::size_t
|
||||||
{
|
{
|
||||||
@@ -627,9 +639,11 @@ template<class T> constexpr auto turns(T value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<std::size_t R, std::size_t C, typename T>
|
template<std::size_t R, std::size_t C, typename T>
|
||||||
requires std::is_arithmetic_v<T> struct Mat;
|
requires std::is_arithmetic_v<T>
|
||||||
|
struct Mat;
|
||||||
|
|
||||||
template<class T> struct Quaternion : Vec<4, T> {
|
template<class T> struct Quaternion : Vec<4, T>
|
||||||
|
{
|
||||||
using Base = Vec<4, T>;
|
using Base = Vec<4, T>;
|
||||||
using Base::Base;
|
using Base::Base;
|
||||||
using Base::operator=;
|
using Base::operator=;
|
||||||
@@ -759,7 +773,9 @@ requires std::is_floating_point_v<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t R, std::size_t C, typename T = float>
|
template<std::size_t R, std::size_t C, typename T = float>
|
||||||
requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
|
requires std::is_arithmetic_v<T>
|
||||||
|
struct Mat : std::array<Vec<R, T>, C>
|
||||||
|
{
|
||||||
using Base = std::array<Vec<R, T>, C>;
|
using Base = std::array<Vec<R, T>, C>;
|
||||||
using Base::operator[];
|
using Base::operator[];
|
||||||
|
|
||||||
@@ -792,10 +808,8 @@ requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
|
|||||||
template<typename... Cols>
|
template<typename... Cols>
|
||||||
requires(sizeof...(Cols) == C
|
requires(sizeof...(Cols) == C
|
||||||
&& (std::same_as<std::remove_cvref_t<Cols>, Vec<R, T>> && ...))
|
&& (std::same_as<std::remove_cvref_t<Cols>, Vec<R, T>> && ...))
|
||||||
constexpr Mat(Cols const &...cols) noexcept
|
constexpr Mat(Cols const &...cols) noexcept : Base { cols... }
|
||||||
: Base { cols... }
|
{ }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto col(std::size_t j) noexcept -> Vec<R, T> &
|
constexpr auto col(std::size_t j) noexcept -> Vec<R, T> &
|
||||||
{
|
{
|
||||||
@@ -1100,8 +1114,12 @@ template<typename T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]] inline auto matrix_ortho3d(T const left, T const right,
|
[[nodiscard]] inline auto matrix_ortho3d(T const left,
|
||||||
T const bottom, T const top, T const near, T const far,
|
T const right,
|
||||||
|
T const bottom,
|
||||||
|
T const top,
|
||||||
|
T const near,
|
||||||
|
T const far,
|
||||||
bool const flip_z_axis = true) -> Mat<4, 4, T>
|
bool const flip_z_axis = true) -> Mat<4, 4, T>
|
||||||
{
|
{
|
||||||
Mat<4, 4, T> res {};
|
Mat<4, 4, T> res {};
|
||||||
@@ -1148,8 +1166,9 @@ inline auto matrix_perspective(
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]] inline auto matrix_look_at(Vec<3, T> const eye,
|
[[nodiscard]] inline auto matrix_look_at(
|
||||||
Vec<3, T> const center, Vec<3, T> const up) -> Mat<4, 4, T>
|
Vec<3, T> const eye, Vec<3, T> const center, Vec<3, T> const up)
|
||||||
|
-> Mat<4, 4, T>
|
||||||
{
|
{
|
||||||
auto f = (center - eye).normalized_safe();
|
auto f = (center - eye).normalized_safe();
|
||||||
auto s = f.cross(up).normalized_safe();
|
auto s = f.cross(up).normalized_safe();
|
||||||
@@ -1164,8 +1183,9 @@ template<typename T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[nodiscard]] inline auto matrix_infinite_perspective(T const fovy,
|
[[nodiscard]] inline auto matrix_infinite_perspective(
|
||||||
T const aspect, T const znear, bool flip_z_axis = false) -> Mat<4, 4, T>
|
T const fovy, T const aspect, T const znear, bool flip_z_axis = false)
|
||||||
|
-> Mat<4, 4, T>
|
||||||
{
|
{
|
||||||
Mat<4, 4, T> m {};
|
Mat<4, 4, T> m {};
|
||||||
|
|
||||||
@@ -1186,12 +1206,11 @@ template<typename T>
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<class Ext> struct interop_adapter;
|
template<class Ext> struct interop_adapter;
|
||||||
|
|
||||||
template<class Ext>
|
template<class Ext>
|
||||||
constexpr auto from_external(Ext const &ext)
|
constexpr auto from_external(Ext const &ext) ->
|
||||||
-> typename interop_adapter<Ext>::smath_type
|
typename interop_adapter<Ext>::smath_type
|
||||||
{
|
{
|
||||||
return interop_adapter<Ext>::to_smath(ext);
|
return interop_adapter<Ext>::to_smath(ext);
|
||||||
}
|
}
|
||||||
@@ -1202,12 +1221,12 @@ constexpr auto to_external(SMathT const &value) -> Ext
|
|||||||
return interop_adapter<Ext>::from_smath(value);
|
return interop_adapter<Ext>::from_smath(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace smath
|
} // namespace smath
|
||||||
|
|
||||||
template<std::size_t N, typename T>
|
template<std::size_t N, typename T>
|
||||||
requires std::formattable<T, char>
|
requires std::formattable<T, char>
|
||||||
struct std::formatter<smath::Vec<N, T>> : std::formatter<T> {
|
struct std::formatter<smath::Vec<N, T>> : std::formatter<T>
|
||||||
|
{
|
||||||
constexpr auto parse(std::format_parse_context &ctx)
|
constexpr auto parse(std::format_parse_context &ctx)
|
||||||
{
|
{
|
||||||
return std::formatter<T>::parse(ctx);
|
return std::formatter<T>::parse(ctx);
|
||||||
@@ -1230,12 +1249,14 @@ struct std::formatter<smath::Vec<N, T>> : std::formatter<T> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace std {
|
namespace std
|
||||||
|
{
|
||||||
template<size_t N, class T>
|
template<size_t N, class T>
|
||||||
struct tuple_size<smath::Vec<N, T>> : std::integral_constant<size_t, N> { };
|
struct tuple_size<smath::Vec<N, T>> : std::integral_constant<size_t, N>
|
||||||
|
{ };
|
||||||
|
|
||||||
template<size_t I, size_t N, class T>
|
template<size_t I, size_t N, class T> struct tuple_element<I, smath::Vec<N, T>>
|
||||||
struct tuple_element<I, smath::Vec<N, T>> {
|
{
|
||||||
static_assert(I < N);
|
static_assert(I < N);
|
||||||
using type = T;
|
using type = T;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,14 +2,17 @@
|
|||||||
|
|
||||||
#include <smath.hpp>
|
#include <smath.hpp>
|
||||||
|
|
||||||
TEST(AngleReturnRadians, DegInput) {
|
TEST(AngleReturnRadians, DegInput)
|
||||||
EXPECT_NEAR(smath::deg(180.0), std::numbers::pi, 1e-12);
|
{
|
||||||
|
EXPECT_NEAR(smath::deg(180.0), std::numbers::pi, 1e-12);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AngleReturnRadians, RadInput) {
|
TEST(AngleReturnRadians, RadInput)
|
||||||
EXPECT_DOUBLE_EQ(smath::rad(std::numbers::pi), std::numbers::pi);
|
{
|
||||||
|
EXPECT_DOUBLE_EQ(smath::rad(std::numbers::pi), std::numbers::pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AngleReturnRadians, TurnsInput) {
|
TEST(AngleReturnRadians, TurnsInput)
|
||||||
EXPECT_NEAR(smath::turns(0.5), std::numbers::pi, 1e-12);
|
{
|
||||||
|
EXPECT_NEAR(smath::turns(0.5), std::numbers::pi, 1e-12);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,22 +4,26 @@
|
|||||||
|
|
||||||
#include <smath.hpp>
|
#include <smath.hpp>
|
||||||
|
|
||||||
struct ExternalVec3f {
|
struct ExternalVec3f
|
||||||
|
{
|
||||||
float x;
|
float x;
|
||||||
float y;
|
float y;
|
||||||
float z;
|
float z;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExternalMat2f {
|
struct ExternalMat2f
|
||||||
|
{
|
||||||
float m00;
|
float m00;
|
||||||
float m01;
|
float m01;
|
||||||
float m10;
|
float m10;
|
||||||
float m11;
|
float m11;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace smath {
|
namespace smath
|
||||||
|
{
|
||||||
|
|
||||||
template<> struct interop_adapter<ExternalVec3f> {
|
template<> struct interop_adapter<ExternalVec3f>
|
||||||
|
{
|
||||||
using smath_type = Vec<3, float>;
|
using smath_type = Vec<3, float>;
|
||||||
|
|
||||||
static constexpr auto to_smath(ExternalVec3f const &v) -> smath_type
|
static constexpr auto to_smath(ExternalVec3f const &v) -> smath_type
|
||||||
@@ -33,7 +37,8 @@ template<> struct interop_adapter<ExternalVec3f> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<> struct interop_adapter<ExternalMat2f> {
|
template<> struct interop_adapter<ExternalMat2f>
|
||||||
|
{
|
||||||
using smath_type = Mat<2, 2, float>;
|
using smath_type = Mat<2, 2, float>;
|
||||||
|
|
||||||
static constexpr auto to_smath(ExternalMat2f const &m) -> smath_type
|
static constexpr auto to_smath(ExternalMat2f const &m) -> smath_type
|
||||||
@@ -54,7 +59,8 @@ template<> struct interop_adapter<ExternalMat2f> {
|
|||||||
|
|
||||||
} // namespace smath
|
} // namespace smath
|
||||||
|
|
||||||
TEST(Interop, FromExternalVec) {
|
TEST(Interop, FromExternalVec)
|
||||||
|
{
|
||||||
ExternalVec3f ext { 1.0f, 2.0f, 3.0f };
|
ExternalVec3f ext { 1.0f, 2.0f, 3.0f };
|
||||||
auto v = smath::from_external(ext);
|
auto v = smath::from_external(ext);
|
||||||
|
|
||||||
@@ -64,7 +70,8 @@ TEST(Interop, FromExternalVec) {
|
|||||||
EXPECT_EQ(v[2], 3.0f);
|
EXPECT_EQ(v[2], 3.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Interop, ToExternalVec) {
|
TEST(Interop, ToExternalVec)
|
||||||
|
{
|
||||||
smath::Vec3 v { 4.0f, 5.0f, 6.0f };
|
smath::Vec3 v { 4.0f, 5.0f, 6.0f };
|
||||||
auto ext = smath::to_external<ExternalVec3f>(v);
|
auto ext = smath::to_external<ExternalVec3f>(v);
|
||||||
|
|
||||||
@@ -73,7 +80,8 @@ TEST(Interop, ToExternalVec) {
|
|||||||
EXPECT_EQ(ext.z, 6.0f);
|
EXPECT_EQ(ext.z, 6.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Interop, RoundtripVec) {
|
TEST(Interop, RoundtripVec)
|
||||||
|
{
|
||||||
ExternalVec3f ext { 7.0f, 8.0f, 9.0f };
|
ExternalVec3f ext { 7.0f, 8.0f, 9.0f };
|
||||||
auto v = smath::from_external(ext);
|
auto v = smath::from_external(ext);
|
||||||
auto ext2 = smath::to_external<ExternalVec3f>(v);
|
auto ext2 = smath::to_external<ExternalVec3f>(v);
|
||||||
@@ -83,7 +91,8 @@ TEST(Interop, RoundtripVec) {
|
|||||||
EXPECT_EQ(ext2.z, 9.0f);
|
EXPECT_EQ(ext2.z, 9.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Interop, MatrixConversion) {
|
TEST(Interop, MatrixConversion)
|
||||||
|
{
|
||||||
ExternalMat2f ext { 1.0f, 2.0f, 3.0f, 4.0f };
|
ExternalMat2f ext { 1.0f, 2.0f, 3.0f, 4.0f };
|
||||||
auto m = smath::from_external(ext);
|
auto m = smath::from_external(ext);
|
||||||
|
|
||||||
|
|||||||
251
tests/vec.cpp
251
tests/vec.cpp
@@ -11,169 +11,184 @@ using smath::Vec2;
|
|||||||
using smath::Vec3;
|
using smath::Vec3;
|
||||||
using smath::Vec4;
|
using smath::Vec4;
|
||||||
|
|
||||||
template <class T>
|
template<class T>
|
||||||
static void ExpectVecNear(const Vec<3, T> &a, const Vec<3, T> &b,
|
static void ExpectVecNear(
|
||||||
T eps = T(1e-6)) {
|
const Vec<3, T> &a, const Vec<3, T> &b, T eps = T(1e-6))
|
||||||
for (int i = 0; i < 3; ++i)
|
{
|
||||||
EXPECT_NEAR(double(a[i]), double(b[i]), double(eps));
|
for (int i = 0; i < 3; ++i)
|
||||||
|
EXPECT_NEAR(double(a[i]), double(b[i]), double(eps));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructors and accessors
|
// Constructors and accessors
|
||||||
TEST(Vec, DefaultZero) {
|
TEST(Vec, DefaultZero)
|
||||||
Vec3 v;
|
{
|
||||||
EXPECT_EQ(v[0], 0.0f);
|
Vec3 v;
|
||||||
EXPECT_EQ(v[1], 0.0f);
|
EXPECT_EQ(v[0], 0.0f);
|
||||||
EXPECT_EQ(v[2], 0.0f);
|
EXPECT_EQ(v[1], 0.0f);
|
||||||
|
EXPECT_EQ(v[2], 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vec, ScalarFillCtor) {
|
TEST(Vec, ScalarFillCtor)
|
||||||
Vec4 v{2.0f};
|
{
|
||||||
EXPECT_EQ(v.x(), 2.0f);
|
Vec4 v { 2.0f };
|
||||||
EXPECT_EQ(v.y(), 2.0f);
|
EXPECT_EQ(v.x(), 2.0f);
|
||||||
EXPECT_EQ(v.z(), 2.0f);
|
EXPECT_EQ(v.y(), 2.0f);
|
||||||
EXPECT_EQ(v.w(), 2.0f);
|
EXPECT_EQ(v.z(), 2.0f);
|
||||||
|
EXPECT_EQ(v.w(), 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vec, VariadicCtorScalarsAndSubvectors) {
|
TEST(Vec, VariadicCtorScalarsAndSubvectors)
|
||||||
Vec2 a{1.0f, 2.0f};
|
{
|
||||||
Vec2 b{3.0f, 4.0f};
|
Vec2 a { 1.0f, 2.0f };
|
||||||
Vec4 v{a, b};
|
Vec2 b { 3.0f, 4.0f };
|
||||||
EXPECT_EQ(v.r(), 1.0f);
|
Vec4 v { a, b };
|
||||||
EXPECT_EQ(v.g(), 2.0f);
|
EXPECT_EQ(v.r(), 1.0f);
|
||||||
EXPECT_EQ(v.b(), 3.0f);
|
EXPECT_EQ(v.g(), 2.0f);
|
||||||
EXPECT_EQ(v.a(), 4.0f);
|
EXPECT_EQ(v.b(), 3.0f);
|
||||||
|
EXPECT_EQ(v.a(), 4.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vec, NamedAccessorsAliases) {
|
TEST(Vec, NamedAccessorsAliases)
|
||||||
Vec3 v{1.0f, 2.0f, 3.0f};
|
{
|
||||||
EXPECT_EQ(v.x(), v.r());
|
Vec3 v { 1.0f, 2.0f, 3.0f };
|
||||||
EXPECT_EQ(v.y(), v.g());
|
EXPECT_EQ(v.x(), v.r());
|
||||||
EXPECT_EQ(v.z(), v.b());
|
EXPECT_EQ(v.y(), v.g());
|
||||||
|
EXPECT_EQ(v.z(), v.b());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
TEST(Vec, ElementwiseAndScalarOps) {
|
TEST(Vec, ElementwiseAndScalarOps)
|
||||||
Vec3 a{1.0f, 2.0f, 3.0f};
|
{
|
||||||
Vec3 b{4.0f, 5.0f, 6.0f};
|
Vec3 a { 1.0f, 2.0f, 3.0f };
|
||||||
|
Vec3 b { 4.0f, 5.0f, 6.0f };
|
||||||
|
|
||||||
auto s1 = a + b;
|
auto s1 = a + b;
|
||||||
EXPECT_EQ(s1[0], 5.0f);
|
EXPECT_EQ(s1[0], 5.0f);
|
||||||
EXPECT_EQ(s1[1], 7.0f);
|
EXPECT_EQ(s1[1], 7.0f);
|
||||||
EXPECT_EQ(s1[2], 9.0f);
|
EXPECT_EQ(s1[2], 9.0f);
|
||||||
|
|
||||||
auto s2 = a * 2.0f;
|
auto s2 = a * 2.0f;
|
||||||
EXPECT_EQ(s2[0], 2.0f);
|
EXPECT_EQ(s2[0], 2.0f);
|
||||||
EXPECT_EQ(s2[1], 4.0f);
|
EXPECT_EQ(s2[1], 4.0f);
|
||||||
EXPECT_EQ(s2[2], 6.0f);
|
EXPECT_EQ(s2[2], 6.0f);
|
||||||
|
|
||||||
auto s3 = 2.0f * a; // RHS overloads
|
auto s3 = 2.0f * a; // RHS overloads
|
||||||
EXPECT_EQ(s3[0], 2.0f);
|
EXPECT_EQ(s3[0], 2.0f);
|
||||||
EXPECT_EQ(s3[1], 4.0f);
|
EXPECT_EQ(s3[1], 4.0f);
|
||||||
EXPECT_EQ(s3[2], 6.0f);
|
EXPECT_EQ(s3[2], 6.0f);
|
||||||
|
|
||||||
Vec3 c{1.0f, 2.0f, 3.0f};
|
Vec3 c { 1.0f, 2.0f, 3.0f };
|
||||||
c += Vec3{1.0f, 1.0f, 1.0f};
|
c += Vec3 { 1.0f, 1.0f, 1.0f };
|
||||||
EXPECT_EQ(c[0], 2.0f);
|
EXPECT_EQ(c[0], 2.0f);
|
||||||
EXPECT_EQ(c[1], 3.0f);
|
EXPECT_EQ(c[1], 3.0f);
|
||||||
EXPECT_EQ(c[2], 4.0f);
|
EXPECT_EQ(c[2], 4.0f);
|
||||||
|
|
||||||
c *= 2.0f;
|
c *= 2.0f;
|
||||||
EXPECT_EQ(c[0], 4.0f);
|
EXPECT_EQ(c[0], 4.0f);
|
||||||
EXPECT_EQ(c[1], 6.0f);
|
EXPECT_EQ(c[1], 6.0f);
|
||||||
EXPECT_EQ(c[2], 8.0f);
|
EXPECT_EQ(c[2], 8.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length, dot, cross, normalize
|
// Length, dot, cross, normalize
|
||||||
TEST(Vec, MagnitudeAndDot) {
|
TEST(Vec, MagnitudeAndDot)
|
||||||
Vec3 v{3.0f, 4.0f, 12.0f};
|
{
|
||||||
EXPECT_FLOAT_EQ(v.magnitude(), 13.0f);
|
Vec3 v { 3.0f, 4.0f, 12.0f };
|
||||||
EXPECT_FLOAT_EQ(v.length(), 13.0f);
|
EXPECT_FLOAT_EQ(v.magnitude(), 13.0f);
|
||||||
|
EXPECT_FLOAT_EQ(v.length(), 13.0f);
|
||||||
|
|
||||||
Vec3 u{1.0f, 0.0f, 2.0f};
|
Vec3 u { 1.0f, 0.0f, 2.0f };
|
||||||
EXPECT_FLOAT_EQ(v.dot(u), 27.0f);
|
EXPECT_FLOAT_EQ(v.dot(u), 27.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vec, Cross3D) {
|
TEST(Vec, Cross3D)
|
||||||
Vec3 x{1.0f, 0.0f, 0.0f};
|
{
|
||||||
Vec3 y{0.0f, 1.0f, 0.0f};
|
Vec3 x { 1.0f, 0.0f, 0.0f };
|
||||||
auto z = x.cross(y);
|
Vec3 y { 0.0f, 1.0f, 0.0f };
|
||||||
EXPECT_EQ(z[0], 0.0f);
|
auto z = x.cross(y);
|
||||||
EXPECT_EQ(z[1], 0.0f);
|
EXPECT_EQ(z[0], 0.0f);
|
||||||
EXPECT_EQ(z[2], 1.0f);
|
EXPECT_EQ(z[1], 0.0f);
|
||||||
|
EXPECT_EQ(z[2], 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vec, NormalizeAndSafeNormalize) {
|
TEST(Vec, NormalizeAndSafeNormalize)
|
||||||
Vec3 v{10.0f, 0.0f, 0.0f};
|
{
|
||||||
auto n = v.normalized();
|
Vec3 v { 10.0f, 0.0f, 0.0f };
|
||||||
auto ns = v.normalized_safe();
|
auto n = v.normalized();
|
||||||
ExpectVecNear(n, Vec3{1.0f, 0.0f, 0.0f});
|
auto ns = v.normalized_safe();
|
||||||
|
ExpectVecNear(n, Vec3 { 1.0f, 0.0f, 0.0f });
|
||||||
|
|
||||||
Vec3 zero{};
|
Vec3 zero {};
|
||||||
auto zs = zero.normalized_safe();
|
auto zs = zero.normalized_safe();
|
||||||
EXPECT_EQ(zs[0], 0.0f);
|
EXPECT_EQ(zs[0], 0.0f);
|
||||||
EXPECT_EQ(zs[1], 0.0f);
|
EXPECT_EQ(zs[1], 0.0f);
|
||||||
EXPECT_EQ(zs[2], 0.0f);
|
EXPECT_EQ(zs[2], 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vec, DistanceAndProjection) {
|
TEST(Vec, DistanceAndProjection)
|
||||||
Vec3 a{1.0f, 2.0f, 3.0f};
|
{
|
||||||
Vec3 b{4.0f, 6.0f, 3.0f};
|
Vec3 a { 1.0f, 2.0f, 3.0f };
|
||||||
EXPECT_FLOAT_EQ(a.distance(b), 5.0f);
|
Vec3 b { 4.0f, 6.0f, 3.0f };
|
||||||
|
EXPECT_FLOAT_EQ(a.distance(b), 5.0f);
|
||||||
|
|
||||||
Vec3 n{2.0f, 0.0f, 0.0f}; // onto x-axis scaled
|
Vec3 n { 2.0f, 0.0f, 0.0f }; // onto x-axis scaled
|
||||||
auto p = a.project_onto(n); // (a·n)/(n·n) * n = (2)/4 * n = 0.5 * n
|
auto p = a.project_onto(n); // (a·n)/(n·n) * n = (2)/4 * n = 0.5 * n
|
||||||
ExpectVecNear(p, Vec3{1.0f, 0.0f, 0.0f});
|
ExpectVecNear(p, Vec3 { 1.0f, 0.0f, 0.0f });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Approx equal
|
// Approx equal
|
||||||
TEST(Vec, ApproxEqual) {
|
TEST(Vec, ApproxEqual)
|
||||||
Vec3 a{1.0f, 2.0f, 3.0f};
|
{
|
||||||
Vec3 b{1.0f + 1e-7f, 2.0f - 1e-7f, 3.0f};
|
Vec3 a { 1.0f, 2.0f, 3.0f };
|
||||||
EXPECT_TRUE(a.approx_equal(b, 1e-6f));
|
Vec3 b { 1.0f + 1e-7f, 2.0f - 1e-7f, 3.0f };
|
||||||
EXPECT_FALSE(a.approx_equal(b, 1e-9f));
|
EXPECT_TRUE(a.approx_equal(b, 1e-6f));
|
||||||
|
EXPECT_FALSE(a.approx_equal(b, 1e-9f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::get & tuple interop
|
// std::get & tuple interop
|
||||||
TEST(Vec, StdGetAndTuple) {
|
TEST(Vec, StdGetAndTuple)
|
||||||
Vec3 v{7.0f, 8.0f, 9.0f};
|
{
|
||||||
static_assert(std::tuple_size_v<Vec3> == 3);
|
Vec3 v { 7.0f, 8.0f, 9.0f };
|
||||||
static_assert(std::is_same_v<std::tuple_element_t<1, Vec3>, float>);
|
static_assert(std::tuple_size_v<Vec3> == 3);
|
||||||
EXPECT_EQ(std::get<0>(v), 7.0f);
|
static_assert(std::is_same_v<std::tuple_element_t<1, Vec3>, float>);
|
||||||
EXPECT_EQ(std::get<1>(v), 8.0f);
|
EXPECT_EQ(std::get<0>(v), 7.0f);
|
||||||
EXPECT_EQ(std::get<2>(v), 9.0f);
|
EXPECT_EQ(std::get<1>(v), 8.0f);
|
||||||
|
EXPECT_EQ(std::get<2>(v), 9.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swizzle
|
// Swizzle
|
||||||
TEST(Vec, SwizzleBasic) {
|
TEST(Vec, SwizzleBasic)
|
||||||
const Vec3 v{1.0f, 2.0f, 3.0f};
|
{
|
||||||
|
const Vec3 v { 1.0f, 2.0f, 3.0f };
|
||||||
|
|
||||||
auto yz = smath::swizzle<"yz">(v);
|
auto yz = smath::swizzle<"yz">(v);
|
||||||
EXPECT_EQ(yz[0], 2.0f);
|
EXPECT_EQ(yz[0], 2.0f);
|
||||||
EXPECT_EQ(yz[1], 3.0f);
|
EXPECT_EQ(yz[1], 3.0f);
|
||||||
|
|
||||||
auto rxx = smath::swizzle<"xxy">(v);
|
auto rxx = smath::swizzle<"xxy">(v);
|
||||||
EXPECT_EQ(rxx[0], 1.0f);
|
EXPECT_EQ(rxx[0], 1.0f);
|
||||||
EXPECT_EQ(rxx[1], 1.0f);
|
EXPECT_EQ(rxx[1], 1.0f);
|
||||||
EXPECT_EQ(rxx[2], 2.0f);
|
EXPECT_EQ(rxx[2], 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::formatter
|
// std::formatter
|
||||||
TEST(Vec, Formatter) {
|
TEST(Vec, Formatter)
|
||||||
smath::Vec<3, int> vi{1, 2, 3};
|
{
|
||||||
std::string s = std::format("{}", vi);
|
smath::Vec<3, int> vi { 1, 2, 3 };
|
||||||
EXPECT_EQ(s, "{1, 2, 3}");
|
std::string s = std::format("{}", vi);
|
||||||
|
EXPECT_EQ(s, "{1, 2, 3}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conversions
|
// Conversions
|
||||||
TEST(Vec, ExplicitConversionBetweenScalarTypes) {
|
TEST(Vec, ExplicitConversionBetweenScalarTypes)
|
||||||
smath::Vec<3, int> vi{1, 2, 3};
|
{
|
||||||
smath::Vec<3, float> vf{vi};
|
smath::Vec<3, int> vi { 1, 2, 3 };
|
||||||
EXPECT_EQ(vf[0], 1.0f);
|
smath::Vec<3, float> vf { vi };
|
||||||
EXPECT_EQ(vf[1], 2.0f);
|
EXPECT_EQ(vf[0], 1.0f);
|
||||||
EXPECT_EQ(vf[2], 3.0f);
|
EXPECT_EQ(vf[1], 2.0f);
|
||||||
|
EXPECT_EQ(vf[2], 3.0f);
|
||||||
|
|
||||||
auto vi2 = static_cast<smath::Vec<3, int>>(vf);
|
auto vi2 = static_cast<smath::Vec<3, int>>(vf);
|
||||||
EXPECT_EQ(vi2[0], 1);
|
EXPECT_EQ(vi2[0], 1);
|
||||||
EXPECT_EQ(vi2[1], 2);
|
EXPECT_EQ(vi2[1], 2);
|
||||||
EXPECT_EQ(vi2[2], 3);
|
EXPECT_EQ(vi2[2], 3);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user