High performance rendering of implicit surfaces presented in blobtree view. Use Vulkan(NVVK) as backend.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
8.6 KiB

1 year ago
#include "imgui_graphnode.h"
#include "imgui_graphnode_internal.h"
#include "imgui_internal.h"
void IMGUI_GRAPHNODE_NAMESPACE::CreateContext()
{
IM_ASSERT(g_ctx.gvcontext == nullptr);
g_ctx.gvcontext = gvContext();
}
void IMGUI_GRAPHNODE_NAMESPACE::DestroyContext()
{
IM_ASSERT(g_ctx.gvcontext != nullptr);
gvFreeContext(g_ctx.gvcontext);
g_ctx.gvcontext = nullptr;
}
bool IMGUI_GRAPHNODE_NAMESPACE::BeginNodeGraph(char const * id, ImGuiGraphNodeLayout layout, float pixel_per_unit)
{
g_ctx.lastid = ImGui::GetID(id);
auto & cache = g_ctx.graph_caches[g_ctx.lastid];
IM_ASSERT(g_ctx.gvgraph == nullptr);
IM_ASSERT(cache.graphid_current.empty());
g_ctx.gvgraph = agopen(const_cast<char *>("g"), Agdirected, 0);
cache.layout = layout;
cache.pixel_per_unit = pixel_per_unit;
char graphid_buf[16] = { 0 };
snprintf(graphid_buf, sizeof(graphid_buf) - 1, "%d", (int)layout);
cache.graphid_current += graphid_buf;
return true;
}
void IMGUI_GRAPHNODE_NAMESPACE::NodeGraphAddNode(char const * id)
{
ImVec4 const color = ImGui::GetStyle().Colors[ImGuiCol_Text];
ImVec4 const fillcolor = ImVec4(0.f, 0.f, 0.f, 0.f);
NodeGraphAddNode(id, color, fillcolor);
}
void IMGUI_GRAPHNODE_NAMESPACE::NodeGraphAddNode(char const * id, ImVec4 const & color, ImVec4 const & fillcolor)
{
auto & cache = g_ctx.graph_caches[g_ctx.lastid];
IM_ASSERT(g_ctx.gvgraph != nullptr);
Agnode_t * const n = agnode(g_ctx.gvgraph, ImGuiIDToString(id), 1);
IM_ASSERT(n != nullptr);
IMGUI_GRAPHNODE_CREATE_LABEL_ALLOCA(text, id);
auto const color_str = ImVec4ColorToString(color);
auto const fillcolor_str = ImVec4ColorToString(fillcolor);
agsafeset(n, (char *)"label", text, "");
agsafeset(n, (char *)"color", color_str, "");
agsafeset(n, (char *)"fillcolor", fillcolor_str, "");
cache.graphid_current += id;
cache.graphid_current += color_str;
cache.graphid_current += fillcolor_str;
}
void IMGUI_GRAPHNODE_NAMESPACE::NodeGraphAddEdge(char const * id, char const * node_id_a, char const * node_id_b)
{
ImVec4 const color = ImGui::GetStyle().Colors[ImGuiCol_Text];
NodeGraphAddEdge(id, node_id_a, node_id_b, color);
}
void IMGUI_GRAPHNODE_NAMESPACE::NodeGraphAddEdge(char const * id, char const * node_id_a, char const * node_id_b, ImVec4 const & color)
{
auto & cache = g_ctx.graph_caches[g_ctx.lastid];
IM_ASSERT(g_ctx.gvgraph != nullptr);
Agnode_t * const a = agnode(g_ctx.gvgraph, ImGuiIDToString(node_id_a), 0);
Agnode_t * const b = agnode(g_ctx.gvgraph, ImGuiIDToString(node_id_b), 0);
IM_ASSERT(a != nullptr);
IM_ASSERT(b != nullptr);
Agedge_t * const e = agedge(g_ctx.gvgraph, a, b, ImGuiIDToString(id), 1);
IM_ASSERT(e != nullptr);
IMGUI_GRAPHNODE_CREATE_LABEL_ALLOCA(text, id);
auto const color_str = ImVec4ColorToString(color);
agsafeset(e, (char *)"label", text, "");
agsafeset(e, (char *)"color", color_str, "");
cache.graphid_current += id;
cache.graphid_current += node_id_a;
cache.graphid_current += node_id_b;
cache.graphid_current += color_str;
}
int ImGuiGraphNodeFillDrawNodeBuffer(ImGuiGraphNode_Graph const & graph, ImGuiGraphNode_DrawNode * drawnodes, ImVec2 cursor_pos, float ppu)
{
int const count = (int)graph.nodes.size();
if (drawnodes)
{
constexpr int num_segments = IMGUI_GRAPHNODE_DRAW_NODE_PATH_COUNT - 1;
static_assert(num_segments > 0, "");
float a_min = 0.f;
float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
for (int i = 0; i < count; ++i)
{
ImGuiGraphNode_Node const & node = graph.nodes[i];
ImVec2 const textsize = ImGui::CalcTextSize(node.label.c_str());
for (int j = 0; j <= num_segments; j++)
{
const float a = a_min + ((float)j / (float)num_segments) * (a_max - a_min);
drawnodes[i].path[j].x = cursor_pos.x + (node.pos.x + ImCos(a) * node.size.x / 2.f) * ppu;
drawnodes[i].path[j].y = cursor_pos.y + ((graph.size.y - node.pos.y) + ImSin(a) * node.size.y / 2.f) * ppu;
}
drawnodes[i].textpos.x = cursor_pos.x + node.pos.x * ppu - textsize.x / 2.f;
drawnodes[i].textpos.y = cursor_pos.y + (graph.size.y - node.pos.y) * ppu - textsize.y / 2.f;
drawnodes[i].text = node.label.c_str();
drawnodes[i].color = node.color;
drawnodes[i].fillcolor = node.fillcolor;
}
}
return count;
}
int ImGuiGraphNodeFillDrawEdgeBuffer(ImGuiGraphNode_Graph const & graph, ImGuiGraphNode_DrawEdge * drawedges, ImVec2 cursor_pos, float ppu)
{
int const count = (int)graph.edges.size();
if (drawedges)
{
constexpr int points_count = IMGUI_GRAPHNODE_DRAW_EDGE_PATH_COUNT;
static_assert(points_count > 1, "");
for (int i = 0; i < count; ++i)
{
ImGuiGraphNode_Edge const & edge = graph.edges[i];
ImVec2 const textsize = ImGui::CalcTextSize(edge.label.c_str());
for (int x = 0; x < points_count; ++x)
{
drawedges[i].path[x] = ImGuiGraphNode_BezierVec2(edge.points.data(), (int)edge.points.size(), x / float(points_count - 1));
drawedges[i].path[x].y = graph.size.y - drawedges[i].path[x].y;
drawedges[i].path[x].x *= ppu;
drawedges[i].path[x].y *= ppu;
drawedges[i].path[x].x += cursor_pos.x;
drawedges[i].path[x].y += cursor_pos.y;
}
drawedges[i].textpos.x = cursor_pos.x + edge.labelPos.x * ppu - textsize.x / 2.f;
drawedges[i].textpos.y = cursor_pos.y + (graph.size.y - edge.labelPos.y) * ppu - textsize.y / 2.f;
drawedges[i].text = edge.label.c_str();
drawedges[i].color = edge.color;
ImVec2 const lastpoint = drawedges[i].path[points_count - 1];
float dirx = lastpoint.x - drawedges[i].path[points_count - 2].x;
float diry = lastpoint.y - drawedges[i].path[points_count - 2].y;
float const mag = ImSqrt(dirx * dirx + diry * diry);
float const mul1 = ppu * 0.1f;
float const mul2 = ppu * 0.0437f;
dirx /= mag;
diry /= mag;
drawedges[i].arrow1.x = lastpoint.x - dirx * mul1 - diry * mul2;
drawedges[i].arrow1.y = lastpoint.y - diry * mul1 + dirx * mul2;
drawedges[i].arrow2.x = lastpoint.x - dirx * mul1 + diry * mul2;
drawedges[i].arrow2.y = lastpoint.y - diry * mul1 - dirx * mul2;
drawedges[i].arrow3 = lastpoint;
}
}
return count;
}
void IMGUI_GRAPHNODE_NAMESPACE::EndNodeGraph(bool saveFig)
{
auto & cache = g_ctx.graph_caches[g_ctx.lastid];
float const ppu = cache.pixel_per_unit;
ImVec2 const cursor_pos = ImGui::GetCursorScreenPos();
ImDrawList * const drawlist = ImGui::GetWindowDrawList();
if (cache.graphid_current != cache.graphid_previous)
{
ImGuiGraphNodeRenderGraphLayout(cache.graph, cache.layout);
cache.graphid_previous = cache.graphid_current;
cache.cursor_previous.x = cursor_pos.x - 1; // force recompute draw buffers
}
cache.graphid_current.clear();
if (saveFig)
gvRenderFilename(g_ctx.gvcontext, g_ctx.gvgraph, "svg", "blobtree.svg");
agclose(g_ctx.gvgraph);
g_ctx.gvgraph = nullptr;
cache.cursor_current = cursor_pos;
if (cache.cursor_current.x != cache.cursor_previous.x || cache.cursor_current.y != cache.cursor_previous.y)
{
cache.drawnodes.resize(ImGuiGraphNodeFillDrawNodeBuffer(cache.graph, nullptr, cursor_pos, ppu));
ImGuiGraphNodeFillDrawNodeBuffer(cache.graph, cache.drawnodes.data(), cursor_pos, ppu);
cache.drawedges.resize(ImGuiGraphNodeFillDrawEdgeBuffer(cache.graph, nullptr, cursor_pos, ppu));
ImGuiGraphNodeFillDrawEdgeBuffer(cache.graph, cache.drawedges.data(), cursor_pos, ppu);
cache.cursor_previous = cache.cursor_current;
}
for (auto const & node : cache.drawnodes)
{
drawlist->AddConvexPolyFilled(node.path, IMGUI_GRAPHNODE_DRAW_NODE_PATH_COUNT, node.fillcolor);
drawlist->AddPolyline(node.path, IMGUI_GRAPHNODE_DRAW_NODE_PATH_COUNT, node.color, ImDrawFlags_Closed, 1.f);
drawlist->AddText(node.textpos, node.color, node.text);
}
for (auto const & edge : cache.drawedges)
{
drawlist->AddText(edge.textpos, edge.color, edge.text);
drawlist->AddPolyline(edge.path, IMGUI_GRAPHNODE_DRAW_EDGE_PATH_COUNT, edge.color, ImDrawFlags_None, 1.f);
drawlist->AddTriangleFilled(edge.arrow1, edge.arrow2, edge.arrow3, edge.color);
}
ImGui::Dummy(ImVec2(cache.graph.size.x * ppu, cache.graph.size.y * ppu));
}