diff --git a/examples/projection.cpp b/examples/projection.cpp new file mode 100644 index 0000000..ba419ed --- /dev/null +++ b/examples/projection.cpp @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#endif + +#include +using smath::Vec2; + +enum Color : uint8_t { + CLR_NONE = 0, // default + CLR_AXES = 90, // bright black (gray) + CLR_A = 32, // green + CLR_B = 33, // yellow + CLR_P = 35, // magenta + CLR_DOT = 36 // cyan +}; + +struct Cell { + char ch{' '}; + uint8_t color{CLR_NONE}; + int prio{0}; +}; + +struct Canvas { + int w, h; + std::vector pix; + Canvas(int W, int H) : w(W), h(H), pix(W * H) {} + + void put(int x, int y, char c, int prio, uint8_t color) { + if (x < 0 || x >= w || y < 0 || y >= h) + return; + Cell &cell = pix[y * w + x]; + if (prio >= cell.prio) { + cell.ch = c; + cell.prio = prio; + cell.color = color; + } + } + void hline(int y, char c, int prio, uint8_t color) { + for (int x = 0; x < w; ++x) + put(x, y, c, prio, color); + } + void vline(int x, char c, int prio, uint8_t color) { + for (int y = 0; y < h; ++y) + put(x, y, c, prio, color); + } + + void line_dir(int x0, int y0, int x1, int y1, int prio, uint8_t color) { + int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = dx + dy; + + int px = x0, py = y0; + put(px, py, '+', prio, color); + + while (true) { + if (px == x1 && py == y1) + break; + int e2 = 2 * err; + int nx = px, ny = py; + if (e2 >= dy) { + err += dy; + nx += sx; + } + if (e2 <= dx) { + err += dx; + ny += sy; + } + + int sdx = nx - px; + int sdy = ny - py; + int adx = std::abs(sdx); + int ady = std::abs(sdy); + char g; + if (adx >= 2 * ady) + g = '-'; + else if (ady >= 2 * adx) + g = '|'; + else + g = ((sdx > 0 && sdy > 0) || (sdx < 0 && sdy < 0)) ? '\\' : '/'; + + put(nx, ny, g, prio, color); + px = nx; + py = ny; + } + } + + void flush() const { + uint8_t cur = 255; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + const Cell &c = pix[y * w + x]; + if (c.color != cur) { + if (c.color == CLR_NONE) + std::print("\x1b[0m"); + else + std::print("\x1b[{}m", c.color); + cur = c.color; + } + std::print("{}", c.ch); + } + std::println(""); + } + std::print("\x1b[0m"); + } +}; + +struct Mapper { + int W, H; + double scale; + int cx, cy; + Mapper(int w, int h, double s) : W(w), H(h), scale(s), cx(w / 2), cy(h / 2) {} + std::pair map(Vec2 v) const { + int x = cx + (int)std::llround(v.x() * scale); + int y = cy - (int)std::llround(v.y() * scale); + return {x, y}; + } +}; + +int main() { +#if defined(_WIN32) + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + if (h != INVALID_HANDLE_VALUE) { + DWORD m; + if (GetConsoleMode(h, &m)) + SetConsoleMode(h, m | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } +#endif + + std::print("Enter vector a (ax ay): "); + double ax, ay; + if (!(std::cin >> ax >> ay)) + return 0; + std::print("Enter vector b (bx by): "); + double bx, by; + if (!(std::cin >> bx >> by)) + return 0; + + Vec2 a{(float)ax, (float)ay}; + Vec2 b{(float)bx, (float)by}; + Vec2 p = a.project_onto(b); + + std::println("\nResults:"); + std::println("a = {}", a); + std::println("b = {}", b); + std::println("proj_b(a) = p = {}", p); + std::println("|a|={} |b|={} |p|={}", a.magnitude(), b.magnitude(), + p.magnitude()); + double dot = a.dot(b); + double ang = (a.magnitude() > 0 && b.magnitude() > 0) + ? std::acos(std::clamp(dot / (a.magnitude() * b.magnitude()), + -1.0, 1.0)) * + 180.0 / M_PI + : 0.0; + std::println("a·b={} angle(a,b)={} deg\n", dot, ang); + + const int W = 73, H = 33; + double maxr = std::max({(double)a.magnitude(), (double)b.magnitude(), + (double)p.magnitude(), 1.0}); + double usable = 0.45 * std::min(W, H); + double scale = usable / maxr; + + Canvas cvs(W, H); + Mapper mp(W, H, scale); + + int pr_axes = 1; + auto [cx, cy] = mp.map({0, 0}); + cvs.hline(H / 2, '-', pr_axes, CLR_AXES); + cvs.vline(W / 2, '|', pr_axes, CLR_AXES); + cvs.put(W / 2, H / 2, 'O', pr_axes + 1, CLR_AXES); + + auto [ox, oy] = mp.map({0, 0}); + auto [ax1, ay1] = mp.map(a); + auto [bx1, by1] = mp.map(b); + auto [px1, py1] = mp.map(p); + + int pr_a = 3, pr_b = 3, pr_p = 4; + + cvs.line_dir(ox, oy, ax1, ay1, pr_a, CLR_A); + cvs.line_dir(ox, oy, bx1, by1, pr_b, CLR_B); + cvs.line_dir(ox, oy, px1, py1, pr_p, CLR_P); + + if (!a.approx_equal(p) && b.magnitude() > 0) { + int steps = std::max(std::abs(ax1 - px1), std::abs(ay1 - py1)); + for (int i = 0; i <= steps; ++i) { + double t = steps ? double(i) / steps : 0.0; + int x = (int)std::llround(px1 + t * (ax1 - px1)); + int y = (int)std::llround(py1 + t * (ay1 - py1)); + if (i % 2 == 0) + cvs.put(x, y, '.', 2, CLR_DOT); + } + } + + auto place_label = [&](Vec2 v, const char *txt, uint8_t c, int pr) { + auto tip = mp.map(v * 1.05f); + for (int i = 0; txt[i]; ++i) + cvs.put(tip.first + i, tip.second, txt[i], pr, c); + }; + place_label(a, "A", CLR_A, pr_a + 1); + place_label(b, "B", CLR_B, pr_b + 1); + place_label(p, "P", CLR_P, pr_p + 1); + + std::println("Legend: axes(gray), a(green), b(yellow), p=proj(magenta), " + "dotted=cross-perp\n"); + cvs.flush(); + return 0; +} diff --git a/examples/random_steps.cpp b/examples/random_steps.cpp new file mode 100644 index 0000000..9c2b684 --- /dev/null +++ b/examples/random_steps.cpp @@ -0,0 +1,43 @@ +/* + * smath - Single-file linear algebra math library for C++23. + * + * Copyright 2025 Slendi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * You can define the following macros to change functionality: + * - SMATH_IMPLICIT_CONVERSIONS + */ + +#include +#include + +#include + +auto main() -> int { + using namespace smath; + + Vec2d point; + std::random_device rd; + std::mt19937 rng{rd()}; + std::uniform_real_distribution<> dis(-5, 5); + int i = 0; + do { + Vec2d add{dis(rng), dis(rng)}; + auto const n = point + add; + std::println("{}: {:.2f} + {:.2f} -> {:.2f}", i, point, add, n); + point = n; + + i++; + } while (i < 15); +} diff --git a/include/smath.hpp b/include/smath.hpp index ae69b0a..45829ac 100644 --- a/include/smath.hpp +++ b/include/smath.hpp @@ -1,4 +1,5 @@ -/* smath - Single-file linear algebra math library for C++23. +/* + * smath - Single-file linear algebra math library for C++23. * * Copyright 2025 Slendi *