#pragma once
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
#include <imgui.h>
|
|
#include <imgui_internal.h>
|
|
|
|
#include "../geom.h"
|
|
|
|
class GuiNode {
|
|
public:
|
|
GuiNode(std::string name, Rect bbox, uint n_slots_in, uint n_slots_out)
|
|
: name(name), bbox(bbox), n_slots_in(n_slots_in), n_slots_out(n_slots_out)
|
|
{}
|
|
|
|
enum SlotDirection {
|
|
IN,
|
|
OUT,
|
|
};
|
|
|
|
ImVec2 slot_location(SlotDirection direction, uint i) const {
|
|
uint n_slots;
|
|
switch (direction) {
|
|
case IN:
|
|
n_slots = n_slots_in; break;
|
|
case OUT:
|
|
n_slots = n_slots_out; break;
|
|
}
|
|
|
|
auto x_stride = bbox.size.y/(n_slots+1);
|
|
|
|
ImVec2 base;
|
|
switch(direction) {
|
|
case IN:
|
|
base = bbox.pos; break;
|
|
case OUT:
|
|
base = bbox.pos + ImVec2(bbox.size.x, 0); break;
|
|
}
|
|
|
|
return base + ImVec2(0, (i + 1) * x_stride);
|
|
}
|
|
|
|
uint n_slots_in;
|
|
uint n_slots_out;
|
|
|
|
std::string name;
|
|
Rect bbox;
|
|
};
|
|
|
|
class GuiEdge {
|
|
public:
|
|
GuiEdge(GuiNode *lhs, GuiNode *rhs, uint fromSlot, uint toSlot)
|
|
: lhs(lhs), rhs(rhs), fromSlot(fromSlot), toSlot(toSlot) {}
|
|
|
|
|
|
GuiNode *lhs;
|
|
GuiNode *rhs;
|
|
uint fromSlot;
|
|
uint toSlot;
|
|
};
|
|
|
|
class NodeEditor {
|
|
public:
|
|
NodeEditor() {
|
|
nodeList.emplace_back("hi", Rect(10, 10, 60, 60), 3, 1);
|
|
nodeList.emplace_back("hi2", Rect(80, 10, 60, 60), 1, 1);
|
|
|
|
edgeList.emplace_back(&nodeList[0], &nodeList[1], 0, 0);
|
|
}
|
|
|
|
void draw_window(const char *title, bool *opened) {
|
|
ImGui::SetNextWindowSize(ImVec2(700,600), ImGuiSetCond_FirstUseEver);
|
|
if (ImGui::Begin(title, opened)) {
|
|
// -- Create child canvas
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1,1));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0,0));
|
|
ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, ImColor(60,60,70,200));
|
|
ImGui::BeginChild("nodes_region", ImVec2(0, 0), true, ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoMove);
|
|
|
|
// -- Prepare to draw
|
|
// Get the draw list for this canvas
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
draw_list->ChannelsSplit(2);
|
|
|
|
// this gets the offset to the upper-right corner of the child canvas.
|
|
// Note: here, cursor means "draw cursor", not "mouse pointing cursor".
|
|
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
|
ImVec2 canvas_size = ImGui::GetWindowSize();
|
|
|
|
ImVec2 offset = canvas_pos - scrolling; // TODO: add scrolling
|
|
|
|
// -- Draw the grid
|
|
draw_list->ChannelsSetCurrent(0);
|
|
if (show_grid) {
|
|
ImVec2 local_offset = ImGui::GetCursorPos() - scrolling;
|
|
|
|
float spacing = 32.0f; ImU32 color = IM_COL32(200, 200, 200, 40);
|
|
|
|
for (float x = fmodf(local_offset.x, spacing); x < canvas_size.x; x += spacing)
|
|
draw_list->AddLine(ImVec2(x, 0.0f)+canvas_pos,
|
|
ImVec2(x, canvas_size.y)+canvas_pos, color);
|
|
for (float y = fmodf(local_offset.y, spacing); y < canvas_size.y; y += spacing)
|
|
draw_list->AddLine(ImVec2(0.0f, y)+canvas_pos,
|
|
ImVec2(canvas_size.x, y)+canvas_pos, color);
|
|
}
|
|
|
|
|
|
draw_list->ChannelsSetCurrent(0);
|
|
for (const auto &edge : edgeList) {
|
|
auto head = offset + edge.lhs->slot_location(GuiNode::OUT, edge.fromSlot);
|
|
auto tail = offset + edge.rhs->slot_location(GuiNode::IN, edge.fromSlot);
|
|
|
|
draw_list->AddBezierCurve(head, head+ImVec2(50, 0),
|
|
tail+ImVec2(-50, 0), tail,
|
|
ImColor(200, 200, 200), 3.0f);
|
|
}
|
|
|
|
Rect viewportRect = Rect(scrolling, canvas_size);
|
|
|
|
for (const auto &node : nodeList) {
|
|
bool visible = viewportRect.intersects(node.bbox);
|
|
if (visible) {
|
|
// -- Draw the node boxes
|
|
draw_list->ChannelsSetCurrent(0); // Switch to the background layer
|
|
|
|
ImVec2 upper_left = offset + node.bbox.pos;
|
|
ImVec2 lower_right = upper_left + node.bbox.size;
|
|
|
|
draw_list->AddRectFilled(upper_left, lower_right, ImColor(60, 60, 60), 4.0f);
|
|
draw_list->AddRect(upper_left, lower_right, ImColor(100, 100, 100), 4.0f);
|
|
|
|
// for (int i=0; i<N; i++) {
|
|
// auto position = node.bbox.pos;
|
|
// position.y += (i+1) * (node.bbox.size.y)/(N+1);
|
|
|
|
// draw_list->AddCircleFilled(offset + position, 4.0f, ImColor(150, 150, 150, 150));
|
|
// }
|
|
}
|
|
}
|
|
|
|
// -- merge channels back together
|
|
draw_list->ChannelsMerge();
|
|
|
|
// -- scrolling!
|
|
// [ pointer over neditor ] [ not interacting ctrls ] [ middle mouse dragging ]
|
|
if (ImGui::IsWindowHovered() && !ImGui::IsAnyItemActive() && ImGui::IsMouseDragging(2, 0.0f))
|
|
scrolling = scrolling - ImGui::GetIO().MouseDelta;
|
|
|
|
// -- clean up
|
|
ImGui::EndChild(); // end :nodes_region
|
|
ImGui::PopStyleColor(); // pop ChildWindowBg
|
|
ImGui::PopStyleVar(2); // pop FramePadding, WindowPadding
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
private:
|
|
std::vector<GuiNode> nodeList;
|
|
std::vector<GuiEdge> edgeList;
|
|
|
|
ImVec2 scrolling = ImVec2(0.0f, 0.0f);
|
|
bool show_grid = true;
|
|
};
|