f8f72e4bf7
Aggregates the planning artifacts for the 0049 series (umbrella + 0049a..0049k): - New rule cpp_apps.md (registered in INDEX) — standardize structure, CMake patterns, app.md frontmatter and sub-repo for C++ apps; points to the authoritative cpp/PATTERNS.md and cpp/DESIGN_SYSTEM.md. - Feature flag osint_graph_v1 (disabled until 0049k closes). - Issue 0049 (umbrella) and sub-issues 0049b..0049k describing the GPU rendering system, force-layout, types, sources, labels and the final graph_explorer app integration. - README updated with the new rows (all pending; 0049a will flip to completed in the next commit).
5.3 KiB
5.3 KiB
0049j — graph_labels: render de etiquetas con LabelPolicy
Metadata
| Campo | Valor |
|---|---|
| ID | 0049j |
| Estado | pendiente |
| Prioridad | media |
| Tipo | feature — parte de #0049 |
Dependencias
Bloqueada por: 0049e (necesita label_idx + flags).
Objetivo
Funcion graph_labels_draw que renderiza etiquetas de nodos seleccionados/hover/pinned + top-N por importancia, con politica configurable y culling por viewport. Independiente del renderer GPU — usa ImDrawList sobre el FBO.
Contexto
Maltego/OSINT necesita leer "juan@x.com", IBAN, etc. No se pueden mostrar 20k labels — pero se puede mostrar:
- Siempre los selected/hovered/pinned (suelen ser pocos).
- Top-N por tamaño de nodo o grado (configurable).
- Todos cuando el zoom es alto y el nodo mide > X pixels en pantalla.
Esto se decide cada frame. ImDrawList es eficiente y se compone sobre la imagen del FBO ya pintada.
Arquitectura
cpp/functions/viz/
├── graph_labels.h # NEW
├── graph_labels.cpp # NEW
└── graph_labels.md # NEW
cpp/tests/
└── test_graph_labels.cpp # NEW (smoke + culling logic)
API
namespace graph {
struct LabelPolicy {
bool always_for_selected = true;
bool always_for_hovered = true;
bool always_for_pinned = false;
int max_visible = 200; // top-N por size + degree
float min_zoom_for_all = 4.0f; // a este zoom, mostrar todos los visibles del viewport
float min_node_pixel_size = 12.0f; // skip si en pantalla mide menos
float font_size = 13.0f; // pixels
uint32_t color = 0xFFFFFFFF; // ABGR
uint32_t bg_color = 0xC8000000; // semi-transparente
float padding_x = 4.0f;
float padding_y = 2.0f;
};
// Callback que devuelve el texto del label dado un node_idx.
// El consumer maneja su propio string pool / metadata.
typedef const char* (*GetLabelFn)(int node_idx, void* user);
// Llamar tras ImGui::Image(...) del FBO. Usa el ImDrawList del current window.
void graph_labels_draw(const GraphData&, const GraphViewportState&,
const LabelPolicy&, GetLabelFn cb, void* user);
} // namespace graph
Algoritmo (cada frame)
- Determinar AABB visible en world coords desde camera+zoom.
- Colectar nodos visibles + nodos con
flags & (NF_SELECTED|NF_HOVERED|NF_PINNED). - Si
zoom >= min_zoom_for_all: candidatos = todos los visibles del viewport. Else: top-N por(size * degree). - Filtrar:
node_pixel_size = node.size * zoom; skip si< min_node_pixel_size(excepto losalways_*). - Para cada candidato superviviente:
- World → screen.
text = cb(idx, user).ImDrawList::AddRectFilled(bg)+AddText(color)con padding.
- Limit hard: nunca dibujar mas de
max_visible + |selected| + |hovered| + |pinned|.
Tareas
Fase 1 — Funcion + helpers
- 1.1 Crear
graph_labels.{h,cpp,md}. Implementar_drawsegun el algoritmo. - 1.2 Helper interno
score(node) = size * (degree+1)calculado tras frustum cull para top-N. - 1.3 Cache opcional del
degreepor nodo si el consumer la quiere precalcular y pasarsela (parametro avanzado en LabelPolicy o helper aparte). Para v1, calcular o-fly desde edges en O(E) y guardar en un thread_local vector — no critico.
Fase 2 — Tests
- 2.1 Test culling: setup grafo de 100 nodos, viewport pequeño, verificar que el numero de labels devuelto (mock callback que cuenta) respeta max_visible.
- 2.2 Test always_for_selected: setear NF_SELECTED en uno fuera del viewport, verificar que NO se dibuja (selected pero off-screen — segun politica). Decision: documentar comportamiento (default: no, para no spamear).
- 2.3 Test min_node_pixel_size: zoom bajo, nodo pequeño, no se dibuja.
Fase 3 — Integrar en demos_graph
- 3.1 Tras la
ImGui::Image(...)del viewport, llamargraph_labels_drawcon un callback que devuelve"#" + node_idx. - 3.2 Anadir controles en demo para variar
LabelPolicy: max_visible slider, font_size slider, toggle always_*.
Fase 4 — Cleanup
params/outputdocumentados en.md.fn index.- Commit
feat(viz): graph_labels con LabelPolicy + ImDrawList.
Criterio de done
- En
demos_graphcon 20k nodos: labels visibles para selected/hovered + top-N a fps estable. - Zoom alto muestra todos los visibles, zoom bajo solo los importantes — sin saltos bruscos.
- Tests verdes.
- No rompe perf: con
LabelPolicy.max_visible = 0y todos losalways_*off, la funcion es practicamente gratis.
Riesgos
| Riesgo | Mitigacion |
|---|---|
| ImDrawList con miles de AddText degrada fps | max_visible = 200 por default; cap es duro |
| Texto recortado por el clip rect del child window | Si el FBO esta dentro de un BeginChild/EndChild, usar el draw list correcto (probablemente el del window padre con clip ajustado) |
| Cambios de zoom hacen aparecer/desaparecer labels en avalancha | Hysteresis opcional en min_zoom_for_all (umbral on != umbral off). Para v1, simple |
Costo de calcular degree cada frame |
Aceptable a 100k aristas (un pase O(E)); cachear si se vuelve hot path |