Compare commits

...
24 Commits
Author SHA1 Message Date
Slendi c390ef3761 docs: Add DCO
Signed-off-by: Slendi <slendi@socopon.com>
2026-07-02 15:31:10 +03:00
Slendi 4a6f7df498 docs: Add AI guidelines
Signed-off-by: Slendi <slendi@socopon.com>
2026-07-02 15:28:17 +03:00
Slendi 720d3edfe3 Add formatting checks for PRs
Signed-off-by: Slendi <slendi@socopon.com>
2026-07-02 15:22:56 +03:00
Slendi 14873489ed Run clang-format on smath.cppm
Signed-off-by: Slendi <slendi@socopon.com>
2026-07-02 15:22:27 +03:00
Slendi 01a917880f Format all CMake scripts
Signed-off-by: Slendi <slendi@socopon.com>
2026-07-02 15:16:51 +03:00
Slendi aea3be7158 Remove nested exports from module
Signed-off-by: Slendi <slendi@socopon.com>
2026-07-02 14:34:30 +03:00
Slendi dedab14b28 docs: add issue templates, contributing file
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-15 17:21:34 +02:00
Slendi e063b10af5 docs: Ignore macros the user should not be aware of
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 09:16:30 +02:00
Slendi cf490494c3 Fix .gitignore and add build_docs.sh
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 08:35:40 +02:00
Slendi 2719e1d746 ci: update docs workflow to use new build script
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 08:33:03 +02:00
Slendi d113c492c9 Try another approach on removing the header
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 08:31:55 +02:00
Slendi 24c3769e22 Add logo to Doxygen documentation
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 08:27:02 +02:00
Slendi 6471d48d35 Fix some unused variable warnings
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 03:09:02 +02:00
Slendi 79dc2d43e6 ci: Try another approach to docs workflow
I hate CI.

Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 02:44:52 +02:00
Slendi cc08dd190c ci: add docs dir creation
Please work

Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 02:40:33 +02:00
Slendi 87a12de2bf ci: add doxygen workflow
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 02:38:43 +02:00
Slendi 6079705bb6 Add Doxygen documentation
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 02:35:04 +02:00
Slendi 48eff7d784 Document SMATH_ANGLE_UNIT in smath.hpp's top comment
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 01:42:20 +02:00
Slendi 224b924772 Add optional interop headers
Those headers are used for interoping with various popular C++
libraries. For now, those libraries are Eigen, GLM, HandmadeMath,
Raylib, and SFML. Some other ones may be added in the future.

Those are disabled by default, as most projects I would imagine would
not use them. But in case you need any of them for special reasons like
I did, they are there.

Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 01:34:35 +02:00
Slendi 2a161e36ab Move smath.hpp to smath/smath.hpp
This is done in preparation for optional interop headers for different
libraries.

Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 00:40:20 +02:00
Slendi 6f9e8814b1 ci: print build logs
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 00:17:19 +02:00
Slendi 4f71b3ac90 Add .clang-format and format codebase
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 00:13:58 +02:00
Slendi 01538457a5 Add interop API
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-12 00:13:58 +02:00
Slendi c4109f0ce3 bump cmake project version
Signed-off-by: Slendi <slendi@socopon.com>
2026-03-11 20:24:45 +02:00
41 changed files with 6199 additions and 526 deletions
+51
View 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
...
+59
View File
@@ -0,0 +1,59 @@
name: Bug report
description: Report a reproducible problem in smath
title: "bug: "
labels:
- bug
body:
- type: markdown
attributes:
value: |
Thanks for filing a bug report.
Please provide enough detail for us to reproduce and diagnose the issue.
- type: textarea
id: summary
attributes:
label: Summary
description: What happened?
placeholder: Briefly describe the bug.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
placeholder: Describe what you expected to happen.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Minimal reproduction
description: Include a minimal code snippet or steps to reproduce.
render: cpp
validations:
required: true
- type: input
id: compiler
attributes:
label: Compiler and version
placeholder: e.g. clang 18.1.8, GCC 14.2, MSVC 19.40
validations:
required: true
- type: input
id: platform
attributes:
label: Platform
placeholder: e.g. macOS 15 arm64, Ubuntu 24.04 x86_64, Windows 11
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add logs, screenshots, or links if relevant.
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: true
@@ -0,0 +1,41 @@
name: Feature request
description: Propose an enhancement to smath
title: "feat: "
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
Thanks for suggesting an improvement.
Clear use cases help us evaluate feature requests quickly.
- type: textarea
id: problem
attributes:
label: Problem statement
description: What problem are you trying to solve?
placeholder: Describe the limitation or pain point.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: Describe the API or behavior you want.
render: cpp
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: What other approaches did you consider?
- type: textarea
id: impact
attributes:
label: Impact and compatibility
description: Any concerns around performance, API compatibility, or migration?
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: Install Nix - name: Install Nix
uses: DeterminateSystems/nix-installer-action@main uses: DeterminateSystems/nix-installer-action@main
- name: Building default package - name: Building default package
run: nix build .#default run: nix build .#default --print-build-logs
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
+50
View File
@@ -0,0 +1,50 @@
name: Deploy Doxygen docs
on:
push:
branches: [master]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Doxygen
run: sudo apt-get update && sudo apt-get install -y doxygen graphviz perl
- name: Create output directory
run: mkdir -p build/docs
- name: Generate docs
run: ./docs/build_docs.sh
- name: Disable Jekyll
run: touch build/docs/html/.nojekyll
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: build/docs/html
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+24
View File
@@ -0,0 +1,24 @@
name: Format checks
on:
pull_request:
branches:
- master
jobs:
format:
name: Format
runs-on: ubuntu-22.04
permissions:
contents: read
steps:
- name: git checkout
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Check clang-format
run: |
nix develop -c sh -lc 'git ls-files "*.cpp" "*.hpp" "*.cppm" | xargs clang-format --dry-run --Werror'
- name: Check cmake-format
run: |
nix develop -c sh -lc 'cmake-format --check CMakeLists.txt src/modules/CMakeLists.txt cmake/smathConfig.cmake.in'
+1
View File
@@ -1,4 +1,5 @@
[Bb]uild* [Bb]uild*
![Bb]uild*.*
.cache .cache
result result
.direnv .direnv
+11
View File
@@ -0,0 +1,11 @@
# AGENTS.md
Instructions for agents working in this repo.
See the [`CONTRIBUTING.md`](CONTRIBUTING.md) file. Said file **must** be read, examined,
and understood before any change is made by agents.
For agents that understand `@`-inclusion of other markdown files, said inclusion line for
`CONTRIBUTING.md` follows, such that explicit reading of the file is not necessary:
@CONTRIBUTING.md
+7
View File
@@ -0,0 +1,7 @@
# CLAUDE.md
This project's agent guidance lives in [AGENTS.md](AGENTS.md), the single source of truth
shared with other tools. Claude Code does not load `AGENTS.md` automatically, so it is
imported here:
@AGENTS.md
+68 -64
View File
@@ -1,5 +1,8 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(SmathExamples LANGUAGES CXX VERSION 0.1.0) project(
SmathExamples
LANGUAGES CXX
VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -7,93 +10,94 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(SMATH_BUILD_EXAMPLES "Build example programs" ON) option(SMATH_BUILD_EXAMPLES "Build example programs" ON)
option(SMATH_BUILD_TESTS "Build unit tests" ON) option(SMATH_BUILD_TESTS "Build unit tests" ON)
option(SMATH_BUILD_MODULES "Enable C++20 modules support" OFF) option(SMATH_BUILD_MODULES "Enable C++20 modules support" OFF)
option(SMATH_ENABLE_LEGACY_HEADER "Install legacy include <smath.hpp> shim" OFF)
option(SMATH_INSTALL_INTEROP_HEADERS "Install interop headers" OFF)
if(SMATH_BUILD_MODULES) if(SMATH_BUILD_MODULES)
message(STATUS "Building smath C++ module") message(STATUS "Building smath C++ module")
add_subdirectory(src/modules) add_subdirectory(src/modules)
endif() endif()
add_library(smath INTERFACE) add_library(smath INTERFACE)
target_include_directories(smath target_include_directories(
INTERFACE smath
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> INTERFACE
$<INSTALL_INTERFACE:include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
) $<$<BOOL:${SMATH_ENABLE_LEGACY_HEADER}>:$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>>
$<INSTALL_INTERFACE:include>)
add_library(smath::smath ALIAS smath) add_library(smath::smath ALIAS smath)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
install(TARGETS smath install(TARGETS smath EXPORT smathTargets)
EXPORT smathTargets install(FILES include/smath/smath.hpp DESTINATION include/smath)
)
install(DIRECTORY include/ DESTINATION include)
install(EXPORT smathTargets if(SMATH_INSTALL_INTEROP_HEADERS)
NAMESPACE smath:: install(DIRECTORY include/smath/interop DESTINATION include/smath)
FILE smathTargets.cmake endif()
DESTINATION lib/cmake/smath
) if(SMATH_ENABLE_LEGACY_HEADER)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/smath_legacy_header.hpp.in"
"${CMAKE_CURRENT_BINARY_DIR}/include/smath.hpp" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/smath.hpp"
DESTINATION include)
endif()
install(
EXPORT smathTargets
NAMESPACE smath::
FILE smathTargets.cmake
DESTINATION lib/cmake/smath)
write_basic_package_version_file( write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/smathConfigVersion.cmake" "${CMAKE_CURRENT_BINARY_DIR}/smathConfigVersion.cmake"
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion COMPATIBILITY SameMajorVersion)
)
configure_package_config_file( configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/smathConfig.cmake.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/smathConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/smathConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/smathConfig.cmake"
INSTALL_DESTINATION "lib/cmake/smath" INSTALL_DESTINATION "lib/cmake/smath")
)
configure_file( configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/smath.pc.in"
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/smath.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/smath.pc" @ONLY)
"${CMAKE_CURRENT_BINARY_DIR}/smath.pc"
@ONLY
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/smath.pc" install(FILES "${CMAKE_CURRENT_BINARY_DIR}/smath.pc" DESTINATION lib/pkgconfig)
DESTINATION lib/pkgconfig
)
install(FILES install(FILES "${CMAKE_CURRENT_BINARY_DIR}/smathConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/smathConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/smathConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/smathConfigVersion.cmake" DESTINATION lib/cmake/smath)
DESTINATION lib/cmake/smath
)
if(SMATH_BUILD_EXAMPLES) if(SMATH_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})
get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE) get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE)
add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE}) add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE})
target_link_libraries(${EXAMPLE_NAME} PRIVATE smath::smath) target_link_libraries(${EXAMPLE_NAME} PRIVATE smath::smath)
endforeach() endforeach()
endif() endif()
if(SMATH_BUILD_TESTS) if(SMATH_BUILD_TESTS)
enable_testing() enable_testing()
find_package(GTest QUIET) find_package(GTest QUIET)
if(NOT GTest_FOUND) if(NOT GTest_FOUND)
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
googletest googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip)
) set(gtest_force_shared_crt
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) ON
FetchContent_MakeAvailable(googletest) CACHE BOOL "" FORCE)
endif() FetchContent_MakeAvailable(googletest)
endif()
file(GLOB TEST_SOURCES "${CMAKE_SOURCE_DIR}/tests/*.cpp") file(GLOB TEST_SOURCES "${CMAKE_SOURCE_DIR}/tests/*.cpp")
add_executable(smath_tests ${TEST_SOURCES}) add_executable(smath_tests ${TEST_SOURCES})
target_link_libraries(smath_tests PRIVATE target_link_libraries(smath_tests PRIVATE smath::smath GTest::gtest_main)
smath::smath
GTest::gtest_main
)
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(smath_tests) gtest_discover_tests(smath_tests)
endif() endif()
+131
View File
@@ -0,0 +1,131 @@
# Contributing to smath
Welcome to `smath` contributors!
This document contains a set of guidelines to contribute to the project. If you
feel like there's a mistake or something can be improved to this document, feel
free to propose changes in a pull request.
## Philosophy
- `smath` itself should be kept header only, in a single file.
- `smath` should be simple and easy to use.
## Development setup
You can work with either Nix (recommended, same tooling used in CI) or a local CMake toolchain.
### Option 1: Nix
Make sure you have flakes enabled and then execute the following to enter the shell:
```bash
nix develop
```
You can also run `direnv allow` if you have [direnv](https://direnv.net/) installed and configured.
### Option 2: Local toolchain
Requirements:
- C++23-capable compiler
- CMake (3.15+)
- Ninja (recommended)
### Configure and build
```bash
cmake -S . -B build -G Ninja -DSMATH_BUILD_TESTS=ON -DSMATH_BUILD_EXAMPLES=ON
cmake --build build
```
## Running tests
```bash
ctest --test-dir build --output-on-failure
```
## Code style
This project uses `clang-format` to maintain a consistent code style. Before opening a pull request, format changed C++ files:
```bash
git ls-files '*.hpp' '*.cpp' | xargs clang-format -i
```
## Sign-off (DCO)
Every commit must be signed off by at least one (1) human contributor:
```
git commit -s
```
This adds a `Signed-off-by: Your Name <your@email>` trailer certifying that you
wrote the change (or otherwise have reviewed the commit and have the right to
submit it) and agree to contribute it under the project's license, per the
Developer Certificate of Origin (the DCO file in this repository - also at
https://developercertificate.org).
## AI-assisted contributions
AI-assisted contributions are allowed, subject to the following:
- A human must be in the loop at all times. AI tools may assist, but a human
contributor must drive the work, review and understand every generated
change, and take full responsibility for it. Do not submit code you have not
read and understood.
- The assistance must be disclosed. Any commit produced with material AI help
must carry an `Assisted-by:` trailer using a format similar to the Linux
kernel's AI coding assistant format:
```
Assisted-by: AGENT_NAME:MODEL_VERSION
```
`AGENT_NAME` is the AI tool or framework (e.g. Codex, Claude, ...).
`MODEL_VERSION` is the full model identifier, all lowercase, including any
numeric, snapshot, or version suffix. Do not shorten it to the model family
(e.g. use gpt-5-5, not gpt-5; use claude-opus-4-8, not claude-opus).
- Do not add `Co-authored-by:` trailers for the AI assistant. The
`Assisted-by:` trailer already serves that purpose.
- The human contributor still signs off (see above). `Signed-off-by:` is the
human's certification of, and responsibility for, the change.
`Assisted-by:` only records which tool helped. It does not replace the sign-off or the human review.
Unreviewed, bulk, or fully-automated submissions are not accepted.
## Pull requests
- Keep changes focused and scoped to one topic.
- Add or update tests when behavior changes.
- Ensure the project builds and tests pass locally.
### Commit style
Use the commit message format: `<category>: <brief>`.
Allowed categories: `feat`, `fix`, `test`, `docs`, `ci`.
Examples:
- `feat: add as_matrix() to quaternion`
- `fix: correct mat4 approx_equal ignoring a column`
- `test: add tests for vector swizzle edge cases`
- `docs: fix typo in README.md`
- `ci: update nix build command in pull request workflow`
If a commit fixes a tracked issue, include an issue-closing footer: `Closes: #<num>` (example: `Closes: #42`).
## Reporting issues
When opening an issue, include:
- what you expected to happen
- what happened instead
- a minimal reproducible example
- compiler and platform details
+35
View File
@@ -0,0 +1,35 @@
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
+30 -14
View File
@@ -1,7 +1,7 @@
<p align="center"> <p align="center">
<img alt="smath logo" src="https://github.com/user-attachments/assets/d6f2ef4b-eca0-4004-9099-37423528bcba" /> <img alt="smath logo" width="100%" src="https://paste.slendi.dev/LcCPsJ.svg" />
<br><br> <br><br>
<a href="https://github.com/slendidev/smath/actions/workflows/build.yml"> <a href="https://github.com/slendidev/smath/actions/workflows/build.yml">
<img src="https://github.com/slendidev/smath/actions/workflows/build.yml/badge.svg" alt="Build"> <img src="https://github.com/slendidev/smath/actions/workflows/build.yml/badge.svg" alt="Build">
</a> </a>
<a href="LICENSE.txt"> <a href="LICENSE.txt">
@@ -12,13 +12,26 @@
</a> </a>
</p> </p>
## 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.).
- C++20 modules support.
- Built-in interop adapter and optional interop headers for various libraries.
## Quick Start ## Quick Start
Create `main.cpp`: Create `main.cpp`:
```cpp ```cpp
#include <print> #include <print>
#include <smath.hpp> #include <smath/smath.hpp>
int main() { int main() {
using namespace smath; using namespace smath;
@@ -39,17 +52,20 @@ g++ -std=c++23 -Iinclude main.cpp -o quickstart
./quickstart ./quickstart
``` ```
## Features ## Interop Headers
- 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. smath ships optional interop adapters under `include/smath/interop/`.
- `std::format` support.
- Compile-time swizzles via `swizzle<"...">`. When installing with CMake, interop headers are gated behind:
- Generic matrix `Mat` class with useful aliases `Mat2/Mat3/Mat4`.
- `Quaternion<T>` built on `Vec4`. - `-DSMATH_INSTALL_INTEROP_HEADERS=ON`
- Angle helpers `rad/deg/turns` respecting a configurable base unit via the macro `SMATH_ANGLE_UNIT`.
- Optional implicit conversions. All of them plug into the same generic API defined in `smath.hpp`:
- Packing utilities for normalized RGBA (`pack_unorm4x8`, `unpack_snorm4x8`, etc.).
- C++20 modules support. ```cpp
auto v = smath::from_external(external_value);
auto ext = smath::to_external<ExternalType>(smath_value);
```
## License ## License
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include <smath/smath.hpp>
+30
View File
@@ -0,0 +1,30 @@
PROJECT_NAME = "smath"
OUTPUT_DIRECTORY = build/docs
CREATE_SUBDIRS = NO
INPUT = include README.md
USE_MDFILE_AS_MAINPAGE = README.md
TOC_INCLUDE_HEADINGS = 0
RECURSIVE = YES
FILE_PATTERNS = *.hpp *.h *.md
EXCLUDE = include/smath/interop
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_STATIC = NO
GENERATE_HTML = YES
GENERATE_LATEX = NO
GENERATE_MAN = YES
GENERATE_TREEVIEW = YES
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_COLORSTYLE = LIGHT
PROJECT_LOGO = docs/smath_light.svg
HTML_EXTRA_STYLESHEET = docs/vendor/doxygen-awesome.css docs/custom.css
HTML_EXTRA_FILES = docs/smath_dark.svg docs/smath_big.svg
LAYOUT_FILE = docs/DoxygenLayout.xml
WARN_IF_UNDOCUMENTED = YES
QUIET = NO
EXCLUDE_SYMBOLS = std smath::detail smath::interop_adapter interop_adapter smath::from_external from_external smath::to_external to_external
+270
View File
@@ -0,0 +1,270 @@
<?xml version="1.0" encoding="UTF-8"?>
<doxygenlayout version="2.0">
<!-- Generated by doxygen 1.14.0 -->
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="topics" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="" intro="">
<tab type="modulelist" visible="yes" title="" intro=""/>
<tab type="modulemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="concepts" visible="yes" title="">
</tab>
<tab type="interfaces" visible="yes" title="">
<tab type="interfacelist" visible="yes" title="" intro=""/>
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="yes" title="">
<tab type="classlist" visible="yes" title="" intro=""/>
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="hierarchy" visible="yes" title="" intro=""/>
<tab type="classmembers" visible="yes" title="" intro=""/>
</tab>
<tab type="structs" visible="yes" title="">
<tab type="structlist" visible="yes" title="" intro=""/>
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
</tab>
<tab type="exceptions" visible="yes" title="">
<tab type="exceptionlist" visible="yes" title="" intro=""/>
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="yes" title="">
<tab type="filelist" visible="yes" title="" intro=""/>
<tab type="globals" visible="yes" title="" intro=""/>
</tab>
<tab type="user" url="https://github.com/slendidev/smath" title="GitHub"/>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
<!-- Layout definition for a class page -->
<class>
<briefdescription visible="yes"/>
<includes visible="$SHOW_HEADERFILE"/>
<inheritancegraph visible="yes"/>
<collaborationgraph visible="yes"/>
<memberdecl>
<nestedclasses visible="yes" title=""/>
<publictypes visible="yes" title=""/>
<services visible="yes" title=""/>
<interfaces visible="yes" title=""/>
<publicslots visible="yes" title=""/>
<signals visible="yes" title=""/>
<publicmethods visible="yes" title=""/>
<publicstaticmethods visible="yes" title=""/>
<publicattributes visible="yes" title=""/>
<publicstaticattributes visible="yes" title=""/>
<protectedtypes visible="yes" title=""/>
<protectedslots visible="yes" title=""/>
<protectedmethods visible="yes" title=""/>
<protectedstaticmethods visible="yes" title=""/>
<protectedattributes visible="yes" title=""/>
<protectedstaticattributes visible="yes" title=""/>
<packagetypes visible="yes" title=""/>
<packagemethods visible="yes" title=""/>
<packagestaticmethods visible="yes" title=""/>
<packageattributes visible="yes" title=""/>
<packagestaticattributes visible="yes" title=""/>
<properties visible="yes" title=""/>
<events visible="yes" title=""/>
<privatetypes visible="yes" title=""/>
<privateslots visible="yes" title=""/>
<privatemethods visible="yes" title=""/>
<privatestaticmethods visible="yes" title=""/>
<privateattributes visible="yes" title=""/>
<privatestaticattributes visible="yes" title=""/>
<friends visible="yes" title=""/>
<related visible="yes" title="" subtitle=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription visible="yes" title=""/>
<memberdef>
<inlineclasses visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<enums visible="yes" title=""/>
<services visible="yes" title=""/>
<interfaces visible="yes" title=""/>
<constructors visible="yes" title=""/>
<functions visible="yes" title=""/>
<related visible="yes" title=""/>
<variables visible="yes" title=""/>
<properties visible="yes" title=""/>
<events visible="yes" title=""/>
</memberdef>
<allmemberslink visible="yes"/>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
<!-- Layout definition for a namespace page -->
<namespace>
<briefdescription visible="yes"/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<concepts visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<sequences visible="yes" title=""/>
<dictionaries visible="yes" title=""/>
<enums visible="yes" title=""/>
<functions visible="yes" title=""/>
<variables visible="yes" title=""/>
<properties visible="yes" title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription visible="yes" title=""/>
<memberdef>
<inlineclasses visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<sequences visible="yes" title=""/>
<dictionaries visible="yes" title=""/>
<enums visible="yes" title=""/>
<functions visible="yes" title=""/>
<variables visible="yes" title=""/>
<properties visible="yes" title=""/>
</memberdef>
<authorsection visible="yes"/>
</namespace>
<!-- Layout definition for a concept page -->
<concept>
<briefdescription visible="yes"/>
<includes visible="$SHOW_HEADERFILE"/>
<definition visible="yes" title=""/>
<detaileddescription visible="yes" title=""/>
<authorsection visible="yes"/>
</concept>
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="yes"/>
<includedbygraph visible="yes"/>
<sourcelink visible="yes"/>
<memberdecl>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<concepts visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<defines visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<sequences visible="yes" title=""/>
<dictionaries visible="yes" title=""/>
<enums visible="yes" title=""/>
<functions visible="yes" title=""/>
<variables visible="yes" title=""/>
<properties visible="yes" title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription visible="yes" title=""/>
<memberdef>
<inlineclasses visible="yes" title=""/>
<defines visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<sequences visible="yes" title=""/>
<dictionaries visible="yes" title=""/>
<enums visible="yes" title=""/>
<functions visible="yes" title=""/>
<variables visible="yes" title=""/>
<properties visible="yes" title=""/>
</memberdef>
<authorsection/>
</file>
<!-- Layout definition for a group page -->
<group>
<briefdescription visible="yes"/>
<groupgraph visible="yes"/>
<memberdecl>
<nestedgroups visible="yes" title=""/>
<modules visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<concepts visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<sequences visible="yes" title=""/>
<dictionaries visible="yes" title=""/>
<enums visible="yes" title=""/>
<enumvalues visible="yes" title=""/>
<functions visible="yes" title=""/>
<variables visible="yes" title=""/>
<signals visible="yes" title=""/>
<publicslots visible="yes" title=""/>
<protectedslots visible="yes" title=""/>
<privateslots visible="yes" title=""/>
<events visible="yes" title=""/>
<properties visible="yes" title=""/>
<friends visible="yes" title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription visible="yes" title=""/>
<memberdef>
<pagedocs/>
<inlineclasses visible="yes" title=""/>
<defines visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<sequences visible="yes" title=""/>
<dictionaries visible="yes" title=""/>
<enums visible="yes" title=""/>
<enumvalues visible="yes" title=""/>
<functions visible="yes" title=""/>
<variables visible="yes" title=""/>
<signals visible="yes" title=""/>
<publicslots visible="yes" title=""/>
<protectedslots visible="yes" title=""/>
<privateslots visible="yes" title=""/>
<events visible="yes" title=""/>
<properties visible="yes" title=""/>
<friends visible="yes" title=""/>
</memberdef>
<authorsection visible="yes"/>
</group>
<!-- Layout definition for a C++20 module page -->
<module>
<briefdescription visible="yes"/>
<exportedmodules visible="yes"/>
<memberdecl>
<concepts visible="yes" title=""/>
<classes visible="yes" title=""/>
<enums visible="yes" title=""/>
<typedefs visible="yes" title=""/>
<functions visible="yes" title=""/>
<variables visible="yes" title=""/>
<membergroups visible="yes" title=""/>
</memberdecl>
<detaileddescription visible="yes" title=""/>
<memberdecl>
<files visible="yes"/>
</memberdecl>
</module>
<!-- Layout definition for a directory page -->
<directory>
<briefdescription visible="yes"/>
<directorygraph visible="yes"/>
<memberdecl>
<dirs visible="yes"/>
<files visible="yes"/>
</memberdecl>
<detaileddescription visible="yes" title=""/>
</directory>
</doxygenlayout>
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
doxygen "${repo_root}/docs/Doxyfile"
"${repo_root}/docs/postprocess-doxygen.sh" "${repo_root}/build/docs/html"
echo "Docs generated at ${repo_root}/build/docs/html"
+17
View File
@@ -0,0 +1,17 @@
#titlearea {
padding-right: 0;
}
#projectlogo img {
height: 40px;
width: auto;
}
#projectname,
#projectbrief {
display: none;
}
html.dark-mode #projectlogo img {
content: url('smath_dark.svg');
}
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
html_dir="${1:-build/docs/html}"
index_file="${html_dir%/}/index.html"
if [[ ! -f "${index_file}" ]]; then
echo "error: ${index_file} not found" >&2
exit 1
fi
perl -0pi -e 's{<div><div class="header">\s*<div class="headertitle"><div class="title">.*?</div></div>\s*</div><!--header-->\s*}{}s' "${index_file}"
+190
View File
@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1280"
height="640"
viewBox="0 0 1280.0001 640.00006"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="linearGradient13">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop12" />
<stop
style="stop-color:#16d800;stop-opacity:1;"
offset="1"
id="stop13" />
</linearGradient>
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient8">
<stop
style="stop-color:#c50000;stop-opacity:1;"
offset="0"
id="stop8" />
<stop
style="stop-color:#ff0c0c;stop-opacity:1;"
offset="1"
id="stop9" />
</linearGradient>
<linearGradient
id="linearGradient5">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#969696;stop-opacity:1;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient5"
id="linearGradient6-2"
x1="482.5849"
y1="589.83472"
x2="613.15253"
y2="-24.437733"
gradientUnits="userSpaceOnUse" />
<rect
x="227.90907"
y="175.81558"
width="659.85107"
height="227.90907"
id="rect1-5" />
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11-0"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-45"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-2" />
</marker>
<linearGradient
xlink:href="#linearGradient8"
id="linearGradient9-6"
x1="107.55984"
y1="103.63171"
x2="51.891579"
y2="103.63171"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-4-1"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-3-1" />
</marker>
<linearGradient
xlink:href="#linearGradient13"
id="linearGradient16"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.93927134,0,6.2326702)"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821" />
</defs>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="rect16"
width="1280.0001"
height="640"
x="1.1474608e-06"
y="2.8421709e-14" />
<g
id="layer1"
transform="translate(-419.99999)">
<g
id="g13-2"
transform="matrix(4.6783888,0,0,4.6783888,411.42933,-46.143325)">
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,2.4199432,-2.3937254)"
id="text1-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1-5);display:inline;fill:url(#linearGradient6-2);stroke-width:7.55906;stroke-dasharray:none"><tspan
x="227.9082"
y="353.41641"
id="tspan1">SMATH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446"
id="text2-1"><tspan
id="tspan2-24"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446">Linear algebra library for C++23</tspan></text>
<path
style="fill:#000000;stroke:url(#linearGradient11-0);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-45)"
d="M 50.894116,102.63145 V 56.432683"
id="path4-5" />
<path
style="fill:#000000;stroke:url(#linearGradient16);stroke-width:1.93832;stroke-dasharray:none;stroke-opacity:1"
d="M 50.894116,102.63145 V 59.238272"
id="path4-0-5" />
<path
style="fill:none;stroke:url(#linearGradient9-6);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-4-1)"
d="m 51.891583,103.63172 h 46.19876"
id="path4-9-23" />
<path
style="fill:#ff0c0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 51.891583,104.63115 v -1.9997 l -1.998196,1.99844 z"
id="path5-2" />
<path
style="fill:#25ff0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 49.895018,102.63082 v 1.9997 l 1.998196,-1.99844 z"
id="path5-8-3" />
<circle
style="fill:#1162ff;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="path13-1"
cx="50.894115"
cy="103.6313"
r="3.4155982" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

+186
View File
@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1280"
height="640"
viewBox="0 0 1280.0001 640.00006"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="linearGradient15">
<stop
style="stop-color:#666666;stop-opacity:1;"
offset="0.27173913"
id="stop14" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop15" />
</linearGradient>
<linearGradient
id="linearGradient13">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop12" />
<stop
style="stop-color:#16d800;stop-opacity:1;"
offset="1"
id="stop13" />
</linearGradient>
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient8">
<stop
style="stop-color:#c50000;stop-opacity:1;"
offset="0"
id="stop8" />
<stop
style="stop-color:#ff0c0c;stop-opacity:1;"
offset="1"
id="stop9" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient15"
id="linearGradient6-4-9"
x1="482.5849"
y1="589.83472"
x2="613.15253"
y2="-24.437733"
gradientUnits="userSpaceOnUse" />
<rect
x="227.90907"
y="175.81558"
width="659.85107"
height="227.90907"
id="rect1-9-2" />
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11-6-9"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-7-9"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-31-8" />
</marker>
<linearGradient
xlink:href="#linearGradient8"
id="linearGradient9-9-6"
x1="107.55984"
y1="103.63171"
x2="51.891579"
y2="103.63171"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-4-8-5"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-3-0-8" />
</marker>
<linearGradient
xlink:href="#linearGradient13"
id="linearGradient18"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.93927134,0,6.2326702)"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821" />
</defs>
<g
id="layer1"
transform="translate(-1710.0001)">
<g
id="g13-6-1"
transform="matrix(4.678389,0,0,4.6783889,1701.4295,-46.143332)">
<g
id="g15-6">
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,2.4199432,-2.3937254)"
id="text1-5-1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1-9-2);display:inline;fill:url(#linearGradient6-4-9);stroke-width:7.55906;stroke-dasharray:none"><tspan
x="227.9082"
y="353.41641"
id="tspan1">SMATH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#d1d1d1;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446"
id="text2-4-3"><tspan
id="tspan2-2-2"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#d1d1d1;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446">Linear algebra library for C++23</tspan></text>
<path
style="fill:#000000;stroke:url(#linearGradient11-6-9);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-7-9)"
d="M 50.894116,102.63145 V 56.432683"
id="path4-3-2" />
<path
style="fill:#000000;stroke:url(#linearGradient18);stroke-width:1.93832;stroke-dasharray:none;stroke-opacity:1"
d="M 50.894116,102.63145 V 59.238272"
id="path4-0-1-1" />
<path
style="fill:none;stroke:url(#linearGradient9-9-6);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-4-8-5)"
d="m 51.891583,103.63172 h 46.19876"
id="path4-9-2-1" />
<path
style="fill:#ff0c0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 51.891583,104.63115 v -1.9997 l -1.998196,1.99844 z"
id="path5-1-1" />
<path
style="fill:#25ff0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 49.895018,102.63082 v 1.9997 l 1.998196,-1.99844 z"
id="path5-8-2-2" />
<circle
style="fill:#1162ff;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="path13-5-3"
cx="50.894115"
cy="103.6313"
r="3.4155982" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

+193
View File
@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="200"
height="80"
viewBox="0 0 200.00001 80.000008"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="linearGradient15">
<stop
style="stop-color:#666666;stop-opacity:1;"
offset="0.27173913"
id="stop14" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop15" />
</linearGradient>
<linearGradient
id="linearGradient13">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop12" />
<stop
style="stop-color:#16d800;stop-opacity:1;"
offset="1"
id="stop13" />
</linearGradient>
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient8">
<stop
style="stop-color:#c50000;stop-opacity:1;"
offset="0"
id="stop8" />
<stop
style="stop-color:#ff0c0c;stop-opacity:1;"
offset="1"
id="stop9" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient15"
id="linearGradient6-4"
x1="482.5849"
y1="589.83472"
x2="613.15253"
y2="-24.437733"
gradientUnits="userSpaceOnUse" />
<rect
x="227.90907"
y="175.81558"
width="659.85107"
height="227.90907"
id="rect1-9" />
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11-6"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-7"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-31" />
</marker>
<linearGradient
xlink:href="#linearGradient8"
id="linearGradient9-9"
x1="107.55984"
y1="103.63171"
x2="51.891579"
y2="103.63171"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-4-8"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-3-0" />
</marker>
<linearGradient
xlink:href="#linearGradient13"
id="linearGradient14"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.93927134,0,6.2326702)"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821" />
</defs>
<rect
style="display:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="rect14"
width="200"
height="80"
x="-4.7587887e-06"
y="0" />
<g
id="layer1"
transform="translate(-210)">
<g
id="g13-6"
transform="translate(171.3688,-38.262697)">
<g
id="g15">
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,2.4199432,-2.3937254)"
id="text1-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1-9);display:inline;fill:url(#linearGradient6-4);stroke-width:7.55906;stroke-dasharray:none"><tspan
x="227.9082"
y="353.41641"
id="tspan1">SMATH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#d1d1d1;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446"
id="text2-4"><tspan
id="tspan2-2"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#d1d1d1;fill-opacity:1;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446">Linear algebra library for C++23</tspan></text>
<path
style="fill:#000000;stroke:url(#linearGradient11-6);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-7)"
d="M 50.894116,102.63145 V 56.432683"
id="path4-3" />
<path
style="fill:#000000;stroke:url(#linearGradient14);stroke-width:1.93832;stroke-dasharray:none;stroke-opacity:1"
d="M 50.894116,102.63145 V 59.238272"
id="path4-0-1" />
<path
style="fill:none;stroke:url(#linearGradient9-9);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-4-8)"
d="m 51.891583,103.63172 h 46.19876"
id="path4-9-2" />
<path
style="fill:#ff0c0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 51.891583,104.63115 v -1.9997 l -1.998196,1.99844 z"
id="path5-1" />
<path
style="fill:#25ff0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 49.895018,102.63082 v 1.9997 l 1.998196,-1.99844 z"
id="path5-8-2" />
<circle
style="fill:#1162ff;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="path13-5"
cx="50.894115"
cy="103.6313"
r="3.4155982" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

+182
View File
@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="200"
height="80"
viewBox="0 0 200.00001 80.000008"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="linearGradient13">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop12" />
<stop
style="stop-color:#16d800;stop-opacity:1;"
offset="1"
id="stop13" />
</linearGradient>
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient8">
<stop
style="stop-color:#c50000;stop-opacity:1;"
offset="0"
id="stop8" />
<stop
style="stop-color:#ff0c0c;stop-opacity:1;"
offset="1"
id="stop9" />
</linearGradient>
<linearGradient
id="linearGradient5">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#969696;stop-opacity:1;"
offset="1"
id="stop6" />
</linearGradient>
<marker
style="overflow:visible"
id="RoundedArrow"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8" />
</marker>
<rect
x="227.90907"
y="175.81558"
width="659.85107"
height="227.90907"
id="rect1" />
<marker
style="overflow:visible"
id="RoundedArrow-4"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-3" />
</marker>
<linearGradient
xlink:href="#linearGradient5"
id="linearGradient6"
x1="482.5849"
y1="589.83472"
x2="613.15253"
y2="-24.437733"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient8"
id="linearGradient9"
x1="107.55984"
y1="103.63171"
x2="51.891579"
y2="103.63171"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821"
gradientUnits="userSpaceOnUse" />
<linearGradient
xlink:href="#linearGradient13"
id="linearGradient12"
gradientUnits="userSpaceOnUse"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821"
gradientTransform="matrix(1,0,0,0.93927134,0,6.2326702)" />
</defs>
<g
id="layer1">
<g
id="g13"
transform="translate(-38.631211,-38.262697)">
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,2.4199432,-2.3937254)"
id="text1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1);display:inline;fill:url(#linearGradient6);stroke-width:7.55906;stroke-dasharray:none"><tspan
x="227.9082"
y="353.41641"
id="tspan3">SMATH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446"
id="text2"><tspan
id="tspan2"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446">Linear algebra library for C++23</tspan></text>
<path
style="fill:#000000;stroke:url(#linearGradient11);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow)"
d="M 50.894116,102.63145 V 56.432683"
id="path4" />
<path
style="fill:#000000;stroke:url(#linearGradient12);stroke-width:1.93832;stroke-dasharray:none;stroke-opacity:1"
d="M 50.894116,102.63145 V 59.238272"
id="path4-0" />
<path
style="fill:none;stroke:url(#linearGradient9);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-4)"
d="m 51.891583,103.63172 h 46.19876"
id="path4-9" />
<path
style="fill:#ff0c0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 51.891583,104.63115 v -1.9997 l -1.998196,1.99844 z"
id="path5" />
<path
style="fill:#25ff0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 49.895018,102.63082 v 1.9997 l 1.998196,-1.99844 z"
id="path5-8" />
<circle
style="fill:#1162ff;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="path13"
cx="50.894115"
cy="103.6313"
r="3.4155982" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

+3020
View File
File diff suppressed because it is too large Load Diff
+183 -170
View File
@@ -10,205 +10,218 @@
#include <Windows.h> #include <Windows.h>
#endif #endif
#include <smath.hpp> #include <smath/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}); 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;
} }
+16 -15
View File
@@ -22,22 +22,23 @@
#include <print> #include <print>
#include <random> #include <random>
#include <smath.hpp> #include <smath/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);
} }
+42 -41
View File
@@ -17,53 +17,54 @@
// #define SMATH_IMPLICIT_CONVERSIONS // #define SMATH_IMPLICIT_CONVERSIONS
#include <smath.hpp> #include <smath/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);
} }
+8 -2
View File
@@ -23,10 +23,12 @@
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
packages = with pkgs; [ packages = with pkgs; [
cmake cmake
cmake-format
ninja ninja
clang-tools clang-tools
lldb lldb
pkg-config pkg-config
doxygen
]; ];
}; };
@@ -55,10 +57,14 @@
}) })
]; ];
extraCMakeFlags = [
"-DSMATH_INSTALL_INTEROP_HEADERS=ON"
];
installPhase = '' installPhase = ''
runHook preInstall runHook preInstall
mkdir -p $out/include mkdir -p $out/include/
cp ../include/smath.hpp $out/include/ cp -r ../include/smath/ $out/include/
runHook postInstall runHook postInstall
''; '';
+131
View File
@@ -0,0 +1,131 @@
/*
* 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.
*/
#pragma once
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <smath/smath.hpp>
namespace smath
{
template<class Scalar, int N, int Options, int MaxRows, int MaxCols>
requires(std::is_arithmetic_v<Scalar> && (N > 0))
struct interop_adapter<Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols>>
{
using smath_type = Vec<static_cast<std::size_t>(N), Scalar>;
static constexpr auto to_smath(
Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols> const &v)
-> smath_type
{
smath_type out {};
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
out[i] = v(static_cast<int>(i));
return out;
}
static constexpr auto from_smath(smath_type const &v)
-> Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols>
{
Eigen::Matrix<Scalar, N, 1, Options, MaxRows, MaxCols> out;
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
out(static_cast<int>(i)) = v[i];
return out;
}
};
template<class Scalar, int N, int Options, int MaxRows, int MaxCols>
requires(std::is_arithmetic_v<Scalar> && (N > 0))
struct interop_adapter<Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols>>
{
using smath_type = Vec<static_cast<std::size_t>(N), Scalar>;
static constexpr auto to_smath(
Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols> const &v)
-> smath_type
{
smath_type out {};
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
out[i] = v(static_cast<int>(i));
return out;
}
static constexpr auto from_smath(smath_type const &v)
-> Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols>
{
Eigen::Matrix<Scalar, 1, N, Options, MaxRows, MaxCols> out;
for (std::size_t i = 0; i < static_cast<std::size_t>(N); ++i)
out(static_cast<int>(i)) = v[i];
return out;
}
};
template<class Scalar, int R, int C, int Options, int MaxRows, int MaxCols>
requires(std::is_arithmetic_v<Scalar> && (R > 1) && (C > 1))
struct interop_adapter<Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols>>
{
using smath_type
= Mat<static_cast<std::size_t>(R), static_cast<std::size_t>(C), Scalar>;
static constexpr auto to_smath(
Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols> const &m)
-> smath_type
{
smath_type out {};
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r)
out[r, c] = m(static_cast<int>(r), static_cast<int>(c));
}
return out;
}
static constexpr auto from_smath(smath_type const &m)
-> Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols>
{
Eigen::Matrix<Scalar, R, C, Options, MaxRows, MaxCols> out;
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r)
out(static_cast<int>(r), static_cast<int>(c)) = m[r, c];
}
return out;
}
};
template<class Scalar, int Options>
requires std::is_arithmetic_v<Scalar>
struct interop_adapter<Eigen::Quaternion<Scalar, Options>>
{
using smath_type = Quaternion<Scalar>;
static constexpr auto to_smath(Eigen::Quaternion<Scalar, Options> const &q)
-> smath_type
{
return { q.x(), q.y(), q.z(), q.w() };
}
static constexpr auto from_smath(smath_type const &q)
-> Eigen::Quaternion<Scalar, Options>
{
return { q.w(), q.x(), q.y(), q.z() };
}
};
} // namespace smath
+103
View File
@@ -0,0 +1,103 @@
/*
* 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.
*/
#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <smath/smath.hpp>
namespace smath
{
template<glm::length_t L, class T, glm::qualifier Q>
requires std::is_arithmetic_v<T>
struct interop_adapter<glm::vec<L, T, Q>>
{
using smath_type = Vec<static_cast<std::size_t>(L), T>;
static constexpr auto to_smath(glm::vec<L, T, Q> const &v) -> smath_type
{
smath_type out {};
for (std::size_t i = 0; i < static_cast<std::size_t>(L); ++i)
out[i] = v[static_cast<glm::length_t>(i)];
return out;
}
static constexpr auto from_smath(smath_type const &v) -> glm::vec<L, T, Q>
{
glm::vec<L, T, Q> out {};
for (std::size_t i = 0; i < static_cast<std::size_t>(L); ++i)
out[static_cast<glm::length_t>(i)] = v[i];
return out;
}
};
template<glm::length_t C, glm::length_t R, class T, glm::qualifier Q>
requires std::is_arithmetic_v<T>
struct interop_adapter<glm::mat<C, R, T, Q>>
{
using smath_type
= Mat<static_cast<std::size_t>(R), static_cast<std::size_t>(C), T>;
static constexpr auto to_smath(glm::mat<C, R, T, Q> const &m) -> smath_type
{
smath_type out {};
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r) {
out[r, c] = m[static_cast<glm::length_t>(c)]
[static_cast<glm::length_t>(r)];
}
}
return out;
}
static constexpr auto from_smath(smath_type const &m)
-> glm::mat<C, R, T, Q>
{
glm::mat<C, R, T, Q> out {};
for (std::size_t c = 0; c < static_cast<std::size_t>(C); ++c) {
for (std::size_t r = 0; r < static_cast<std::size_t>(R); ++r) {
out[static_cast<glm::length_t>(c)]
[static_cast<glm::length_t>(r)]
= m[r, c];
}
}
return out;
}
};
template<class T, glm::qualifier Q>
requires std::is_arithmetic_v<T>
struct interop_adapter<glm::qua<T, Q>>
{
using smath_type = Quaternion<T>;
static constexpr auto to_smath(glm::qua<T, Q> const &q) -> smath_type
{
return { q.x, q.y, q.z, q.w };
}
static constexpr auto from_smath(smath_type const &q) -> glm::qua<T, Q>
{
return { q.w(), q.x(), q.y(), q.z() };
}
};
} // namespace smath
+125
View File
@@ -0,0 +1,125 @@
/*
* 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.
*/
#pragma once
#include <HandmadeMath.h>
#include <smath/smath.hpp>
namespace smath
{
template<> struct interop_adapter<HMM_Vec2>
{
using smath_type = Vec<2, float>;
static constexpr auto to_smath(HMM_Vec2 const &v) -> smath_type
{
return { v.X, v.Y };
}
static constexpr auto from_smath(smath_type const &v) -> HMM_Vec2
{
return { v.x(), v.y() };
}
};
template<> struct interop_adapter<HMM_Vec3>
{
using smath_type = Vec<3, float>;
static constexpr auto to_smath(HMM_Vec3 const &v) -> smath_type
{
return { v.X, v.Y, v.Z };
}
static constexpr auto from_smath(smath_type const &v) -> HMM_Vec3
{
return { v.x(), v.y(), v.z() };
}
};
template<> struct interop_adapter<HMM_Vec4>
{
using smath_type = Vec<4, float>;
static constexpr auto to_smath(HMM_Vec4 const &v) -> smath_type
{
return { v.X, v.Y, v.Z, v.W };
}
static constexpr auto from_smath(smath_type const &v) -> HMM_Vec4
{
return { v.x(), v.y(), v.z(), v.w() };
}
};
template<> struct interop_adapter<HMM_Mat4>
{
using smath_type = Mat<4, 4, float>;
static constexpr auto to_smath(HMM_Mat4 const &m) -> smath_type
{
smath_type out {};
out[0, 0] = m.Columns[0].X;
out[1, 0] = m.Columns[0].Y;
out[2, 0] = m.Columns[0].Z;
out[3, 0] = m.Columns[0].W;
out[0, 1] = m.Columns[1].X;
out[1, 1] = m.Columns[1].Y;
out[2, 1] = m.Columns[1].Z;
out[3, 1] = m.Columns[1].W;
out[0, 2] = m.Columns[2].X;
out[1, 2] = m.Columns[2].Y;
out[2, 2] = m.Columns[2].Z;
out[3, 2] = m.Columns[2].W;
out[0, 3] = m.Columns[3].X;
out[1, 3] = m.Columns[3].Y;
out[2, 3] = m.Columns[3].Z;
out[3, 3] = m.Columns[3].W;
return out;
}
static constexpr auto from_smath(smath_type const &m) -> HMM_Mat4
{
HMM_Mat4 out {};
out.Columns[0] = { m[0, 0], m[1, 0], m[2, 0], m[3, 0] };
out.Columns[1] = { m[0, 1], m[1, 1], m[2, 1], m[3, 1] };
out.Columns[2] = { m[0, 2], m[1, 2], m[2, 2], m[3, 2] };
out.Columns[3] = { m[0, 3], m[1, 3], m[2, 3], m[3, 3] };
return out;
}
};
template<> struct interop_adapter<HMM_Quat>
{
using smath_type = Quaternion<float>;
static constexpr auto to_smath(HMM_Quat const &q) -> smath_type
{
return { q.X, q.Y, q.Z, q.W };
}
static constexpr auto from_smath(smath_type const &q) -> HMM_Quat
{
return { q.x(), q.y(), q.z(), q.w() };
}
};
} // namespace smath
+122
View File
@@ -0,0 +1,122 @@
/*
* 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.
*/
#pragma once
#include <raylib.h>
#include <smath/smath.hpp>
namespace smath
{
template<> struct interop_adapter<Vector2>
{
using smath_type = Vec<2, float>;
static constexpr auto to_smath(Vector2 const &v) -> smath_type
{
return { v.x, v.y };
}
static constexpr auto from_smath(smath_type const &v) -> Vector2
{
return { v.x(), v.y() };
}
};
template<> struct interop_adapter<Vector3>
{
using smath_type = Vec<3, float>;
static constexpr auto to_smath(Vector3 const &v) -> smath_type
{
return { v.x, v.y, v.z };
}
static constexpr auto from_smath(smath_type const &v) -> Vector3
{
return { v.x(), v.y(), v.z() };
}
};
template<> struct interop_adapter<Vector4>
{
using smath_type = Vec<4, float>;
static constexpr auto to_smath(Vector4 const &v) -> smath_type
{
return { v.x, v.y, v.z, v.w };
}
static constexpr auto from_smath(smath_type const &v) -> Vector4
{
return { v.x(), v.y(), v.z(), v.w() };
}
};
template<> struct interop_adapter<Matrix>
{
using smath_type = Mat<4, 4, float>;
static constexpr auto to_smath(Matrix const &m) -> smath_type
{
smath_type out {};
out[0, 0] = m.m0;
out[1, 0] = m.m1;
out[2, 0] = m.m2;
out[3, 0] = m.m3;
out[0, 1] = m.m4;
out[1, 1] = m.m5;
out[2, 1] = m.m6;
out[3, 1] = m.m7;
out[0, 2] = m.m8;
out[1, 2] = m.m9;
out[2, 2] = m.m10;
out[3, 2] = m.m11;
out[0, 3] = m.m12;
out[1, 3] = m.m13;
out[2, 3] = m.m14;
out[3, 3] = m.m15;
return out;
}
static constexpr auto from_smath(smath_type const &m) -> Matrix
{
return {
m[0, 0],
m[0, 1],
m[0, 2],
m[0, 3],
m[1, 0],
m[1, 1],
m[1, 2],
m[1, 3],
m[2, 0],
m[2, 1],
m[2, 2],
m[2, 3],
m[3, 0],
m[3, 1],
m[3, 2],
m[3, 3],
};
}
};
} // namespace smath
+63
View File
@@ -0,0 +1,63 @@
/*
* 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.
*/
#pragma once
#include <SFML/System/Vector2.hpp>
#include <SFML/System/Vector3.hpp>
#include <smath/smath.hpp>
namespace smath
{
template<class T>
requires std::is_arithmetic_v<T>
struct interop_adapter<sf::Vector2<T>>
{
using smath_type = Vec<2, T>;
static constexpr auto to_smath(sf::Vector2<T> const &v) -> smath_type
{
return { v.x, v.y };
}
static constexpr auto from_smath(smath_type const &v) -> sf::Vector2<T>
{
return { v.x(), v.y() };
}
};
template<class T>
requires std::is_arithmetic_v<T>
struct interop_adapter<sf::Vector3<T>>
{
using smath_type = Vec<3, T>;
static constexpr auto to_smath(sf::Vector3<T> const &v) -> smath_type
{
return { v.x, v.y, v.z };
}
static constexpr auto from_smath(smath_type const &v) -> sf::Vector3<T>
{
return { v.x(), v.y(), v.z() };
}
};
} // namespace smath
+298 -54
View File
@@ -17,6 +17,7 @@
* *
* You can define the following macros to change functionality: * You can define the following macros to change functionality:
* - SMATH_IMPLICIT_CONVERSIONS * - SMATH_IMPLICIT_CONVERSIONS
* - SMATH_ANGLE_UNIT (=rad, =deg, =turns)
*/ */
#pragma once #pragma once
@@ -32,20 +33,29 @@
#include <type_traits> #include <type_traits>
#ifndef SMATH_ANGLE_UNIT #ifndef SMATH_ANGLE_UNIT
# define SMATH_ANGLE_UNIT rad /**
* @brief Angle unit to be used for the actual values. Can be rad, deg, or
* turns.
*/
#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
{
/// \cond DO_NOT_DOCUMENT
#define SMATH_STR(x) #x #define SMATH_STR(x) #x
#define SMATH_XSTR(x) SMATH_STR(x) #define SMATH_XSTR(x) SMATH_STR(x)
/// \endcond
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 +65,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 +87,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 +98,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 +109,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 +159,16 @@ 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>
/**
* @brief Fixed-size arithmetic vector.
* @tparam N Number of components.
* @tparam T Component type.
*/
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,19 +177,24 @@ 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);
} }
public: public:
// Constructors // Constructors
/** @brief Constructs a zero-initialized vector. */
constexpr Vec() noexcept constexpr Vec() noexcept
{ {
for (auto &v : *this) for (auto &v : *this)
v = T(0); v = T(0);
} }
/**
* @brief Fills all components with the same scalar.
* @param s Scalar value assigned to every component.
*/
explicit constexpr Vec(T const &s) noexcept explicit constexpr Vec(T const &s) noexcept
{ {
for (auto &v : *this) for (auto &v : *this)
@@ -178,6 +205,10 @@ public:
requires((detail::is_scalar_v<Args> || detail::is_Vec_v<Args>) && ...) requires((detail::is_scalar_v<Args> || detail::is_Vec_v<Args>) && ...)
&& (total_extent<Args...>() == N) && (total_extent<Args...>() == N)
&& (!(sizeof...(Args) == 1 && (detail::is_Vec_v<Args> && ...))) && (!(sizeof...(Args) == 1 && (detail::is_Vec_v<Args> && ...)))
/**
* @brief Constructs from scalars and/or vectors whose flattened extent is
* N.
*/
constexpr Vec(Args &&...args) noexcept constexpr Vec(Args &&...args) noexcept
{ {
std::size_t i = 0; std::size_t i = 0;
@@ -187,13 +218,12 @@ public:
// Member accesses // Member accesses
// 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.
/// \cond DO_NOT_DOCUMENT
#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]; \
@@ -217,6 +247,7 @@ public:
VEC_ACC(u, 1, 0) VEC_ACC(u, 1, 0)
VEC_ACC(v, 2, 1) VEC_ACC(v, 2, 1)
#undef VEC_ACC #undef VEC_ACC
/// \endcond
template<class... Args, std::size_t... Is> template<class... Args, std::size_t... Is>
constexpr void unpack_impl( constexpr void unpack_impl(
@@ -225,7 +256,11 @@ public:
((args = (*this)[Is]), ...); ((args = (*this)[Is]), ...);
} }
template<class... Args> constexpr void unpack(Args &...args) noexcept template<class... Args>
/**
* @brief Unpacks components into output references.
*/
constexpr auto unpack(Args &...args) noexcept -> void
{ {
unpack_impl(std::index_sequence_for<Args...> {}, args...); unpack_impl(std::index_sequence_for<Args...> {}, args...);
} }
@@ -261,6 +296,7 @@ public:
} }
// Members // Members
/// \cond DO_NOT_DOCUMENT
#define VEC_OP(op) \ #define VEC_OP(op) \
constexpr auto operator op(Vec const &rhs) const noexcept -> Vec \ constexpr auto operator op(Vec const &rhs) const noexcept -> Vec \
{ \ { \
@@ -284,13 +320,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; \
@@ -301,6 +337,7 @@ public:
VEC_OP_ASSIGN(*) VEC_OP_ASSIGN(*)
VEC_OP_ASSIGN(/) VEC_OP_ASSIGN(/)
#undef VEC_OP_ASSIGN #undef VEC_OP_ASSIGN
/// \endcond
constexpr auto operator==(Vec const &v) const noexcept -> bool constexpr auto operator==(Vec const &v) const noexcept -> bool
{ {
@@ -315,6 +352,7 @@ public:
return !(*this == v); return !(*this == v);
} }
/** @brief Returns Euclidean magnitude. */
constexpr auto magnitude() const noexcept -> T constexpr auto magnitude() const noexcept -> T
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
@@ -323,6 +361,7 @@ public:
total += v * v; total += v * v;
return std::sqrt(total); return std::sqrt(total);
} }
/** @brief Alias for magnitude(). */
constexpr auto length() const noexcept -> T constexpr auto length() const noexcept -> T
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
@@ -331,6 +370,9 @@ public:
template<typename U = T> template<typename U = T>
requires std::is_floating_point_v<U> requires std::is_floating_point_v<U>
/**
* @brief Normalizes with zero fallback for very small magnitudes.
*/
constexpr auto normalized_safe(U eps = EPS_DEFAULT) const noexcept -> Vec constexpr auto normalized_safe(U eps = EPS_DEFAULT) const noexcept -> Vec
{ {
auto m = magnitude(); auto m = magnitude();
@@ -338,27 +380,32 @@ public:
} }
template<typename U = T> template<typename U = T>
requires std::is_floating_point_v<U> requires std::is_floating_point_v<U>
/** @brief Alias for normalized_safe(). */
constexpr auto normalize_safe(U eps = EPS_DEFAULT) const noexcept -> Vec constexpr auto normalize_safe(U eps = EPS_DEFAULT) const noexcept -> Vec
{ {
return normalized_safe(eps); return normalized_safe(eps);
} }
/** @brief Returns normalized vector; undefined for zero magnitude. */
[[nodiscard]] constexpr auto normalized() noexcept -> Vec<N, T> [[nodiscard]] constexpr auto normalized() noexcept -> Vec<N, T>
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
return (*this) / this->magnitude(); return (*this) / this->magnitude();
} }
/** @brief Alias for normalized(). */
[[nodiscard]] constexpr auto normalize() noexcept -> Vec<N, T> [[nodiscard]] constexpr auto normalize() noexcept -> Vec<N, T>
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
return this->normalized(); return this->normalized();
} }
/** @brief Alias for normalized(). */
[[nodiscard]] constexpr auto unit() noexcept -> Vec<N, T> [[nodiscard]] constexpr auto unit() noexcept -> Vec<N, T>
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
return this->normalized(); return this->normalized();
} }
/** @brief Dot product with `other`. */
[[nodiscard]] constexpr auto dot(Vec<N, T> const &other) const noexcept -> T [[nodiscard]] constexpr auto dot(Vec<N, T> const &other) const noexcept -> T
{ {
T res = 0; T res = 0;
@@ -369,6 +416,13 @@ public:
} }
static constexpr T EPS_DEFAULT = T(1e-6); static constexpr T EPS_DEFAULT = T(1e-6);
/**
* @brief Approximate equality with per-component absolute epsilon.
* @param rhs Vec to compare against.
* @param eps Absolute tolerance per component.
* @return `true` when all components are approximately equal.
*/
template<class U = T> template<class U = T>
requires std::is_floating_point_v<U> requires std::is_floating_point_v<U>
[[nodiscard]] constexpr auto approx_equal( [[nodiscard]] constexpr auto approx_equal(
@@ -382,6 +436,7 @@ public:
} }
template<class U = T> template<class U = T>
/** @brief Magnitude computed in promoted type `U` (or `double`). */
constexpr auto magnitude_promoted() const noexcept constexpr auto magnitude_promoted() const noexcept
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
@@ -393,19 +448,25 @@ public:
} }
template<typename U = T> template<typename U = T>
requires(N == 3) constexpr auto cross(Vec const &r) const noexcept -> Vec requires(N == 3)
/** @brief 3D cross product. */
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],
};
} }
/** @brief Distance between this vector and `r`. */
constexpr auto distance(Vec const &r) const noexcept -> T constexpr auto distance(Vec const &r) const noexcept -> T
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
return (*this - r).magnitude(); return (*this - r).magnitude();
} }
/** @brief Projection of this vector onto `n`. */
constexpr auto project_onto(Vec const &n) const noexcept -> Vec constexpr auto project_onto(Vec const &n) const noexcept -> Vec
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
{ {
@@ -425,8 +486,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 +505,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]);
@@ -495,9 +556,15 @@ constexpr T const &&get(Vec<N, T> const &&v) noexcept
template<std::size_t N, typename T = float> template<std::size_t N, typename T = float>
requires std::is_arithmetic_v<T> requires std::is_arithmetic_v<T>
/**
* @brief Return type used by swizzle operations.
*
* Produces `T` when `N == 1`, otherwise `Vec<N, 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
{ {
@@ -567,22 +634,39 @@ concept ValidSwizzle
template<detail::FixedString S, std::size_t N, typename T> template<detail::FixedString S, std::size_t N, typename T>
requires detail::ValidSwizzle<S, N> requires detail::ValidSwizzle<S, N>
/**
* @brief Compile-time swizzle selection.
* @tparam S Swizzle pattern like `"xy"`, `"rgba"`, or `"stp"`.
* @param v Source vector.
* @return Swizzled scalar/vector according to `S`.
*/
constexpr auto swizzle(Vec<N, T> const &v) -> VecOrScalar<S.size, T> constexpr auto swizzle(Vec<N, T> const &v) -> VecOrScalar<S.size, T>
{ {
return detail::swizzle_impl<S>(v, std::make_index_sequence<S.size> {}); return detail::swizzle_impl<S>(v, std::make_index_sequence<S.size> {});
} }
/** @brief 2D float vector alias. */
using Vec2 = Vec<2>; using Vec2 = Vec<2>;
/** @brief 3D float vector alias. */
using Vec3 = Vec<3>; using Vec3 = Vec<3>;
/** @brief 4D float vector alias. */
using Vec4 = Vec<4>; using Vec4 = Vec<4>;
/** @brief 2D double vector alias. */
using Vec2d = Vec<2, double>; using Vec2d = Vec<2, double>;
/** @brief 3D double vector alias. */
using Vec3d = Vec<3, double>; using Vec3d = Vec<3, double>;
/** @brief 4D double vector alias. */
using Vec4d = Vec<4, double>; using Vec4d = Vec<4, double>;
template<class T> template<class T>
using angle_ret_t = std::conditional_t<std::is_same_v<T, float>, float, double>; using angle_ret_t = std::conditional_t<std::is_same_v<T, float>, float, double>;
/**
* @brief Convert degrees to the configured angle unit.
* @param value Angle in degrees.
* @return Angle in `SMATH_ANGLE_UNIT`.
*/
template<class T> constexpr auto deg(T value) template<class T> constexpr auto deg(T value)
{ {
using R = angle_ret_t<T>; using R = angle_ret_t<T>;
@@ -597,6 +681,11 @@ template<class T> constexpr auto deg(T value)
} }
} }
/**
* @brief Convert radians to the configured angle unit.
* @param value Angle in radians.
* @return Angle in `SMATH_ANGLE_UNIT`.
*/
template<class T> constexpr auto rad(T value) template<class T> constexpr auto rad(T value)
{ {
using R = angle_ret_t<T>; using R = angle_ret_t<T>;
@@ -612,6 +701,11 @@ template<class T> constexpr auto rad(T value)
} }
} }
/**
* @brief Convert turns to the configured angle unit.
* @param value Angle in turns.
* @return Angle in `SMATH_ANGLE_UNIT`.
*/
template<class T> constexpr auto turns(T value) template<class T> constexpr auto turns(T value)
{ {
using R = angle_ret_t<T>; using R = angle_ret_t<T>;
@@ -627,14 +721,22 @@ 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> { /**
* @brief Quaternion represented as `(x, y, z, w)`.
* @tparam T Component type.
*/
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=;
/** @brief Returns this quaternion as its vector base type. */
constexpr Base &vec() noexcept { return *this; } constexpr Base &vec() noexcept { return *this; }
/** @brief Returns this quaternion as its vector base type. */
constexpr Base const &vec() const noexcept { return *this; } constexpr Base const &vec() const noexcept { return *this; }
constexpr T &x() noexcept { return Base::x(); } constexpr T &x() noexcept { return Base::x(); }
@@ -646,6 +748,11 @@ template<class T> struct Quaternion : Vec<4, T> {
constexpr T &w() noexcept { return Base::w(); } constexpr T &w() noexcept { return Base::w(); }
constexpr T const &w() const noexcept { return Base::w(); } constexpr T const &w() const noexcept { return Base::w(); }
/**
* @brief Hamilton product.
* @param rhs Right-hand quaternion.
* @return Product quaternion.
*/
constexpr auto operator*(Quaternion const &rhs) const noexcept -> Quaternion constexpr auto operator*(Quaternion const &rhs) const noexcept -> Quaternion
{ {
Quaternion r; Quaternion r;
@@ -663,6 +770,10 @@ template<class T> struct Quaternion : Vec<4, T> {
return r; return r;
} }
/**
* @brief Converts this quaternion to a 4x4 rotation matrix.
* @return Homogeneous rotation matrix.
*/
[[nodiscard]] constexpr auto as_matrix() const noexcept -> Mat<4, 4, T> [[nodiscard]] constexpr auto as_matrix() const noexcept -> Mat<4, 4, T>
{ {
auto const xx = x() * x(); auto const xx = x() * x();
@@ -683,6 +794,12 @@ template<class T> struct Quaternion : Vec<4, T> {
}; };
} }
/**
* @brief Builds a quaternion from axis-angle input.
* @param axis Rotation axis.
* @param angle Rotation angle in configured angle units.
* @return Quaternion representing the rotation.
*/
[[nodiscard]] static constexpr auto from_axis_angle( [[nodiscard]] static constexpr auto from_axis_angle(
Vec<3, T> const &axis, T const angle) noexcept -> Quaternion Vec<3, T> const &axis, T const angle) noexcept -> Quaternion
{ {
@@ -702,6 +819,10 @@ template<class T> struct Quaternion : Vec<4, T> {
template<class T> template<class T>
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
/**
* @brief Packs `[0, 1]` RGBA values into 4x8-bit UNORM.
* @return Packed 32-bit value in RGBA byte order.
*/
[[nodiscard]] constexpr auto pack_unorm4x8(Vec<4, T> const &v) -> std::uint32_t [[nodiscard]] constexpr auto pack_unorm4x8(Vec<4, T> const &v) -> std::uint32_t
{ {
std::uint32_t r = detail::pack_unorm8(v[0]); std::uint32_t r = detail::pack_unorm8(v[0]);
@@ -714,6 +835,10 @@ requires std::is_floating_point_v<T>
template<class T> template<class T>
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
/**
* @brief Packs `[-1, 1]` RGBA values into 4x8-bit SNORM.
* @return Packed 32-bit value in RGBA byte order.
*/
[[nodiscard]] constexpr auto pack_snorm4x8(Vec<4, T> const &v) -> std::uint32_t [[nodiscard]] constexpr auto pack_snorm4x8(Vec<4, T> const &v) -> std::uint32_t
{ {
std::uint32_t r = static_cast<std::uint8_t>(detail::pack_snorm8(v[0])); std::uint32_t r = static_cast<std::uint8_t>(detail::pack_snorm8(v[0]));
@@ -726,6 +851,10 @@ requires std::is_floating_point_v<T>
template<class T = float> template<class T = float>
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
/**
* @brief Unpacks a 4x8-bit UNORM RGBA value.
* @return RGBA vector with components in `[0, 1]`.
*/
[[nodiscard]] constexpr auto unpack_unorm4x8(std::uint32_t packed) -> Vec<4, T> [[nodiscard]] constexpr auto unpack_unorm4x8(std::uint32_t packed) -> Vec<4, T>
{ {
std::uint8_t r = static_cast<std::uint8_t>(packed & 0xFFu); std::uint8_t r = static_cast<std::uint8_t>(packed & 0xFFu);
@@ -743,6 +872,10 @@ requires std::is_floating_point_v<T>
template<class T = float> template<class T = float>
requires std::is_floating_point_v<T> requires std::is_floating_point_v<T>
/**
* @brief Unpacks a 4x8-bit SNORM RGBA value.
* @return RGBA vector with components in `[-1, 1]`.
*/
[[nodiscard]] constexpr auto unpack_snorm4x8(std::uint32_t packed) -> Vec<4, T> [[nodiscard]] constexpr auto unpack_snorm4x8(std::uint32_t packed) -> Vec<4, T>
{ {
std::int8_t r = static_cast<std::int8_t>(packed & 0xFFu); std::int8_t r = static_cast<std::int8_t>(packed & 0xFFu);
@@ -759,7 +892,15 @@ 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>
/**
* @brief Column-major matrix with `R` rows and `C` columns.
* @tparam R Row count.
* @tparam C Column count.
* @tparam T Scalar type.
*/
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[];
@@ -774,12 +915,17 @@ requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
return col(column)[row]; return col(column)[row];
} }
/** @brief Constructs a zero matrix. */
constexpr Mat() noexcept constexpr Mat() noexcept
{ {
for (auto &col : *this) for (auto &col : *this)
col = Vec<R, T> {}; col = Vec<R, T> {};
} }
/**
* @brief Constructs a diagonal matrix.
* @param diag Value used for every diagonal component.
*/
constexpr explicit Mat(T const &diag) noexcept constexpr explicit Mat(T const &diag) noexcept
requires(R == C) requires(R == C)
{ {
@@ -792,10 +938,12 @@ 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 /**
: Base { cols... } * @brief Constructs from explicit column vectors.
{ * @param cols Columns in column-major order.
} */
constexpr Mat(Cols const &...cols) noexcept : Base { cols... }
{ }
constexpr auto col(std::size_t j) noexcept -> Vec<R, T> & constexpr auto col(std::size_t j) noexcept -> Vec<R, T> &
{ {
@@ -890,6 +1038,13 @@ requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
} }
static constexpr T EPS_DEFAULT = T(1e-6); static constexpr T EPS_DEFAULT = T(1e-6);
/**
* @brief Approximate matrix equality check.
* @param rhs Matrix to compare against.
* @param eps Absolute tolerance per component.
* @return `true` when all columns are approximately equal.
*/
template<class U = T> template<class U = T>
requires std::is_floating_point_v<U> requires std::is_floating_point_v<U>
[[nodiscard]] constexpr auto approx_equal( [[nodiscard]] constexpr auto approx_equal(
@@ -901,6 +1056,7 @@ requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
return true; return true;
} }
/** @brief Returns the transposed matrix. */
[[nodiscard]] constexpr auto transposed() const noexcept -> Mat<C, R, T> [[nodiscard]] constexpr auto transposed() const noexcept -> Mat<C, R, T>
{ {
Mat<C, R, T> r {}; Mat<C, R, T> r {};
@@ -910,6 +1066,7 @@ requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
return r; return r;
} }
/** @brief Returns an identity matrix. */
[[nodiscard]] static constexpr auto identity() noexcept -> Mat<R, C, T> [[nodiscard]] static constexpr auto identity() noexcept -> Mat<R, C, T>
requires(R == C) requires(R == C)
{ {
@@ -920,15 +1077,25 @@ requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
} }
}; };
/** @brief 2x2 float matrix alias. */
using Mat2 = Mat<2, 2>; using Mat2 = Mat<2, 2>;
/** @brief 3x3 float matrix alias. */
using Mat3 = Mat<3, 3>; using Mat3 = Mat<3, 3>;
/** @brief 4x4 float matrix alias. */
using Mat4 = Mat<4, 4>; using Mat4 = Mat<4, 4>;
/** @brief 2x2 double matrix alias. */
using Mat2d = Mat<2, 2, double>; using Mat2d = Mat<2, 2, double>;
/** @brief 3x3 double matrix alias. */
using Mat3d = Mat<3, 3, double>; using Mat3d = Mat<3, 3, double>;
/** @brief 4x4 double matrix alias. */
using Mat4d = Mat<4, 4, double>; using Mat4d = Mat<4, 4, double>;
template<std::size_t R, std::size_t C, typename T> template<std::size_t R, std::size_t C, typename T>
/**
* @brief Matrix-vector multiplication.
* @return Product vector.
*/
[[nodiscard]] constexpr Vec<R, T> operator*( [[nodiscard]] constexpr Vec<R, T> operator*(
Mat<R, C, T> const &m, Vec<C, T> const &v) noexcept Mat<R, C, T> const &m, Vec<C, T> const &v) noexcept
{ {
@@ -938,7 +1105,10 @@ template<std::size_t R, std::size_t C, typename T>
return out; return out;
} }
// Matrix * Matrix /**
* @brief Matrix-matrix multiplication.
* @return Product matrix.
*/
template<std::size_t R, std::size_t C, std::size_t K, typename T> template<std::size_t R, std::size_t C, std::size_t K, typename T>
[[nodiscard]] constexpr Mat<R, K, T> operator*( [[nodiscard]] constexpr Mat<R, K, T> operator*(
Mat<R, C, T> const &a, Mat<C, K, T> const &b) noexcept Mat<R, C, T> const &a, Mat<C, K, T> const &b) noexcept
@@ -955,7 +1125,10 @@ template<std::size_t R, std::size_t C, std::size_t K, typename T>
return out; return out;
} }
// Mat3 transformations /** @name 2D transforms (Mat3)
* @{ */
/** @brief Applies translation to an existing 3x3 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto translate(Mat<3, 3, T> const &m, Vec<2, T> const &v) [[nodiscard]] inline auto translate(Mat<3, 3, T> const &m, Vec<2, T> const &v)
-> Mat<3, 3, T> -> Mat<3, 3, T>
@@ -965,6 +1138,7 @@ template<typename T>
return res; return res;
} }
/** @brief Creates a 3x3 translation matrix. */
template<typename T> template<typename T>
[[nodiscard]] inline auto translate(Vec<2, T> const &v) -> Mat<3, 3, T> [[nodiscard]] inline auto translate(Vec<2, T> const &v) -> Mat<3, 3, T>
{ {
@@ -974,6 +1148,7 @@ template<typename T>
return res; return res;
} }
/** @brief Applies rotation to an existing 3x3 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto rotate(Mat<3, 3, T> const &m, T const angle) [[nodiscard]] inline auto rotate(Mat<3, 3, T> const &m, T const angle)
-> Mat<3, 3, T> -> Mat<3, 3, T>
@@ -990,6 +1165,7 @@ template<typename T>
return res; return res;
} }
/** @brief Applies scaling to an existing 3x3 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto scale(Mat<3, 3, T> const &m, Vec<2, T> const &v) [[nodiscard]] inline auto scale(Mat<3, 3, T> const &m, Vec<2, T> const &v)
-> Mat<3, 3, T> -> Mat<3, 3, T>
@@ -1003,6 +1179,7 @@ template<typename T>
return res; return res;
} }
/** @brief Applies X shear to an existing 3x3 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto shear_x(Mat<3, 3, T> const &m, T const v) [[nodiscard]] inline auto shear_x(Mat<3, 3, T> const &m, T const v)
-> Mat<3, 3, T> -> Mat<3, 3, T>
@@ -1012,6 +1189,7 @@ template<typename T>
return m * res; return m * res;
} }
/** @brief Applies Y shear to an existing 3x3 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto shear_y(Mat<3, 3, T> const &m, T const v) [[nodiscard]] inline auto shear_y(Mat<3, 3, T> const &m, T const v)
-> Mat<3, 3, T> -> Mat<3, 3, T>
@@ -1021,7 +1199,12 @@ template<typename T>
return m * res; return m * res;
} }
// Mat4 transformations /** @} */
/** @name 3D transforms (Mat4)
* @{ */
/** @brief Applies translation to an existing 4x4 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto translate(Mat<4, 4, T> const &m, Vec<3, T> const &v) [[nodiscard]] inline auto translate(Mat<4, 4, T> const &m, Vec<3, T> const &v)
-> Mat<4, 4, T> -> Mat<4, 4, T>
@@ -1031,6 +1214,7 @@ template<typename T>
return res; return res;
} }
/** @brief Creates a 4x4 translation matrix. */
template<typename T> template<typename T>
[[nodiscard]] inline auto translate(Vec<3, T> const &v) -> Mat<4, 4, T> [[nodiscard]] inline auto translate(Vec<3, T> const &v) -> Mat<4, 4, T>
{ {
@@ -1041,6 +1225,7 @@ template<typename T>
return res; return res;
} }
/** @brief Applies rotation to an existing 4x4 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto rotate(Mat<4, 4, T> const &m, T const angle) [[nodiscard]] inline auto rotate(Mat<4, 4, T> const &m, T const angle)
-> Mat<4, 4, T> -> Mat<4, 4, T>
@@ -1058,6 +1243,7 @@ template<typename T>
return res; return res;
} }
/** @brief Applies scaling to an existing 4x4 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto scale(Mat<4, 4, T> const &m, Vec<3, T> const &v) [[nodiscard]] inline auto scale(Mat<4, 4, T> const &m, Vec<3, T> const &v)
-> Mat<4, 4, T> -> Mat<4, 4, T>
@@ -1072,6 +1258,7 @@ template<typename T>
return res; return res;
} }
/** @brief Applies X shear to an existing 4x4 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto shear_x(Mat<4, 4, T> const &m, T const v) [[nodiscard]] inline auto shear_x(Mat<4, 4, T> const &m, T const v)
-> Mat<4, 4, T> -> Mat<4, 4, T>
@@ -1081,6 +1268,7 @@ template<typename T>
return m * res; return m * res;
} }
/** @brief Applies Y shear to an existing 4x4 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto shear_y(Mat<4, 4, T> const &m, T const v) [[nodiscard]] inline auto shear_y(Mat<4, 4, T> const &m, T const v)
-> Mat<4, 4, T> -> Mat<4, 4, T>
@@ -1090,6 +1278,7 @@ template<typename T>
return m * res; return m * res;
} }
/** @brief Applies Z shear to an existing 4x4 transform. */
template<typename T> template<typename T>
[[nodiscard]] inline auto shear_z(Mat<4, 4, T> const &m, T const v) [[nodiscard]] inline auto shear_z(Mat<4, 4, T> const &m, T const v)
-> Mat<4, 4, T> -> Mat<4, 4, T>
@@ -1099,9 +1288,24 @@ template<typename T>
return m * res; return m * res;
} }
/**
* @brief Builds an orthographic projection matrix.
* @param left Left clipping plane.
* @param right Right clipping plane.
* @param bottom Bottom clipping plane.
* @param top Top clipping plane.
* @param near Near clipping plane.
* @param far Far clipping plane.
* @param flip_z_axis Whether to flip the output Z axis convention.
* @return Orthographic projection matrix.
*/
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 {};
@@ -1121,6 +1325,9 @@ template<typename T>
return res; return res;
} }
/**
* @brief Builds a finite perspective projection matrix.
*/
template<typename T> template<typename T>
inline auto matrix_perspective( inline auto matrix_perspective(
T fovy, T aspect, T znear, T zfar, bool flip_z_axis = false) -> Mat<4, 4, T> T fovy, T aspect, T znear, T zfar, bool flip_z_axis = false) -> Mat<4, 4, T>
@@ -1147,9 +1354,13 @@ inline auto matrix_perspective(
return m; return m;
} }
/**
* @brief Builds a right-handed look-at view matrix.
*/
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();
@@ -1163,9 +1374,13 @@ template<typename T>
}; };
} }
/**
* @brief Builds a perspective matrix with an infinite far plane.
*/
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,11 +1401,38 @@ template<typename T>
return m; return m;
} }
/** @} */
/**
* @brief Extension point for external math-type interoperability.
*/
template<class Ext> struct interop_adapter;
/**
* @brief Converts an external value into its mapped smath type.
*/
template<class Ext>
constexpr auto from_external(Ext const &ext) ->
typename interop_adapter<Ext>::smath_type
{
return interop_adapter<Ext>::to_smath(ext);
}
/**
* @brief Converts a smath value into an external type.
*/
template<class Ext, class SMathT>
constexpr auto to_external(SMathT const &value) -> Ext
{
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);
@@ -1213,12 +1455,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;
}; };
+190
View File
@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1280"
height="640"
viewBox="0 0 1280.0001 640.00006"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="linearGradient13">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop12" />
<stop
style="stop-color:#16d800;stop-opacity:1;"
offset="1"
id="stop13" />
</linearGradient>
<linearGradient
id="linearGradient10">
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="0"
id="stop10" />
<stop
style="stop-color:#25ff0c;stop-opacity:1;"
offset="1"
id="stop11" />
</linearGradient>
<linearGradient
id="linearGradient8">
<stop
style="stop-color:#c50000;stop-opacity:1;"
offset="0"
id="stop8" />
<stop
style="stop-color:#ff0c0c;stop-opacity:1;"
offset="1"
id="stop9" />
</linearGradient>
<linearGradient
id="linearGradient5">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5" />
<stop
style="stop-color:#969696;stop-opacity:1;"
offset="1"
id="stop6" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient5"
id="linearGradient6-2"
x1="482.5849"
y1="589.83472"
x2="613.15253"
y2="-24.437733"
gradientUnits="userSpaceOnUse" />
<rect
x="227.90907"
y="175.81558"
width="659.85107"
height="227.90907"
id="rect1-5" />
<linearGradient
xlink:href="#linearGradient10"
id="linearGradient11-0"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-45"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-2" />
</marker>
<linearGradient
xlink:href="#linearGradient8"
id="linearGradient9-6"
x1="107.55984"
y1="103.63171"
x2="51.891579"
y2="103.63171"
gradientUnits="userSpaceOnUse" />
<marker
style="overflow:visible"
id="RoundedArrow-4-1"
refX="0"
refY="0"
orient="auto-start-reverse"
markerWidth="1"
markerHeight="1"
viewBox="0 0 1 1"
preserveAspectRatio="xMidYMid">
<path
transform="scale(0.7)"
d="m -0.21114562,-4.1055728 6.42229122,3.21114561 a 1,1 90 0 1 0,1.78885438 L -0.21114562,4.1055728 A 1.236068,1.236068 31.717474 0 1 -2,3 v -6 a 1.236068,1.236068 148.28253 0 1 1.78885438,-1.1055728 z"
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
id="path8-3-1" />
</marker>
<linearGradient
xlink:href="#linearGradient13"
id="linearGradient16"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.93927134,0,6.2326702)"
x1="50.894115"
y1="80.727814"
x2="50.894115"
y2="68.866821" />
</defs>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="rect16"
width="1280.0001"
height="640"
x="1.1474608e-06"
y="2.8421709e-14" />
<g
id="layer1"
transform="translate(-419.99999)">
<g
id="g13-2"
transform="matrix(4.6783888,0,0,4.6783888,411.42933,-46.143325)">
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,2.4199432,-2.3937254)"
id="text1-0"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1-5);display:inline;fill:url(#linearGradient6-2);stroke-width:7.55906;stroke-dasharray:none"><tspan
x="227.9082"
y="353.41641"
id="tspan1">SMATH</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#000000;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446"
id="text2-1"><tspan
id="tspan2-24"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.37934px;font-family:'Shree Devanagari 714';-inkscape-font-specification:'Shree Devanagari 714, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:2;stroke-dasharray:none"
x="110.62661"
y="105.75446">Linear algebra library for C++23</tspan></text>
<path
style="fill:#000000;stroke:url(#linearGradient11-0);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-45)"
d="M 50.894116,102.63145 V 56.432683"
id="path4-5" />
<path
style="fill:#000000;stroke:url(#linearGradient16);stroke-width:1.93832;stroke-dasharray:none;stroke-opacity:1"
d="M 50.894116,102.63145 V 59.238272"
id="path4-0-5" />
<path
style="fill:none;stroke:url(#linearGradient9-6);stroke-width:2;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#RoundedArrow-4-1)"
d="m 51.891583,103.63172 h 46.19876"
id="path4-9-23" />
<path
style="fill:#ff0c0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 51.891583,104.63115 v -1.9997 l -1.998196,1.99844 z"
id="path5-2" />
<path
style="fill:#25ff0c;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
d="m 49.895018,102.63082 v 1.9997 l 1.998196,-1.99844 z"
id="path5-8-3" />
<circle
style="fill:#1162ff;fill-opacity:1;stroke:none;stroke-width:2;stroke-dasharray:none;stroke-opacity:1"
id="path13-1"
cx="50.894115"
cy="103.6313"
r="3.4155982" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

-1
View File
@@ -5,4 +5,3 @@ target_sources(smath_modules PUBLIC FILE_SET CXX_MODULES FILES smath.cppm)
target_link_libraries(smath_modules PUBLIC smath::smath) target_link_libraries(smath_modules PUBLIC smath::smath)
target_compile_features(smath_modules PUBLIC cxx_std_20) target_compile_features(smath_modules PUBLIC cxx_std_20)
+39 -38
View File
@@ -1,54 +1,55 @@
module; module;
#include "smath.hpp" #include "smath/smath.hpp"
export module smath; export module smath;
export namespace smath { export namespace smath
export using ::smath::Vec; {
export using ::smath::VecOrScalar; using ::smath::Mat;
export using ::smath::Quaternion; using ::smath::Quaternion;
export using ::smath::Mat; using ::smath::Vec;
using ::smath::VecOrScalar;
export using ::smath::Vec2; using ::smath::Vec2;
export using ::smath::Vec3; using ::smath::Vec3;
export using ::smath::Vec4; using ::smath::Vec4;
export using ::smath::Vec2d; using ::smath::Vec2d;
export using ::smath::Vec3d; using ::smath::Vec3d;
export using ::smath::Vec4d; using ::smath::Vec4d;
export using ::smath::Mat2; using ::smath::Mat2;
export using ::smath::Mat3; using ::smath::Mat3;
export using ::smath::Mat4; using ::smath::Mat4;
export using ::smath::Mat2d; using ::smath::Mat2d;
export using ::smath::Mat3d; using ::smath::Mat3d;
export using ::smath::Mat4d; using ::smath::Mat4d;
export using ::smath::swizzle; using ::smath::swizzle;
export using ::smath::deg; using ::smath::deg;
export using ::smath::rad; using ::smath::rad;
export using ::smath::turns; using ::smath::turns;
export using ::smath::pack_unorm4x8; using ::smath::pack_snorm4x8;
export using ::smath::pack_snorm4x8; using ::smath::pack_unorm4x8;
export using ::smath::unpack_unorm4x8; using ::smath::unpack_snorm4x8;
export using ::smath::unpack_snorm4x8; using ::smath::unpack_unorm4x8;
export using ::smath::operator*; using ::smath::operator*;
export using ::smath::translate; using ::smath::rotate;
export using ::smath::rotate; using ::smath::scale;
export using ::smath::scale; using ::smath::translate;
export using ::smath::shear_x; using ::smath::shear_x;
export using ::smath::shear_y; using ::smath::shear_y;
export using ::smath::shear_z; using ::smath::shear_z;
export using ::smath::matrix_ortho3d; using ::smath::matrix_infinite_perspective;
export using ::smath::matrix_perspective; using ::smath::matrix_look_at;
export using ::smath::matrix_infinite_perspective; using ::smath::matrix_ortho3d;
export using ::smath::matrix_look_at; using ::smath::matrix_perspective;
} } // namespace smath
+10 -7
View File
@@ -1,15 +1,18 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <smath.hpp> #include <smath/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);
} }
+110
View File
@@ -0,0 +1,110 @@
#include <type_traits>
#include <gtest/gtest.h>
#include <smath/smath.hpp>
struct ExternalVec3f
{
float x;
float y;
float z;
};
struct ExternalMat2f
{
float m00;
float m01;
float m10;
float m11;
};
namespace smath
{
template<> struct interop_adapter<ExternalVec3f>
{
using smath_type = Vec<3, float>;
static constexpr auto to_smath(ExternalVec3f const &v) -> smath_type
{
return { v.x, v.y, v.z };
}
static constexpr auto from_smath(smath_type const &v) -> ExternalVec3f
{
return { v[0], v[1], v[2] };
}
};
template<> struct interop_adapter<ExternalMat2f>
{
using smath_type = Mat<2, 2, float>;
static constexpr auto to_smath(ExternalMat2f const &m) -> smath_type
{
smath_type out {};
out(0, 0) = m.m00;
out(0, 1) = m.m01;
out(1, 0) = m.m10;
out(1, 1) = m.m11;
return out;
}
static constexpr auto from_smath(smath_type const &m) -> ExternalMat2f
{
return { m(0, 0), m(0, 1), m(1, 0), m(1, 1) };
}
};
} // namespace smath
TEST(Interop, FromExternalVec)
{
ExternalVec3f ext { 1.0f, 2.0f, 3.0f };
auto v = smath::from_external(ext);
static_assert(std::is_same_v<decltype(v), smath::Vec<3, float>>);
EXPECT_EQ(v[0], 1.0f);
EXPECT_EQ(v[1], 2.0f);
EXPECT_EQ(v[2], 3.0f);
}
TEST(Interop, ToExternalVec)
{
smath::Vec3 v { 4.0f, 5.0f, 6.0f };
auto ext = smath::to_external<ExternalVec3f>(v);
EXPECT_EQ(ext.x, 4.0f);
EXPECT_EQ(ext.y, 5.0f);
EXPECT_EQ(ext.z, 6.0f);
}
TEST(Interop, RoundtripVec)
{
ExternalVec3f ext { 7.0f, 8.0f, 9.0f };
auto v = smath::from_external(ext);
auto ext2 = smath::to_external<ExternalVec3f>(v);
EXPECT_EQ(ext2.x, 7.0f);
EXPECT_EQ(ext2.y, 8.0f);
EXPECT_EQ(ext2.z, 9.0f);
}
TEST(Interop, MatrixConversion)
{
ExternalMat2f ext { 1.0f, 2.0f, 3.0f, 4.0f };
auto m = smath::from_external(ext);
static_assert(std::is_same_v<decltype(m), smath::Mat<2, 2, float>>);
EXPECT_EQ(m(0, 0), 1.0f);
EXPECT_EQ(m(0, 1), 2.0f);
EXPECT_EQ(m(1, 0), 3.0f);
EXPECT_EQ(m(1, 1), 4.0f);
auto ext2 = smath::to_external<ExternalMat2f>(m);
EXPECT_EQ(ext2.m00, 1.0f);
EXPECT_EQ(ext2.m01, 2.0f);
EXPECT_EQ(ext2.m10, 3.0f);
EXPECT_EQ(ext2.m11, 4.0f);
}
+135 -119
View File
@@ -4,176 +4,192 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <smath.hpp> #include <smath/smath.hpp>
using smath::Vec; using smath::Vec;
using smath::Vec2; 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 });
ExpectVecNear(ns, 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);
} }