// Pintado de etiquetas via ImDrawList (issue 0049j). La logica de seleccion // de candidatos (frustum cull + top-N) vive en graph_labels_select.cpp para // que sea testeable sin enlazar ImGui. #include "viz/graph_labels.h" #include "viz/graph_types.h" #include "viz/graph_viewport.h" #include "imgui.h" #include #include namespace graph { void graph_labels_draw_at(const GraphData& g, float cam_x, float cam_y, float zoom, float wmin_x, float wmin_y, float w, float h, const LabelPolicy& p, GetLabelFn cb, void* user) { if (!cb) return; if (g.node_count <= 0) return; if (w <= 0.0f || h <= 0.0f) return; // Degrees on-the-fly: O(E) por frame. Cachear si se vuelve hot path. static thread_local std::vector degrees; degrees.assign((size_t)g.node_count, 0); graph_compute_degrees(g, degrees.data()); // Cap duro: max_visible + |always_*|. |always_*| <= node_count. const int max_total = p.max_visible + g.node_count; static thread_local std::vector indices; indices.resize((size_t)max_total); const int n_labels = graph_labels_select( g, p, cam_x, cam_y, zoom, w, h, degrees.data(), indices.data(), max_total); if (n_labels <= 0) return; ImDrawList* dl = ImGui::GetWindowDrawList(); ImFont* font = ImGui::GetFont(); if (!dl || !font) return; const float cx = wmin_x + w * 0.5f; const float cy = wmin_y + h * 0.5f; for (int k = 0; k < n_labels; ++k) { const int idx = indices[k]; const GraphNode& n = g.nodes[idx]; const char* text = cb(idx, user); if (!text || !*text) continue; const float vx = (n.x - cam_x) * zoom + cx; const float vy = (n.y - cam_y) * zoom + cy; const float node_px = resolve_node_size(n, g.types, g.type_count) * zoom; const float ox = node_px * 0.5f + p.padding_x; const float oy = -p.font_size * 0.5f; const ImVec2 ts = font->CalcTextSizeA(p.font_size, FLT_MAX, 0.0f, text); const ImVec2 a(vx + ox - p.padding_x, vy + oy - p.padding_y); const ImVec2 b(vx + ox + ts.x + p.padding_x, vy + oy + ts.y + p.padding_y); dl->AddRectFilled(a, b, p.bg_color, 2.0f); dl->AddText(font, p.font_size, ImVec2(vx + ox, vy + oy), p.color, text); } } void graph_labels_draw(const GraphData& g, const GraphViewportState& s, const LabelPolicy& p, GetLabelFn cb, void* user) { const ImVec2 wmin = ImGui::GetItemRectMin(); const ImVec2 wmax = ImGui::GetItemRectMax(); const float w = wmax.x - wmin.x; const float h = wmax.y - wmin.y; graph_labels_draw_at(g, s.cam_x, s.cam_y, s.zoom, wmin.x, wmin.y, w, h, p, cb, user); } } // namespace graph