357 lines
13 KiB
C++
357 lines
13 KiB
C++
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
#include "node_renderer_base.h"
|
|
#include "../app.h"
|
|
#include <imgui_internal.h>
|
|
#include <imgui_node_editor.h>
|
|
|
|
namespace ed = ax::NodeEditor;
|
|
|
|
namespace ax {
|
|
namespace NodeRendering {
|
|
|
|
// ===== Icon Drawing (moved from drawing.cpp) =====
|
|
void NodeRendererBase::DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor)
|
|
{
|
|
auto rect = ImRect(a, b);
|
|
auto rect_x = rect.Min.x;
|
|
auto rect_y = rect.Min.y;
|
|
auto rect_w = rect.Max.x - rect.Min.x;
|
|
auto rect_h = rect.Max.y - rect.Min.y;
|
|
auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
|
|
auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
|
|
auto rect_center = ImVec2(rect_center_x, rect_center_y);
|
|
const auto outline_scale = rect_w / 24.0f;
|
|
const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
|
|
|
|
if (type == IconType::Flow)
|
|
{
|
|
const auto origin_scale = rect_w / 24.0f;
|
|
|
|
const auto offset_x = 1.0f * origin_scale;
|
|
auto offset_y = 0.0f * origin_scale;
|
|
const auto margin = (filled ? 2.0f : 2.0f) * origin_scale;
|
|
const auto rounding = 0.1f * origin_scale;
|
|
const auto tip_round = 0.7f; // percentage of triangle edge (for tip)
|
|
|
|
const auto canvas = ImRect(
|
|
rect.Min.x + margin + offset_x,
|
|
rect.Min.y + margin + offset_y,
|
|
rect.Max.x - margin + offset_x,
|
|
rect.Max.y - margin + offset_y);
|
|
|
|
const auto canvas_x = canvas.Min.x;
|
|
const auto canvas_y = canvas.Min.y;
|
|
const auto canvas_w = canvas.Max.x - canvas.Min.x;
|
|
const auto canvas_h = canvas.Max.y - canvas.Min.y;
|
|
|
|
const auto left = canvas_x + canvas_w * 0.5f * 0.3f;
|
|
const auto right = canvas_x + canvas_w - canvas_w * 0.5f * 0.3f;
|
|
const auto top = canvas_y + canvas_h * 0.5f * 0.2f;
|
|
const auto bottom = canvas_y + canvas_h - canvas_h * 0.5f * 0.2f;
|
|
const auto center_y = (top + bottom) * 0.5f;
|
|
|
|
const auto tip_top = ImVec2(canvas_x + canvas_w * 0.5f, top);
|
|
const auto tip_right = ImVec2(right, center_y);
|
|
const auto tip_bottom = ImVec2(canvas_x + canvas_w * 0.5f, bottom);
|
|
|
|
drawList->PathLineTo(ImVec2(left, top) + ImVec2(0, rounding));
|
|
drawList->PathBezierCubicCurveTo(
|
|
ImVec2(left, top),
|
|
ImVec2(left, top),
|
|
ImVec2(left, top) + ImVec2(rounding, 0));
|
|
drawList->PathLineTo(tip_top);
|
|
drawList->PathLineTo(tip_top + (tip_right - tip_top) * tip_round);
|
|
drawList->PathBezierCubicCurveTo(
|
|
tip_right,
|
|
tip_right,
|
|
tip_bottom + (tip_right - tip_bottom) * tip_round);
|
|
drawList->PathLineTo(tip_bottom);
|
|
drawList->PathLineTo(ImVec2(left, bottom) + ImVec2(rounding, 0));
|
|
drawList->PathBezierCubicCurveTo(
|
|
ImVec2(left, bottom),
|
|
ImVec2(left, bottom),
|
|
ImVec2(left, bottom) - ImVec2(0, rounding));
|
|
|
|
if (!filled)
|
|
{
|
|
if (innerColor & 0xFF000000)
|
|
drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
|
|
|
|
drawList->PathStroke(color, true, 2.0f * outline_scale);
|
|
}
|
|
else
|
|
drawList->PathFillConvex(color);
|
|
}
|
|
else
|
|
{
|
|
auto triangleStart = rect_center_x + 0.32f * rect_w;
|
|
|
|
auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
|
|
|
|
rect.Min.x += rect_offset;
|
|
rect.Max.x += rect_offset;
|
|
rect_x += rect_offset;
|
|
rect_center_x += rect_offset * 0.5f;
|
|
rect_center.x += rect_offset * 0.5f;
|
|
|
|
if (type == IconType::Circle)
|
|
{
|
|
const auto c = rect_center;
|
|
|
|
if (!filled)
|
|
{
|
|
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
|
|
|
if (innerColor & 0xFF000000)
|
|
drawList->AddCircleFilled(c, r, innerColor, 12 + extra_segments);
|
|
drawList->AddCircle(c, r, color, 12 + extra_segments, 2.0f * outline_scale);
|
|
}
|
|
else
|
|
{
|
|
drawList->AddCircleFilled(c, 0.5f * rect_w / 2.0f, color, 12 + extra_segments);
|
|
}
|
|
}
|
|
|
|
if (type == IconType::Square)
|
|
{
|
|
if (filled)
|
|
{
|
|
const auto r = 0.5f * rect_w / 2.0f;
|
|
const auto p0 = rect_center - ImVec2(r, r);
|
|
const auto p1 = rect_center + ImVec2(r, r);
|
|
|
|
#if IMGUI_VERSION_NUM > 18101
|
|
drawList->AddRectFilled(p0, p1, color, 0, ImDrawFlags_RoundCornersAll);
|
|
#else
|
|
drawList->AddRectFilled(p0, p1, color, 0, 15);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
|
const auto p0 = rect_center - ImVec2(r, r);
|
|
const auto p1 = rect_center + ImVec2(r, r);
|
|
|
|
if (innerColor & 0xFF000000)
|
|
{
|
|
#if IMGUI_VERSION_NUM > 18101
|
|
drawList->AddRectFilled(p0, p1, innerColor, 0, ImDrawFlags_RoundCornersAll);
|
|
#else
|
|
drawList->AddRectFilled(p0, p1, innerColor, 0, 15);
|
|
#endif
|
|
}
|
|
|
|
#if IMGUI_VERSION_NUM > 18101
|
|
drawList->AddRect(p0, p1, color, 0, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
|
|
#else
|
|
drawList->AddRect(p0, p1, color, 0, 15, 2.0f * outline_scale);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (type == IconType::Grid)
|
|
{
|
|
const auto r = 0.5f * rect_w / 2.0f;
|
|
const auto w = ceilf(r / 3.0f);
|
|
|
|
const auto baseTl = ImVec2(floorf(rect_center_x - w * 2.5f), floorf(rect_center_y - w * 2.5f));
|
|
const auto baseBr = ImVec2(floorf(baseTl.x + w), floorf(baseTl.y + w));
|
|
|
|
auto tl = baseTl;
|
|
auto br = baseBr;
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
tl.x = baseTl.x;
|
|
br.x = baseBr.x;
|
|
drawList->AddRectFilled(tl, br, color);
|
|
tl.x += w * 2;
|
|
br.x += w * 2;
|
|
if (i != 1 || filled)
|
|
drawList->AddRectFilled(tl, br, color);
|
|
tl.x += w * 2;
|
|
br.x += w * 2;
|
|
drawList->AddRectFilled(tl, br, color);
|
|
|
|
tl.y += w * 2;
|
|
br.y += w * 2;
|
|
}
|
|
|
|
triangleStart = br.x + w + 1.0f / 24.0f * rect_w;
|
|
}
|
|
|
|
if (type == IconType::RoundSquare)
|
|
{
|
|
if (filled)
|
|
{
|
|
const auto r = 0.5f * rect_w / 2.0f;
|
|
const auto cr = r * 0.5f;
|
|
const auto p0 = rect_center - ImVec2(r, r);
|
|
const auto p1 = rect_center + ImVec2(r, r);
|
|
|
|
#if IMGUI_VERSION_NUM > 18101
|
|
drawList->AddRectFilled(p0, p1, color, cr, ImDrawFlags_RoundCornersAll);
|
|
#else
|
|
drawList->AddRectFilled(p0, p1, color, cr, 15);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
|
const auto cr = r * 0.5f;
|
|
const auto p0 = rect_center - ImVec2(r, r);
|
|
const auto p1 = rect_center + ImVec2(r, r);
|
|
|
|
if (innerColor & 0xFF000000)
|
|
{
|
|
#if IMGUI_VERSION_NUM > 18101
|
|
drawList->AddRectFilled(p0, p1, innerColor, cr, ImDrawFlags_RoundCornersAll);
|
|
#else
|
|
drawList->AddRectFilled(p0, p1, innerColor, cr, 15);
|
|
#endif
|
|
}
|
|
|
|
#if IMGUI_VERSION_NUM > 18101
|
|
drawList->AddRect(p0, p1, color, cr, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
|
|
#else
|
|
drawList->AddRect(p0, p1, color, cr, 15, 2.0f * outline_scale);
|
|
#endif
|
|
}
|
|
}
|
|
else if (type == IconType::Diamond)
|
|
{
|
|
if (filled)
|
|
{
|
|
const auto r = 0.607f * rect_w / 2.0f;
|
|
const auto c = rect_center;
|
|
|
|
drawList->PathLineTo(c + ImVec2( 0, -r));
|
|
drawList->PathLineTo(c + ImVec2( r, 0));
|
|
drawList->PathLineTo(c + ImVec2( 0, r));
|
|
drawList->PathLineTo(c + ImVec2(-r, 0));
|
|
drawList->PathFillConvex(color);
|
|
}
|
|
else
|
|
{
|
|
const auto r = 0.607f * rect_w / 2.0f - 0.5f;
|
|
const auto c = rect_center;
|
|
|
|
drawList->PathLineTo(c + ImVec2( 0, -r));
|
|
drawList->PathLineTo(c + ImVec2( r, 0));
|
|
drawList->PathLineTo(c + ImVec2( 0, r));
|
|
drawList->PathLineTo(c + ImVec2(-r, 0));
|
|
|
|
if (innerColor & 0xFF000000)
|
|
drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
|
|
|
|
drawList->PathStroke(color, true, 2.0f * outline_scale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===== ImGui-friendly Icon Widget =====
|
|
void NodeRendererBase::Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color, const ImVec4& innerColor)
|
|
{
|
|
if (ImGui::IsRectVisible(size))
|
|
{
|
|
auto cursorPos = ImGui::GetCursorScreenPos();
|
|
auto drawList = ImGui::GetWindowDrawList();
|
|
DrawIcon(drawList, cursorPos, cursorPos + size, type, filled, ImColor(color), ImColor(innerColor));
|
|
}
|
|
|
|
ImGui::Dummy(size);
|
|
}
|
|
|
|
// ===== Pin Icon Drawing =====
|
|
void NodeRendererBase::DrawPinIcon(const Pin& pin, bool connected, int alpha, const ImVec2& offset, App* app)
|
|
{
|
|
auto drawList = ImGui::GetWindowDrawList();
|
|
auto cursorPos = ImGui::GetCursorScreenPos();
|
|
auto iconSize = ImVec2(static_cast<float>(app->m_PinIconSize), static_cast<float>(app->m_PinIconSize));
|
|
|
|
// Calculate icon position with offset
|
|
auto iconPos = cursorPos + offset;
|
|
auto iconRect = ImRect(iconPos, iconPos + iconSize);
|
|
|
|
// Get icon type and color
|
|
IconType iconType;
|
|
ImColor color = app->GetIconColor(pin.Type);
|
|
color.Value.w = alpha / 255.0f;
|
|
|
|
switch (pin.Type)
|
|
{
|
|
case PinType::Flow: iconType = IconType::Flow; break;
|
|
case PinType::Bool: iconType = IconType::Circle; break;
|
|
case PinType::Int: iconType = IconType::Circle; break;
|
|
case PinType::Float: iconType = IconType::Circle; break;
|
|
case PinType::String: iconType = IconType::Circle; break;
|
|
case PinType::Object: iconType = IconType::Circle; break;
|
|
case PinType::Function: iconType = IconType::Circle; break;
|
|
case PinType::Delegate: iconType = IconType::Square; break;
|
|
default: return;
|
|
}
|
|
|
|
// Draw icon at offset position
|
|
DrawIcon(drawList, iconRect.Min, iconRect.Max, iconType, false, color, ImColor(32, 32, 32, alpha));
|
|
}
|
|
|
|
// ===== Style Management =====
|
|
NodeRendererBase::NodeStyleScope::NodeStyleScope(const ImColor& bgColor, const ImColor& borderColor,
|
|
float rounding, float borderWidth, const ImVec4& padding,
|
|
const ImVec2& sourceDir, const ImVec2& targetDir)
|
|
{
|
|
ed::PushStyleColor(ed::StyleColor_NodeBg, bgColor);
|
|
ed::PushStyleColor(ed::StyleColor_NodeBorder, borderColor);
|
|
ed::PushStyleVar(ed::StyleVar_NodeRounding, rounding);
|
|
ed::PushStyleVar(ed::StyleVar_NodeBorderWidth, borderWidth);
|
|
ed::PushStyleVar(ed::StyleVar_NodePadding, padding);
|
|
ed::PushStyleVar(ed::StyleVar_SourceDirection, sourceDir);
|
|
ed::PushStyleVar(ed::StyleVar_TargetDirection, targetDir);
|
|
ed::PushStyleVar(ed::StyleVar_PinArrowSize, 0.0f);
|
|
ed::PushStyleVar(ed::StyleVar_PinArrowWidth, 0.0f);
|
|
}
|
|
|
|
NodeRendererBase::NodeStyleScope::~NodeStyleScope()
|
|
{
|
|
ed::PopStyleVar(m_StyleVarCount);
|
|
ed::PopStyleColor(m_StyleColorCount);
|
|
}
|
|
|
|
// ===== Tooltip Management =====
|
|
void NodeRendererBase::SetPinTooltip(Pin* pin, const char* label, const char* typeName, App* app)
|
|
{
|
|
app->m_HoveredPin = pin;
|
|
char tooltip[256];
|
|
snprintf(tooltip, sizeof(tooltip), "%s\nType: %s", label, typeName);
|
|
app->m_HoveredPinTooltip = tooltip;
|
|
}
|
|
|
|
// ===== Input Field Helpers =====
|
|
void NodeRendererBase::HandleTextInput(bool isActive, ed::NodeId nodeId, ed::NodeId& staticEditingNode)
|
|
{
|
|
if (isActive && staticEditingNode != nodeId)
|
|
{
|
|
ed::EnableShortcuts(false);
|
|
staticEditingNode = nodeId;
|
|
}
|
|
else if (!isActive && staticEditingNode == nodeId)
|
|
{
|
|
ed::EnableShortcuts(true);
|
|
staticEditingNode = 0;
|
|
}
|
|
}
|
|
|
|
// ===== Pin Alpha Calculation =====
|
|
float NodeRendererBase::GetPinAlpha(Pin* pin, Pin* newLinkPin, App* app)
|
|
{
|
|
float alpha = ImGui::GetStyle().Alpha;
|
|
if (newLinkPin && !app->CanCreateLink(newLinkPin, pin) && pin != newLinkPin)
|
|
alpha = alpha * (48.0f / 255.0f);
|
|
return alpha;
|
|
}
|
|
|
|
} // namespace NodeRendering
|
|
} // namespace ax
|
|
|
|
|