numerics 0.1.0
Loading...
Searching...
No Matches
viz.hpp
Go to the documentation of this file.
1/// @file include/viz/viz.hpp
2/// @brief Lightweight real-time visualization layer for numerics apps.
3#pragma once
4
5#include <algorithm>
6#include <cstdarg>
7#include <cstdint>
8#include <cstdio>
9#include <raylib.h>
10#include <raymath.h>
11#include <vector>
12
13namespace num::viz {
14
15struct Color {
16 uint8_t r = 0, g = 0, b = 0, a = 255;
17};
18
19inline constexpr Color kBlack = {0, 0, 0, 255};
20inline constexpr Color kWhite = {255, 255, 255, 255};
21inline constexpr Color kRed = {220, 40, 40, 255};
22inline constexpr Color kGreen = {50, 200, 50, 255};
23inline constexpr Color kBlue = {50, 100, 230, 255};
24inline constexpr Color kYellow = {240, 220, 30, 255};
25inline constexpr Color kGray = {120, 120, 120, 255};
26inline constexpr Color kSkyBlue = {100, 180, 240, 255};
27
28inline ::Color to_rl(Color c) {
29 return {c.r, c.g, c.b, c.a};
30}
31inline Color from_rl(::Color c) {
32 return {c.r, c.g, c.b, c.a};
33}
34
35inline Color unpack(uint32_t c) {
36 return {uint8_t(c >> 24), uint8_t(c >> 16), uint8_t(c >> 8), uint8_t(c)};
37}
38
39/// @brief Heat colormap for \f$t\in[0,1]\f$.
40inline Color heat_color(float t) {
41 t = std::clamp(t, 0.0f, 1.0f);
42 return from_rl(ColorFromHSV((1.0f - t) * 240.0f, 1.0f, 0.95f));
43}
44
45/// @brief Diverging colormap for \f$t\in[-1,1]\f$.
46inline Color diverging_color(float t) {
47 t = std::clamp(t, -1.0f, 1.0f);
48 if (t >= 0.0f) {
49 auto v = uint8_t(255 * t * t);
50 return {v, 0, uint8_t(v / 10), 255};
51 } else {
52 auto v = uint8_t(255 * t * t);
53 return {uint8_t(v / 10), 0, v, 255};
54 }
55}
56
57/// @brief Phase/probability colormap for quantum wavefunctions.
58inline Color phase_hsv_color(double prob, double phase, double max_prob) {
59 if (max_prob < 1e-20)
60 return kBlack;
61 float amp = std::min(1.0f, float(std::sqrt(prob / max_prob)));
62 float hue = float((phase + 3.14159265) / (2.0 * 3.14159265)) * 360.0f;
63 float h6 = hue / 60.0f;
64 int hi = int(h6) % 6;
65 float f = h6 - int(h6);
66 float s = 0.95f;
67 float p = amp * (1.0f - s);
68 float q = amp * (1.0f - s * f);
69 float u = amp * (1.0f - s * (1.0f - f));
70 float r, g, b;
71 switch (hi) {
72 case 0:
73 r = amp;
74 g = u;
75 b = p;
76 break;
77 case 1:
78 r = q;
79 g = amp;
80 b = p;
81 break;
82 case 2:
83 r = p;
84 g = amp;
85 b = u;
86 break;
87 case 3:
88 r = p;
89 g = q;
90 b = amp;
91 break;
92 case 4:
93 r = u;
94 g = p;
95 b = amp;
96 break;
97 default:
98 r = amp;
99 g = p;
100 b = q;
101 break;
102 }
103 return {uint8_t(r * 255), uint8_t(g * 255), uint8_t(b * 255), 255};
104}
105
106inline Color lerp_color(Color a, Color b, float t) {
107 t = std::clamp(t, 0.0f, 1.0f);
108 return {
109 uint8_t(a.r + t * (b.r - a.r)),
110 uint8_t(a.g + t * (b.g - a.g)),
111 uint8_t(a.b + t * (b.b - a.b)),
112 uint8_t(a.a + t * (b.a - a.a)),
113 };
114}
115
116namespace detail {
118 Texture2D tex{};
119 std::vector<::Color> buf;
120 int N = 0;
121 bool valid = false;
122
123 void ensure(int n) {
124 if (n == N && valid)
125 return;
126 if (valid)
127 UnloadTexture(tex);
128 N = n;
129 buf.assign(n * n, ::BLACK);
130 Image img = GenImageColor(n, n, ::BLACK);
131 tex = LoadTextureFromImage(img);
132 UnloadImage(img);
133 valid = true;
134 }
135 void unload() {
136 if (valid) {
137 UnloadTexture(tex);
138 valid = false;
139 N = 0;
140 }
141 }
142};
143
144struct State {
146 bool paused = false;
147 int substeps = 1;
148 int slider_count = 0; // reset each frame
149};
150} // namespace detail
151
152// Frame -- passed to the draw callback on every tick.
153struct Frame {
155 bool& paused;
158
159 // Advance the simulation fn() `substeps` times if not paused.
160 // fn() -> void
161 template<class Fn>
162 void step(Fn fn) {
163 if (!paused)
164 for (int i = 0; i < substeps; ++i)
165 fn();
166 }
167
168 // True on the frame R was pressed.
169 bool reset_pressed() const { return IsKeyPressed(KEY_R); }
170
171 // 2D primitives -- pixel coordinates, (0,0) = top-left.
172 void dot(float x, float y, Color c, float r = 3.0f) {
173 DrawCircleV({x, y}, r, to_rl(c));
174 }
175 void circle(float x, float y, float r, Color c) {
176 DrawCircleLines(int(x), int(y), r, to_rl(c));
177 }
178 void line(float x0, float y0, float x1, float y1, Color c, float thick = 1.0f) {
179 DrawLineEx({x0, y0}, {x1, y1}, thick, to_rl(c));
180 }
181 void rect(float x, float y, float w, float h, Color c) {
182 DrawRectangleV({x, y}, {w, h}, to_rl(c));
183 }
184 void rect_outline(float x, float y, float w, float h, Color c, float thick = 1.0f) {
185 DrawRectangleLinesEx({x, y, w, h}, thick, to_rl(c));
186 }
187 void text(const char* s, float x, float y, int sz, Color c) {
188 DrawText(s, int(x), int(y), sz, to_rl(c));
189 }
190 // printf-style text -- uses a 256-byte internal buffer (safe for HUD
191 // strings)
192 void textf(float x, float y, int sz, Color c, const char* fmt, ...) {
193 char buf[256];
194 va_list args;
195 va_start(args, fmt);
196 vsnprintf(buf, sizeof(buf), fmt, args);
197 va_end(args);
198 DrawText(buf, int(x), int(y), sz, to_rl(c));
199 }
200
201 // Pixel field covering the full window.
202 template<class Fn>
203 void field(int N, Fn color_fn) {
204 auto& cv = _s.canvas;
205 cv.ensure(N);
206 for (int row = 0; row < N; ++row)
207 for (int col = 0; col < N; ++col)
208 cv.buf[row * N + col] = to_rl(color_fn(col, row));
209 UpdateTexture(cv.tex, cv.buf.data());
210 DrawTexturePro(cv.tex,
211 {0, 0, float(N), float(N)},
212 {0, 0, float(width), float(height)},
213 {0, 0},
214 0.0f,
215 ::WHITE);
216 }
217
218 // GUI slider -- stacks vertically in the top-left corner.
219 // Reads mouse drag to update val.
220 void slider(const char* label, double lo, double hi, double& val) {
221 constexpr int PAD = 10;
222 constexpr int SLOT_H = 38;
223 constexpr int BAR_W = 220;
224 constexpr int BAR_H = 8;
225 const int X = PAD;
226 const int Y = PAD + _s.slider_count * SLOT_H;
228
229 DrawRectangle(X - 4, Y - 2, BAR_W + 8, SLOT_H - 4, {0, 0, 0, 170});
230 DrawRectangle(X, Y + 18, BAR_W, BAR_H, {80, 80, 80, 220});
231
232 float t = std::clamp(float((val - lo) / (hi - lo)), 0.0f, 1.0f);
233 int tx = X + int(t * BAR_W);
234
235 Vector2 mp = GetMousePosition();
236 Rectangle hit = {float(X), float(Y), float(BAR_W), float(SLOT_H)};
237 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && CheckCollisionPointRec(mp, hit)) {
238 t = std::clamp((mp.x - X) / BAR_W, 0.0f, 1.0f);
239 val = lo + t * (hi - lo);
240 tx = X + int(t * BAR_W);
241 }
242
243 DrawCircle(tx, Y + 18 + BAR_H / 2, 9, {200, 200, 200, 240});
244 DrawText(TextFormat("%s: %.3g", label, val), X, Y + 2, 13, {210, 210, 210, 230});
245 }
246
247 // 3D camera descriptor
248 struct Cam3 {
249 float px = 0, py = 0, pz = 0; // camera position
250 float tx = 0, ty = 0, tz = 0; // look-at target
251 float ux = 0, uy = 1, uz = 0; // up vector (default: world-Y)
252 float fovy = 45.0f;
253 };
254
255 // Begin 3D rendering mode. All 3D draw calls must sit between
256 // begin3d/end3d.
257 void begin3d(Cam3 cam) {
258 Camera3D c{};
259 c.position = {cam.px, cam.py, cam.pz};
260 c.target = {cam.tx, cam.ty, cam.tz};
261 c.up = {cam.ux, cam.uy, cam.uz};
262 c.fovy = cam.fovy;
263 c.projection = CAMERA_PERSPECTIVE;
264 BeginMode3D(c);
265 }
266 void end3d() { EndMode3D(); }
267
268 void sphere3d(float x, float y, float z, float r, Color c) {
269 DrawSphere({x, y, z}, r, to_rl(c));
270 }
271 void sphere3d_wire(float x, float y, float z, float r, Color c) {
272 DrawSphereWires({x, y, z}, r, 6, 6, to_rl(c));
273 }
274 void line3d(float x0, float y0, float z0, float x1, float y1, float z1, Color c) {
275 DrawLine3D({x0, y0, z0}, {x1, y1, z1}, to_rl(c));
276 }
277 void cube3d(float x, float y, float z, float sx, float sy, float sz, Color c) {
278 DrawCube({x, y, z}, sx, sy, sz, to_rl(c));
279 }
280};
281
282// run() -- open a window and call draw(Frame&) at 60 Hz until ESC / close.
283//
284// Built-in key bindings:
285// SPACE pause / unpause
286// R sets reset flag (check via f.reset_pressed())
287// +/- increase / decrease substeps (capped at 16)
288// ESC quit
289template<class DrawFn>
290void run(const char* title, int w, int h, DrawFn draw, Color bg = {15, 15, 15, 255}) {
291 SetConfigFlags(FLAG_MSAA_4X_HINT);
292 InitWindow(w, h, title);
293 SetTargetFPS(60);
294
295 detail::State state;
296 Frame frame{w, h, state.paused, state.substeps, state};
297
298 while (!WindowShouldClose()) {
299 if (IsKeyPressed(KEY_SPACE))
300 state.paused = !state.paused;
301 if (IsKeyPressed(KEY_EQUAL) || IsKeyPressed(KEY_KP_ADD))
302 state.substeps = std::min(state.substeps + 1, 16);
303 if (IsKeyPressed(KEY_MINUS) || IsKeyPressed(KEY_KP_SUBTRACT))
304 state.substeps = std::max(state.substeps - 1, 1);
305
306 state.slider_count = 0;
307
308 BeginDrawing();
309 ClearBackground(to_rl(bg));
310
311 draw(frame);
312
313 if (state.paused) {
314 DrawRectangle(w / 2 - 70, h / 2 - 18, 140, 36, {0, 0, 0, 160});
315 DrawText("PAUSED", w / 2 - 46, h / 2 - 10, 24, ::YELLOW);
316 }
317
318 EndDrawing();
319 }
320
321 state.canvas.unload();
322 CloseWindow();
323}
324
325} // namespace num::viz
constexpr Color kWhite
Definition viz.hpp:20
Color lerp_color(Color a, Color b, float t)
Definition viz.hpp:106
void run(const char *title, int w, int h, DrawFn draw, Color bg={15, 15, 15, 255})
Definition viz.hpp:290
Color unpack(uint32_t c)
Definition viz.hpp:35
Color from_rl(::Color c)
Definition viz.hpp:31
Color phase_hsv_color(double prob, double phase, double max_prob)
Phase/probability colormap for quantum wavefunctions.
Definition viz.hpp:58
constexpr Color kBlue
Definition viz.hpp:23
constexpr Color kBlack
Definition viz.hpp:19
Color heat_color(float t)
Heat colormap for .
Definition viz.hpp:40
Color diverging_color(float t)
Diverging colormap for .
Definition viz.hpp:46
constexpr Color kYellow
Definition viz.hpp:24
constexpr Color kGray
Definition viz.hpp:25
constexpr Color kGreen
Definition viz.hpp:22
constexpr Color kSkyBlue
Definition viz.hpp:26
inline ::Color to_rl(Color c)
Definition viz.hpp:28
constexpr Color kRed
Definition viz.hpp:21
constexpr real e
Definition math.hpp:44
uint8_t r
Definition viz.hpp:16
uint8_t a
Definition viz.hpp:16
uint8_t g
Definition viz.hpp:16
uint8_t b
Definition viz.hpp:16
void dot(float x, float y, Color c, float r=3.0f)
Definition viz.hpp:172
void line(float x0, float y0, float x1, float y1, Color c, float thick=1.0f)
Definition viz.hpp:178
bool & paused
Definition viz.hpp:155
void field(int N, Fn color_fn)
Definition viz.hpp:203
void begin3d(Cam3 cam)
Definition viz.hpp:257
void sphere3d_wire(float x, float y, float z, float r, Color c)
Definition viz.hpp:271
void cube3d(float x, float y, float z, float sx, float sy, float sz, Color c)
Definition viz.hpp:277
void rect_outline(float x, float y, float w, float h, Color c, float thick=1.0f)
Definition viz.hpp:184
void slider(const char *label, double lo, double hi, double &val)
Definition viz.hpp:220
void line3d(float x0, float y0, float z0, float x1, float y1, float z1, Color c)
Definition viz.hpp:274
bool reset_pressed() const
Definition viz.hpp:169
void textf(float x, float y, int sz, Color c, const char *fmt,...)
Definition viz.hpp:192
void end3d()
Definition viz.hpp:266
int & substeps
Definition viz.hpp:156
void step(Fn fn)
Definition viz.hpp:162
void sphere3d(float x, float y, float z, float r, Color c)
Definition viz.hpp:268
void text(const char *s, float x, float y, int sz, Color c)
Definition viz.hpp:187
void circle(float x, float y, float r, Color c)
Definition viz.hpp:175
void rect(float x, float y, float w, float h, Color c)
Definition viz.hpp:181
detail::State & _s
Definition viz.hpp:157
std::vector<::Color > buf
Definition viz.hpp:119
FieldCanvas canvas
Definition viz.hpp:145