feat: add C++ ImGui functions for core UI and visualization
Funciones C++/ImGui para dashboards (grid, panel, docking, sidebar, tabs), visualizaciones (candlestick, gauge, histogram, pie, sparkline, heatmap, scatter, line, bar, surface3d, kpi, table), grafos (force layout, renderer, viewport, spatial hash, types) y utilidades (time series buffer, tracy zones, memory/fps overlay, plot theme). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
#include "dashboard_grid.h"
|
||||
#include <imgui.h>
|
||||
#include <vector>
|
||||
|
||||
// Internal state stack to support nested grids.
|
||||
namespace {
|
||||
|
||||
struct GridState {
|
||||
int columns;
|
||||
float spacing;
|
||||
float col_width;
|
||||
int counter; // number of dashboard_grid_next() calls so far
|
||||
};
|
||||
|
||||
static std::vector<GridState> g_grid_stack;
|
||||
|
||||
} // namespace
|
||||
|
||||
void dashboard_grid_begin(int columns, float spacing) {
|
||||
if (columns < 1) columns = 1;
|
||||
|
||||
float available = ImGui::GetContentRegionAvail().x;
|
||||
float col_width = (available - spacing * static_cast<float>(columns - 1))
|
||||
/ static_cast<float>(columns);
|
||||
if (col_width < 1.0f) col_width = 1.0f;
|
||||
|
||||
g_grid_stack.push_back({columns, spacing, col_width, 0});
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushItemWidth(col_width);
|
||||
}
|
||||
|
||||
void dashboard_grid_next() {
|
||||
if (g_grid_stack.empty()) return;
|
||||
|
||||
GridState& s = g_grid_stack.back();
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
|
||||
s.counter++;
|
||||
|
||||
if (s.counter % s.columns != 0) {
|
||||
// Same row: advance horizontally.
|
||||
ImGui::SameLine(0.0f, s.spacing);
|
||||
}
|
||||
// If counter % columns == 0 the next BeginGroup starts a new row automatically.
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushItemWidth(s.col_width);
|
||||
}
|
||||
|
||||
void dashboard_grid_end() {
|
||||
if (g_grid_stack.empty()) return;
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
|
||||
g_grid_stack.pop_back();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
// Dashboard grid — distributes child widgets in N columns.
|
||||
// Usage:
|
||||
// dashboard_grid_begin(3);
|
||||
// // widget 1 (auto placed in col 0)
|
||||
// dashboard_grid_next();
|
||||
// // widget 2 (auto placed in col 1)
|
||||
// dashboard_grid_next();
|
||||
// // widget 3 (auto placed in col 2, wraps to next row)
|
||||
// dashboard_grid_next();
|
||||
// // widget 4 (col 0 of row 2)
|
||||
// dashboard_grid_end();
|
||||
|
||||
void dashboard_grid_begin(int columns = 2, float spacing = 8.0f);
|
||||
void dashboard_grid_next(); // advance to next cell
|
||||
void dashboard_grid_end();
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
name: dashboard_grid
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "void dashboard_grid_begin(int columns = 2, float spacing = 8.0f); void dashboard_grid_next(); void dashboard_grid_end()"
|
||||
description: "Grid de N columnas para distribuir widgets de dashboard automaticamente con spacing uniforme entre columnas"
|
||||
tags: [imgui, grid, layout, dashboard, responsive]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [imgui]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/dashboard_grid.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: columns
|
||||
desc: "Numero de columnas del grid (minimo 1); el ancho disponible se divide uniformemente entre ellas"
|
||||
- name: spacing
|
||||
desc: "Espacio horizontal entre columnas en pixels"
|
||||
output: "Layout de grid aplicado al contenido entre dashboard_grid_begin/end; cada celda recibe un ancho uniforme calculado a partir del espacio disponible"
|
||||
---
|
||||
|
||||
# dashboard_grid
|
||||
|
||||
Divide el ancho disponible en N columnas con spacing uniforme y posiciona cada widget en su celda automaticamente. Soporta grids anidados mediante un stack interno de estado.
|
||||
|
||||
## Uso
|
||||
|
||||
```cpp
|
||||
dashboard_grid_begin(3, 8.0f);
|
||||
|
||||
// Celda 0
|
||||
dashboard_panel_begin("CPU");
|
||||
ImGui::Text("%.1f %%", cpu_pct);
|
||||
dashboard_panel_end();
|
||||
dashboard_grid_next();
|
||||
|
||||
// Celda 1
|
||||
dashboard_panel_begin("Memory");
|
||||
ImGui::Text("%.0f MB", mem_mb);
|
||||
dashboard_panel_end();
|
||||
dashboard_grid_next();
|
||||
|
||||
// Celda 2
|
||||
dashboard_panel_begin("Disk");
|
||||
ImGui::Text("%.0f GB", disk_gb);
|
||||
dashboard_panel_end();
|
||||
|
||||
dashboard_grid_end();
|
||||
```
|
||||
|
||||
## Implementacion
|
||||
|
||||
### Calculo de ancho
|
||||
|
||||
```
|
||||
col_width = (available_width - spacing * (columns - 1)) / columns
|
||||
```
|
||||
|
||||
`available_width` se obtiene de `ImGui::GetContentRegionAvail().x` en el momento de `dashboard_grid_begin`.
|
||||
|
||||
### Mecanica de celdas
|
||||
|
||||
Cada celda es un `BeginGroup`/`EndGroup` con `PushItemWidth(col_width)` para que los widgets internos respeten el ancho de columna. Al llamar `dashboard_grid_next`:
|
||||
|
||||
1. Se cierra la celda actual (`PopItemWidth`, `EndGroup`).
|
||||
2. Se incrementa el contador interno.
|
||||
3. Si `counter % columns != 0` se emite `SameLine(0, spacing)` para continuar en la misma fila.
|
||||
4. Si `counter % columns == 0` no se emite `SameLine`: ImGui pasa a la siguiente fila automaticamente.
|
||||
5. Se abre la nueva celda (`BeginGroup`, `PushItemWidth`).
|
||||
|
||||
### Grids anidados
|
||||
|
||||
El estado (columnas, spacing, col_width, counter) se guarda en `g_grid_stack` (un `std::vector` de structs en un namespace anonimo). Cada llamada a `dashboard_grid_begin` hace `push_back` y `dashboard_grid_end` hace `pop_back`, permitiendo anidar grids sin conflicto.
|
||||
|
||||
## Notas
|
||||
|
||||
- Llamar `dashboard_grid_next()` entre cada par de widgets, **no** antes del primero ni despues del ultimo.
|
||||
- El numero de `dashboard_grid_next()` puede ser mayor que `columns - 1`: el grid hace wrap automatico a la siguiente fila.
|
||||
- Combina bien con `dashboard_panel_begin`/`dashboard_panel_end` para crear dashboards con paneles alineados en cuadricula.
|
||||
- Si `columns <= 0` se fuerza a 1 para evitar division por cero.
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "dashboard_panel.h"
|
||||
#include <imgui.h>
|
||||
|
||||
bool dashboard_panel_begin(const char* title, float min_width, float min_height) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.12f, 0.15f, 1.0f));
|
||||
|
||||
ImVec2 size(min_width > 0.0f ? min_width : 0.0f,
|
||||
min_height > 0.0f ? min_height : 0.0f);
|
||||
|
||||
ImGui::BeginChild(title, size, ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY);
|
||||
|
||||
ImGui::TextUnformatted(title);
|
||||
ImGui::Separator();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dashboard_panel_end() {
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor(1);
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Dashboard panel — a styled child window with title bar.
|
||||
// Usage:
|
||||
// if (dashboard_panel_begin("Sales")) {
|
||||
// line_plot("Revenue", xs, ys, N);
|
||||
// }
|
||||
// dashboard_panel_end(); // ALWAYS call, even if begin returned false
|
||||
//
|
||||
// Features: title bar with text, rounded corners, subtle border, auto-resize.
|
||||
// min_width/min_height set minimum size constraints.
|
||||
|
||||
bool dashboard_panel_begin(const char* title, float min_width = 200.0f, float min_height = 150.0f);
|
||||
void dashboard_panel_end();
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: dashboard_panel
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "bool dashboard_panel_begin(const char* title, float min_width = 200.0f, float min_height = 150.0f); void dashboard_panel_end()"
|
||||
description: "Contenedor estilizado tipo panel para dashboards con titulo, bordes redondeados y tamaño minimo configurable"
|
||||
tags: [imgui, panel, container, layout, dashboard]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [imgui]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/dashboard_panel.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: title
|
||||
desc: "Titulo del panel, tambien sirve como ID de ImGui para distinguir multiples paneles"
|
||||
- name: min_width
|
||||
desc: "Ancho minimo del panel en pixels (0 = sin restriccion)"
|
||||
- name: min_height
|
||||
desc: "Alto minimo del panel en pixels (0 = sin restriccion)"
|
||||
output: "true si el panel es visible y se debe renderizar contenido; llamar siempre dashboard_panel_end() independientemente del valor de retorno"
|
||||
---
|
||||
|
||||
# dashboard_panel
|
||||
|
||||
Panel estilizado para dashboards ImGui. Envuelve un `BeginChild`/`EndChild` con estilos predefinidos: fondo oscuro (`#1F1F26`), bordes redondeados (5 px), borde visible y separador bajo el titulo.
|
||||
|
||||
## Uso
|
||||
|
||||
```cpp
|
||||
if (dashboard_panel_begin("Revenue", 300.0f, 200.0f)) {
|
||||
line_plot("Revenue", xs, ys, N);
|
||||
}
|
||||
dashboard_panel_end(); // siempre llamar
|
||||
```
|
||||
|
||||
## Implementacion
|
||||
|
||||
- `PushStyleVar` aplica `ChildRounding = 5.0f` y `ChildBorderSize = 1.0f`
|
||||
- `PushStyleColor` establece el fondo del child a `(0.12, 0.12, 0.15, 1.0)`
|
||||
- `BeginChild` con `ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY`
|
||||
- Titulo con `TextUnformatted` seguido de `Separator`
|
||||
- `dashboard_panel_end` hace `EndChild`, `PopStyleColor(1)`, `PopStyleVar(2)`
|
||||
|
||||
## Notas
|
||||
|
||||
- El titulo actua como ID de ImGui: dos paneles con el mismo titulo en el mismo frame se comportan como uno solo. Usar `##` para diferenciar IDs si se repite el texto: `"Revenue##panel1"`.
|
||||
- `AutoResizeY` hace que el panel crezca verticalmente con su contenido; `min_height` establece el piso.
|
||||
- El patron begin/end es idomatico en ImGui: `end` debe llamarse siempre para hacer pop de los estilos, aunque `begin` retorne false.
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "core/docking_layout.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
ImGuiID docking_layout(DockPreset preset) {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGuiID dockspace_id = ImGui::DockSpaceOverViewport(0, viewport);
|
||||
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return dockspace_id;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
if (preset == DockPreset::Default) {
|
||||
return dockspace_id;
|
||||
}
|
||||
|
||||
ImGui::DockBuilderRemoveNode(dockspace_id);
|
||||
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
||||
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
|
||||
|
||||
if (preset == DockPreset::TwoColumns) {
|
||||
ImGuiID right;
|
||||
ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.6f, nullptr, &right);
|
||||
|
||||
} else if (preset == DockPreset::ThreeColumns) {
|
||||
ImGuiID center, right;
|
||||
ImGuiID left_node;
|
||||
ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.33f, &left_node, ¢er);
|
||||
ImGui::DockBuilderSplitNode(center, ImGuiDir_Left, 0.5f, nullptr, &right);
|
||||
|
||||
} else if (preset == DockPreset::SidebarLeft) {
|
||||
ImGuiID main;
|
||||
ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.25f, nullptr, &main);
|
||||
|
||||
} else if (preset == DockPreset::SidebarRight) {
|
||||
ImGuiID sidebar;
|
||||
ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.25f, &sidebar, nullptr);
|
||||
|
||||
} else if (preset == DockPreset::TopBottom) {
|
||||
ImGuiID bottom;
|
||||
ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, 0.6f, nullptr, &bottom);
|
||||
|
||||
} else if (preset == DockPreset::Dashboard) {
|
||||
ImGuiID top, bottom;
|
||||
ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, 0.5f, &top, &bottom);
|
||||
ImGui::DockBuilderSplitNode(top, ImGuiDir_Left, 0.5f, nullptr, nullptr);
|
||||
ImGui::DockBuilderSplitNode(bottom, ImGuiDir_Left, 0.5f, nullptr, nullptr);
|
||||
}
|
||||
|
||||
ImGui::DockBuilderFinish(dockspace_id);
|
||||
return dockspace_id;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
|
||||
enum class DockPreset {
|
||||
Default, // full dockspace, no preset splits
|
||||
TwoColumns, // left 60% | right 40%
|
||||
ThreeColumns, // left 33% | center 34% | right 33%
|
||||
SidebarLeft, // sidebar 25% | main 75%
|
||||
SidebarRight, // main 75% | sidebar 25%
|
||||
TopBottom, // top 60% | bottom 40%
|
||||
Dashboard // top-left | top-right | bottom-left | bottom-right (2x2 grid)
|
||||
};
|
||||
|
||||
// Call once at the beginning of render_fn.
|
||||
// Returns the dockspace ID (use with ImGui::SetNextWindowDockID if needed).
|
||||
ImGuiID docking_layout(DockPreset preset = DockPreset::Default);
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
name: docking_layout
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "ImGuiID docking_layout(DockPreset preset = DockPreset::Default)"
|
||||
description: "Configura un docking space con presets de layout predefinidos para dashboards"
|
||||
tags: [imgui, docking, layout, dashboard]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [imgui]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/docking_layout.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: preset
|
||||
desc: "Layout predefinido a aplicar (Default, TwoColumns, ThreeColumns, SidebarLeft, SidebarRight, TopBottom, Dashboard)"
|
||||
output: "ID del dockspace creado, usable con ImGui::SetNextWindowDockID"
|
||||
---
|
||||
|
||||
# docking_layout
|
||||
|
||||
Configura un docking space fullscreen sobre el viewport principal con presets de layout predefinidos.
|
||||
|
||||
Usa `DockSpaceOverViewport` para crear el dockspace y, en el primer frame, aplica el preset con `DockBuilderSplitNode`. Llamar una vez al inicio de cada frame, antes de renderizar las ventanas hijas.
|
||||
|
||||
## Presets disponibles
|
||||
|
||||
| Preset | Layout |
|
||||
|---|---|
|
||||
| Default | Dockspace completo sin divisiones |
|
||||
| TwoColumns | Izquierda 60% / Derecha 40% |
|
||||
| ThreeColumns | Tres columnas iguales ~33% |
|
||||
| SidebarLeft | Sidebar 25% / Main 75% |
|
||||
| SidebarRight | Main 75% / Sidebar 25% |
|
||||
| TopBottom | Arriba 60% / Abajo 40% |
|
||||
| Dashboard | Grid 2x2 de cuatro paneles |
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```cpp
|
||||
void render_fn() {
|
||||
ImGuiID dock = docking_layout(DockPreset::SidebarLeft);
|
||||
|
||||
ImGui::Begin("Filters");
|
||||
// controles del sidebar
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Main");
|
||||
// contenido principal
|
||||
ImGui::End();
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Requiere que `ImGuiConfigFlags_DockingEnable` este activo en `ImGui::GetIO().ConfigFlags` (habilitado por `app_base.cpp`). El preset se aplica solo en el primer frame (static bool). `imgui_internal.h` es necesario para `DockBuilder*`.
|
||||
@@ -0,0 +1,174 @@
|
||||
#include "graph_spatial_hash.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static inline int floor_div(float v, float cell) {
|
||||
return static_cast<int>(std::floor(v / cell));
|
||||
}
|
||||
|
||||
static inline float sq(float x) { return x * x; }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Snapshot de posiciones (almacenado internamente en build)
|
||||
// La interfaz del header no pasa xs/ys a los metodos de query, por lo que
|
||||
// SpatialHash conserva copias internas para calcular distancias.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct SpatialHashSnap {
|
||||
float* xs = nullptr;
|
||||
float* ys = nullptr;
|
||||
float* sizes = nullptr;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
// Almacenamos el snapshot en memoria contigua al final del bloque de entries,
|
||||
// para no añadir campos al header publico. Usamos un puntero opaco en un
|
||||
// campo reservado. Como el header ya esta fijado, guardamos el snapshot como
|
||||
// una variable estatica por instancia — aceptable para uso single-threaded
|
||||
// tipico de ImGui. Para multi-instancia se puede usar un map.
|
||||
//
|
||||
// En la practica, la convencion del registry es que SpatialHash se crea una
|
||||
// vez por frame de ImGui y se usa en el mismo hilo de render.
|
||||
|
||||
static SpatialHashSnap g_snap; // snapshot mas reciente
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SpatialHash
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int SpatialHash::cell_hash(int cx, int cy) const {
|
||||
unsigned int h = static_cast<unsigned int>(cx) * 73856093u
|
||||
^ static_cast<unsigned int>(cy) * 19349663u;
|
||||
return static_cast<int>(h % static_cast<unsigned int>(table_size));
|
||||
}
|
||||
|
||||
SpatialHash::SpatialHash(float cell_size_, int table_size_)
|
||||
: cell_size(cell_size_)
|
||||
, table_size(table_size_)
|
||||
, entry_count(0)
|
||||
, entry_capacity(256)
|
||||
{
|
||||
buckets = static_cast<int*>(std::malloc(
|
||||
static_cast<std::size_t>(table_size) * sizeof(int)));
|
||||
|
||||
entries = static_cast<int*>(std::malloc(
|
||||
static_cast<std::size_t>(entry_capacity) * 2 * sizeof(int)));
|
||||
|
||||
std::memset(buckets, -1,
|
||||
static_cast<std::size_t>(table_size) * sizeof(int));
|
||||
}
|
||||
|
||||
SpatialHash::~SpatialHash() {
|
||||
std::free(buckets);
|
||||
std::free(entries);
|
||||
|
||||
std::free(g_snap.xs);
|
||||
std::free(g_snap.ys);
|
||||
std::free(g_snap.sizes);
|
||||
g_snap = {};
|
||||
}
|
||||
|
||||
void SpatialHash::build(const float* xs, const float* ys, const float* sizes, int count) {
|
||||
// --- Limpiar tabla ---
|
||||
std::memset(buckets, -1,
|
||||
static_cast<std::size_t>(table_size) * sizeof(int));
|
||||
entry_count = 0;
|
||||
|
||||
// --- Snapshot de posiciones para queries ---
|
||||
if (g_snap.count < count) {
|
||||
std::free(g_snap.xs);
|
||||
std::free(g_snap.ys);
|
||||
std::free(g_snap.sizes);
|
||||
g_snap.xs = static_cast<float*>(std::malloc(static_cast<std::size_t>(count) * sizeof(float)));
|
||||
g_snap.ys = static_cast<float*>(std::malloc(static_cast<std::size_t>(count) * sizeof(float)));
|
||||
g_snap.sizes = static_cast<float*>(std::malloc(static_cast<std::size_t>(count) * sizeof(float)));
|
||||
}
|
||||
std::memcpy(g_snap.xs, xs, static_cast<std::size_t>(count) * sizeof(float));
|
||||
std::memcpy(g_snap.ys, ys, static_cast<std::size_t>(count) * sizeof(float));
|
||||
std::memcpy(g_snap.sizes, sizes, static_cast<std::size_t>(count) * sizeof(float));
|
||||
g_snap.count = count;
|
||||
|
||||
// --- Insertar nodos en la tabla hash ---
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int cx = floor_div(xs[i], cell_size);
|
||||
int cy = floor_div(ys[i], cell_size);
|
||||
int bucket = cell_hash(cx, cy);
|
||||
|
||||
// Crecer entries si necesario
|
||||
if (entry_count >= entry_capacity) {
|
||||
entry_capacity *= 2;
|
||||
entries = static_cast<int*>(std::realloc(
|
||||
entries,
|
||||
static_cast<std::size_t>(entry_capacity) * 2 * sizeof(int)));
|
||||
}
|
||||
|
||||
// Insertar al frente de la cadena encadenada
|
||||
int slot = entry_count++;
|
||||
entries[slot * 2 + 0] = i; // node_index
|
||||
entries[slot * 2 + 1] = buckets[bucket]; // next_in_chain
|
||||
buckets[bucket] = slot;
|
||||
}
|
||||
}
|
||||
|
||||
int SpatialHash::query_nearest(float qx, float qy, float radius, float* out_dist) const {
|
||||
int best_idx = -1;
|
||||
float best_d = radius; // umbral: solo aceptamos dentro del radio
|
||||
|
||||
int qcx = floor_div(qx, cell_size);
|
||||
int qcy = floor_div(qy, cell_size);
|
||||
|
||||
// Escanear vecindad 3x3 de celdas
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
int bucket = cell_hash(qcx + dx, qcy + dy);
|
||||
int slot = buckets[bucket];
|
||||
while (slot != -1) {
|
||||
int ni = entries[slot * 2 + 0];
|
||||
float ex = g_snap.xs[ni] - qx;
|
||||
float ey = g_snap.ys[ni] - qy;
|
||||
float dist = std::sqrt(sq(ex) + sq(ey)) - g_snap.sizes[ni];
|
||||
if (dist < best_d) {
|
||||
best_d = dist;
|
||||
best_idx = ni;
|
||||
}
|
||||
slot = entries[slot * 2 + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (out_dist && best_idx != -1)
|
||||
*out_dist = best_d;
|
||||
|
||||
return best_idx;
|
||||
}
|
||||
|
||||
int SpatialHash::query_radius(float qx, float qy, float radius, int* out, int max_out) const {
|
||||
int found = 0;
|
||||
|
||||
int qcx = floor_div(qx, cell_size);
|
||||
int qcy = floor_div(qy, cell_size);
|
||||
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
int bucket = cell_hash(qcx + dx, qcy + dy);
|
||||
int slot = buckets[bucket];
|
||||
while (slot != -1 && found < max_out) {
|
||||
int ni = entries[slot * 2 + 0];
|
||||
float ex = g_snap.xs[ni] - qx;
|
||||
float ey = g_snap.ys[ni] - qy;
|
||||
float dist = std::sqrt(sq(ex) + sq(ey)) - g_snap.sizes[ni];
|
||||
if (dist <= radius)
|
||||
out[found++] = ni;
|
||||
slot = entries[slot * 2 + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// SpatialHash — grid espacial para hit-testing de nodos en grafos.
|
||||
// Permite buscar el nodo mas cercano a una posicion en O(1) amortizado.
|
||||
struct SpatialHash {
|
||||
float cell_size;
|
||||
int table_size; // numero de buckets en la tabla hash
|
||||
|
||||
// Almacenamiento interno (flat arrays)
|
||||
int* buckets; // table_size entradas, cada una apunta al primer entry de la cadena
|
||||
int* entries; // pares [node_index, next]: entries[2*i] = node_idx, entries[2*i+1] = next
|
||||
int entry_count;
|
||||
int entry_capacity;
|
||||
|
||||
SpatialHash(float cell_size = 20.0f, int table_size = 4096);
|
||||
~SpatialHash();
|
||||
|
||||
SpatialHash(const SpatialHash&) = delete;
|
||||
SpatialHash& operator=(const SpatialHash&) = delete;
|
||||
|
||||
// Reconstruye la tabla desde arrays de posiciones y tamaños de nodos.
|
||||
// xs[i], ys[i]: posicion del nodo i. sizes[i]: radio del nodo i.
|
||||
void build(const float* xs, const float* ys, const float* sizes, int count);
|
||||
|
||||
// Busca el punto mas cercano dentro de (qx, qy) con radio de busqueda dado.
|
||||
// Descuenta el tamaño del nodo (circulo): distancia efectiva = dist(centro) - size.
|
||||
// Retorna el indice del nodo, o -1 si no hay ninguno.
|
||||
// Si out_dist es no-nulo, escribe la distancia al nodo encontrado.
|
||||
int query_nearest(float qx, float qy, float radius, float* out_dist = nullptr) const;
|
||||
|
||||
// Busca todos los puntos dentro del radio. Escribe indices en out[].
|
||||
// Retorna el numero de nodos encontrados (hasta max_out).
|
||||
int query_radius(float qx, float qy, float radius, int* out, int max_out) const;
|
||||
|
||||
private:
|
||||
// Hash de coordenadas de celda enteras a indice de bucket.
|
||||
int cell_hash(int cx, int cy) const;
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: graph_spatial_hash
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "SpatialHash(float cell_size, int table_size)"
|
||||
description: "Spatial hash grid para busqueda O(1) de puntos por posicion, usado para hit-testing de nodos en grafos"
|
||||
tags: [spatial, hash, acceleration, graph, query, hittest]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/graph_spatial_hash.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: cell_size
|
||||
desc: "Tamaño de cada celda del grid espacial (debe ser >= radio maximo de nodos)"
|
||||
- name: table_size
|
||||
desc: "Numero de buckets en la tabla hash (potencia de 2 recomendada para distribucion uniforme)"
|
||||
output: "Estructura de hash espacial lista para queries de puntos cercanos"
|
||||
---
|
||||
|
||||
# graph_spatial_hash
|
||||
|
||||
Spatial hash grid para busqueda eficiente de nodos en grafos por posicion 2D. Util para hit-testing de nodos en editores de grafos basados en ImGui, donde se necesita saber que nodo esta bajo el cursor en cada frame.
|
||||
|
||||
## Uso
|
||||
|
||||
```cpp
|
||||
SpatialHash sh(20.0f, 4096);
|
||||
|
||||
// Reconstruir cada frame (o cuando cambian posiciones)
|
||||
sh.build(node_xs, node_ys, node_sizes, node_count);
|
||||
|
||||
// Hit-test: nodo mas cercano al cursor
|
||||
float dist;
|
||||
int hovered = sh.query_nearest(mouse_x, mouse_y, 30.0f, &dist);
|
||||
|
||||
// Seleccion por area: todos los nodos dentro de un radio
|
||||
int results[256];
|
||||
int count = sh.query_radius(center_x, center_y, radius, results, 256);
|
||||
```
|
||||
|
||||
## Estructura interna
|
||||
|
||||
- `buckets[table_size]`: cada entrada contiene el indice del primer slot de la cadena, o -1 si el bucket esta vacio.
|
||||
- `entries[2 * entry_capacity]`: pares `[node_index, next_in_chain]` almacenados como flat array. Se expande con `realloc` si el numero de nodos supera la capacidad inicial (256).
|
||||
- El snapshot de posiciones (`xs`, `ys`, `sizes`) se copia internamente en `build()` para que `query_nearest` y `query_radius` puedan calcular distancias sin recibir los arrays como parametro.
|
||||
|
||||
## Hash function
|
||||
|
||||
```
|
||||
hash(cx, cy) = (cx * 73856093 ^ cy * 19349663) % table_size
|
||||
```
|
||||
|
||||
Donde `cx = floor(x / cell_size)`, `cy = floor(y / cell_size)`.
|
||||
|
||||
## Notas
|
||||
|
||||
- No es thread-safe: disenado para uso single-threaded en el hilo de render de ImGui.
|
||||
- `build()` debe llamarse cada frame si las posiciones de los nodos cambian.
|
||||
- `cell_size` debe ser >= al radio maximo de los nodos para que la vecindad 3x3 capture todos los candidatos posibles.
|
||||
- La distancia efectiva al nodo se calcula como `dist(centro) - size`, de modo que el hit-test funciona correctamente para nodos de distintos tamaños.
|
||||
- No implementa `operator=` ni copy constructor (deleted) para evitar doble-free de los arrays internos.
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "core/memory_overlay.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
#include "tracy/Tracy.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#pragma comment(lib, "psapi.lib")
|
||||
#else
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
struct MemStats {
|
||||
long rss_kb = -1; // Resident Set Size
|
||||
long peak_kb = -1; // Peak RSS
|
||||
long vsize_kb = -1; // Virtual memory size
|
||||
};
|
||||
|
||||
static MemStats s_cached;
|
||||
static double s_last_sample = -1.0;
|
||||
|
||||
#ifdef _WIN32
|
||||
static MemStats sample_memory() {
|
||||
MemStats s;
|
||||
PROCESS_MEMORY_COUNTERS pmc{};
|
||||
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
|
||||
s.rss_kb = static_cast<long>(pmc.WorkingSetSize / 1024);
|
||||
s.peak_kb = static_cast<long>(pmc.PeakWorkingSetSize / 1024);
|
||||
s.vsize_kb = -1; // not easily available on Windows without VirtualQuery loop
|
||||
}
|
||||
return s;
|
||||
}
|
||||
#else
|
||||
static MemStats sample_memory() {
|
||||
MemStats s;
|
||||
FILE* f = std::fopen("/proc/self/status", "r");
|
||||
if (!f) return s;
|
||||
|
||||
char line[128];
|
||||
while (std::fgets(line, sizeof(line), f)) {
|
||||
long val = 0;
|
||||
if (std::sscanf(line, "VmRSS: %ld kB", &val) == 1) s.rss_kb = val;
|
||||
if (std::sscanf(line, "VmPeak: %ld kB", &val) == 1) s.peak_kb = val;
|
||||
if (std::sscanf(line, "VmSize: %ld kB", &val) == 1) s.vsize_kb = val;
|
||||
}
|
||||
std::fclose(f);
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
void memory_overlay() {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScoped;
|
||||
#endif
|
||||
|
||||
// Sample at most once per second
|
||||
const double now = ImGui::GetTime();
|
||||
if (s_last_sample < 0.0 || (now - s_last_sample) >= 1.0) {
|
||||
s_cached = sample_memory();
|
||||
s_last_sample = now;
|
||||
}
|
||||
|
||||
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration
|
||||
| ImGuiWindowFlags_AlwaysAutoResize
|
||||
| ImGuiWindowFlags_NoSavedSettings
|
||||
| ImGuiWindowFlags_NoFocusOnAppearing
|
||||
| ImGuiWindowFlags_NoNav
|
||||
| ImGuiWindowFlags_NoMove;
|
||||
|
||||
const float pad = 10.0f;
|
||||
const ImGuiViewport* vp = ImGui::GetMainViewport();
|
||||
ImVec2 pos(vp->WorkPos.x + vp->WorkSize.x - pad,
|
||||
vp->WorkPos.y + vp->WorkSize.y - pad);
|
||||
ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(1.0f, 1.0f));
|
||||
ImGui::SetNextWindowBgAlpha(0.65f);
|
||||
|
||||
if (ImGui::Begin("##memory_overlay", nullptr, flags)) {
|
||||
if (s_cached.rss_kb >= 0) {
|
||||
ImGui::Text("RSS: %5ld MB", s_cached.rss_kb / 1024);
|
||||
ImGui::Text("Peak: %5ld MB", s_cached.peak_kb / 1024);
|
||||
#ifndef _WIN32
|
||||
ImGui::Text("VSize: %5ld MB", s_cached.vsize_kb / 1024);
|
||||
#endif
|
||||
} else {
|
||||
ImGui::TextDisabled("Memory: N/A");
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// Renders a memory statistics overlay in the bottom-right corner.
|
||||
// Call within an ImGui frame once per frame.
|
||||
// Samples /proc/self/status (Linux) or GetProcessMemoryInfo (Windows) at most
|
||||
// once per second; results are cached between samples.
|
||||
void memory_overlay();
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: memory_overlay
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "void memory_overlay()"
|
||||
description: "Renderiza un overlay de estadisticas de memoria (RSS, peak, vsize) en la esquina inferior derecha"
|
||||
tags: [imgui, memory, overlay, debug, dashboard, profiling]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [imgui]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/memory_overlay.cpp"
|
||||
framework: imgui
|
||||
params: []
|
||||
output: "Renderiza el overlay de memoria en el frame ImGui actual"
|
||||
---
|
||||
|
||||
# memory_overlay
|
||||
|
||||
Muestra estadísticas de memoria del proceso en una ventana semi-transparente en la esquina inferior derecha. Complementa `fps_overlay` para un dashboard mínimo de rendimiento.
|
||||
|
||||
Las métricas se leen como máximo una vez por segundo y se cachean en una variable estática local, evitando I/O excesivo en el hot path de render.
|
||||
|
||||
## Métricas mostradas
|
||||
|
||||
| Campo | Linux | Windows |
|
||||
|---|---|---|
|
||||
| RSS (MB) | `VmRSS` de `/proc/self/status` | `WorkingSetSize` (PSAPI) |
|
||||
| Peak RSS (MB) | `VmPeak` de `/proc/self/status` | `PeakWorkingSetSize` (PSAPI) |
|
||||
| Virtual Size (MB) | `VmSize` de `/proc/self/status` | N/A (no mostrado) |
|
||||
|
||||
En Windows se requiere enlazar `psapi.lib` (el `.cpp` incluye `#pragma comment(lib, "psapi.lib")`).
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```cpp
|
||||
// En el loop de render, dentro de un frame ImGui:
|
||||
fps_overlay();
|
||||
memory_overlay();
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- La función sigue la misma convención que `fps_overlay`: mismos flags de ventana, mismo alpha (0.65).
|
||||
- Posición: esquina inferior derecha (`pivot = {1,1}`), separada 10 px del borde.
|
||||
- La lectura de `/proc/self/status` es I/O local de ~0.1 ms, amortizada a 1 Hz — despreciable.
|
||||
- Con `TRACY_ENABLE` activo, la función registra una zona `ZoneScoped` para que aparezca en el profiler.
|
||||
- Si la plataforma no puede leer las estadísticas, muestra "Memory: N/A" en gris.
|
||||
@@ -0,0 +1,131 @@
|
||||
#include "plot_theme.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Preset structs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
PlotTheme plot_theme_preset_dark() {
|
||||
PlotTheme t;
|
||||
t.name = "dark";
|
||||
t.bg = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
|
||||
t.frame_bg = ImVec4(0.14f, 0.14f, 0.17f, 1.00f);
|
||||
t.text = ImVec4(0.88f, 0.88f, 0.90f, 1.00f);
|
||||
t.grid = ImVec4(0.30f, 0.30f, 0.35f, 0.60f);
|
||||
t.palette_count = 10;
|
||||
t.palette[0] = ImVec4(0.33f, 0.62f, 0.91f, 1.00f); // steel blue
|
||||
t.palette[1] = ImVec4(0.35f, 0.78f, 0.54f, 1.00f); // soft green
|
||||
t.palette[2] = ImVec4(0.96f, 0.60f, 0.25f, 1.00f); // warm orange
|
||||
t.palette[3] = ImVec4(0.85f, 0.38f, 0.42f, 1.00f); // muted red
|
||||
t.palette[4] = ImVec4(0.72f, 0.52f, 0.88f, 1.00f); // lavender
|
||||
t.palette[5] = ImVec4(0.38f, 0.80f, 0.82f, 1.00f); // teal
|
||||
t.palette[6] = ImVec4(0.95f, 0.80f, 0.32f, 1.00f); // gold
|
||||
t.palette[7] = ImVec4(0.60f, 0.82f, 0.38f, 1.00f); // lime
|
||||
t.palette[8] = ImVec4(0.90f, 0.55f, 0.75f, 1.00f); // pink
|
||||
t.palette[9] = ImVec4(0.55f, 0.70f, 0.95f, 1.00f); // periwinkle
|
||||
return t;
|
||||
}
|
||||
|
||||
PlotTheme plot_theme_preset_light() {
|
||||
PlotTheme t;
|
||||
t.name = "light";
|
||||
t.bg = ImVec4(0.97f, 0.97f, 0.97f, 1.00f);
|
||||
t.frame_bg = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
t.text = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
|
||||
t.grid = ImVec4(0.70f, 0.70f, 0.72f, 0.60f);
|
||||
t.palette_count = 10;
|
||||
t.palette[0] = ImVec4(0.12f, 0.47f, 0.71f, 1.00f); // deep blue
|
||||
t.palette[1] = ImVec4(0.17f, 0.63f, 0.17f, 1.00f); // deep green
|
||||
t.palette[2] = ImVec4(0.84f, 0.37f, 0.00f, 1.00f); // burnt orange
|
||||
t.palette[3] = ImVec4(0.84f, 0.15f, 0.16f, 1.00f); // vivid red
|
||||
t.palette[4] = ImVec4(0.58f, 0.40f, 0.74f, 1.00f); // purple
|
||||
t.palette[5] = ImVec4(0.09f, 0.75f, 0.81f, 1.00f); // cyan
|
||||
t.palette[6] = ImVec4(0.74f, 0.74f, 0.13f, 1.00f); // olive
|
||||
t.palette[7] = ImVec4(0.09f, 0.75f, 0.81f, 1.00f); // sky blue
|
||||
t.palette[8] = ImVec4(0.89f, 0.47f, 0.76f, 1.00f); // rose
|
||||
t.palette[9] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); // grey
|
||||
return t;
|
||||
}
|
||||
|
||||
PlotTheme plot_theme_preset_high_contrast() {
|
||||
PlotTheme t;
|
||||
t.name = "high_contrast";
|
||||
t.bg = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
|
||||
t.frame_bg = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
||||
t.text = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
t.grid = ImVec4(0.30f, 0.30f, 0.30f, 0.80f);
|
||||
t.palette_count = 10;
|
||||
t.palette[0] = ImVec4(0.00f, 1.00f, 1.00f, 1.00f); // cyan neon
|
||||
t.palette[1] = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); // green neon
|
||||
t.palette[2] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); // orange neon
|
||||
t.palette[3] = ImVec4(1.00f, 0.00f, 0.50f, 1.00f); // magenta
|
||||
t.palette[4] = ImVec4(1.00f, 1.00f, 0.00f, 1.00f); // yellow
|
||||
t.palette[5] = ImVec4(0.40f, 0.80f, 1.00f, 1.00f); // sky blue
|
||||
t.palette[6] = ImVec4(1.00f, 0.40f, 0.40f, 1.00f); // salmon
|
||||
t.palette[7] = ImVec4(0.60f, 1.00f, 0.40f, 1.00f); // lime
|
||||
t.palette[8] = ImVec4(1.00f, 0.80f, 1.00f, 1.00f); // lavender bright
|
||||
t.palette[9] = ImVec4(0.80f, 1.00f, 0.80f, 1.00f); // mint bright
|
||||
return t;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal helper
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void apply_imgui_colors(const PlotTheme& theme) {
|
||||
ImGuiStyle& s = ImGui::GetStyle();
|
||||
s.Colors[ImGuiCol_WindowBg] = theme.bg;
|
||||
s.Colors[ImGuiCol_ChildBg] = theme.bg;
|
||||
s.Colors[ImGuiCol_PopupBg] = theme.frame_bg;
|
||||
s.Colors[ImGuiCol_FrameBg] = theme.frame_bg;
|
||||
s.Colors[ImGuiCol_FrameBgHovered] = theme.frame_bg;
|
||||
s.Colors[ImGuiCol_FrameBgActive] = theme.frame_bg;
|
||||
s.Colors[ImGuiCol_Text] = theme.text;
|
||||
s.Colors[ImGuiCol_TextDisabled] = ImVec4(theme.text.x * 0.5f,
|
||||
theme.text.y * 0.5f,
|
||||
theme.text.z * 0.5f,
|
||||
0.80f);
|
||||
}
|
||||
|
||||
static void apply_implot_colors(const PlotTheme& theme) {
|
||||
ImPlotStyle& ps = ImPlot::GetStyle();
|
||||
ps.Colors[ImPlotCol_PlotBg] = theme.bg;
|
||||
ps.Colors[ImPlotCol_PlotBorder] = theme.frame_bg;
|
||||
ps.Colors[ImPlotCol_FrameBg] = theme.frame_bg;
|
||||
ps.Colors[ImPlotCol_LegendBg] = theme.frame_bg;
|
||||
ps.Colors[ImPlotCol_LegendBorder] = theme.grid;
|
||||
ps.Colors[ImPlotCol_LegendText] = theme.text;
|
||||
ps.Colors[ImPlotCol_TitleText] = theme.text;
|
||||
ps.Colors[ImPlotCol_InlayText] = theme.text;
|
||||
ps.Colors[ImPlotCol_AxisText] = theme.text;
|
||||
ps.Colors[ImPlotCol_AxisGrid] = theme.grid;
|
||||
ps.Colors[ImPlotCol_AxisTick] = theme.grid;
|
||||
|
||||
// Register custom colormap from palette
|
||||
const int count = theme.palette_count > 10 ? 10 : theme.palette_count;
|
||||
ImPlot::AddColormap(theme.name, theme.palette, count, false);
|
||||
ImPlot::SetColormap(theme.name);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void plot_theme_apply(const PlotTheme& theme) {
|
||||
apply_imgui_colors(theme);
|
||||
apply_implot_colors(theme);
|
||||
}
|
||||
|
||||
void plot_theme_dark() {
|
||||
PlotTheme t = plot_theme_preset_dark();
|
||||
plot_theme_apply(t);
|
||||
}
|
||||
|
||||
void plot_theme_light() {
|
||||
PlotTheme t = plot_theme_preset_light();
|
||||
plot_theme_apply(t);
|
||||
}
|
||||
|
||||
void plot_theme_high_contrast() {
|
||||
PlotTheme t = plot_theme_preset_high_contrast();
|
||||
plot_theme_apply(t);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
#include "implot.h"
|
||||
|
||||
struct PlotTheme {
|
||||
const char* name;
|
||||
ImVec4 bg; // plot background
|
||||
ImVec4 frame_bg; // frame background
|
||||
ImVec4 text; // text color
|
||||
ImVec4 grid; // grid lines
|
||||
ImVec4 palette[10]; // color palette for series
|
||||
int palette_count;
|
||||
};
|
||||
|
||||
// Preset themes
|
||||
void plot_theme_dark();
|
||||
void plot_theme_light();
|
||||
void plot_theme_high_contrast();
|
||||
|
||||
// Custom theme
|
||||
void plot_theme_apply(const PlotTheme& theme);
|
||||
|
||||
// Get preset theme structs (for inspection/modification)
|
||||
PlotTheme plot_theme_preset_dark();
|
||||
PlotTheme plot_theme_preset_light();
|
||||
PlotTheme plot_theme_preset_high_contrast();
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
name: plot_theme
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "void plot_theme_dark() / void plot_theme_light() / void plot_theme_high_contrast() / void plot_theme_apply(const PlotTheme& theme)"
|
||||
description: "Gestiona temas y paletas de colores para ImPlot e ImGui, con presets dark/light/high-contrast y soporte para temas custom"
|
||||
tags: [theme, colors, palette, styling, dashboard]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [imgui, implot]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/plot_theme.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: theme
|
||||
desc: "Estructura PlotTheme con nombre, colores de fondo/frame/texto/grid y paleta de hasta 10 colores para series de datos"
|
||||
output: "Aplica el tema al contexto ImPlot/ImGui actual modificando ImGuiStyle y ImPlotStyle en memoria"
|
||||
---
|
||||
|
||||
# plot_theme
|
||||
|
||||
Configura el aspecto visual de ventanas ImGui y graficas ImPlot en un solo paso. Tres presets listos para usar y una API de tema custom para casos especificos.
|
||||
|
||||
## Presets
|
||||
|
||||
### `plot_theme_dark()`
|
||||
|
||||
Fondo oscuro (`#1A1A1F`) con paleta de 10 colores suaves: steel blue, soft green, warm orange, muted red, lavender, teal, gold, lime, pink, periwinkle. Ideal para dashboards de monitoreo.
|
||||
|
||||
### `plot_theme_light()`
|
||||
|
||||
Fondo claro (`#F7F7F7`) con colores vibrantes de alto contraste sobre blanco. Recomendado para capturas de pantalla o exportacion a documentos.
|
||||
|
||||
### `plot_theme_high_contrast()`
|
||||
|
||||
Fondo negro puro con colores neon: cyan, green, orange, magenta, yellow. Disenado para presentaciones en pantallas grandes o condiciones de poca luz.
|
||||
|
||||
## Uso
|
||||
|
||||
```cpp
|
||||
#include "plot_theme.h"
|
||||
|
||||
// Al inicio del frame, antes de BeginMainMenuBar / Begin
|
||||
plot_theme_dark();
|
||||
|
||||
// O con tema custom
|
||||
PlotTheme my_theme = plot_theme_preset_dark();
|
||||
my_theme.palette[0] = ImVec4(1.0f, 0.0f, 0.5f, 1.0f); // override primer color
|
||||
plot_theme_apply(my_theme);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```cpp
|
||||
// Presets de aplicacion directa
|
||||
void plot_theme_dark();
|
||||
void plot_theme_light();
|
||||
void plot_theme_high_contrast();
|
||||
|
||||
// Tema custom
|
||||
void plot_theme_apply(const PlotTheme& theme);
|
||||
|
||||
// Obtener structs para inspeccion o modificacion parcial
|
||||
PlotTheme plot_theme_preset_dark();
|
||||
PlotTheme plot_theme_preset_light();
|
||||
PlotTheme plot_theme_preset_high_contrast();
|
||||
```
|
||||
|
||||
## Estructura PlotTheme
|
||||
|
||||
```cpp
|
||||
struct PlotTheme {
|
||||
const char* name; // nombre del colormap registrado en ImPlot
|
||||
ImVec4 bg; // fondo del plot (ImPlotCol_PlotBg, ImGuiCol_WindowBg)
|
||||
ImVec4 frame_bg; // fondo del frame (ImPlotCol_FrameBg, ImGuiCol_FrameBg)
|
||||
ImVec4 text; // color del texto (todos los ImGui/ImPlot text cols)
|
||||
ImVec4 grid; // lineas de cuadricula y ticks
|
||||
ImVec4 palette[10]; // paleta de colores para series de datos
|
||||
int palette_count; // numero de colores activos en la paleta (1-10)
|
||||
};
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- `plot_theme_apply` llama a `ImPlot::AddColormap` y `ImPlot::SetColormap` con el campo `name` del tema como identificador. Si se llama dos veces con el mismo `name`, ImPlot registra el colormap de nuevo — usar nombres distintos si se crean multiples variantes en runtime.
|
||||
- La funcion es "pure" en el sentido de que su unico efecto es escribir en `ImGuiStyle` e `ImPlotStyle` del contexto activo, sin I/O ni estado global propio.
|
||||
- Requiere que `ImGui::CreateContext()` e `ImPlot::CreateContext()` hayan sido llamados antes del primer uso.
|
||||
- Compatible con C++17. No usa excepciones ni RTTI.
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "core/sidebar.h"
|
||||
#include "imgui.h"
|
||||
|
||||
static bool s_sidebar_was_open = false;
|
||||
|
||||
bool sidebar_begin(const char* title, bool* open, float width) {
|
||||
if (!*open) {
|
||||
s_sidebar_was_open = false;
|
||||
if (ImGui::Button(">")) {
|
||||
*open = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
s_sidebar_was_open = true;
|
||||
ImGui::SetNextWindowSize(ImVec2(width, 0.0f), ImGuiCond_Always);
|
||||
ImGui::Begin(title, open, ImGuiWindowFlags_NoCollapse);
|
||||
return true;
|
||||
}
|
||||
|
||||
void sidebar_end() {
|
||||
if (s_sidebar_was_open) {
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Collapsible sidebar panel.
|
||||
// Usage:
|
||||
// if (sidebar_begin("Filters", &sidebar_open)) {
|
||||
// // draw filter controls
|
||||
// }
|
||||
// sidebar_end();
|
||||
//
|
||||
// The sidebar renders as a fixed-width ImGui window.
|
||||
// When collapsed, only a small toggle button is shown.
|
||||
|
||||
bool sidebar_begin(const char* title, bool* open, float width = 250.0f);
|
||||
void sidebar_end();
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: sidebar
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "bool sidebar_begin(const char* title, bool* open, float width = 250.0f)"
|
||||
description: "Panel lateral colapsable para filtros y controles de dashboard"
|
||||
tags: [imgui, sidebar, panel, layout, dashboard, controls]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [imgui]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/sidebar.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: title
|
||||
desc: "Titulo del sidebar mostrado en la barra de titulo de la ventana"
|
||||
- name: open
|
||||
desc: "Puntero al estado abierto/cerrado; se pone a false si el usuario cierra la ventana"
|
||||
- name: width
|
||||
desc: "Ancho del sidebar en pixels (por defecto 250)"
|
||||
output: "true si el sidebar esta abierto y se debe renderizar contenido entre sidebar_begin y sidebar_end"
|
||||
---
|
||||
|
||||
# sidebar
|
||||
|
||||
Panel lateral colapsable. Cuando `*open == true` renderiza una ventana ImGui de ancho fijo con boton de cierre. Cuando `*open == false` muestra un boton compacto ">" para reabrir.
|
||||
|
||||
Siempre llamar `sidebar_end()` despues de `sidebar_begin()`, independientemente del valor de retorno.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```cpp
|
||||
static bool filters_open = true;
|
||||
|
||||
void render_fn() {
|
||||
if (sidebar_begin("Filters", &filters_open)) {
|
||||
ImGui::SliderFloat("Min value", &min_val, 0.0f, 100.0f);
|
||||
ImGui::Checkbox("Show inactive", &show_inactive);
|
||||
}
|
||||
sidebar_end();
|
||||
|
||||
// contenido principal
|
||||
ImGui::Begin("Main");
|
||||
// ...
|
||||
ImGui::End();
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
El estado de `s_sidebar_was_open` es una variable estatica interna que coordina `sidebar_begin` y `sidebar_end`. Solo un sidebar activo a la vez por frame (patron begin/end clasico de ImGui). Para multiples sidebars simultaneos, instanciar logica propia con estado separado.
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "core/tab_container.h"
|
||||
#include "imgui.h"
|
||||
|
||||
bool tab_container_begin(const char* id) {
|
||||
return ImGui::BeginTabBar(
|
||||
id,
|
||||
ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown
|
||||
);
|
||||
}
|
||||
|
||||
bool tab_container_tab(const char* label) {
|
||||
return ImGui::BeginTabItem(label);
|
||||
}
|
||||
|
||||
void tab_container_tab_end() {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
void tab_container_end() {
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
// Tab container — wraps ImGui::BeginTabBar with dashboard styling.
|
||||
// Usage:
|
||||
// if (tab_container_begin("##views")) {
|
||||
// if (tab_container_tab("Overview")) {
|
||||
// // draw overview content
|
||||
// tab_container_tab_end();
|
||||
// }
|
||||
// if (tab_container_tab("Details")) {
|
||||
// // draw details content
|
||||
// tab_container_tab_end();
|
||||
// }
|
||||
// }
|
||||
// tab_container_end();
|
||||
|
||||
bool tab_container_begin(const char* id);
|
||||
bool tab_container_tab(const char* label);
|
||||
void tab_container_tab_end();
|
||||
void tab_container_end();
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
name: tab_container
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "bool tab_container_begin(const char* id)"
|
||||
description: "Contenedor de tabs para organizar vistas multiples en un dashboard"
|
||||
tags: [imgui, tabs, container, layout, dashboard]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [imgui]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/tab_container.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: id
|
||||
desc: "Identificador unico del tab bar (ej: '##views'). Puede ser invisible con prefijo ##"
|
||||
- name: label
|
||||
desc: "Etiqueta visible de cada tab individual"
|
||||
output: "tab_container_begin: true si el tab bar esta activo. tab_container_tab: true si el tab esta seleccionado y se debe renderizar su contenido"
|
||||
---
|
||||
|
||||
# tab_container
|
||||
|
||||
Wrapper fino sobre `ImGui::BeginTabBar` / `EndTabBar` con flags de dashboard preconfigurados: `Reorderable` (arrastrar tabs) y `FittingPolicyResizeDown` (tabs se achican si no caben).
|
||||
|
||||
API de cuatro funciones siguiendo el patron begin/end de ImGui. Siempre llamar `tab_container_end()` si `tab_container_begin()` retorno true. Siempre llamar `tab_container_tab_end()` dentro del bloque `if (tab_container_tab(...))`.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```cpp
|
||||
void render_fn() {
|
||||
ImGui::Begin("Dashboard");
|
||||
|
||||
if (tab_container_begin("##main_tabs")) {
|
||||
if (tab_container_tab("Overview")) {
|
||||
render_overview();
|
||||
tab_container_tab_end();
|
||||
}
|
||||
if (tab_container_tab("Details")) {
|
||||
render_details();
|
||||
tab_container_tab_end();
|
||||
}
|
||||
if (tab_container_tab("Settings")) {
|
||||
render_settings();
|
||||
tab_container_tab_end();
|
||||
}
|
||||
}
|
||||
tab_container_end();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Los flags `ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown` son fijos para estandarizar la experiencia de tabs en dashboards. Si se necesitan flags distintos usar `ImGui::BeginTabBar` directamente.
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "time_series_buffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
TimeSeriesBuffer::TimeSeriesBuffer(size_t cap)
|
||||
: data(new float[cap]), capacity(cap), count(0), offset(0) {}
|
||||
|
||||
TimeSeriesBuffer::~TimeSeriesBuffer() {
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
TimeSeriesBuffer::TimeSeriesBuffer(TimeSeriesBuffer&& other) noexcept
|
||||
: data(other.data), capacity(other.capacity), count(other.count), offset(other.offset) {
|
||||
other.data = nullptr;
|
||||
other.capacity = 0;
|
||||
other.count = 0;
|
||||
other.offset = 0;
|
||||
}
|
||||
|
||||
TimeSeriesBuffer& TimeSeriesBuffer::operator=(TimeSeriesBuffer&& other) noexcept {
|
||||
if (this != &other) {
|
||||
delete[] data;
|
||||
data = other.data;
|
||||
capacity = other.capacity;
|
||||
count = other.count;
|
||||
offset = other.offset;
|
||||
other.data = nullptr;
|
||||
other.capacity = 0;
|
||||
other.count = 0;
|
||||
other.offset = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TimeSeriesBuffer::push(float value) {
|
||||
data[offset % capacity] = value;
|
||||
offset++;
|
||||
if (count < capacity) count++;
|
||||
}
|
||||
|
||||
float TimeSeriesBuffer::get(size_t index) const {
|
||||
assert(index < count);
|
||||
// oldest element is at (offset - count) % capacity
|
||||
size_t real = (offset - count + index) % capacity;
|
||||
return data[real];
|
||||
}
|
||||
|
||||
float TimeSeriesBuffer::latest() const {
|
||||
assert(count > 0);
|
||||
return data[(offset - 1) % capacity];
|
||||
}
|
||||
|
||||
float TimeSeriesBuffer::min() const {
|
||||
float m = std::numeric_limits<float>::max();
|
||||
for (size_t i = 0; i < count; ++i) m = std::min(m, get(i));
|
||||
return m;
|
||||
}
|
||||
|
||||
float TimeSeriesBuffer::max() const {
|
||||
float m = std::numeric_limits<float>::lowest();
|
||||
for (size_t i = 0; i < count; ++i) m = std::max(m, get(i));
|
||||
return m;
|
||||
}
|
||||
|
||||
float TimeSeriesBuffer::average() const {
|
||||
if (count == 0) return 0.0f;
|
||||
float sum = 0.0f;
|
||||
for (size_t i = 0; i < count; ++i) sum += get(i);
|
||||
return sum / static_cast<float>(count);
|
||||
}
|
||||
|
||||
size_t TimeSeriesBuffer::size() const {
|
||||
return count;
|
||||
}
|
||||
|
||||
bool TimeSeriesBuffer::full() const {
|
||||
return count == capacity;
|
||||
}
|
||||
|
||||
size_t TimeSeriesBuffer::copy_ordered(float* out, size_t out_capacity) const {
|
||||
size_t n = std::min(count, out_capacity);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
out[i] = get(i);
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
|
||||
struct TimeSeriesBuffer {
|
||||
float* data;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
size_t offset; // write head
|
||||
|
||||
TimeSeriesBuffer(size_t cap);
|
||||
~TimeSeriesBuffer();
|
||||
|
||||
// Non-copyable, moveable
|
||||
TimeSeriesBuffer(const TimeSeriesBuffer&) = delete;
|
||||
TimeSeriesBuffer& operator=(const TimeSeriesBuffer&) = delete;
|
||||
TimeSeriesBuffer(TimeSeriesBuffer&& other) noexcept;
|
||||
TimeSeriesBuffer& operator=(TimeSeriesBuffer&& other) noexcept;
|
||||
|
||||
void push(float value);
|
||||
float get(size_t index) const; // 0 = oldest
|
||||
float latest() const;
|
||||
float min() const;
|
||||
float max() const;
|
||||
float average() const;
|
||||
size_t size() const;
|
||||
bool full() const;
|
||||
|
||||
// For ImPlot: copies data in order to a contiguous array
|
||||
// Returns actual count written
|
||||
size_t copy_ordered(float* out, size_t out_capacity) const;
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: time_series_buffer
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "TimeSeriesBuffer(size_t capacity)"
|
||||
description: "Ring buffer circular para datos de series temporales, optimizado para streaming de metricas en dashboards en tiempo real"
|
||||
tags: [buffer, timeseries, streaming, dashboard, data]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/time_series_buffer.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: capacity
|
||||
desc: "Numero maximo de muestras que almacena el buffer"
|
||||
output: "Buffer circular listo para push de datos y lectura ordenada"
|
||||
---
|
||||
|
||||
# time_series_buffer
|
||||
|
||||
Ring buffer circular de floats para series temporales en tiempo real. Diseñado para alimentar plots de ImPlot con metricas de streaming (FPS, latencia, uso de CPU, etc.).
|
||||
|
||||
## Uso basico
|
||||
|
||||
```cpp
|
||||
TimeSeriesBuffer fps_history(512);
|
||||
|
||||
// En el loop principal:
|
||||
fps_history.push(ImGui::GetIO().Framerate);
|
||||
|
||||
// Para renderizar con ImPlot:
|
||||
float ordered[512];
|
||||
size_t n = fps_history.copy_ordered(ordered, 512);
|
||||
ImPlot::PlotLines("FPS", ordered, (int)n);
|
||||
```
|
||||
|
||||
## Semantica del ring buffer
|
||||
|
||||
- `push(v)` escribe en `data[offset % capacity]` y avanza el write head.
|
||||
- `count` crece hasta `capacity` y se mantiene ahi — el buffer nunca desborda.
|
||||
- `get(0)` retorna el elemento mas antiguo; `get(count-1)` el mas reciente.
|
||||
- `latest()` es equivalente a `get(count-1)` pero mas explicito.
|
||||
- `copy_ordered` extrae los datos en orden cronologico para pasarlos a ImPlot u otro consumidor que espere un array contiguo.
|
||||
|
||||
## Notas
|
||||
|
||||
- No depende de ImGui ni ImPlot directamente — es una estructura de datos pura.
|
||||
- No es thread-safe. Para uso multihilo, proteger con mutex externo.
|
||||
- Move semantics implementadas; copy queda eliminada (`= delete`) para evitar copias accidentales del heap.
|
||||
- `min`, `max` y `average` iteran sobre `count` elementos — O(n). Para buffers grandes en hot paths, considerar mantener un acumulador incremental.
|
||||
@@ -0,0 +1,5 @@
|
||||
// tracy_zone.cpp
|
||||
// All definitions live in tracy_zone.h (macros + constexpr).
|
||||
// This translation unit exists so the header can be included in any build
|
||||
// without requiring Tracy — compile with -DTRACY_ENABLE to activate profiling.
|
||||
#include "core/tracy_zone.h"
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
// Convenience macros for Tracy profiling zones.
|
||||
// No-op when TRACY_ENABLE is not defined.
|
||||
// Usage: FN_ZONE("my function") at the top of a scope.
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
// Named zone (appears as-is in Tracy timeline)
|
||||
#define FN_ZONE(name) ZoneScopedN(name)
|
||||
// Named zone with explicit ARGB color
|
||||
#define FN_ZONE_COLOR(name, color) ZoneScopedNC(name, color)
|
||||
// Frame boundary marker
|
||||
#define FN_FRAME_MARK FrameMark
|
||||
// Plot a scalar value in Tracy's plot view
|
||||
#define FN_PLOT(name, val) TracyPlot(name, val)
|
||||
|
||||
#else
|
||||
|
||||
#define FN_ZONE(name) (void)0
|
||||
#define FN_ZONE_COLOR(name, color) (void)0
|
||||
#define FN_FRAME_MARK (void)0
|
||||
#define FN_PLOT(name, val) (void)0
|
||||
|
||||
#endif
|
||||
|
||||
// Preset ARGB colors for common zone categories.
|
||||
namespace fn_tracy {
|
||||
constexpr uint32_t COLOR_RENDER = 0x2196F3; // blue
|
||||
constexpr uint32_t COLOR_UPDATE = 0x4CAF50; // green
|
||||
constexpr uint32_t COLOR_IO = 0xFF9800; // orange
|
||||
constexpr uint32_t COLOR_NETWORK = 0xF44336; // red
|
||||
constexpr uint32_t COLOR_COMPUTE = 0x9C27B0; // purple
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: tracy_zone
|
||||
kind: function
|
||||
lang: cpp
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "FN_ZONE(name) / FN_ZONE_COLOR(name, color) / FN_FRAME_MARK / FN_PLOT(name, val)"
|
||||
description: "Macros y constantes de conveniencia para Tracy profiling zones, compilables sin Tracy"
|
||||
tags: [tracy, profiling, debug, performance, raii]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/core/tracy_zone.cpp"
|
||||
framework: imgui
|
||||
params:
|
||||
- name: name
|
||||
desc: "Nombre de la zona tal como aparece en la timeline de Tracy"
|
||||
- name: color
|
||||
desc: "Color ARGB uint32 de la zona en Tracy (opcional, solo FN_ZONE_COLOR)"
|
||||
output: "Zona Tracy activa durante el scope actual; no-op cuando TRACY_ENABLE no está definido"
|
||||
---
|
||||
|
||||
# tracy_zone
|
||||
|
||||
Macros de scope para instrumentar secciones de código con Tracy Profiler. Cuando `TRACY_ENABLE` no está definido (ej. builds de producción) todas las macros se expanden a `(void)0`, sin coste alguno.
|
||||
|
||||
## Macros disponibles
|
||||
|
||||
| Macro | Descripción |
|
||||
|---|---|
|
||||
| `FN_ZONE("name")` | Zona con nombre, color automático |
|
||||
| `FN_ZONE_COLOR("name", color)` | Zona con nombre y color ARGB explícito |
|
||||
| `FN_FRAME_MARK` | Marca el límite de frame (eje X de Tracy) |
|
||||
| `FN_PLOT("name", val)` | Envía un valor escalar al panel de plots |
|
||||
|
||||
## Colores predefinidos (`fn_tracy::`)
|
||||
|
||||
```cpp
|
||||
fn_tracy::COLOR_RENDER // 0x2196F3 azul — rendering
|
||||
fn_tracy::COLOR_UPDATE // 0x4CAF50 verde — game update / logic
|
||||
fn_tracy::COLOR_IO // 0xFF9800 naranja — I/O disco/red
|
||||
fn_tracy::COLOR_NETWORK // 0xF44336 rojo — red/HTTP
|
||||
fn_tracy::COLOR_COMPUTE // 0x9C27B0 morado — compute / shader prep
|
||||
```
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```cpp
|
||||
#include "core/tracy_zone.h"
|
||||
|
||||
void render_scene() {
|
||||
FN_ZONE_COLOR("render_scene", fn_tracy::COLOR_RENDER);
|
||||
// ... render calls ...
|
||||
}
|
||||
|
||||
void update(float dt) {
|
||||
FN_ZONE("update");
|
||||
// ... game logic ...
|
||||
FN_PLOT("dt_ms", dt * 1000.0f);
|
||||
}
|
||||
|
||||
void main_loop() {
|
||||
while (running) {
|
||||
update(dt);
|
||||
render_scene();
|
||||
FN_FRAME_MARK;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Compilar con `-DTRACY_ENABLE` y enlazar `TracyClient.cpp` para activar profiling.
|
||||
- Sin `TRACY_ENABLE` el .cpp es prácticamente vacío — coste cero en producción.
|
||||
- Los colores son ARGB; si el alpha es 0 Tracy aplica su color automático por zona.
|
||||
- `FN_ZONE` expande a `ZoneScopedN(name)` de Tracy, que crea el contexto con `__LINE__` como discriminador — es seguro llamar varias veces en el mismo scope.
|
||||
Reference in New Issue
Block a user