Compare commits

..

19 Commits

Author SHA1 Message Date
d3511a9b52 Forgot to change something in the CI oops
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-12 01:07:10 +02:00
eed719674f Add CI
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-12 01:05:57 +02:00
5f0badfe64 Fix packaging for Nix
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-12 01:05:14 +02:00
1a42238a41 Formatting + new packing related functions
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-11 12:55:04 +02:00
a5d669235e Fix some math
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-07 00:11:21 +02:00
2d86e02038 Translate
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-06 23:01:49 +02:00
b5e0aabe37 Create unpacking function for vectors
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-06 21:37:46 +02:00
e32204db0c Add default case to shut compilers up
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-04 17:42:13 +02:00
37f085ee36 Quick matrix aliases
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-04 17:17:00 +02:00
13288eda01 Matrix stuff
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-04 16:30:30 +02:00
bf1c2ee0c8 Add Matrices
Signed-off-by: Slendi <slendi@socopon.com>
2025-11-28 19:02:52 +02:00
f6e2bc01b1 Add quaternions
Signed-off-by: Slendi <slendi@socopon.com>
2025-11-28 16:14:49 +02:00
271f04581c Quick style fixes
Signed-off-by: Slendi <slendi@socopon.com>
2025-11-14 16:29:56 +02:00
26a0a8d046 Remove some methods if Vec doesn't use floats
Signed-off-by: Slendi <slendi@socopon.com>
2025-11-14 16:17:57 +02:00
446ab9c679 Add angle conversion functions
Signed-off-by: Slendi <slendi@socopon.com>
2025-11-14 16:03:17 +02:00
df49368e9a direnv
Signed-off-by: Slendi <slendi@socopon.com>
2025-11-14 15:21:17 +02:00
9f70b6c72b Add more examples
Signed-off-by: Slendi <slendi@socopon.com>
2025-08-24 22:47:31 +03:00
dfbbd03282 Fixup
Signed-off-by: Slendi <slendi@socopon.com>
2025-08-24 21:36:04 +03:00
e457d0f362 Quick and dirty install target
Signed-off-by: Slendi <slendi@socopon.com>
2025-08-24 21:33:29 +03:00
13 changed files with 1476 additions and 346 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

29
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Build project
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
name: Build
runs-on: ubuntu-22.04
permissions:
id-token: write
contents: read
steps:
- name: git checkout
uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Building default package
run: nix build .#default
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: result
path: result

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
[Bb]uild* [Bb]uild*
.cache .cache
result result
.direnv

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(SmathExamples CXX) project(SmathExamples LANGUAGES CXX VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -8,9 +8,53 @@ option(BUILD_EXAMPLES "Build example programs" ON)
option(BUILD_TESTS "Build unit tests" ON) option(BUILD_TESTS "Build unit tests" ON)
add_library(smath INTERFACE) add_library(smath INTERFACE)
target_include_directories(smath INTERFACE ${CMAKE_SOURCE_DIR}/include) target_include_directories(smath
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
add_library(smath::smath ALIAS smath) add_library(smath::smath ALIAS smath)
include(CMakePackageConfigHelpers)
install(TARGETS smath
EXPORT smathTargets
)
install(DIRECTORY include/ DESTINATION include)
install(EXPORT smathTargets
NAMESPACE smath::
FILE smathTargets.cmake
DESTINATION lib/cmake/smath
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/smathConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/smathConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/smathConfig.cmake"
INSTALL_DESTINATION "lib/cmake/smath"
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/smath.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/smath.pc"
@ONLY
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/smath.pc"
DESTINATION lib/pkgconfig
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/smathConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/smathConfigVersion.cmake"
DESTINATION lib/cmake/smath
)
if(BUILD_EXAMPLES) if(BUILD_EXAMPLES)
file(GLOB EXAMPLE_SOURCES "${CMAKE_SOURCE_DIR}/examples/*.cpp") file(GLOB EXAMPLE_SOURCES "${CMAKE_SOURCE_DIR}/examples/*.cpp")
foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES}) foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES})
@@ -23,13 +67,17 @@ endif()
if(BUILD_TESTS) if(BUILD_TESTS)
enable_testing() enable_testing()
include(FetchContent) find_package(GTest QUIET)
FetchContent_Declare(
googletest if(NOT GTest_FOUND)
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip include(FetchContent)
) FetchContent_Declare(
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) googletest
FetchContent_MakeAvailable(googletest) URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
endif()
file(GLOB TEST_SOURCES "${CMAKE_SOURCE_DIR}/tests/*.cpp") file(GLOB TEST_SOURCES "${CMAKE_SOURCE_DIR}/tests/*.cpp")
@@ -42,3 +90,4 @@ if(BUILD_TESTS)
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(smath_tests) gtest_discover_tests(smath_tests)
endif() endif()

View File

@@ -1,4 +1,19 @@
# smath # smath
Single-file linear algebra math library for C++23. Single-file, header-only linear algebra math library for C++23.
## Features
- Generic `Vec<N, T>` class with useful aliases `Vec2/Vec3/Vec4` and friendly accessors (`x/y/z/w`, `r/g/b/a`). They support approx-equal and tuple/structured bindings.
- `std::format` support.
- Compile-time swizzles via `swizzle<"...">`.
- Generic matrix `Mat` class with useful aliases `Mat2/Mat3/Mat4`.
- `Quaternion<T>` built on `Vec4`.
- Angle helpers `rad/deg/turns` respecting a configurable base unit via the macro `SMATH_ANGLE_UNIT`.
- Optional implicit conversions.
- Packing utilities for normalized RGBA (`pack_unorm4x8`, `unpack_snorm4x8`, etc.).
## License
This library is licensed under the Apache License 2.0. See the (LICENSE.txt)[LICENSE.txt] file for more details.

8
cmake/smath.pc.in Normal file
View File

@@ -0,0 +1,8 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/include
Name: smath
Description: Simple header-only math library
Version: @PROJECT_VERSION@
Cflags: -I${includedir}
Libs:

View File

@@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/smathTargets.cmake")
check_required_components(smath)

214
examples/projection.cpp Normal file
View File

@@ -0,0 +1,214 @@
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <print>
#include <string>
#include <vector>
#if defined(_WIN32)
#include <Windows.h>
#endif
#include <smath.hpp>
using smath::Vec2;
enum Color : uint8_t {
CLR_NONE = 0, // default
CLR_AXES = 90, // bright black (gray)
CLR_A = 32, // green
CLR_B = 33, // yellow
CLR_P = 35, // magenta
CLR_DOT = 36 // cyan
};
struct Cell {
char ch{' '};
uint8_t color{CLR_NONE};
int prio{0};
};
struct Canvas {
int 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) {
if (x < 0 || x >= w || y < 0 || y >= h)
return;
Cell &cell = pix[y * w + x];
if (prio >= cell.prio) {
cell.ch = c;
cell.prio = prio;
cell.color = color;
}
}
void hline(int y, char c, int prio, uint8_t color) {
for (int x = 0; x < w; ++x)
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) {
int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy;
int px = x0, py = y0;
put(px, py, '+', prio, color);
while (true) {
if (px == x1 && py == y1)
break;
int e2 = 2 * err;
int nx = px, ny = py;
if (e2 >= dy) {
err += dy;
nx += sx;
}
if (e2 <= dx) {
err += dx;
ny += sy;
}
int sdx = nx - px;
int sdy = ny - py;
int adx = std::abs(sdx);
int ady = std::abs(sdy);
char g;
if (adx >= 2 * ady)
g = '-';
else if (ady >= 2 * adx)
g = '|';
else
g = ((sdx > 0 && sdy > 0) || (sdx < 0 && sdy < 0)) ? '\\' : '/';
put(nx, ny, g, prio, color);
px = nx;
py = ny;
}
}
void flush() const {
uint8_t cur = 255;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
const Cell &c = pix[y * w + x];
if (c.color != cur) {
if (c.color == CLR_NONE)
std::print("\x1b[0m");
else
std::print("\x1b[{}m", c.color);
cur = c.color;
}
std::print("{}", c.ch);
}
std::println("");
}
std::print("\x1b[0m");
}
};
struct Mapper {
int W, H;
double scale;
int cx, cy;
Mapper(int w, int h, double s) : W(w), H(h), scale(s), cx(w / 2), cy(h / 2) {}
std::pair<int, int> map(Vec2 v) const {
int x = cx + (int)std::llround(v.x() * scale);
int y = cy - (int)std::llround(v.y() * scale);
return {x, y};
}
};
int main() {
#if defined(_WIN32)
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
if (h != INVALID_HANDLE_VALUE) {
DWORD m;
if (GetConsoleMode(h, &m))
SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
#endif
std::print("Enter vector a (ax ay): ");
double ax, ay;
if (!(std::cin >> ax >> ay))
return 0;
std::print("Enter vector b (bx by): ");
double bx, by;
if (!(std::cin >> bx >> by))
return 0;
Vec2 a{(float)ax, (float)ay};
Vec2 b{(float)bx, (float)by};
Vec2 p = a.project_onto(b);
std::println("\nResults:");
std::println("a = {}", a);
std::println("b = {}", b);
std::println("proj_b(a) = p = {}", p);
std::println("|a|={} |b|={} |p|={}", a.magnitude(), b.magnitude(),
p.magnitude());
double dot = a.dot(b);
double ang = (a.magnitude() > 0 && b.magnitude() > 0)
? std::acos(std::clamp(dot / (a.magnitude() * b.magnitude()),
-1.0, 1.0)) *
180.0 / M_PI
: 0.0;
std::println("a·b={} angle(a,b)={} deg\n", dot, ang);
const int W = 73, H = 33;
double maxr = std::max({(double)a.magnitude(), (double)b.magnitude(),
(double)p.magnitude(), 1.0});
double usable = 0.45 * std::min(W, H);
double scale = usable / maxr;
Canvas cvs(W, H);
Mapper mp(W, H, scale);
int pr_axes = 1;
auto [cx, cy] = mp.map({0, 0});
cvs.hline(H / 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);
auto [ox, oy] = mp.map({0, 0});
auto [ax1, ay1] = mp.map(a);
auto [bx1, by1] = mp.map(b);
auto [px1, py1] = mp.map(p);
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, bx1, by1, pr_b, CLR_B);
cvs.line_dir(ox, oy, px1, py1, pr_p, CLR_P);
if (!a.approx_equal(p) && b.magnitude() > 0) {
int steps = std::max(std::abs(ax1 - px1), std::abs(ay1 - py1));
for (int i = 0; i <= steps; ++i) {
double t = steps ? double(i) / steps : 0.0;
int x = (int)std::llround(px1 + t * (ax1 - px1));
int y = (int)std::llround(py1 + t * (ay1 - py1));
if (i % 2 == 0)
cvs.put(x, y, '.', 2, CLR_DOT);
}
}
auto place_label = [&](Vec2 v, const char *txt, uint8_t c, int pr) {
auto tip = mp.map(v * 1.05f);
for (int i = 0; txt[i]; ++i)
cvs.put(tip.first + i, tip.second, txt[i], pr, c);
};
place_label(a, "A", CLR_A, pr_a + 1);
place_label(b, "B", CLR_B, pr_b + 1);
place_label(p, "P", CLR_P, pr_p + 1);
std::println("Legend: axes(gray), a(green), b(yellow), p=proj(magenta), "
"dotted=cross-perp\n");
cvs.flush();
return 0;
}

43
examples/random_steps.cpp Normal file
View File

@@ -0,0 +1,43 @@
/*
* smath - Single-file linear algebra math library for C++23.
*
* Copyright 2025 Slendi <slendi@socopon.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* You can define the following macros to change functionality:
* - SMATH_IMPLICIT_CONVERSIONS
*/
#include <print>
#include <random>
#include <smath.hpp>
auto main() -> int {
using namespace smath;
Vec2d point;
std::random_device rd;
std::mt19937 rng{rd()};
std::uniform_real_distribution<> dis(-5, 5);
int i = 0;
do {
Vec2d add{dis(rng), dis(rng)};
auto const n = point + add;
std::println("{}: {:.2f} + {:.2f} -> {:.2f}", i, point, add, n);
point = n;
i++;
} while (i < 15);
}

View File

@@ -39,6 +39,9 @@ int main() {
std::println("std::get<1>(v): {}", std::get<1>(v)); std::println("std::get<1>(v): {}", std::get<1>(v));
auto [x, y, z] = v; auto [x, y, z] = v;
std::println("Bindings: [{}, {}, {}]", x, y, z); std::println("Bindings: [{}, {}, {}]", x, y, z);
float 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));

View File

@@ -1,8 +1,5 @@
let
desc = "Single-file linear algebra math library for C++23.";
in
{ {
description = desc; description = "Single-file linear algebra math library for C++23.";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@@ -39,7 +36,11 @@ in
src = ./.; src = ./.;
nativeBuildInputs = [ pkgs.copyPkgconfigItems ]; nativeBuildInputs = with pkgs; [
cmake
gtest
copyPkgconfigItems
];
pkgconfigItems = [ pkgconfigItems = [
(pkgs.makePkgconfigItem rec { (pkgs.makePkgconfigItem rec {
@@ -50,21 +51,22 @@ in
prefix = "${placeholder "out"}"; prefix = "${placeholder "out"}";
includedir = "${prefix}/include"; includedir = "${prefix}/include";
}; };
description = desc; description = "Single-file linear algebra math library for C++23.";
}) })
]; ];
dontBuild = true;
installPhase = '' installPhase = ''
runHook preInstall runHook preInstall
mkdir -p $out/include mkdir -p $out/include
cp include/*.hpp $out/include/ cp ../include/smath.hpp $out/include/
runHook postInstall runHook postInstall
''; '';
dontBuild = false;
doCheck = true;
meta = with pkgs.lib; { meta = with pkgs.lib; {
description = desc; description = "Single-file linear algebra math library for C++23.";
homepage = "https://github.com/slendidev/smath"; homepage = "https://github.com/slendidev/smath";
license = licenses.asl20; license = licenses.asl20;
platforms = platforms.all; platforms = platforms.all;

File diff suppressed because it is too large Load Diff

15
tests/angles.cpp Normal file
View File

@@ -0,0 +1,15 @@
#include <gtest/gtest.h>
#include <smath.hpp>
TEST(AngleReturnRadians, DegInput) {
EXPECT_NEAR(smath::deg(180.0), std::numbers::pi, 1e-12);
}
TEST(AngleReturnRadians, RadInput) {
EXPECT_DOUBLE_EQ(smath::rad(std::numbers::pi), std::numbers::pi);
}
TEST(AngleReturnRadians, TurnsInput) {
EXPECT_NEAR(smath::turns(0.5), std::numbers::pi, 1e-12);
}