1221 lines
42 KiB
C++
1221 lines
42 KiB
C++
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
#include <crude_json.h>
|
|
|
|
#include "../all.h"
|
|
|
|
#include "parameter_node.h"
|
|
#include "../app.h"
|
|
#include "block.h"
|
|
#include "../utilities/node_renderer_base.h"
|
|
#include "../containers/container.h"
|
|
#include "NodeEx.h"
|
|
#include "constants.h"
|
|
#include <imgui_internal.h>
|
|
#include <imgui_node_editor.h>
|
|
|
|
namespace ed = ax::NodeEditor;
|
|
using namespace ax::NodeRendering;
|
|
using namespace NodeConstants;
|
|
|
|
void ParameterNode::InitializeDefaultValue()
|
|
{
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool: m_BoolValue = false; break;
|
|
case PinType::Int: m_IntValue = 0; break;
|
|
case PinType::Float: m_FloatValue = 0.0f; break;
|
|
case PinType::String: m_StringValue = ""; break;
|
|
default: m_IntValue = 0; break;
|
|
}
|
|
}
|
|
|
|
void ParameterNode::CycleDisplayMode()
|
|
{
|
|
switch (m_DisplayMode)
|
|
{
|
|
case ParameterDisplayMode::NameOnly:
|
|
m_DisplayMode = ParameterDisplayMode::NameAndValue;
|
|
break;
|
|
case ParameterDisplayMode::NameAndValue:
|
|
m_DisplayMode = ParameterDisplayMode::SmallBox;
|
|
break;
|
|
case ParameterDisplayMode::SmallBox:
|
|
m_DisplayMode = ParameterDisplayMode::Minimal;
|
|
break;
|
|
case ParameterDisplayMode::Minimal:
|
|
m_DisplayMode = ParameterDisplayMode::MinimalLinks;
|
|
break;
|
|
case ParameterDisplayMode::MinimalLinks:
|
|
m_DisplayMode = ParameterDisplayMode::NameOnly;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::string ParameterNode::GetValueString() const
|
|
{
|
|
char buffer[256];
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool: return m_BoolValue ? "true" : "false";
|
|
case PinType::Int: snprintf(buffer, sizeof(buffer), "%d", m_IntValue); return buffer;
|
|
case PinType::Float: snprintf(buffer, sizeof(buffer), "%.2f", m_FloatValue); return buffer;
|
|
case PinType::String: return m_StringValue;
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
void ParameterNode::SyncValueToSource(App* app)
|
|
{
|
|
// Only sync if this is a shortcut (has source ID)
|
|
if (!app || m_SourceID == 0)
|
|
return;
|
|
|
|
// Find the source node
|
|
Node* sourceNode = app->FindNode(ed::NodeId(m_SourceID));
|
|
if (!sourceNode || !sourceNode->ParameterInstance)
|
|
{
|
|
// Source doesn't exist - orphaned shortcut, clear reference
|
|
printf("[SYNC] Parameter node: Source node %d not found, clearing shortcut reference\n", m_SourceID);
|
|
m_SourceID = 0;
|
|
m_IsSource = false;
|
|
return;
|
|
}
|
|
|
|
// Update source's value based on this shortcut's value
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool:
|
|
sourceNode->ParameterInstance->SetBool(m_BoolValue);
|
|
sourceNode->BoolValue = m_BoolValue;
|
|
break;
|
|
case PinType::Int:
|
|
sourceNode->ParameterInstance->SetInt(m_IntValue);
|
|
sourceNode->IntValue = m_IntValue;
|
|
break;
|
|
case PinType::Float:
|
|
sourceNode->ParameterInstance->SetFloat(m_FloatValue);
|
|
sourceNode->FloatValue = m_FloatValue;
|
|
break;
|
|
case PinType::String:
|
|
sourceNode->ParameterInstance->SetString(m_StringValue);
|
|
sourceNode->StringValue = m_StringValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ParameterNode::SyncValueToAllShortcuts(Node& node, App* app)
|
|
{
|
|
// Only sync if this is a source node
|
|
if (!app || !m_IsSource)
|
|
return;
|
|
|
|
// Get all nodes from active root container
|
|
auto* container = app->GetActiveRootContainer();
|
|
if (!container)
|
|
return;
|
|
|
|
auto allNodes = container->GetNodes(app);
|
|
|
|
// Find all shortcuts that reference this source
|
|
for (Node* otherNode : allNodes)
|
|
{
|
|
if (!otherNode || otherNode->ID == node.ID)
|
|
continue;
|
|
|
|
if (otherNode->Type == NodeType::Parameter && otherNode->ParameterInstance)
|
|
{
|
|
// Check if this node is a shortcut referencing us
|
|
if (otherNode->ParameterInstance->GetSourceID() == m_ID)
|
|
{
|
|
// Update shortcut's value from source
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool:
|
|
otherNode->ParameterInstance->SetBool(m_BoolValue);
|
|
otherNode->BoolValue = m_BoolValue;
|
|
break;
|
|
case PinType::Int:
|
|
otherNode->ParameterInstance->SetInt(m_IntValue);
|
|
otherNode->IntValue = m_IntValue;
|
|
break;
|
|
case PinType::Float:
|
|
otherNode->ParameterInstance->SetFloat(m_FloatValue);
|
|
otherNode->FloatValue = m_FloatValue;
|
|
break;
|
|
case PinType::String:
|
|
otherNode->ParameterInstance->SetString(m_StringValue);
|
|
otherNode->StringValue = m_StringValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParameterNode::SyncNameToSource(App* app)
|
|
{
|
|
// Only sync if this is a shortcut (has source ID)
|
|
if (!app || m_SourceID == 0)
|
|
return;
|
|
|
|
// Find the source node
|
|
Node* sourceNode = app->FindNode(ed::NodeId(m_SourceID));
|
|
if (!sourceNode || !sourceNode->ParameterInstance)
|
|
{
|
|
// Source doesn't exist - orphaned shortcut, clear reference
|
|
printf("[SYNC] Parameter node: Source node %d not found for name sync, clearing shortcut reference\n", m_SourceID);
|
|
m_SourceID = 0;
|
|
m_IsSource = false;
|
|
return;
|
|
}
|
|
|
|
// Update source's name from this shortcut
|
|
sourceNode->ParameterInstance->SetName(m_Name.c_str());
|
|
sourceNode->Name = m_Name;
|
|
}
|
|
|
|
void ParameterNode::SyncNameToAllShortcuts(Node& node, App* app)
|
|
{
|
|
// Only sync if this is a source node
|
|
if (!app || !m_IsSource)
|
|
return;
|
|
|
|
// Get all nodes from active root container
|
|
auto* container = app->GetActiveRootContainer();
|
|
if (!container)
|
|
return;
|
|
|
|
auto allNodes = container->GetNodes(app);
|
|
|
|
// Find all shortcuts that reference this source
|
|
for (Node* otherNode : allNodes)
|
|
{
|
|
if (!otherNode || otherNode->ID == node.ID)
|
|
continue;
|
|
|
|
if (otherNode->Type == NodeType::Parameter && otherNode->ParameterInstance)
|
|
{
|
|
// Check if this node is a shortcut referencing us
|
|
if (otherNode->ParameterInstance->GetSourceID() == m_ID)
|
|
{
|
|
// Update shortcut's name from source
|
|
otherNode->ParameterInstance->SetName(m_Name.c_str());
|
|
otherNode->Name = m_Name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int ParameterNode::Run(Node& node, App* app)
|
|
{
|
|
return RunInternal(node, app, 0);
|
|
}
|
|
|
|
int ParameterNode::RunInternal(Node& node, App* app, int depth)
|
|
{
|
|
// Prevent infinite recursion (max depth of 10)
|
|
if (depth > 10 || !app || node.Inputs.empty())
|
|
return E_OK;
|
|
|
|
// If this is a shortcut, sync value from source first (before checking input connections)
|
|
if (m_SourceID > 0)
|
|
{
|
|
Node* sourceNode = app->FindNode(ed::NodeId(m_SourceID));
|
|
if (sourceNode && sourceNode->ParameterInstance)
|
|
{
|
|
// Source exists - sync value from source
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool:
|
|
m_BoolValue = sourceNode->ParameterInstance->GetBool();
|
|
node.BoolValue = m_BoolValue;
|
|
break;
|
|
case PinType::Int:
|
|
m_IntValue = sourceNode->ParameterInstance->GetInt();
|
|
node.IntValue = m_IntValue;
|
|
break;
|
|
case PinType::Float:
|
|
m_FloatValue = sourceNode->ParameterInstance->GetFloat();
|
|
node.FloatValue = m_FloatValue;
|
|
break;
|
|
case PinType::String:
|
|
m_StringValue = sourceNode->ParameterInstance->GetString();
|
|
node.StringValue = m_StringValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Source doesn't exist - orphaned shortcut, clear reference
|
|
printf("[RUN] Parameter node %d: Source node %d not found, clearing shortcut reference\n",
|
|
ToRuntimeId(node.ID), m_SourceID);
|
|
m_SourceID = 0;
|
|
m_IsSource = false;
|
|
}
|
|
}
|
|
|
|
// Get the input pin (should be at index 0)
|
|
auto& inputPin = node.Inputs[0];
|
|
|
|
// Find link connected to this input pin (must be EndPinID - data flows TO this input)
|
|
auto* link = app->FindLinkConnectedToPin(inputPin.ID);
|
|
if (!link || link->EndPinID != inputPin.ID)
|
|
{
|
|
// No connection or link is in wrong direction, keep current value
|
|
return E_OK;
|
|
}
|
|
|
|
// Get the source pin (StartPinID of the link - where data flows FROM)
|
|
auto* sourcePin = app->FindPin(link->StartPinID);
|
|
if (!sourcePin || !sourcePin->Node)
|
|
{
|
|
// Invalid source
|
|
return E_OK;
|
|
}
|
|
|
|
// Get value from source node based on its type
|
|
auto* sourceNode = sourcePin->Node;
|
|
|
|
if (sourceNode->Type == NodeType::Parameter && sourceNode->ParameterInstance)
|
|
{
|
|
// First, run the source node to ensure it has the latest value from its inputs
|
|
// This propagates values through the chain (with depth limit to prevent recursion)
|
|
sourceNode->ParameterInstance->RunInternal(*sourceNode, app, depth + 1);
|
|
|
|
// Read from node structure values (source of truth - these are updated via UI and Run())
|
|
// This ensures we always get the current displayed value
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool:
|
|
m_BoolValue = sourceNode->BoolValue;
|
|
node.BoolValue = m_BoolValue;
|
|
break;
|
|
case PinType::Int:
|
|
m_IntValue = sourceNode->IntValue;
|
|
node.IntValue = m_IntValue;
|
|
break;
|
|
case PinType::Float:
|
|
m_FloatValue = sourceNode->FloatValue;
|
|
node.FloatValue = m_FloatValue;
|
|
break;
|
|
case PinType::String:
|
|
m_StringValue = sourceNode->StringValue;
|
|
node.StringValue = m_StringValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Sync the source ParameterInstance to match its node value (only the matching type)
|
|
// This ensures ParameterInstance stays in sync with node structure
|
|
switch (sourceNode->ParameterType)
|
|
{
|
|
case PinType::Bool:
|
|
sourceNode->ParameterInstance->SetBool(sourceNode->BoolValue);
|
|
break;
|
|
case PinType::Int:
|
|
sourceNode->ParameterInstance->SetInt(sourceNode->IntValue);
|
|
break;
|
|
case PinType::Float:
|
|
sourceNode->ParameterInstance->SetFloat(sourceNode->FloatValue);
|
|
break;
|
|
case PinType::String:
|
|
sourceNode->ParameterInstance->SetString(sourceNode->StringValue);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (sourceNode->IsBlockBased() && sourceNode->BlockInstance)
|
|
{
|
|
// Source is a block output - read value from block's output parameter
|
|
// Block outputs store their values in UnconnectedParamValues
|
|
const int sourcePinId = ToRuntimeId(sourcePin->ID);
|
|
auto& paramValues = sourceNode->UnconnectedParamValues;
|
|
|
|
if (paramValues.find(sourcePinId) != paramValues.end())
|
|
{
|
|
const std::string& valueStr = paramValues[sourcePinId];
|
|
|
|
// Convert string value to appropriate type
|
|
try {
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool:
|
|
m_BoolValue = (valueStr == "true" || valueStr == "1");
|
|
node.BoolValue = m_BoolValue;
|
|
break;
|
|
case PinType::Int:
|
|
m_IntValue = std::stoi(valueStr);
|
|
node.IntValue = m_IntValue;
|
|
break;
|
|
case PinType::Float:
|
|
m_FloatValue = std::stof(valueStr);
|
|
node.FloatValue = m_FloatValue;
|
|
break;
|
|
case PinType::String:
|
|
m_StringValue = valueStr;
|
|
node.StringValue = m_StringValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} catch (...) {
|
|
// Conversion failed, keep current value
|
|
}
|
|
}
|
|
}
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
void ParameterNode::Build(Node& node, App* app)
|
|
{
|
|
// Parameter node has both input and output pins
|
|
int inputPinId = app->GetNextId();
|
|
node.Inputs.emplace_back(inputPinId, "", m_Type);
|
|
|
|
int outputPinId = app->GetNextId();
|
|
node.Outputs.emplace_back(outputPinId, "", m_Type);
|
|
|
|
// Store parameter data in node
|
|
node.Type = NodeType::Parameter;
|
|
node.ParameterType = m_Type;
|
|
|
|
// Copy values to node structure
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool: node.BoolValue = m_BoolValue; break;
|
|
case PinType::Int: node.IntValue = m_IntValue; break;
|
|
case PinType::Float: node.FloatValue = m_FloatValue; break;
|
|
case PinType::String: node.StringValue = m_StringValue; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void ParameterNode::Render(Node& node, App* app, Pin* newLinkPin)
|
|
{
|
|
switch (m_DisplayMode)
|
|
{
|
|
case ParameterDisplayMode::NameOnly:
|
|
RenderNameOnly(node, app, newLinkPin);
|
|
break;
|
|
case ParameterDisplayMode::NameAndValue:
|
|
RenderNameAndValue(node, app, newLinkPin);
|
|
break;
|
|
case ParameterDisplayMode::SmallBox:
|
|
RenderSmallBox(node, app, newLinkPin);
|
|
break;
|
|
case ParameterDisplayMode::Minimal:
|
|
RenderMinimal(node, app, newLinkPin);
|
|
break;
|
|
case ParameterDisplayMode::MinimalLinks:
|
|
RenderMinimalLinks(node, app, newLinkPin);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ParameterNode::RenderNameOnly(Node& node, App* app, Pin* newLinkPin)
|
|
{
|
|
// Get styles from StyleManager
|
|
auto& styleManager = app->GetStyleManager();
|
|
auto& paramStyle = styleManager.ParameterStyle;
|
|
|
|
// Use dedicated ParameterStyle background color (visually distinct from blocks)
|
|
ImColor bgColor = paramStyle.BgColor;
|
|
ImColor borderColor = paramStyle.BorderColor;
|
|
float borderWidth = paramStyle.BorderWidth;
|
|
|
|
if (m_IsSource)
|
|
{
|
|
borderColor = styleManager.ParamBorderColorSource;
|
|
borderWidth = styleManager.ParamBorderWidthSource;
|
|
}
|
|
else if (m_SourceID > 0)
|
|
{
|
|
// Shortcut node: dimmed background
|
|
bgColor = ImColor(bgColor.Value.x * 0.7f, bgColor.Value.y * 0.7f, bgColor.Value.z * 0.7f, bgColor.Value.w);
|
|
borderColor = styleManager.ParamBorderColorShortcut;
|
|
}
|
|
|
|
NodeStyleScope style(
|
|
bgColor,
|
|
borderColor,
|
|
paramStyle.Rounding, borderWidth,
|
|
styleManager.ParamPaddingNameOnly,
|
|
ImVec2(0.0f, 1.0f),
|
|
ImVec2(0.0f, -1.0f)
|
|
);
|
|
|
|
ed::BeginNode(node.ID);
|
|
ImGui::PushID(node.ID.AsPointer());
|
|
ImGui::BeginVertical("param_node");
|
|
|
|
// Editable name
|
|
ImGui::BeginHorizontal("param_name");
|
|
ImGui::PushItemWidth(styleManager.ParamInputWidthNameOnly);
|
|
char nameBuffer[64];
|
|
strncpy(nameBuffer, node.Name.c_str(), 63);
|
|
nameBuffer[63] = '\0';
|
|
|
|
static ed::NodeId editingNode = 0;
|
|
if (ImGui::InputText("##name", nameBuffer, 64))
|
|
{
|
|
node.Name = nameBuffer;
|
|
SetName(nameBuffer);
|
|
|
|
if (m_IsSource)
|
|
SyncNameToAllShortcuts(node, app);
|
|
}
|
|
|
|
HandleTextInput(ImGui::IsItemActive(), node.ID, editingNode);
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndHorizontal();
|
|
|
|
ImGui::EndVertical();
|
|
|
|
// Save cursor and get node bounds
|
|
ImVec2 contentEndPos = ImGui::GetCursorScreenPos();
|
|
ImVec2 nodePos = ed::GetNodePosition(node.ID);
|
|
ImVec2 nodeSize = ed::GetNodeSize(node.ID);
|
|
|
|
if (nodeSize.x <= 0 || nodeSize.y <= 0)
|
|
{
|
|
ImVec2 contentMin = ImGui::GetItemRectMin();
|
|
ImVec2 contentMax = ImGui::GetItemRectMax();
|
|
nodeSize = contentMax - contentMin;
|
|
}
|
|
|
|
ImRect nodeRect = ImRect(nodePos, nodePos + nodeSize);
|
|
|
|
// Place pins at top/bottom edges using NodeEx
|
|
auto& input = node.Inputs[0];
|
|
auto& output = node.Outputs[0];
|
|
|
|
float inputAlpha = GetPinAlpha(&input, newLinkPin, app);
|
|
float outputAlpha = GetPinAlpha(&output, newLinkPin, app);
|
|
|
|
ed::PinState inputState = (inputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
ed::PinState outputState = (outputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
|
|
// Input pin at top center
|
|
ImRect inputRect = ed::PinEx(input.ID, ed::PinKind::Input, ed::PinEdge::Top,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, inputState);
|
|
input.LastPivotPosition = ImVec2(inputRect.GetCenter().x, inputRect.Min.y);
|
|
input.LastRenderBounds = inputRect;
|
|
input.HasPositionData = true;
|
|
|
|
// Output pin at bottom center
|
|
ImRect outputRect = ed::PinEx(output.ID, ed::PinKind::Output, ed::PinEdge::Bottom,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, outputState);
|
|
output.LastPivotPosition = ImVec2(outputRect.GetCenter().x, outputRect.Max.y);
|
|
output.LastRenderBounds = outputRect;
|
|
output.HasPositionData = true;
|
|
|
|
// Restore cursor
|
|
ImGui::SetCursorScreenPos(contentEndPos);
|
|
|
|
ImGui::PopID();
|
|
ed::EndNode();
|
|
}
|
|
|
|
void ParameterNode::RenderNameAndValue(Node& node, App* app, Pin* newLinkPin)
|
|
{
|
|
// Get styles from StyleManager
|
|
auto& styleManager = app->GetStyleManager();
|
|
auto& paramStyle = styleManager.ParameterStyle;
|
|
|
|
// Use dedicated ParameterStyle background color (visually distinct from blocks)
|
|
ImColor bgColor = paramStyle.BgColor;
|
|
ImColor borderColor = paramStyle.BorderColor;
|
|
float borderWidth = styleManager.ParamBorderWidthNameAndValue;
|
|
|
|
if (m_IsSource)
|
|
{
|
|
borderColor = styleManager.ParamBorderColorSource;
|
|
borderWidth = styleManager.ParamBorderWidthSourceNameAndValue;
|
|
}
|
|
else if (m_SourceID > 0)
|
|
{
|
|
// Shortcut node: dimmed background
|
|
bgColor = ImColor(bgColor.Value.x * 0.7f, bgColor.Value.y * 0.7f, bgColor.Value.z * 0.7f, bgColor.Value.w);
|
|
borderColor = styleManager.ParamBorderColorShortcut;
|
|
}
|
|
|
|
NodeStyleScope style(
|
|
bgColor,
|
|
borderColor,
|
|
paramStyle.Rounding, borderWidth,
|
|
styleManager.ParamPaddingNameAndValue,
|
|
ImVec2(0.0f, 1.0f),
|
|
ImVec2(0.0f, -1.0f)
|
|
);
|
|
|
|
ed::BeginNode(node.ID);
|
|
ImGui::PushID(node.ID.AsPointer());
|
|
ImGui::BeginVertical("param_node");
|
|
|
|
// Editable name
|
|
ImGui::BeginHorizontal("param_name");
|
|
ImGui::PushItemWidth(styleManager.ParamInputWidthNameAndValue);
|
|
char nameBuffer[64];
|
|
strncpy(nameBuffer, node.Name.c_str(), 63);
|
|
nameBuffer[63] = '\0';
|
|
|
|
static ed::NodeId editingNameNode = 0;
|
|
if (ImGui::InputText("##name", nameBuffer, 64))
|
|
{
|
|
node.Name = nameBuffer;
|
|
SetName(nameBuffer);
|
|
|
|
if (m_IsSource)
|
|
SyncNameToAllShortcuts(node, app);
|
|
}
|
|
|
|
HandleTextInput(ImGui::IsItemActive(), node.ID, editingNameNode);
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndHorizontal();
|
|
|
|
// Value editor
|
|
ImGui::BeginHorizontal("param_value");
|
|
ImGui::PushItemWidth(styleManager.ParamInputWidthNameAndValue);
|
|
|
|
static ed::NodeId editingValueNode = 0;
|
|
bool wasEditing = false;
|
|
|
|
switch (node.ParameterType)
|
|
{
|
|
case PinType::Bool:
|
|
if (ImGui::Checkbox("##value", &node.BoolValue))
|
|
{
|
|
m_BoolValue = node.BoolValue;
|
|
if (m_SourceID > 0)
|
|
SyncValueToSource(app);
|
|
else if (m_IsSource)
|
|
SyncValueToAllShortcuts(node, app);
|
|
}
|
|
break;
|
|
case PinType::Int:
|
|
if (ImGui::DragInt("##value", &node.IntValue, 1.0f))
|
|
{
|
|
m_IntValue = node.IntValue;
|
|
if (m_SourceID > 0)
|
|
SyncValueToSource(app);
|
|
else if (m_IsSource)
|
|
SyncValueToAllShortcuts(node, app);
|
|
}
|
|
wasEditing = ImGui::IsItemActive();
|
|
break;
|
|
case PinType::Float:
|
|
if (ImGui::DragFloat("##value", &node.FloatValue, 0.01f))
|
|
{
|
|
m_FloatValue = node.FloatValue;
|
|
if (m_SourceID > 0)
|
|
SyncValueToSource(app);
|
|
else if (m_IsSource)
|
|
SyncValueToAllShortcuts(node, app);
|
|
}
|
|
wasEditing = ImGui::IsItemActive();
|
|
break;
|
|
case PinType::String:
|
|
{
|
|
char strBuffer[256];
|
|
strncpy(strBuffer, node.StringValue.c_str(), 255);
|
|
strBuffer[255] = '\0';
|
|
if (ImGui::InputText("##value", strBuffer, 256))
|
|
{
|
|
node.StringValue = strBuffer;
|
|
m_StringValue = strBuffer;
|
|
if (m_SourceID > 0)
|
|
SyncValueToSource(app);
|
|
else if (m_IsSource)
|
|
SyncValueToAllShortcuts(node, app);
|
|
}
|
|
wasEditing = ImGui::IsItemActive();
|
|
break;
|
|
}
|
|
default:
|
|
ImGui::Text("Unknown");
|
|
break;
|
|
}
|
|
|
|
HandleTextInput(wasEditing, node.ID, editingValueNode);
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndHorizontal();
|
|
|
|
ImGui::EndVertical();
|
|
|
|
// Save cursor and get node bounds
|
|
ImVec2 contentEndPos = ImGui::GetCursorScreenPos();
|
|
ImVec2 nodePos = ed::GetNodePosition(node.ID);
|
|
ImVec2 nodeSize = ed::GetNodeSize(node.ID);
|
|
|
|
if (nodeSize.x <= 0 || nodeSize.y <= 0)
|
|
{
|
|
ImVec2 contentMin = ImGui::GetItemRectMin();
|
|
ImVec2 contentMax = ImGui::GetItemRectMax();
|
|
nodeSize = contentMax - contentMin;
|
|
}
|
|
|
|
ImRect nodeRect = ImRect(nodePos, nodePos + nodeSize);
|
|
|
|
// Place pins at top/bottom edges using NodeEx
|
|
auto& input = node.Inputs[0];
|
|
auto& output = node.Outputs[0];
|
|
|
|
float inputAlpha = GetPinAlpha(&input, newLinkPin, app);
|
|
float outputAlpha = GetPinAlpha(&output, newLinkPin, app);
|
|
|
|
ed::PinState inputState = (inputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
ed::PinState outputState = (outputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
|
|
// Input pin at top center
|
|
ImRect inputRect = ed::PinEx(input.ID, ed::PinKind::Input, ed::PinEdge::Top,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, inputState);
|
|
input.LastPivotPosition = ImVec2(inputRect.GetCenter().x, inputRect.Min.y);
|
|
input.LastRenderBounds = inputRect;
|
|
input.HasPositionData = true;
|
|
|
|
// Output pin at bottom center
|
|
ImRect outputRect = ed::PinEx(output.ID, ed::PinKind::Output, ed::PinEdge::Bottom,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, outputState);
|
|
output.LastPivotPosition = ImVec2(outputRect.GetCenter().x, outputRect.Max.y);
|
|
output.LastRenderBounds = outputRect;
|
|
output.HasPositionData = true;
|
|
|
|
// Restore cursor
|
|
ImGui::SetCursorScreenPos(contentEndPos);
|
|
|
|
ImGui::PopID();
|
|
ed::EndNode();
|
|
}
|
|
|
|
void ParameterNode::RenderSmallBox(Node& node, App* app, Pin* newLinkPin)
|
|
{
|
|
// Get styles from StyleManager
|
|
auto& styleManager = app->GetStyleManager();
|
|
auto& paramStyle = styleManager.ParameterStyle;
|
|
|
|
// Use dedicated ParameterStyle background color (visually distinct from blocks)
|
|
ImColor bgColor = paramStyle.BgColor;
|
|
ImColor borderColor = paramStyle.BorderColor;
|
|
float borderWidth = paramStyle.BorderWidth;
|
|
|
|
if (m_IsSource)
|
|
{
|
|
borderColor = styleManager.ParamBorderColorSource;
|
|
borderWidth = styleManager.ParamBorderWidthSource;
|
|
}
|
|
else if (m_SourceID > 0)
|
|
{
|
|
// Shortcut node: dimmed background
|
|
bgColor = ImColor(bgColor.Value.x * 0.7f, bgColor.Value.y * 0.7f, bgColor.Value.z * 0.7f, bgColor.Value.w);
|
|
borderColor = styleManager.ParamBorderColorShortcut;
|
|
}
|
|
|
|
NodeStyleScope style(
|
|
bgColor,
|
|
borderColor,
|
|
paramStyle.Rounding, borderWidth,
|
|
styleManager.ParamPaddingSmallBox,
|
|
ImVec2(0.0f, 1.0f),
|
|
ImVec2(0.0f, -1.0f)
|
|
);
|
|
|
|
ed::BeginNode(node.ID);
|
|
ImGui::PushID(node.ID.AsPointer());
|
|
ImGui::BeginVertical("param_box");
|
|
|
|
// Value editor row
|
|
ImGui::BeginHorizontal("value_row");
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1));
|
|
std::string valueStr = GetValueString();
|
|
|
|
ImGui::PushItemWidth(styleManager.ParamInputWidthSmallBox);
|
|
char valueBuffer[32];
|
|
strncpy(valueBuffer, valueStr.c_str(), 31);
|
|
valueBuffer[31] = '\0';
|
|
|
|
static ed::NodeId editingBoxNode = 0;
|
|
bool edited = false;
|
|
|
|
switch (node.ParameterType)
|
|
{
|
|
case PinType::Bool:
|
|
{
|
|
bool val = node.BoolValue;
|
|
if (ImGui::Checkbox("##val", &val))
|
|
{
|
|
node.BoolValue = val;
|
|
m_BoolValue = val;
|
|
edited = true;
|
|
if (m_SourceID > 0)
|
|
SyncValueToSource(app);
|
|
else if (m_IsSource)
|
|
SyncValueToAllShortcuts(node, app);
|
|
}
|
|
break;
|
|
}
|
|
case PinType::Int:
|
|
if (ImGui::DragInt("##val", &node.IntValue, 1.0f))
|
|
{
|
|
m_IntValue = node.IntValue;
|
|
edited = true;
|
|
if (m_SourceID > 0)
|
|
SyncValueToSource(app);
|
|
else if (m_IsSource)
|
|
SyncValueToAllShortcuts(node, app);
|
|
}
|
|
break;
|
|
case PinType::Float:
|
|
if (ImGui::DragFloat("##val", &node.FloatValue, 0.01f, 0.0f, 0.0f, "%.1f"))
|
|
{
|
|
m_FloatValue = node.FloatValue;
|
|
edited = true;
|
|
if (m_SourceID > 0)
|
|
SyncValueToSource(app);
|
|
else if (m_IsSource)
|
|
SyncValueToAllShortcuts(node, app);
|
|
}
|
|
break;
|
|
default:
|
|
ImGui::TextUnformatted(valueBuffer);
|
|
break;
|
|
}
|
|
|
|
HandleTextInput(ImGui::IsItemActive() || edited, node.ID, editingBoxNode);
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::PopStyleColor();
|
|
ImGui::EndHorizontal();
|
|
|
|
ImGui::EndVertical();
|
|
|
|
// Save cursor and get node bounds
|
|
ImVec2 contentEndPos = ImGui::GetCursorScreenPos();
|
|
ImVec2 nodePos = ed::GetNodePosition(node.ID);
|
|
ImVec2 nodeSize = ed::GetNodeSize(node.ID);
|
|
|
|
if (nodeSize.x <= 0 || nodeSize.y <= 0)
|
|
{
|
|
ImVec2 contentMin = ImGui::GetItemRectMin();
|
|
ImVec2 contentMax = ImGui::GetItemRectMax();
|
|
nodeSize = contentMax - contentMin;
|
|
}
|
|
|
|
ImRect nodeRect = ImRect(nodePos, nodePos + nodeSize);
|
|
|
|
// Place pins at top/bottom edges using NodeEx
|
|
auto& input = node.Inputs[0];
|
|
auto& output = node.Outputs[0];
|
|
|
|
float inputAlpha = GetPinAlpha(&input, newLinkPin, app);
|
|
float outputAlpha = GetPinAlpha(&output, newLinkPin, app);
|
|
|
|
ed::PinState inputState = (inputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
ed::PinState outputState = (outputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
|
|
// Input pin at top center
|
|
ImRect inputRect = ed::PinEx(input.ID, ed::PinKind::Input, ed::PinEdge::Top,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, inputState);
|
|
input.LastPivotPosition = ImVec2(inputRect.GetCenter().x, inputRect.Min.y);
|
|
input.LastRenderBounds = inputRect;
|
|
input.HasPositionData = true;
|
|
|
|
// Output pin at bottom center
|
|
ImRect outputRect = ed::PinEx(output.ID, ed::PinKind::Output, ed::PinEdge::Bottom,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, outputState);
|
|
output.LastPivotPosition = ImVec2(outputRect.GetCenter().x, outputRect.Max.y);
|
|
output.LastRenderBounds = outputRect;
|
|
output.HasPositionData = true;
|
|
|
|
// Restore cursor
|
|
ImGui::SetCursorScreenPos(contentEndPos);
|
|
|
|
ImGui::PopID();
|
|
ed::EndNode();
|
|
}
|
|
|
|
void ParameterNode::RenderMinimal(Node& node, App* app, Pin* newLinkPin)
|
|
{
|
|
// Get styles from StyleManager
|
|
auto& styleManager = app->GetStyleManager();
|
|
auto& paramStyle = styleManager.ParameterStyle;
|
|
|
|
// Use dedicated ParameterStyle background color (visually distinct from blocks)
|
|
ImColor bgColor = paramStyle.BgColor;
|
|
ImColor borderColor = paramStyle.BorderColor;
|
|
float borderWidth = paramStyle.BorderWidth;
|
|
|
|
if (m_IsSource)
|
|
{
|
|
borderColor = styleManager.ParamBorderColorSource;
|
|
borderWidth = styleManager.ParamBorderWidthSource;
|
|
}
|
|
else if (m_SourceID > 0)
|
|
{
|
|
// Shortcut node: dimmed background
|
|
bgColor = ImColor(bgColor.Value.x * 0.7f, bgColor.Value.y * 0.7f, bgColor.Value.z * 0.7f, bgColor.Value.w);
|
|
borderColor = styleManager.ParamBorderColorShortcut;
|
|
}
|
|
|
|
NodeStyleScope style(
|
|
bgColor,
|
|
borderColor,
|
|
paramStyle.Rounding, borderWidth,
|
|
styleManager.ParamPaddingMinimal,
|
|
ImVec2(0.0f, 0.0f),
|
|
ImVec2(0.0f, 0.0f)
|
|
);
|
|
|
|
ed::BeginNode(node.ID);
|
|
ImGui::PushID(node.ID.AsPointer());
|
|
ImGui::BeginVertical("param_minimal");
|
|
|
|
// Just a minimal rectangle - fixed size, no name, no pins, no links
|
|
ImGui::Dummy(styleManager.ParamMinimalSize);
|
|
|
|
ImGui::EndVertical();
|
|
ImGui::PopID();
|
|
ed::EndNode();
|
|
// Note: Pins are NOT rendered in Minimal mode
|
|
}
|
|
|
|
void ParameterNode::RenderMinimalLinks(Node& node, App* app, Pin* newLinkPin)
|
|
{
|
|
// Get styles from StyleManager
|
|
auto& styleManager = app->GetStyleManager();
|
|
auto& paramStyle = styleManager.ParameterStyle;
|
|
|
|
// Use dedicated ParameterStyle background color (visually distinct from blocks)
|
|
ImColor bgColor = paramStyle.BgColor;
|
|
ImColor borderColor = paramStyle.BorderColor;
|
|
float borderWidth = paramStyle.BorderWidth;
|
|
|
|
if (m_IsSource)
|
|
{
|
|
borderColor = styleManager.ParamBorderColorSource;
|
|
borderWidth = styleManager.ParamBorderWidthSource;
|
|
}
|
|
else if (m_SourceID > 0)
|
|
{
|
|
// Shortcut node: dimmed background
|
|
bgColor = ImColor(bgColor.Value.x * 0.7f, bgColor.Value.y * 0.7f, bgColor.Value.z * 0.7f, bgColor.Value.w);
|
|
borderColor = styleManager.ParamBorderColorShortcut;
|
|
}
|
|
|
|
NodeStyleScope style(
|
|
bgColor,
|
|
borderColor,
|
|
paramStyle.Rounding, borderWidth,
|
|
styleManager.ParamPaddingMinimal,
|
|
ImVec2(0.0f, 1.0f),
|
|
ImVec2(0.0f, -1.0f)
|
|
);
|
|
|
|
ed::BeginNode(node.ID);
|
|
ImGui::PushID(node.ID.AsPointer());
|
|
ImGui::BeginVertical("param_minimal_links");
|
|
|
|
// Minimal content - just a small rectangle, no name
|
|
ImGui::Dummy(styleManager.ParamMinimalLinksSize);
|
|
|
|
ImGui::EndVertical();
|
|
|
|
// Save cursor and get node bounds
|
|
ImVec2 contentEndPos = ImGui::GetCursorScreenPos();
|
|
ImVec2 nodePos = ed::GetNodePosition(node.ID);
|
|
ImVec2 nodeSize = ed::GetNodeSize(node.ID);
|
|
|
|
if (nodeSize.x <= 0 || nodeSize.y <= 0)
|
|
{
|
|
ImVec2 contentMin = ImGui::GetItemRectMin();
|
|
ImVec2 contentMax = ImGui::GetItemRectMax();
|
|
nodeSize = contentMax - contentMin;
|
|
}
|
|
|
|
ImRect nodeRect = ImRect(nodePos, nodePos + nodeSize);
|
|
|
|
// Place pins at top/bottom edges using NodeEx
|
|
auto& input = node.Inputs[0];
|
|
auto& output = node.Outputs[0];
|
|
|
|
float inputAlpha = GetPinAlpha(&input, newLinkPin, app);
|
|
float outputAlpha = GetPinAlpha(&output, newLinkPin, app);
|
|
|
|
ed::PinState inputState = (inputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
ed::PinState outputState = (outputAlpha < 1.0f) ? ed::PinState::Deactivated : ed::PinState::Normal;
|
|
|
|
// Input pin at top center (smaller size for minimal mode)
|
|
ImRect inputRect = ed::PinEx(input.ID, ed::PinKind::Input, ed::PinEdge::Top,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, inputState);
|
|
input.LastPivotPosition = ImVec2(inputRect.GetCenter().x, inputRect.Min.y);
|
|
input.LastRenderBounds = inputRect;
|
|
input.HasPositionData = true;
|
|
|
|
// Output pin at bottom center (smaller size for minimal mode)
|
|
ImRect outputRect = ed::PinEx(output.ID, ed::PinKind::Output, ed::PinEdge::Bottom,
|
|
0.5f, styleManager.ParameterPinEdgeOffset, nodeRect, outputState);
|
|
output.LastPivotPosition = ImVec2(outputRect.GetCenter().x, outputRect.Max.y);
|
|
output.LastRenderBounds = outputRect;
|
|
output.HasPositionData = true;
|
|
|
|
// Restore cursor
|
|
ImGui::SetCursorScreenPos(contentEndPos);
|
|
|
|
ImGui::PopID();
|
|
ed::EndNode();
|
|
}
|
|
|
|
void ParameterNode::OnMenu(Node& node, App* app)
|
|
{
|
|
ImGui::Separator();
|
|
|
|
auto mode = GetDisplayMode();
|
|
const char* modeStr = "Unknown";
|
|
if (mode == ParameterDisplayMode::NameOnly) modeStr = "Name Only";
|
|
else if (mode == ParameterDisplayMode::NameAndValue) modeStr = "Name + Value";
|
|
else if (mode == ParameterDisplayMode::SmallBox) modeStr = "Small Box";
|
|
else if (mode == ParameterDisplayMode::Minimal) modeStr = "Minimal";
|
|
else if (mode == ParameterDisplayMode::MinimalLinks) modeStr = "Minimal Links";
|
|
|
|
ImGui::Text("Display: %s", modeStr);
|
|
|
|
if (ImGui::MenuItem("Cycle Display Mode (Space)"))
|
|
{
|
|
CycleDisplayMode();
|
|
// Notify editor that display mode changed (triggers link auto-adjustment)
|
|
ed::NotifyBlockDisplayModeChanged(node.ID);
|
|
}
|
|
|
|
if (ImGui::MenuItem("Run (R)"))
|
|
{
|
|
int result = Run(node, app);
|
|
}
|
|
|
|
// Source/Shortcut management
|
|
ImGui::Separator();
|
|
|
|
// Only allow marking as source if not already a shortcut
|
|
if (m_SourceID == 0)
|
|
{
|
|
bool isSource = m_IsSource;
|
|
if (ImGui::MenuItem("As Source", nullptr, &isSource))
|
|
{
|
|
SetIsSource(isSource);
|
|
// If unmarking as source, ensure SourceID is cleared
|
|
if (!isSource)
|
|
{
|
|
SetSourceID(0);
|
|
}
|
|
}
|
|
|
|
// Show "Create Shortcut" option only if this node is a source
|
|
if (m_IsSource)
|
|
{
|
|
if (ImGui::MenuItem("Create Shortcut"))
|
|
{
|
|
Node* shortcut = CreateShortcut(node, app);
|
|
if (shortcut)
|
|
{
|
|
printf("Shortcut created successfully\n");
|
|
}
|
|
else
|
|
{
|
|
printf("Failed to create shortcut\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is a shortcut - show info about source
|
|
ImGui::Text("Shortcut to source: %d", m_SourceID);
|
|
}
|
|
}
|
|
|
|
void ParameterNode::SaveState(Node& node, crude_json::value& nodeData, const Container* container, App* app)
|
|
{
|
|
// Parameter node saves its own state
|
|
nodeData["node_type"] = "parameter";
|
|
nodeData["param_type"] = (double)static_cast<int>(m_Type);
|
|
|
|
// Save display mode
|
|
nodeData["display_mode"] = (double)static_cast<int>(m_DisplayMode);
|
|
|
|
// Save source/shortcut state
|
|
nodeData["is_source"] = m_IsSource;
|
|
nodeData["source_id"] = (double)m_SourceID;
|
|
|
|
// Check if input pin is connected - only save value if input is NOT connected
|
|
bool shouldSaveValue = true;
|
|
|
|
if (app)
|
|
{
|
|
// Find the input pin (parameter nodes have one input pin)
|
|
for (const auto& pin : node.Inputs)
|
|
{
|
|
// Skip flow pins - only check parameter input pins
|
|
if (pin.Type == PinType::Flow)
|
|
continue;
|
|
|
|
// Check if this input pin is connected
|
|
auto* link = app->FindLinkConnectedToPin(pin.ID);
|
|
bool isConnected = (link != nullptr && link->EndPinID == pin.ID);
|
|
|
|
// If input is connected, don't save the local value (it comes from the connection)
|
|
if (isConnected)
|
|
{
|
|
shouldSaveValue = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save value only if input is not connected
|
|
if (shouldSaveValue)
|
|
{
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool: nodeData["value"] = m_BoolValue; break;
|
|
case PinType::Int: nodeData["value"] = (double)m_IntValue; break;
|
|
case PinType::Float: nodeData["value"] = (double)m_FloatValue; break;
|
|
case PinType::String: nodeData["value"] = m_StringValue; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
// Note: container and app parameters are available for subclasses that need them
|
|
(void)container;
|
|
}
|
|
|
|
void ParameterNode::LoadState(Node& node, const crude_json::value& nodeData, Container* container, App* app)
|
|
{
|
|
// Load source/shortcut state
|
|
if (nodeData.contains("is_source"))
|
|
{
|
|
m_IsSource = nodeData["is_source"].get<bool>();
|
|
}
|
|
|
|
if (nodeData.contains("source_id"))
|
|
{
|
|
int sourceId = (int)nodeData["source_id"].get<double>();
|
|
|
|
// Validate source node exists
|
|
if (sourceId > 0 && app)
|
|
{
|
|
Node* sourceNode = app->FindNode(ed::NodeId(sourceId));
|
|
if (sourceNode && sourceNode->ParameterInstance)
|
|
{
|
|
// Source exists - set as shortcut
|
|
m_SourceID = sourceId;
|
|
m_IsSource = false; // Shortcuts are not sources
|
|
}
|
|
else
|
|
{
|
|
// Source doesn't exist - orphaned shortcut, clear reference
|
|
m_SourceID = 0;
|
|
m_IsSource = false;
|
|
}
|
|
}
|
|
else if (sourceId == 0)
|
|
{
|
|
// Not a shortcut
|
|
m_SourceID = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
Node* ParameterNode::CreateShortcut(Node& sourceNode, App* app)
|
|
{
|
|
if (!app)
|
|
return nullptr;
|
|
|
|
// Get source node's position
|
|
ImVec2 sourcePos = ed::GetNodePosition(sourceNode.ID);
|
|
|
|
// Calculate offset position (200px to the right)
|
|
const float offsetX = 200.0f;
|
|
ImVec2 shortcutPos(sourcePos.x + offsetX, sourcePos.y);
|
|
|
|
// Create new parameter node with same type and display mode
|
|
Node* shortcutNode = app->SpawnParameterNode(m_Type, -1, m_DisplayMode);
|
|
if (!shortcutNode)
|
|
{
|
|
printf("[SHORTCUT] Failed to create shortcut node\n");
|
|
return nullptr;
|
|
}
|
|
|
|
// Configure shortcut node
|
|
if (shortcutNode->ParameterInstance)
|
|
{
|
|
// Set as shortcut (not source)
|
|
shortcutNode->ParameterInstance->SetIsSource(false);
|
|
shortcutNode->ParameterInstance->SetSourceID(m_ID); // Reference to source
|
|
|
|
// Copy current value from source
|
|
switch (m_Type)
|
|
{
|
|
case PinType::Bool:
|
|
shortcutNode->ParameterInstance->SetBool(m_BoolValue);
|
|
shortcutNode->BoolValue = m_BoolValue;
|
|
break;
|
|
case PinType::Int:
|
|
shortcutNode->ParameterInstance->SetInt(m_IntValue);
|
|
shortcutNode->IntValue = m_IntValue;
|
|
break;
|
|
case PinType::Float:
|
|
shortcutNode->ParameterInstance->SetFloat(m_FloatValue);
|
|
shortcutNode->FloatValue = m_FloatValue;
|
|
break;
|
|
case PinType::String:
|
|
shortcutNode->ParameterInstance->SetString(m_StringValue);
|
|
shortcutNode->StringValue = m_StringValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Copy name from source (no suffix)
|
|
shortcutNode->ParameterInstance->SetName(m_Name.c_str());
|
|
shortcutNode->Name = m_Name;
|
|
}
|
|
|
|
// Position the shortcut node
|
|
ed::SetNodePosition(shortcutNode->ID, shortcutPos);
|
|
|
|
printf("[SHORTCUT] Created shortcut node %d for source %d at (%.1f, %.1f)\n",
|
|
ToRuntimeId(shortcutNode->ID), m_ID, shortcutPos.x, shortcutPos.y);
|
|
|
|
return shortcutNode;
|
|
}
|
|
|
|
ParameterNode* ParameterRegistry::CreateParameter(PinType type, int id, NH_CSTRING name)
|
|
{
|
|
NH_CSTRING defaultName = nullptr;
|
|
switch (type)
|
|
{
|
|
case PinType::Bool: defaultName = "Bool"; break;
|
|
case PinType::Int: defaultName = "Int"; break;
|
|
case PinType::Float: defaultName = "Float"; break;
|
|
case PinType::String: defaultName = "String"; break;
|
|
default: return nullptr;
|
|
}
|
|
|
|
if (!name)
|
|
name = defaultName;
|
|
|
|
return new ParameterNode(id, name, type);
|
|
}
|
|
|