Compare commits

..

8 Commits

Author SHA1 Message Date
fee135d82e added vscode config 2026-02-12 23:13:37 +03:00
e8c7abf925 fix 2026-02-08 20:57:10 +03:00
6ae3e37172 Merge pull request #148 from orange-cpp/feature/engine-units-to-metric
Feature/engine units to metric
2026-02-08 03:28:54 +03:00
afc613fcc0 added tests 2026-02-08 03:15:21 +03:00
d7a721f62e added frostbite tests 2026-02-08 03:03:23 +03:00
5aae9d6842 added for other engines 2026-02-08 02:58:59 +03:00
3e4598313d improved naming 2026-02-08 02:51:48 +03:00
d231139b83 added for source 2026-02-08 02:43:10 +03:00
26 changed files with 1004 additions and 1090 deletions

1
.gitignore vendored
View File

@@ -7,7 +7,6 @@
/clang-coverage/ /clang-coverage/
*.gcov *.gcov
*.bin *.bin
/site/
# pixi lock # pixi lock
pixi.lock pixi.lock
# pixi environments # pixi environments

24
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": " Unit Tests (Debug)",
"type": "lldb",
"request": "launch",
"program": "${workspaceRoot}/out/Debug/unit_tests",
"args": [],
"cwd": "${workspaceRoot}"
},
{
"name": " Unit Tests (Release)",
"type": "lldb",
"request": "launch",
"program": "${workspaceRoot}/out/Release/unit_tests",
"args": [],
"cwd": "${workspaceRoot}"
}
]
}

View File

@@ -1,93 +0,0 @@
# `omath::collision::ColliderInterface` — Abstract collider base class
> Header: `omath/collision/collider_interface.hpp`
> Namespace: `omath::collision`
> Depends on: `omath/linear_algebra/vector3.hpp`
`ColliderInterface` is the abstract base class for all colliders used by the GJK and EPA algorithms. Any shape that can report its furthest vertex along a given direction can implement this interface and be used for collision detection.
---
## API
```cpp
namespace omath::collision {
template<class VecType = Vector3<float>>
class ColliderInterface {
public:
using VectorType = VecType;
virtual ~ColliderInterface() = default;
// Return the world-space position of the vertex furthest along `direction`.
[[nodiscard]]
virtual VectorType find_abs_furthest_vertex_position(
const VectorType& direction) const = 0;
// Get the collider's origin (center / position).
[[nodiscard]]
virtual const VectorType& get_origin() const = 0;
// Reposition the collider.
virtual void set_origin(const VectorType& new_origin) = 0;
};
} // namespace omath::collision
```
---
## Implementing a custom collider
To create a new collider shape, derive from `ColliderInterface` and implement the three pure-virtual methods:
```cpp
#include "omath/collision/collider_interface.hpp"
class SphereCollider final
: public omath::collision::ColliderInterface<omath::Vector3<float>>
{
public:
SphereCollider(omath::Vector3<float> center, float radius)
: m_center(center), m_radius(radius) {}
[[nodiscard]]
omath::Vector3<float> find_abs_furthest_vertex_position(
const omath::Vector3<float>& direction) const override
{
return m_center + direction.normalized() * m_radius;
}
[[nodiscard]]
const omath::Vector3<float>& get_origin() const override
{ return m_center; }
void set_origin(const omath::Vector3<float>& new_origin) override
{ m_center = new_origin; }
private:
omath::Vector3<float> m_center;
float m_radius;
};
```
---
## Notes
* **Template parameter**: The default vector type is `Vector3<float>`, but any vector type with a `dot()` method can be used.
* **GJK/EPA compatibility**: Both `GjkAlgorithm` and `EpaAlgorithm` accept any type satisfying the `ColliderInterface` contract through their template parameters.
---
## See also
* [GJK Algorithm](gjk_algorithm.md) — collision detection using GJK.
* [EPA Algorithm](epa_algorithm.md) — penetration depth via EPA.
* [Mesh Collider](mesh_collider.md) — concrete collider wrapping a `Mesh`.
* [Simplex](simplex.md) — simplex data structure used by GJK/EPA.
---
*Last updated: Feb 2026*

View File

@@ -1,115 +0,0 @@
# `omath::EncryptedVariable` — Compile-time XOR-encrypted variable
> Header: `omath/containers/encrypted_variable.hpp`
> Namespace: `omath`
> Depends on: `<array>`, `<cstddef>`, `<cstdint>`, `<span>`
`EncryptedVariable` keeps a value XOR-encrypted in memory at rest, using a **compile-time generated random key**. It is designed to hinder static analysis and memory scanners from reading sensitive values (e.g., game constants, keys, thresholds) directly from process memory.
---
## Key concepts
* **Compile-time key generation** — a unique random byte array is produced at compile time via SplitMix64 + FNV-1a seeded from `__FILE__`, `__DATE__`, and `__TIME__`. Each `OMATH_DEF_CRYPT_VAR` expansion receives a distinct key.
* **XOR cipher** — `encrypt()` / `decrypt()` toggle the encrypted state by XOR-ing the raw bytes of the stored value with the key.
* **VarAnchor (RAII guard)** — `drop_anchor()` returns a `VarAnchor` that decrypts on construction and re-encrypts on destruction, ensuring the plaintext window is as short as possible.
---
## API
```cpp
namespace omath {
template<class T, std::size_t key_size, std::array<std::uint8_t, key_size> key>
class EncryptedVariable final {
public:
using value_type = std::remove_cvref_t<T>;
constexpr explicit EncryptedVariable(const value_type& data);
[[nodiscard]] constexpr bool is_encrypted() const;
constexpr void decrypt();
constexpr void encrypt();
[[nodiscard]] constexpr value_type& value();
[[nodiscard]] constexpr const value_type& value() const;
[[nodiscard]] constexpr auto drop_anchor(); // returns VarAnchor
constexpr ~EncryptedVariable(); // decrypts on destruction
};
template<class EncryptedVarType>
class VarAnchor final {
public:
constexpr VarAnchor(EncryptedVarType& var); // decrypts
constexpr ~VarAnchor(); // re-encrypts
};
} // namespace omath
```
### Helper macros
```cpp
// Generate a compile-time random byte array of length N
#define OMATH_CT_RAND_ARRAY_BYTE(N) /* ... */
// Declare a type alias for EncryptedVariable<TYPE> with KEY_SIZE random bytes
#define OMATH_DEF_CRYPT_VAR(TYPE, KEY_SIZE) /* ... */
```
---
## Usage examples
### Basic encrypt / decrypt
```cpp
#include "omath/containers/encrypted_variable.hpp"
// Define an encrypted float with a 16-byte key
using EncFloat = OMATH_DEF_CRYPT_VAR(float, 16);
EncFloat secret(3.14f); // encrypted immediately
// secret.value() is XOR-scrambled in memory
secret.decrypt();
float v = secret.value(); // v == 3.14f
secret.encrypt(); // scrambled again
```
### RAII guard with `drop_anchor()`
```cpp
EncFloat secret(42.0f);
{
auto anchor = secret.drop_anchor(); // decrypts
float val = secret.value(); // safe to read
// ... use val ...
} // anchor destroyed → re-encrypts automatically
```
---
## Notes & edge cases
* **Force-inline**: When `OMATH_ENABLE_FORCE_INLINE` is defined, encrypt/decrypt operations use compiler-specific force-inline attributes to reduce the call-site footprint visible in disassembly.
* **Not cryptographically secure**: XOR with a static key is an obfuscation technique, not encryption. It raises the bar for casual memory scanning but does not resist a determined attacker who can read the binary.
* **Destructor decrypts**: The destructor calls `decrypt()` so the value is in plaintext at the end of its lifetime (e.g., for logging or cleanup).
* **Thread safety**: No internal synchronization. Protect concurrent access with external locks.
* **`constexpr` support**: All operations are `constexpr`-friendly (C++20).
---
## See also
* [Pattern Scan](../utility/pattern_scan.md) — scan memory for byte patterns.
* [Getting Started](../getting_started.md) — quick start with OMath.
---
*Last updated: Feb 2026*

View File

@@ -1,76 +0,0 @@
/**
* Dynamic Liquid Glass — mouse-tracking specular highlight
*
* Creates a radial-gradient light spot that follows the cursor across
* glass-styled elements, giving them an interactive "liquid glass"
* refraction/reflection feel inspired by Apple's Liquid Glass design.
*/
(function () {
"use strict";
var SELECTORS = [
".md-header",
".md-content",
".md-sidebar__scrollwrap",
".highlight",
".md-search__form",
".md-footer"
];
/** Apply the radial highlight via CSS custom properties. */
function applyHighlight(el, x, y) {
var rect = el.getBoundingClientRect();
var px = x - rect.left;
var py = y - rect.top;
el.style.setProperty("--glass-x", px + "px");
el.style.setProperty("--glass-y", py + "px");
el.classList.add("glass-active");
}
function clearHighlight(el) {
el.classList.remove("glass-active");
}
/** Bind events once the DOM is ready. */
function init() {
var elements = [];
SELECTORS.forEach(function (sel) {
var nodes = document.querySelectorAll(sel);
for (var i = 0; i < nodes.length; i++) {
elements.push(nodes[i]);
}
});
var ticking = false;
document.addEventListener("mousemove", function (e) {
if (ticking) return;
ticking = true;
requestAnimationFrame(function () {
elements.forEach(function (el) {
var rect = el.getBoundingClientRect();
if (
e.clientX >= rect.left &&
e.clientX <= rect.right &&
e.clientY >= rect.top &&
e.clientY <= rect.bottom
) {
applyHighlight(el, e.clientX, e.clientY);
} else {
clearHighlight(el);
}
});
ticking = false;
});
});
document.addEventListener("mouseleave", function () {
elements.forEach(clearHighlight);
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();

View File

@@ -0,0 +1,11 @@
/* Widen the navbar container */
.navbar .container {
max-width: 100%; /* adjust to your target width */
width: 95%;
}
/* Tighter spacing between navbar items */
.navbar-nav > li > a {
padding-left: 8px;
padding-right: 8px;
}

View File

@@ -1,172 +0,0 @@
/* cyrillic-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19-7DRs5.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19a7DRs5.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1967DRs5.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19G7DRs5.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1927DRs5.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19y7DRs5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19K7DQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19-7DRs5.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19a7DRs5.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1967DRs5.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19G7DRs5.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1927DRs5.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19y7DRs5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19K7DQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19-7DRs5.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19a7DRs5.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1967DRs5.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19G7DRs5.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1927DRs5.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19y7DRs5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19K7DQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
body {
font-family: 'Roboto Condensed', sans-serif;
}

65
docs/styles/links.css Normal file
View File

@@ -0,0 +1,65 @@
/* Normal links */
a {
color: orange;
}
/* On hover/focus */
a:hover,
a:focus {
color: #ff9900; /* a slightly different orange, optional */
}
/* Navbar background */
.navbar,
.navbar-default,
.navbar-inverse {
background-color: #a26228 !important; /* your orange */
border-color: #ff6600 !important;
}
/* Navbar brand + links */
.navbar .navbar-brand,
.navbar .navbar-nav > li > a {
color: #ffffff !important;
}
/* Active and hover states */
.navbar .navbar-nav > .active > a,
.navbar .navbar-nav > .active > a:focus,
.navbar .navbar-nav > .active > a:hover,
.navbar .navbar-nav > li > a:hover,
.navbar .navbar-nav > li > a:focus {
color: #ffffff !important;
}
/* === DROPDOWN MENU BACKGROUND === */
.navbar .dropdown-menu {
border-color: #ff6600 !important;
}
/* Caret icon (the little triangle) */
.navbar .dropdown-toggle .caret {
border-top-color: #ffffff !important;
border-bottom-color: #ffffff !important;
}
/* === BOOTSTRAP 3 STYLE ITEMS (mkdocs + bootswatch darkly often use this) === */
.navbar .dropdown-menu > li > a {
color: #ffffff !important;
}
.navbar .dropdown-menu > li > a:hover,
.navbar .dropdown-menu > li > a:focus {
background-color: #e65c00 !important; /* darker orange on hover */
color: #ffffff !important;
}
/* === BOOTSTRAP 4+ STYLE ITEMS (if your theme uses .dropdown-item) === */
.navbar .dropdown-item {
color: #ffffff !important;
}
.navbar .dropdown-item:hover,
.navbar .dropdown-item:focus {
background-color: #e65c00 !important;
color: #ffffff !important;
}

View File

@@ -1,271 +0,0 @@
/* ============================================================
Apple Liquid Glass Design — glassmorphism overrides
for Material for MkDocs (slate / dark mode)
============================================================ */
/* ---------- shared glass mixin values ---------- */
:root {
--glass-bg: rgba(255, 255, 255, 0.04);
--glass-bg-hover: rgba(255, 255, 255, 0.07);
--glass-border: rgba(255, 255, 255, 0.08);
--glass-border-strong: rgba(255, 255, 255, 0.12);
--glass-blur: 16px;
--glass-radius: 14px;
--glass-shadow: 0 4px 24px rgba(0, 0, 0, 0.25);
--glass-shadow-sm: 0 2px 12px rgba(0, 0, 0, 0.18);
--glass-accent: rgba(255, 152, 0, 0.12);
}
/* ---------- header / top-bar ---------- */
.md-header {
background: rgba(30, 30, 30, 0.55) !important;
-webkit-backdrop-filter: saturate(180%) blur(var(--glass-blur));
backdrop-filter: saturate(180%) blur(var(--glass-blur));
border-bottom: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow-sm);
}
/* ---------- navigation sidebar ---------- */
.md-sidebar {
background: transparent !important;
}
.md-sidebar__scrollwrap {
background: var(--glass-bg);
-webkit-backdrop-filter: saturate(160%) blur(var(--glass-blur));
backdrop-filter: saturate(160%) blur(var(--glass-blur));
border-right: 1px solid var(--glass-border);
border-radius: 0 var(--glass-radius) var(--glass-radius) 0;
}
/* Remove the gradient mask at the top of the table of contents */
.md-sidebar__scrollwrap {
mask-image: none !important;
-webkit-mask-image: none !important;
}
/* active nav item — subtle glass highlight */
.md-nav__item--active > .md-nav__link {
background: var(--glass-accent) !important;
border-radius: 8px;
}
/* ---------- content area ---------- */
.md-main__inner {
background: transparent;
}
.md-content {
background: var(--glass-bg);
-webkit-backdrop-filter: saturate(140%) blur(12px);
backdrop-filter: saturate(140%) blur(12px);
border: 1px solid var(--glass-border);
border-radius: var(--glass-radius);
box-shadow: var(--glass-shadow);
margin: 12px 0;
padding: 4px;
}
/* ---------- code blocks ---------- */
.highlight {
background: rgba(0, 0, 0, 0.25) !important;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border-strong);
border-radius: 12px !important;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04),
var(--glass-shadow-sm);
overflow: hidden;
}
.highlight code {
background: transparent !important;
}
/* inline code */
:not(pre) > code {
background: rgba(255, 255, 255, 0.06) !important;
border: 1px solid var(--glass-border);
border-radius: 6px;
padding: 2px 6px;
}
/* ---------- tables ---------- */
.md-typeset table:not([class]) {
background: var(--glass-bg);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 12px;
overflow: hidden;
box-shadow: var(--glass-shadow-sm);
}
.md-typeset table:not([class]) th {
background: rgba(255, 152, 0, 0.08);
}
/* ---------- admonitions / call-outs ---------- */
.md-typeset .admonition,
.md-typeset details {
background: var(--glass-bg) !important;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border-strong) !important;
border-radius: 12px !important;
box-shadow: var(--glass-shadow-sm);
}
/* ---------- tabs ---------- */
.md-typeset .tabbed-set {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: 12px;
overflow: hidden;
}
/* ---------- search bar ---------- */
.md-search__form {
background: rgba(255, 255, 255, 0.06) !important;
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
border: 1px solid var(--glass-border);
border-radius: 10px !important;
}
/* search results / output — liquid glass */
.md-search__output {
background: rgba(30, 30, 30, 0.70) !important;
-webkit-backdrop-filter: saturate(160%) blur(var(--glass-blur));
backdrop-filter: saturate(160%) blur(var(--glass-blur));
border: 1px solid var(--glass-border);
border-radius: 0 0 var(--glass-radius) var(--glass-radius);
box-shadow: var(--glass-shadow);
}
.md-search-result__link {
border-radius: 8px;
transition: background 0.2s ease;
}
.md-search-result__link:hover {
background: var(--glass-bg-hover) !important;
}
/* ---------- footer ---------- */
.md-footer {
background: rgba(30, 30, 30, 0.50) !important;
-webkit-backdrop-filter: saturate(180%) blur(var(--glass-blur));
backdrop-filter: saturate(180%) blur(var(--glass-blur));
border-top: 1px solid var(--glass-border);
}
/* ---------- horizontal rules — subtle glow ---------- */
.md-typeset hr {
border-image: linear-gradient(
to right,
transparent,
rgba(255, 152, 0, 0.25),
transparent
) 1;
}
/* ---------- scrollbar ---------- */
* {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.10) transparent;
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.10);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.18);
}
/* ---------- links — subtle glass-glow on hover ---------- */
.md-typeset a:hover {
text-shadow: 0 0 8px rgba(255, 152, 0, 0.3);
}
/* ---------- images — soft glass frame ---------- */
.md-typeset img {
border-radius: 10px;
}
/* ============================================================
Dynamic Liquid Glass — mouse-tracking specular highlight
============================================================ */
/* Shared: every glass surface gets a hidden radial-light overlay
that becomes visible when JS adds the .glass-active class and
sets --glass-x / --glass-y custom properties. */
.md-header,
.md-content,
.md-sidebar__scrollwrap,
.highlight,
.md-search__form,
.md-footer {
position: relative;
overflow: hidden;
}
.md-header::after,
.md-content::after,
.md-sidebar__scrollwrap::after,
.highlight::after,
.md-search__form::after,
.md-footer::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
background: radial-gradient(
circle 220px at var(--glass-x, 50%) var(--glass-y, 50%),
rgba(255, 200, 120, 0.10) 0%,
rgba(255, 152, 0, 0.04) 40%,
transparent 70%
);
z-index: 1;
}
.md-header.glass-active::after,
.md-content.glass-active::after,
.md-sidebar__scrollwrap.glass-active::after,
.highlight.glass-active::after,
.md-search__form.glass-active::after,
.md-footer.glass-active::after {
opacity: 1;
}
/* Keep header text / nav icons above the overlay */
.md-header > *,
.md-content > *,
.md-sidebar__scrollwrap > *,
.md-search__form > *,
.md-footer > * {
position: relative;
z-index: 2;
}
/* Highlight code blocks get a slightly brighter spot */
.highlight.glass-active::after {
background: radial-gradient(
circle 180px at var(--glass-x, 50%) var(--glass-y, 50%),
rgba(255, 200, 120, 0.12) 0%,
rgba(255, 152, 0, 0.05) 35%,
transparent 65%
);
}

View File

@@ -1,142 +0,0 @@
# `omath::ElfPatternScanner` — Scan ELF binaries for byte patterns
> Header: `omath/utility/elf_pattern_scan.hpp`
> Namespace: `omath`
> Platform: **Linux / ELF (Executable and Linkable Format) binaries**
> Depends on: `<cstdint>`, `<filesystem>`, `<optional>`, `<string_view>`, `omath/utility/section_scan_result.hpp`
> Companion: works well with `omath::PatternScanner` (same pattern grammar)
`ElfPatternScanner` searches **ELF** binaries for a hex pattern (with wildcards). You can scan:
* a **loaded module** in the current process, or
* an **ELF file on disk** (by section name; defaults to **`.text`**).
---
## Pattern string grammar (same as `PatternScanner`)
* **Hex byte**: two hex digits → one byte (`90`, `4F`, `00`, `ff`).
* **Wildcard byte**: `?` or `??` matches **any byte**.
* **Whitespace**: ignored (use to group tokens).
✔️ `"48 8B ?? ?? 89"`, `"55 48 89 E5"`, `"??"`
❌ odd digit counts, non-hex characters (besides `?` and whitespace)
---
## API
```cpp
namespace omath {
class ElfPatternScanner final {
public:
// Scan a module already loaded in *this* process.
// module_base_address: base address of the loaded ELF (e.g., from dlopen / /proc/self/maps)
// Returns absolute address (process VA) of the first match, or nullopt.
static std::optional<std::uintptr_t>
scan_for_pattern_in_loaded_module(
const void* module_base_address,
const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
// Scan an ELF file on disk, by section name (default ".text").
// Returns section bases (virtual + raw) and match offset within the section, or nullopt.
static std::optional<SectionScanResult>
scan_for_pattern_in_file(
const std::filesystem::path& path_to_file,
const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
};
} // namespace omath
```
---
## Return values
* **Loaded module**: `std::optional<std::uintptr_t>`
* `value()` = **process virtual address** of the first match.
* `nullopt` = no match or parse/ELF error.
* **File scan**: `std::optional<SectionScanResult>`
* `virtual_base_addr` = virtual address base of the scanned section.
* `raw_base_addr` = file offset of section start.
* `target_offset` = offset from section base to the first matched byte.
* To get addresses:
* **Virtual address** of hit = `virtual_base_addr + target_offset`
* **Raw file offset** of hit = `raw_base_addr + target_offset`
---
## Usage examples
### Scan a loaded module (current process)
```cpp
#include <dlfcn.h>
#include "omath/utility/elf_pattern_scan.hpp"
using omath::ElfPatternScanner;
void* handle = dlopen("libexample.so", RTLD_LAZY);
if (handle) {
auto addr = ElfPatternScanner::scan_for_pattern_in_loaded_module(
handle, "55 48 89 E5 ?? ?? 48"
);
if (addr) {
std::uintptr_t hit_va = *addr;
// ...
}
dlclose(handle);
}
```
### Scan an ELF file on disk
```cpp
#include "omath/utility/elf_pattern_scan.hpp"
using omath::ElfPatternScanner;
auto res = ElfPatternScanner::scan_for_pattern_in_file(
"/usr/lib/libexample.so", "55 48 89 E5"
);
if (res) {
auto va_hit = res->virtual_base_addr + res->target_offset;
auto raw_hit = res->raw_base_addr + res->target_offset;
}
```
### Scan another section (e.g., ".rodata")
```cpp
auto res = ElfPatternScanner::scan_for_pattern_in_file(
"myapp", "48 8D 0D ?? ?? ?? ??", ".rodata"
);
```
---
## Notes & edge cases
* **ELF only**: these functions assume a valid ELF layout. Non-ELF files or corrupted headers yield `nullopt`.
* **Section name**: defaults to **`.text`**; pass a different name to target other sections.
* **Performance**: Pattern matching is **O(N × M)** (sliding window with wildcards). For large binaries, prefer scanning only necessary sections.
* **Architecture**: works for 32-bit and 64-bit ELF binaries.
---
## See also
* [`omath::PatternScanner`](pattern_scan.md) — raw buffer/iterator scanning with the same pattern grammar.
* [`omath::PePatternScanner`](pe_pattern_scan.md) — PE (Windows) binary scanner.
* [`omath::MachOPatternScanner`](macho_pattern_scan.md) — Mach-O (macOS) binary scanner.
* [`omath::SectionScanResult`](section_scan_result.md) — return type for file-based scans.
---
*Last updated: Feb 2026*

View File

@@ -1,142 +0,0 @@
# `omath::MachOPatternScanner` — Scan Mach-O binaries for byte patterns
> Header: `omath/utility/macho_pattern_scan.hpp`
> Namespace: `omath`
> Platform: **macOS / Mach-O binaries**
> Depends on: `<cstdint>`, `<filesystem>`, `<optional>`, `<string_view>`, `omath/utility/section_scan_result.hpp`
> Companion: works well with `omath::PatternScanner` (same pattern grammar)
`MachOPatternScanner` searches **Mach-O** binaries for a hex pattern (with wildcards). You can scan:
* a **loaded module** in the current process, or
* a **Mach-O file on disk** (by section name; defaults to **`__text`**).
---
## Pattern string grammar (same as `PatternScanner`)
* **Hex byte**: two hex digits → one byte (`90`, `4F`, `00`, `ff`).
* **Wildcard byte**: `?` or `??` matches **any byte**.
* **Whitespace**: ignored (use to group tokens).
✔️ `"48 8B ?? ?? 89"`, `"55 48 89 E5"`, `"??"`
❌ odd digit counts, non-hex characters (besides `?` and whitespace)
---
## API
```cpp
namespace omath {
class MachOPatternScanner final {
public:
// Scan a module already loaded in *this* process.
// module_base_address: base address of the loaded Mach-O image
// Returns absolute address (process VA) of the first match, or nullopt.
static std::optional<std::uintptr_t>
scan_for_pattern_in_loaded_module(
const void* module_base_address,
const std::string_view& pattern,
const std::string_view& target_section_name = "__text");
// Scan a Mach-O file on disk, by section name (default "__text").
// Returns section bases (virtual + raw) and match offset within the section, or nullopt.
static std::optional<SectionScanResult>
scan_for_pattern_in_file(
const std::filesystem::path& path_to_file,
const std::string_view& pattern,
const std::string_view& target_section_name = "__text");
};
} // namespace omath
```
---
## Return values
* **Loaded module**: `std::optional<std::uintptr_t>`
* `value()` = **process virtual address** of the first match.
* `nullopt` = no match or parse/Mach-O error.
* **File scan**: `std::optional<SectionScanResult>`
* `virtual_base_addr` = virtual address base of the scanned section.
* `raw_base_addr` = file offset of section start.
* `target_offset` = offset from section base to the first matched byte.
* To get addresses:
* **Virtual address** of hit = `virtual_base_addr + target_offset`
* **Raw file offset** of hit = `raw_base_addr + target_offset`
---
## Usage examples
### Scan a loaded module (current process)
```cpp
#include <dlfcn.h>
#include "omath/utility/macho_pattern_scan.hpp"
using omath::MachOPatternScanner;
void* handle = dlopen("libexample.dylib", RTLD_LAZY);
if (handle) {
auto addr = MachOPatternScanner::scan_for_pattern_in_loaded_module(
handle, "55 48 89 E5 ?? ?? 48"
);
if (addr) {
std::uintptr_t hit_va = *addr;
// ...
}
dlclose(handle);
}
```
### Scan a Mach-O file on disk
```cpp
#include "omath/utility/macho_pattern_scan.hpp"
using omath::MachOPatternScanner;
auto res = MachOPatternScanner::scan_for_pattern_in_file(
"/usr/local/lib/libexample.dylib", "55 48 89 E5"
);
if (res) {
auto va_hit = res->virtual_base_addr + res->target_offset;
auto raw_hit = res->raw_base_addr + res->target_offset;
}
```
### Scan another section (e.g., "__cstring")
```cpp
auto res = MachOPatternScanner::scan_for_pattern_in_file(
"myapp", "48 8D 0D ?? ?? ?? ??", "__cstring"
);
```
---
## Notes & edge cases
* **Mach-O only**: these functions assume a valid Mach-O layout. Non-Mach-O files or corrupted headers yield `nullopt`.
* **Section name**: defaults to **`__text`** (note the double underscore, per Mach-O convention); pass a different name to target other sections.
* **Performance**: Pattern matching is **O(N × M)** (sliding window with wildcards). For large binaries, prefer scanning only necessary sections.
* **Architecture**: works for 64-bit Mach-O binaries (x86_64 and arm64).
---
## See also
* [`omath::PatternScanner`](pattern_scan.md) — raw buffer/iterator scanning with the same pattern grammar.
* [`omath::PePatternScanner`](pe_pattern_scan.md) — PE (Windows) binary scanner.
* [`omath::ElfPatternScanner`](elf_pattern_scan.md) — ELF (Linux) binary scanner.
* [`omath::SectionScanResult`](section_scan_result.md) — return type for file-based scans.
---
*Last updated: Feb 2026*

View File

@@ -1,58 +0,0 @@
# `omath::SectionScanResult` — File-based pattern scan result
> Header: `omath/utility/section_scan_result.hpp`
> Namespace: `omath`
> Depends on: `<cstddef>`, `<cstdint>`
`SectionScanResult` is the return type for file-based pattern scans across all binary formats (PE, ELF, Mach-O). It carries the section's virtual and raw base addresses together with the offset to the matched pattern.
---
## API
```cpp
namespace omath {
struct SectionScanResult final {
std::uintptr_t virtual_base_addr; // virtual address base of the scanned section
std::uintptr_t raw_base_addr; // file offset of the section start
std::ptrdiff_t target_offset; // offset from section base to the first matched byte
};
} // namespace omath
```
---
## Computing addresses from a result
```cpp
omath::SectionScanResult res = /* ... */;
// Virtual address of the match (as if the binary were loaded at its preferred base)
auto va_hit = res.virtual_base_addr + res.target_offset;
// Raw file offset of the match
auto raw_hit = res.raw_base_addr + res.target_offset;
```
---
## Notes
* `virtual_base_addr` is computed from the section header (RVA for PE, `sh_addr` for ELF, `addr` for Mach-O).
* `raw_base_addr` is the file offset where the section data begins on disk.
* `target_offset` is always relative to the section base — add it to either address to locate the match.
---
## See also
* [`omath::PePatternScanner`](pe_pattern_scan.md) — PE (Windows) binary scanner.
* [`omath::ElfPatternScanner`](elf_pattern_scan.md) — ELF (Linux) binary scanner.
* [`omath::MachOPatternScanner`](macho_pattern_scan.md) — Mach-O (macOS) binary scanner.
* [`omath::PatternScanner`](pattern_scan.md) — raw buffer/iterator scanning.
---
*Last updated: Feb 2026*

View File

@@ -29,7 +29,7 @@ set_target_properties(
find_package(OpenGL) find_package(OpenGL)
find_package(GLEW REQUIRED) find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(example_glfw3 PRIVATE omath::omath GLEW::GLEW glfw OpenGL::OpenGL) target_link_libraries(example_glfw3 PRIVATE omath::omath GLEW::GLEW glfw OpenGL::GL)
if(OMATH_ENABLE_VALGRIND) if(OMATH_ENABLE_VALGRIND)
omath_setup_valgrind(example_projection_matrix_builder) omath_setup_valgrind(example_projection_matrix_builder)

View File

@@ -23,4 +23,52 @@ namespace omath::frostbite_engine
[[nodiscard]] [[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
} // namespace omath::unity_engine
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::frostbite_engine

View File

@@ -23,4 +23,54 @@ namespace omath::iw_engine
[[nodiscard]] [[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return units * centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units_to_centimeters(units) / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return centimeters / centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return centimeters_to_units(meters * static_cast<FloatingType>(100));
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::iw_engine } // namespace omath::iw_engine

View File

@@ -4,7 +4,6 @@
#pragma once #pragma once
#include "omath/engines/opengl_engine/constants.hpp" #include "omath/engines/opengl_engine/constants.hpp"
namespace omath::opengl_engine namespace omath::opengl_engine
{ {
[[nodiscard]] [[nodiscard]]
@@ -23,4 +22,52 @@ namespace omath::opengl_engine
[[nodiscard]] [[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::opengl_engine } // namespace omath::opengl_engine

View File

@@ -22,4 +22,54 @@ namespace omath::source_engine
[[nodiscard]] [[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return units * centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units_to_centimeters(units) / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return centimeters / centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return centimeters_to_units(meters * static_cast<FloatingType>(100));
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::source_engine } // namespace omath::source_engine

View File

@@ -23,4 +23,52 @@ namespace omath::unity_engine
[[nodiscard]] [[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::unity_engine } // namespace omath::unity_engine

View File

@@ -23,4 +23,52 @@ namespace omath::unreal_engine
[[nodiscard]] [[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::unreal_engine } // namespace omath::unreal_engine

View File

@@ -1,19 +1,7 @@
site_name: OMATH Docs site_name: OMATH Docs
theme: theme:
name: material name: darkly
palette:
scheme: slate
primary: deep orange
accent: orange
font:
text: Roboto Condensed
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences
extra_css: extra_css:
- styles/fonts.css
- styles/center.css - styles/center.css
- styles/liquid-glass.css - styles/custom-header.css
extra_javascript: - styles/links.css
- javascripts/liquid-glass.js

View File

@@ -8,6 +8,125 @@
#include <print> #include <print>
#include <random> #include <random>
TEST(unit_test_frostbite_engine, UnitsToCentimeters_BasicValues)
{
EXPECT_FLOAT_EQ(omath::frostbite_engine::units_to_centimeters(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::frostbite_engine::units_to_centimeters(1.0f), 0.01f);
EXPECT_FLOAT_EQ(omath::frostbite_engine::units_to_centimeters(100.0f), 1.0f);
EXPECT_FLOAT_EQ(omath::frostbite_engine::units_to_centimeters(-250.0f), -2.5f);
}
TEST(unit_test_frostbite_engine, UnitsToMeters_BasicValues)
{
EXPECT_DOUBLE_EQ(omath::frostbite_engine::units_to_meters(0.0), 0.0);
EXPECT_DOUBLE_EQ(omath::frostbite_engine::units_to_meters(1.0), 1.0);
EXPECT_DOUBLE_EQ(omath::frostbite_engine::units_to_meters(123.456), 123.456);
EXPECT_DOUBLE_EQ(omath::frostbite_engine::units_to_meters(-42.0), -42.0);
}
TEST(unit_test_frostbite_engine, UnitsToKilometers_BasicValues)
{
EXPECT_NEAR(omath::frostbite_engine::units_to_kilometers(0.0), 0.0, 1e-15);
EXPECT_NEAR(omath::frostbite_engine::units_to_kilometers(1.0), 0.001, 1e-15);
EXPECT_NEAR(omath::frostbite_engine::units_to_kilometers(1000.0), 1.0, 1e-12);
EXPECT_NEAR(omath::frostbite_engine::units_to_kilometers(-2500.0), -2.5, 1e-12);
}
TEST(unit_test_frostbite_engine, CentimetersToUnits_BasicValues)
{
EXPECT_FLOAT_EQ(omath::frostbite_engine::centimeters_to_units(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::frostbite_engine::centimeters_to_units(0.01f), 1.0f);
EXPECT_FLOAT_EQ(omath::frostbite_engine::centimeters_to_units(1.0f), 100.0f);
EXPECT_FLOAT_EQ(omath::frostbite_engine::centimeters_to_units(-2.5f), -250.0f);
}
TEST(unit_test_frostbite_engine, MetersToUnits_BasicValues)
{
EXPECT_DOUBLE_EQ(omath::frostbite_engine::meters_to_units(0.0), 0.0);
EXPECT_DOUBLE_EQ(omath::frostbite_engine::meters_to_units(1.0), 1.0);
EXPECT_DOUBLE_EQ(omath::frostbite_engine::meters_to_units(123.456), 123.456);
EXPECT_DOUBLE_EQ(omath::frostbite_engine::meters_to_units(-42.0), -42.0);
}
TEST(unit_test_frostbite_engine, KilometersToUnits_BasicValues)
{
EXPECT_NEAR(omath::frostbite_engine::kilometers_to_units(0.0), 0.0, 1e-12);
EXPECT_NEAR(omath::frostbite_engine::kilometers_to_units(0.001), 1.0, 1e-12);
EXPECT_NEAR(omath::frostbite_engine::kilometers_to_units(1.0), 1000.0, 1e-9);
EXPECT_NEAR(omath::frostbite_engine::kilometers_to_units(-2.5), -2500.0, 1e-9);
}
TEST(unit_test_frostbite_engine, RoundTrip_UnitsCentimeters)
{
constexpr float units_f = 12345.678f;
const auto cm_f = omath::frostbite_engine::units_to_centimeters(units_f);
const auto units_f_back = omath::frostbite_engine::centimeters_to_units(cm_f);
EXPECT_NEAR(units_f_back, units_f, 1e-3f);
constexpr double units_d = -987654.321;
const auto cm_d = omath::frostbite_engine::units_to_centimeters(units_d);
const auto units_d_back = omath::frostbite_engine::centimeters_to_units(cm_d);
EXPECT_NEAR(units_d_back, units_d, 1e-9);
}
TEST(unit_test_frostbite_engine, RoundTrip_UnitsMeters)
{
constexpr float units_f = 5432.125f;
constexpr auto m_f = omath::frostbite_engine::units_to_meters(units_f);
constexpr auto units_f_back = omath::frostbite_engine::meters_to_units(m_f);
EXPECT_FLOAT_EQ(units_f_back, units_f);
constexpr double units_d = -123456.789;
constexpr auto m_d = omath::frostbite_engine::units_to_meters(units_d);
constexpr auto units_d_back = omath::frostbite_engine::meters_to_units(m_d);
EXPECT_DOUBLE_EQ(units_d_back, units_d);
}
TEST(unit_test_frostbite_engine, RoundTrip_UnitsKilometers)
{
constexpr float units_f = 100000.0f;
constexpr auto km_f = omath::frostbite_engine::units_to_kilometers(units_f);
constexpr auto units_f_back = omath::frostbite_engine::kilometers_to_units(km_f);
EXPECT_NEAR(units_f_back, units_f, 1e-2f);
constexpr double units_d = -7654321.123;
constexpr auto km_d = omath::frostbite_engine::units_to_kilometers(units_d);
constexpr auto units_d_back = omath::frostbite_engine::kilometers_to_units(km_d);
EXPECT_NEAR(units_d_back, units_d, 1e-6);
}
TEST(unit_test_frostbite_engine, ConversionChainConsistency)
{
const double units = 424242.42;
const auto cm_direct = omath::frostbite_engine::units_to_centimeters(units);
const auto cm_via_units = units / 100.0;
EXPECT_NEAR(cm_direct, cm_via_units, 1e-12);
const auto km_direct = omath::frostbite_engine::units_to_kilometers(units);
const auto km_via_meters = omath::frostbite_engine::units_to_meters(units) / 1000.0;
EXPECT_NEAR(km_direct, km_via_meters, 1e-12);
}
TEST(unit_test_frostbite_engine, SupportsFloatAndDouble)
{
static_assert(std::is_same_v<decltype(omath::frostbite_engine::units_to_centimeters(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::frostbite_engine::units_to_centimeters(1.0)), double>);
static_assert(std::is_same_v<decltype(omath::frostbite_engine::meters_to_units(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::frostbite_engine::kilometers_to_units(1.0)), double>);
}
TEST(unit_test_frostbite_engine, ConstexprConversions)
{
constexpr double units = 1000.0;
constexpr double cm = omath::frostbite_engine::units_to_centimeters(units);
constexpr double m = omath::frostbite_engine::units_to_meters(units);
constexpr double km = omath::frostbite_engine::units_to_kilometers(units);
static_assert(cm == 10.0, "units_to_centimeters constexpr failed");
static_assert(m == 1000.0, "units_to_meters constexpr failed");
static_assert(km == 1.0, "units_to_kilometers constexpr failed");
}
TEST(unit_test_frostbite_engine, ForwardVector) TEST(unit_test_frostbite_engine, ForwardVector)
{ {
const auto forward = omath::frostbite_engine::forward_vector({}); const auto forward = omath::frostbite_engine::forward_vector({});

View File

@@ -7,6 +7,125 @@
#include <omath/engines/opengl_engine/formulas.hpp> #include <omath/engines/opengl_engine/formulas.hpp>
#include <random> #include <random>
TEST(unit_test_opengl, UnitsToCentimeters_BasicValues)
{
EXPECT_FLOAT_EQ(omath::opengl_engine::units_to_centimeters(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::opengl_engine::units_to_centimeters(1.0f), 0.01f);
EXPECT_FLOAT_EQ(omath::opengl_engine::units_to_centimeters(100.0f), 1.0f);
EXPECT_FLOAT_EQ(omath::opengl_engine::units_to_centimeters(-250.0f), -2.5f);
}
TEST(unit_test_opengl, UnitsToMeters_BasicValues)
{
EXPECT_DOUBLE_EQ(omath::opengl_engine::units_to_meters(0.0), 0.0);
EXPECT_DOUBLE_EQ(omath::opengl_engine::units_to_meters(1.0), 1.0);
EXPECT_DOUBLE_EQ(omath::opengl_engine::units_to_meters(123.456), 123.456);
EXPECT_DOUBLE_EQ(omath::opengl_engine::units_to_meters(-42.0), -42.0);
}
TEST(unit_test_opengl, UnitsToKilometers_BasicValues)
{
EXPECT_NEAR(omath::opengl_engine::units_to_kilometers(0.0), 0.0, 1e-15);
EXPECT_NEAR(omath::opengl_engine::units_to_kilometers(1.0), 0.001, 1e-15);
EXPECT_NEAR(omath::opengl_engine::units_to_kilometers(1000.0), 1.0, 1e-12);
EXPECT_NEAR(omath::opengl_engine::units_to_kilometers(-2500.0), -2.5, 1e-12);
}
TEST(unit_test_opengl, CentimetersToUnits_BasicValues)
{
EXPECT_FLOAT_EQ(omath::opengl_engine::centimeters_to_units(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::opengl_engine::centimeters_to_units(0.01f), 1.0f);
EXPECT_FLOAT_EQ(omath::opengl_engine::centimeters_to_units(1.0f), 100.0f);
EXPECT_FLOAT_EQ(omath::opengl_engine::centimeters_to_units(-2.5f), -250.0f);
}
TEST(unit_test_opengl, MetersToUnits_BasicValues)
{
EXPECT_DOUBLE_EQ(omath::opengl_engine::meters_to_units(0.0), 0.0);
EXPECT_DOUBLE_EQ(omath::opengl_engine::meters_to_units(1.0), 1.0);
EXPECT_DOUBLE_EQ(omath::opengl_engine::meters_to_units(123.456), 123.456);
EXPECT_DOUBLE_EQ(omath::opengl_engine::meters_to_units(-42.0), -42.0);
}
TEST(unit_test_opengl, KilometersToUnits_BasicValues)
{
EXPECT_NEAR(omath::opengl_engine::kilometers_to_units(0.0), 0.0, 1e-12);
EXPECT_NEAR(omath::opengl_engine::kilometers_to_units(0.001), 1.0, 1e-12);
EXPECT_NEAR(omath::opengl_engine::kilometers_to_units(1.0), 1000.0, 1e-9);
EXPECT_NEAR(omath::opengl_engine::kilometers_to_units(-2.5), -2500.0, 1e-9);
}
TEST(unit_test_opengl, RoundTrip_UnitsCentimeters)
{
constexpr float units_f = 12345.678f;
const auto cm_f = omath::opengl_engine::units_to_centimeters(units_f);
const auto units_f_back = omath::opengl_engine::centimeters_to_units(cm_f);
EXPECT_NEAR(units_f_back, units_f, 1e-3f);
constexpr double units_d = -987654.321;
const auto cm_d = omath::opengl_engine::units_to_centimeters(units_d);
const auto units_d_back = omath::opengl_engine::centimeters_to_units(cm_d);
EXPECT_NEAR(units_d_back, units_d, 1e-9);
}
TEST(unit_test_opengl, RoundTrip_UnitsMeters)
{
constexpr float units_f = 5432.125f;
const auto m_f = omath::opengl_engine::units_to_meters(units_f);
const auto units_f_back = omath::opengl_engine::meters_to_units(m_f);
EXPECT_FLOAT_EQ(units_f_back, units_f);
constexpr double units_d = -123456.789;
const auto m_d = omath::opengl_engine::units_to_meters(units_d);
const auto units_d_back = omath::opengl_engine::meters_to_units(m_d);
EXPECT_DOUBLE_EQ(units_d_back, units_d);
}
TEST(unit_test_opengl, RoundTrip_UnitsKilometers)
{
constexpr float units_f = 100000.0f;
const auto km_f = omath::opengl_engine::units_to_kilometers(units_f);
const auto units_f_back = omath::opengl_engine::kilometers_to_units(km_f);
EXPECT_NEAR(units_f_back, units_f, 1e-2f);
constexpr double units_d = -7654321.123;
const auto km_d = omath::opengl_engine::units_to_kilometers(units_d);
const auto units_d_back = omath::opengl_engine::kilometers_to_units(km_d);
EXPECT_NEAR(units_d_back, units_d, 1e-6);
}
TEST(unit_test_opengl, ConversionChainConsistency)
{
const double units = 424242.42;
const auto cm_direct = omath::opengl_engine::units_to_centimeters(units);
const auto cm_via_units = units / 100.0;
EXPECT_NEAR(cm_direct, cm_via_units, 1e-12);
const auto km_direct = omath::opengl_engine::units_to_kilometers(units);
const auto km_via_meters = omath::opengl_engine::units_to_meters(units) / 1000.0;
EXPECT_NEAR(km_direct, km_via_meters, 1e-12);
}
TEST(unit_test_opengl, SupportsFloatAndDouble)
{
static_assert(std::is_same_v<decltype(omath::opengl_engine::units_to_centimeters(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::opengl_engine::units_to_centimeters(1.0)), double>);
static_assert(std::is_same_v<decltype(omath::opengl_engine::meters_to_units(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::opengl_engine::kilometers_to_units(1.0)), double>);
}
TEST(unit_test_opengl, ConstexprConversions)
{
constexpr double units = 1000.0;
constexpr double cm = omath::opengl_engine::units_to_centimeters(units);
constexpr double m = omath::opengl_engine::units_to_meters(units);
constexpr double km = omath::opengl_engine::units_to_kilometers(units);
static_assert(cm == 10.0, "units_to_centimeters constexpr failed");
static_assert(m == 1000.0, "units_to_meters constexpr failed");
static_assert(km == 1.0, "units_to_kilometers constexpr failed");
}
TEST(unit_test_opengl, ForwardVector) TEST(unit_test_opengl, ForwardVector)
{ {
const auto forward = omath::opengl_engine::forward_vector({}); const auto forward = omath::opengl_engine::forward_vector({});

View File

@@ -7,6 +7,129 @@
#include <omath/engines/source_engine/formulas.hpp> #include <omath/engines/source_engine/formulas.hpp>
#include <random> #include <random>
TEST(unit_test_source_engine_units, HammerUnitsToCentimeters_BasicValues)
{
EXPECT_FLOAT_EQ(omath::source_engine::units_to_centimeters(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::source_engine::units_to_centimeters(1.0f), 2.54f);
EXPECT_FLOAT_EQ(omath::source_engine::units_to_centimeters(10.0f), 25.4f);
EXPECT_FLOAT_EQ(omath::source_engine::units_to_centimeters(-2.0f), -5.08f);
}
TEST(unit_test_source_engine_units, HammerUnitsToMeters_BasicValues)
{
EXPECT_NEAR(omath::source_engine::units_to_meters(0.0), 0.0, 1e-12);
EXPECT_NEAR(omath::source_engine::units_to_meters(1.0), 0.0254, 1e-12);
EXPECT_NEAR(omath::source_engine::units_to_meters(100.0), 2.54, 1e-12);
EXPECT_NEAR(omath::source_engine::units_to_meters(-4.0), -0.1016, 1e-12);
}
TEST(unit_test_source_engine_units, HammerUnitsToKilometers_BasicValues)
{
EXPECT_NEAR(omath::source_engine::units_to_kilometers(0.0), 0.0, 1e-15);
EXPECT_NEAR(omath::source_engine::units_to_kilometers(1.0), 0.0000254, 1e-15);
EXPECT_NEAR(omath::source_engine::units_to_kilometers(1000.0), 0.0254, 1e-15);
EXPECT_NEAR(omath::source_engine::units_to_kilometers(-10.0), -0.000254, 1e-15);
}
TEST(unit_test_source_engine_units, CentimetersToHammerUnits_BasicValues)
{
EXPECT_FLOAT_EQ(omath::source_engine::centimeters_to_units(0.0f), 0.0f);
EXPECT_NEAR(omath::source_engine::centimeters_to_units(2.54f), 1.0f, 1e-6f);
EXPECT_NEAR(omath::source_engine::centimeters_to_units(25.4f), 10.0f, 1e-5f);
EXPECT_NEAR(omath::source_engine::centimeters_to_units(-5.08f), -2.0f, 1e-6f);
}
TEST(unit_test_source_engine_units, MetersToHammerUnits_BasicValues)
{
EXPECT_NEAR(omath::source_engine::meters_to_units(0.0), 0.0, 1e-12);
EXPECT_NEAR(omath::source_engine::meters_to_units(0.0254), 1.0, 1e-12);
EXPECT_NEAR(omath::source_engine::meters_to_units(2.54), 100.0, 1e-10);
EXPECT_NEAR(omath::source_engine::meters_to_units(-0.0508), -2.0, 1e-12);
}
TEST(unit_test_source_engine_units, KilometersToHammerUnits_BasicValues)
{
EXPECT_NEAR(omath::source_engine::kilometers_to_units(0.0), 0.0, 1e-9);
EXPECT_NEAR(omath::source_engine::kilometers_to_units(0.0000254), 1.0, 1e-9);
EXPECT_NEAR(omath::source_engine::kilometers_to_units(0.00254), 100.0, 1e-7);
EXPECT_NEAR(omath::source_engine::kilometers_to_units(-0.0000508), -2.0, 1e-9);
}
TEST(unit_test_source_engine_units, RoundTrip_HammerToCentimetersToHammer)
{
constexpr float hu_f = 123.456f;
constexpr auto cm_f = omath::source_engine::units_to_centimeters(hu_f);
constexpr auto hu_f_back = omath::source_engine::centimeters_to_units(cm_f);
EXPECT_NEAR(hu_f_back, hu_f, 1e-5f);
constexpr double hu_d = -98765.4321;
constexpr auto cm_d = omath::source_engine::units_to_centimeters(hu_d);
constexpr auto hu_d_back = omath::source_engine::centimeters_to_units(cm_d);
EXPECT_NEAR(hu_d_back, hu_d, 1e-10);
}
TEST(unit_test_source_engine_units, RoundTrip_HammerToMetersToHammer)
{
constexpr float hu_f = 2500.25f;
constexpr auto m_f = omath::source_engine::units_to_meters(hu_f);
constexpr auto hu_f_back = omath::source_engine::meters_to_units(m_f);
EXPECT_NEAR(hu_f_back, hu_f, 1e-4f);
constexpr double hu_d = -42000.125;
constexpr auto m_d = omath::source_engine::units_to_meters(hu_d);
constexpr auto hu_d_back = omath::source_engine::meters_to_units(m_d);
EXPECT_NEAR(hu_d_back, hu_d, 1e-10);
}
TEST(unit_test_source_engine_units, RoundTrip_HammerToKilometersToHammer)
{
constexpr float hu_f = 100000.0f;
constexpr auto km_f = omath::source_engine::units_to_kilometers(hu_f);
constexpr auto hu_f_back = omath::source_engine::kilometers_to_units(km_f);
EXPECT_NEAR(hu_f_back, hu_f, 1e-2f); // looser due to float scaling
constexpr double hu_d = -1234567.89;
constexpr auto km_d = omath::source_engine::units_to_kilometers(hu_d);
constexpr auto hu_d_back = omath::source_engine::kilometers_to_units(km_d);
EXPECT_NEAR(hu_d_back, hu_d, 1e-7);
}
TEST(unit_test_source_engine_units, ConversionChainConsistency)
{
// hu -> cm -> m -> km should match direct helpers
constexpr auto hu = 54321.123;
constexpr auto cm = omath::source_engine::units_to_centimeters(hu);
constexpr auto m_via_cm = cm / 100.0;
constexpr auto km_via_cm = m_via_cm / 1000.0;
constexpr auto m_direct = omath::source_engine::units_to_meters(hu);
constexpr auto km_direct = omath::source_engine::units_to_kilometers(hu);
EXPECT_NEAR(m_direct, m_via_cm, 1e-12);
EXPECT_NEAR(km_direct, km_via_cm, 1e-15);
}
TEST(unit_test_source_engine_units, SupportsFloatAndDoubleTypes)
{
static_assert(std::is_same_v<decltype(omath::source_engine::units_to_centimeters(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::source_engine::units_to_centimeters(1.0)), double>);
static_assert(std::is_same_v<decltype(omath::source_engine::meters_to_units(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::source_engine::kilometers_to_units(1.0)), double>);
}
TEST(unit_test_source_engine_units, ConstexprEvaluation)
{
constexpr double hu = 10.0;
constexpr double cm = omath::source_engine::units_to_centimeters(hu);
constexpr double m = omath::source_engine::units_to_meters(hu);
constexpr double km = omath::source_engine::units_to_kilometers(hu);
static_assert(cm == 25.4, "constexpr hu->cm failed");
static_assert(m == 0.254, "constexpr hu->m failed");
static_assert(km == 0.000254, "constexpr hu->km failed");
}
TEST(unit_test_source_engine, ForwardVector) TEST(unit_test_source_engine, ForwardVector)
{ {
const auto forward = omath::source_engine::forward_vector({}); const auto forward = omath::source_engine::forward_vector({});

View File

@@ -8,6 +8,126 @@
#include <print> #include <print>
#include <random> #include <random>
TEST(unit_test_unity_engine, UnitsToCentimeters_BasicValues)
{
EXPECT_FLOAT_EQ(omath::unity_engine::units_to_centimeters(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::unity_engine::units_to_centimeters(1.0f), 0.01f);
EXPECT_FLOAT_EQ(omath::unity_engine::units_to_centimeters(100.0f), 1.0f);
EXPECT_FLOAT_EQ(omath::unity_engine::units_to_centimeters(-250.0f), -2.5f);
}
TEST(unit_test_unity_engine, UnitsToMeters_BasicValues)
{
EXPECT_DOUBLE_EQ(omath::unity_engine::units_to_meters(0.0), 0.0);
EXPECT_DOUBLE_EQ(omath::unity_engine::units_to_meters(1.0), 1.0);
EXPECT_DOUBLE_EQ(omath::unity_engine::units_to_meters(123.456), 123.456);
EXPECT_DOUBLE_EQ(omath::unity_engine::units_to_meters(-42.0), -42.0);
}
TEST(unit_test_unity_engine, UnitsToKilometers_BasicValues)
{
EXPECT_NEAR(omath::unity_engine::units_to_kilometers(0.0), 0.0, 1e-15);
EXPECT_NEAR(omath::unity_engine::units_to_kilometers(1.0), 0.001, 1e-15);
EXPECT_NEAR(omath::unity_engine::units_to_kilometers(1000.0), 1.0, 1e-12);
EXPECT_NEAR(omath::unity_engine::units_to_kilometers(-2500.0), -2.5, 1e-12);
}
TEST(unit_test_unity_engine, CentimetersToUnits_BasicValues)
{
EXPECT_FLOAT_EQ(omath::unity_engine::centimeters_to_units(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::unity_engine::centimeters_to_units(0.01f), 1.0f);
EXPECT_FLOAT_EQ(omath::unity_engine::centimeters_to_units(1.0f), 100.0f);
EXPECT_FLOAT_EQ(omath::unity_engine::centimeters_to_units(-2.5f), -250.0f);
}
TEST(unit_test_unity_engine, MetersToUnits_BasicValues)
{
EXPECT_DOUBLE_EQ(omath::unity_engine::meters_to_units(0.0), 0.0);
EXPECT_DOUBLE_EQ(omath::unity_engine::meters_to_units(1.0), 1.0);
EXPECT_DOUBLE_EQ(omath::unity_engine::meters_to_units(123.456), 123.456);
EXPECT_DOUBLE_EQ(omath::unity_engine::meters_to_units(-42.0), -42.0);
}
TEST(unit_test_unity_engine, KilometersToUnits_BasicValues)
{
EXPECT_NEAR(omath::unity_engine::kilometers_to_units(0.0), 0.0, 1e-12);
EXPECT_NEAR(omath::unity_engine::kilometers_to_units(0.001), 1.0, 1e-12);
EXPECT_NEAR(omath::unity_engine::kilometers_to_units(1.0), 1000.0, 1e-9);
EXPECT_NEAR(omath::unity_engine::kilometers_to_units(-2.5), -2500.0, 1e-9);
}
TEST(unit_test_unity_engine, RoundTrip_UnitsCentimeters)
{
constexpr float units_f = 12345.678f;
constexpr auto cm_f = omath::unity_engine::units_to_centimeters(units_f);
constexpr auto units_f_back = omath::unity_engine::centimeters_to_units(cm_f);
EXPECT_NEAR(units_f_back, units_f, 1e-3f);
constexpr double units_d = -987654.321;
constexpr auto cm_d = omath::unity_engine::units_to_centimeters(units_d);
constexpr auto units_d_back = omath::unity_engine::centimeters_to_units(cm_d);
EXPECT_NEAR(units_d_back, units_d, 1e-9);
}
TEST(unit_test_unity_engine, RoundTrip_UnitsMeters)
{
constexpr float units_f = 5432.125f;
constexpr auto m_f = omath::unity_engine::units_to_meters(units_f);
constexpr auto units_f_back = omath::unity_engine::meters_to_units(m_f);
EXPECT_FLOAT_EQ(units_f_back, units_f);
constexpr double units_d = -123456.789;
constexpr auto m_d = omath::unity_engine::units_to_meters(units_d);
constexpr auto units_d_back = omath::unity_engine::meters_to_units(m_d);
EXPECT_DOUBLE_EQ(units_d_back, units_d);
}
TEST(unit_test_unity_engine, RoundTrip_UnitsKilometers)
{
constexpr float units_f = 100000.0f;
constexpr auto km_f = omath::unity_engine::units_to_kilometers(units_f);
constexpr auto units_f_back = omath::unity_engine::kilometers_to_units(km_f);
EXPECT_NEAR(units_f_back, units_f, 1e-2f);
constexpr double units_d = -7654321.123;
constexpr auto km_d = omath::unity_engine::units_to_kilometers(units_d);
constexpr auto units_d_back = omath::unity_engine::kilometers_to_units(km_d);
EXPECT_NEAR(units_d_back, units_d, 1e-6);
}
TEST(unit_test_unity_engine, ConversionChainConsistency)
{
constexpr double units = 424242.42;
constexpr auto cm_direct = omath::unity_engine::units_to_centimeters(units);
constexpr auto cm_via_units = units / 100.0;
EXPECT_NEAR(cm_direct, cm_via_units, 1e-12);
constexpr auto km_direct = omath::unity_engine::units_to_kilometers(units);
constexpr auto km_via_meters = omath::unity_engine::units_to_meters(units) / 1000.0;
EXPECT_NEAR(km_direct, km_via_meters, 1e-12);
}
TEST(unit_test_unity_engine, SupportsFloatAndDouble)
{
static_assert(std::is_same_v<decltype(omath::unity_engine::units_to_centimeters(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::unity_engine::units_to_centimeters(1.0)), double>);
static_assert(std::is_same_v<decltype(omath::unity_engine::meters_to_units(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::unity_engine::kilometers_to_units(1.0)), double>);
}
TEST(unit_test_unity_engine, ConstexprConversions)
{
constexpr double units = 1000.0;
constexpr double cm = omath::unity_engine::units_to_centimeters(units);
constexpr double m = omath::unity_engine::units_to_meters(units);
constexpr double km = omath::unity_engine::units_to_kilometers(units);
static_assert(cm == 10.0, "units_to_centimeters constexpr failed");
static_assert(m == 1000.0, "units_to_meters constexpr failed");
static_assert(km == 1.0, "units_to_kilometers constexpr failed");
}
TEST(unit_test_unity_engine, ForwardVector) TEST(unit_test_unity_engine, ForwardVector)
{ {
const auto forward = omath::unity_engine::forward_vector({}); const auto forward = omath::unity_engine::forward_vector({});

View File

@@ -238,4 +238,127 @@ TEST(unit_test_unreal_engine, loook_at_random_z_axis)
failed_points++; failed_points++;
} }
EXPECT_LE(failed_points, 100); EXPECT_LE(failed_points, 100);
}
TEST(unit_test_unreal_engine, UnitsToCentimeters_BasicValues)
{
EXPECT_FLOAT_EQ(omath::unreal_engine::units_to_centimeters(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::unreal_engine::units_to_centimeters(1.0f), 1.0f);
EXPECT_FLOAT_EQ(omath::unreal_engine::units_to_centimeters(250.0f), 250.0f);
EXPECT_FLOAT_EQ(omath::unreal_engine::units_to_centimeters(-42.5f), -42.5f);
}
TEST(unit_test_unreal_engine, UnitsToMeters_BasicValues)
{
EXPECT_NEAR(omath::unreal_engine::units_to_meters(0.0), 0.0, 1e-15);
EXPECT_NEAR(omath::unreal_engine::units_to_meters(1.0), 0.01, 1e-15);
EXPECT_NEAR(omath::unreal_engine::units_to_meters(100.0), 1.0, 1e-12);
EXPECT_NEAR(omath::unreal_engine::units_to_meters(-250.0), -2.5, 1e-12);
}
TEST(unit_test_unreal_engine, UnitsToKilometers_BasicValues)
{
EXPECT_NEAR(omath::unreal_engine::units_to_kilometers(0.0), 0.0, 1e-18);
EXPECT_NEAR(omath::unreal_engine::units_to_kilometers(1.0), 0.00001, 1e-18);
EXPECT_NEAR(omath::unreal_engine::units_to_kilometers(100000.0), 1.0, 1e-12);
EXPECT_NEAR(omath::unreal_engine::units_to_kilometers(-250000.0), -2.5, 1e-12);
}
TEST(unit_test_unreal_engine, CentimetersToUnits_BasicValues)
{
EXPECT_FLOAT_EQ(omath::unreal_engine::centimeters_to_units(0.0f), 0.0f);
EXPECT_FLOAT_EQ(omath::unreal_engine::centimeters_to_units(1.0f), 1.0f);
EXPECT_FLOAT_EQ(omath::unreal_engine::centimeters_to_units(250.0f), 250.0f);
EXPECT_FLOAT_EQ(omath::unreal_engine::centimeters_to_units(-42.5f), -42.5f);
}
TEST(unit_test_unreal_engine, MetersToUnits_BasicValues)
{
EXPECT_NEAR(omath::unreal_engine::meters_to_units(0.0), 0.0, 1e-12);
EXPECT_NEAR(omath::unreal_engine::meters_to_units(0.01), 1.0, 1e-12);
EXPECT_NEAR(omath::unreal_engine::meters_to_units(1.0), 100.0, 1e-9);
EXPECT_NEAR(omath::unreal_engine::meters_to_units(-2.5), -250.0, 1e-9);
}
TEST(unit_test_unreal_engine, KilometersToUnits_BasicValues)
{
EXPECT_NEAR(omath::unreal_engine::kilometers_to_units(0.0), 0.0, 1e-9);
EXPECT_NEAR(omath::unreal_engine::kilometers_to_units(0.00001), 1.0, 1e-9);
EXPECT_NEAR(omath::unreal_engine::kilometers_to_units(1.0), 100000.0, 1e-6);
EXPECT_NEAR(omath::unreal_engine::kilometers_to_units(-2.5), -250000.0, 1e-3);
}
TEST(unit_test_unreal_engine, RoundTrip_UnitsCentimeters)
{
constexpr float units_f = 12345.678f;
constexpr auto cm_f = omath::unreal_engine::units_to_centimeters(units_f);
constexpr auto units_f_back = omath::unreal_engine::centimeters_to_units(cm_f);
EXPECT_FLOAT_EQ(units_f_back, units_f);
constexpr double units_d = -987654.321;
constexpr auto cm_d = omath::unreal_engine::units_to_centimeters(units_d);
constexpr auto units_d_back = omath::unreal_engine::centimeters_to_units(cm_d);
EXPECT_DOUBLE_EQ(units_d_back, units_d);
}
TEST(unit_test_unreal_engine, RoundTrip_UnitsMeters)
{
constexpr float units_f = 5432.125f;
constexpr auto m_f = omath::unreal_engine::units_to_meters(units_f);
constexpr auto units_f_back = omath::unreal_engine::meters_to_units(m_f);
EXPECT_NEAR(units_f_back, units_f, 1e-3f);
constexpr double units_d = -123456.789;
constexpr auto m_d = omath::unreal_engine::units_to_meters(units_d);
constexpr auto units_d_back = omath::unreal_engine::meters_to_units(m_d);
EXPECT_NEAR(units_d_back, units_d, 1e-9);
}
TEST(unit_test_unreal_engine, RoundTrip_UnitsKilometers)
{
constexpr float units_f = 100000.0f;
constexpr auto km_f = omath::unreal_engine::units_to_kilometers(units_f);
constexpr auto units_f_back = omath::unreal_engine::kilometers_to_units(km_f);
EXPECT_NEAR(units_f_back, units_f, 1e-2f);
constexpr double units_d = -7654321.123;
constexpr auto km_d = omath::unreal_engine::units_to_kilometers(units_d);
constexpr auto units_d_back = omath::unreal_engine::kilometers_to_units(km_d);
EXPECT_NEAR(units_d_back, units_d, 1e-6);
}
TEST(unit_test_unreal_engine, ConversionChainConsistency)
{
constexpr double units = 424242.42;
constexpr auto cm_direct = omath::unreal_engine::units_to_centimeters(units);
constexpr auto cm_expected = units; // 1 uu == 1 cm
EXPECT_NEAR(cm_direct, cm_expected, 1e-12);
constexpr auto m_direct = omath::unreal_engine::units_to_meters(units);
constexpr auto m_via_cm = cm_direct / 100.0;
EXPECT_NEAR(m_direct, m_via_cm, 1e-12);
constexpr auto km_direct = omath::unreal_engine::units_to_kilometers(units);
constexpr auto km_via_m = m_direct / 1000.0;
EXPECT_NEAR(km_direct, km_via_m, 1e-15);
}
TEST(unit_test_unreal_engine, SupportsFloatAndDouble)
{
static_assert(std::is_same_v<decltype(omath::unreal_engine::units_to_centimeters(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::unreal_engine::units_to_centimeters(1.0)), double>);
static_assert(std::is_same_v<decltype(omath::unreal_engine::meters_to_units(1.0f)), float>);
static_assert(std::is_same_v<decltype(omath::unreal_engine::kilometers_to_units(1.0)), double>);
}
TEST(unit_test_unreal_engine, ConstexprConversions)
{
constexpr double units = 100000.0;
constexpr double cm = omath::unreal_engine::units_to_centimeters(units);
constexpr double m = omath::unreal_engine::units_to_meters(units);
constexpr double km = omath::unreal_engine::units_to_kilometers(units);
static_assert(cm == 100000.0, "units_to_centimeters constexpr failed");
static_assert(m == 1000.0, "units_to_meters constexpr failed");
static_assert(km == 1.0, "units_to_kilometers constexpr failed");
} }

View File

@@ -27,10 +27,11 @@
] ]
}, },
"examples": { "examples": {
"description": "Build benchmarks", "description": "Build examples",
"dependencies": [ "dependencies": [
"glfw3", "glfw3",
"glew" "glew",
"opengl"
] ]
}, },
"imgui": { "imgui": {