feat(viz): graph_types modelo extendido + EntityType/RelationType + flags (issue 0049e)
Extiende el modelo agnostico de graph_types.h para soportar shapes/iconos/ filtros/labels/streaming sin acoplar a backend. Migra el unico consumer (demos_graph) en el mismo cambio. - GraphNode v2: type_id + shape_override/color_override/size_override + flags (NF_PINNED/VISIBLE/SELECTED/HOVERED) + label_idx + user_data. - GraphEdge v2: type_id + style_override + flags (EF_DIRECTED/VISIBLE). - EntityType / RelationType: tablas en GraphData (types, rel_types). - Helpers de resolucion (resolve_node_color/shape/size, resolve_edge_*) y constructores ergonomicos (graph_node, graph_edge, entity_type, relation_type) — sentinel-based para herencia automatica del tipo. - graph_renderer v1.4: lee NF_VISIBLE / EF_VISIBLE, resuelve apariencia via override → EntityType → fallback indexado por type_id. Skipea aristas con endpoints invisibles. Shapes siguen pintandose como circulo (0049f cableara el dispatch real). - graph_force_layout v1.2: pinned ahora vive en flags & NF_PINNED. - graph_viewport v1.1: hover/seleccion publican NF_HOVERED/SELECTED en el grafo (clear-then-set). Drag usa NF_PINNED. Tooltip muestra Type/ user_data en lugar de community/value/label. - demos_graph: 8 EntityType (paleta antigua) + 1 RelationType. type_id por cluster. user_data = indice numerico del nodo. Apariencia visual identica al pre-cambio. - test_graph_types.cpp: 12 casos cubriendo helpers, defaults, bitmask manipulation y resoluciones override-vs-EntityType. test_graph_edge_ static actualizado al nuevo modelo (ya no tiene .color directo). - 4 .md de tipos nuevos (graph_node, graph_edge, entity_type, relation_type) + GraphData v2.0 actualizado. Tests: 31/31 ctest verdes (incluye test_visual golden). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,33 @@
|
||||
|
||||
namespace gallery {
|
||||
|
||||
// Paleta del demo: 8 colores tipo Mantine. v2.0 los usamos a traves de la
|
||||
// tabla EntityType en lugar de escribirlos por nodo. Asi el modelo nuevo
|
||||
// queda demostrado tal cual lo van a usar las apps reales (osint_graph,
|
||||
// fn_explorer): tabla pequena de tipos + nodos que solo guardan type_id.
|
||||
static const uint32_t k_demo_palette[] = {
|
||||
0xFFEF8D5Bu, 0xFF8CCA58u, 0xFF3E97F5u, 0xFF5051D9u,
|
||||
0xFFE07FB8u, 0xFFCCCD5Fu, 0xFF52CDF2u, 0xFF61D199u,
|
||||
};
|
||||
static constexpr int k_demo_palette_n =
|
||||
sizeof(k_demo_palette) / sizeof(k_demo_palette[0]);
|
||||
|
||||
// Tabla compartida entre regeneraciones — las apariencias no cambian aunque
|
||||
// el usuario regenere el grafo, asi que vive como `static`.
|
||||
static EntityType s_demo_entity_types[k_demo_palette_n];
|
||||
static RelationType s_demo_relation_types[1];
|
||||
static bool s_demo_types_initialized = false;
|
||||
|
||||
static void init_demo_types() {
|
||||
if (s_demo_types_initialized) return;
|
||||
for (int k = 0; k < k_demo_palette_n; ++k) {
|
||||
s_demo_entity_types[k] = entity_type(k_demo_palette[k],
|
||||
SHAPE_CIRCLE, 4.0f, "cluster");
|
||||
}
|
||||
s_demo_relation_types[0] = relation_type(0xFF888888u, EDGE_SOLID, 1.0f, "default");
|
||||
s_demo_types_initialized = true;
|
||||
}
|
||||
|
||||
// Genera un grafo sintetico con N nodos en K clusters. Cada nodo tiene
|
||||
// `edges_per_node` aristas intra-cluster + un pct% global inter-cluster.
|
||||
// Cluster radio escala con sqrt(N) para que la "nube" no sea siempre el
|
||||
@@ -34,13 +61,6 @@ static void generate_synthetic_graph(int N, int K,
|
||||
return static_cast<float>((seed >> 8) & 0xffffff) / 16777216.0f;
|
||||
};
|
||||
|
||||
// Paleta por cluster (ABGR)
|
||||
const uint32_t palette[] = {
|
||||
0xff5b8def, 0xff58ca8c, 0xfff5973e, 0xffd95150,
|
||||
0xffb87fe0, 0xff5fcdcc, 0xfff2cd52, 0xff99d161,
|
||||
};
|
||||
const int palette_n = sizeof(palette) / sizeof(palette[0]);
|
||||
|
||||
// Cluster radius y scatter escalan con sqrt(N) para que los nodos no
|
||||
// queden empaquetados al subir el slider. A 1M nodes el espacio inicial
|
||||
// es ~12k px de lado en lugar de los 280 px hardcoded de antes.
|
||||
@@ -57,12 +77,16 @@ static void generate_synthetic_graph(int N, int K,
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
int k = i % K;
|
||||
GraphNode n = graph_node(static_cast<uint32_t>(i),
|
||||
// type_id mapea al EntityType (k % k_demo_palette_n) que define
|
||||
// color y shape. size_override = 3..5 px para conservar la
|
||||
// variacion sutil del demo v1 — apariencia visual identica.
|
||||
uint16_t tid = static_cast<uint16_t>(k % k_demo_palette_n);
|
||||
GraphNode n = graph_node(
|
||||
cluster_cx[k] + (rnd() - 0.5f) * scatter,
|
||||
cluster_cy[k] + (rnd() - 0.5f) * scatter);
|
||||
n.size = 3.0f + rnd() * 2.0f;
|
||||
n.color = palette[k % palette_n];
|
||||
n.community = static_cast<uint32_t>(k);
|
||||
cluster_cy[k] + (rnd() - 0.5f) * scatter,
|
||||
tid);
|
||||
n.size_override = 3.0f + rnd() * 2.0f;
|
||||
n.user_data = static_cast<uint64_t>(i);
|
||||
nodes_out.push_back(n);
|
||||
}
|
||||
|
||||
@@ -114,13 +138,20 @@ void demo_graph() {
|
||||
static bool s_needs_regen = true;
|
||||
|
||||
if (s_needs_regen) {
|
||||
init_demo_types();
|
||||
generate_synthetic_graph(s_n_nodes, s_n_clusters,
|
||||
s_edges_per_n, s_inter_pct,
|
||||
s_nodes, s_edges);
|
||||
s_graph.nodes = s_nodes.data();
|
||||
s_graph.node_count = static_cast<int>(s_nodes.size());
|
||||
s_graph.edges = s_edges.data();
|
||||
s_graph.edge_count = static_cast<int>(s_edges.size());
|
||||
s_graph.nodes = s_nodes.data();
|
||||
s_graph.node_count = static_cast<int>(s_nodes.size());
|
||||
s_graph.node_capacity = static_cast<int>(s_nodes.capacity());
|
||||
s_graph.edges = s_edges.data();
|
||||
s_graph.edge_count = static_cast<int>(s_edges.size());
|
||||
s_graph.edge_capacity = static_cast<int>(s_edges.capacity());
|
||||
s_graph.types = s_demo_entity_types;
|
||||
s_graph.type_count = k_demo_palette_n;
|
||||
s_graph.rel_types = s_demo_relation_types;
|
||||
s_graph.rel_type_count = 1;
|
||||
s_graph.update_bounds();
|
||||
s_state.layout_running = true;
|
||||
s_state.layout_energy = 0.0f;
|
||||
@@ -168,9 +199,9 @@ void demo_graph() {
|
||||
char hover_buf[32];
|
||||
char sel_buf[32];
|
||||
if (s_state.hovered_node >= 0) {
|
||||
std::snprintf(hover_buf, sizeof(hover_buf), "#%d c%u",
|
||||
std::snprintf(hover_buf, sizeof(hover_buf), "#%d t%u",
|
||||
s_state.hovered_node,
|
||||
s_nodes[s_state.hovered_node].community);
|
||||
(unsigned)s_nodes[s_state.hovered_node].type_id);
|
||||
} else {
|
||||
std::snprintf(hover_buf, sizeof(hover_buf), "-");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user