Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
VisualScriptEditorPanel_NodeManagement.cpp
Go to the documentation of this file.
1/**
2 * @file VisualScriptEditorPanel_NodeManagement.cpp
3 * @brief Node and link creation/deletion management for VisualScriptEditorPanel.
4 * @author Olympe Engine
5 * @date 2026-03-09
6 *
7 * @details This file contains CRUD operations for graph nodes and connections:
8 * - AddNode() — Create a new node on the canvas
9 * - RemoveNode() — Delete a node and its connections
10 * - ConnectExec() — Create an execution pin connection
11 * - ConnectData() — Create a data pin connection
12 *
13 * These methods integrate with the undo/redo command system (ICommand/UndoStack)
14 * to ensure all graph modifications can be reversed via Ctrl+Z. All operations
15 * update both the editor canvas state and the template model.
16 *
17 * Key responsibilities:
18 * - Node allocation and ID assignment
19 * - Type-specific data pin initialization (MathOp, GetBBValue, SetBBValue)
20 * - Command system integration for undo/redo
21 * - Canvas state synchronization
22 * - Link graph rebuilding after modifications
23 *
24 * C++14 compliant — no std::optional, structured bindings, std::filesystem.
25 */
26
28#include "DebugController.h"
30#include "ConditionRegistry.h"
31#include "OperatorRegistry.h"
32#include "BBVariableRegistry.h"
33#include "MathOpOperand.h"
34#include "../system/system_utils.h"
35#include "../system/system_consts.h"
36#include "../NodeGraphCore/GlobalTemplateBlackboard.h"
37
38#include "../third_party/imgui/imgui.h"
39#include "../third_party/imnodes/imnodes.h"
40#include "../json_helper.h"
41#include "../TaskSystem/TaskGraphLoader.h"
42
43#include <fstream>
44#include <iostream>
45#include <algorithm>
46#include <cmath>
47#include <cstring>
48#include <sstream>
49#include <iomanip>
50#include <cstdlib>
51#include <unordered_set>
52
53namespace Olympe {
54
55// ============================================================================
56// Node Management Methods
57// ============================================================================
58
59/**
60 * @brief Create a new node and add it to the editor canvas and template graph.
61 *
62 * Allocates a new unique node ID, creates a TaskNodeDefinition with type-specific
63 * initialization, and pushes an AddNodeCommand to the undo stack for reversibility.
64 *
65 * For special node types, this method initializes data pins:
66 * - **MathOp**: Creates input pins "A" and "B" (Float type), output pin "Result"
67 * - **GetBBValue**: Creates output pin "Value" (type determined by variable selection)
68 * - **SetBBValue**: Creates input pin "Value" (type determined by variable target)
69 *
70 * **EntryPoint Special Case**: If this is the first EntryPoint created, it is marked
71 * as the template's entry and root node (m_template.EntryPointID and m_template.RootNodeID).
72 *
73 * **Position Validation**: Non-finite or out-of-range coordinates are clamped to
74 * prevent JSON corruption or viewport issues. Clamped coordinates default to (0, 0).
75 *
76 * **Undo/Redo Integration**: The node is added to both the editor canvas (m_editorNodes)
77 * and the template model via AddNodeCommand, ensuring Ctrl+Z removes the node.
78 *
79 * @param type The TaskNodeType to create (EntryPoint, Branch, MathOp, etc.)
80 * @param x Canvas X position (will be clamped to [-100000, 100000])
81 * @param y Canvas Y position (will be clamped to [-100000, 100000])
82 *
83 * @return int Allocated node ID (guaranteed unique for this editor session)
84 *
85 * @note Phase 24.2: Data pins for MathOp/GetBBValue/SetBBValue initialized on creation
86 * @note EntryPoint nodes are singleton — multiple creates set the same root node
87 * @note Position parameters are stored in Parameters["__posX"] and Parameters["__posY"]
88 * for redo operations to restore the original placement
89 * @note Sets m_dirty = true and m_verificationDone = false to trigger re-verification
90 *
91 * @see RemoveNode(), AllocNodeID(), AddNodeCommand, TaskNodeDefinition
92 */
93int VisualScriptEditorPanel::AddNode(TaskNodeType type, float x, float y)
94{
95 // Validate incoming position parameters to prevent garbage values
96 if (!std::isfinite(x) || !std::isfinite(y))
97 {
98 SYSTEM_LOG << "[VSEditor] AddNode: warning - non-finite position provided (x="
99 << x << ", y=" << y << "), resetting to (0, 0)\n";
100 x = 0.0f;
101 y = 0.0f;
102 }
103
104 // Clamp to a reasonable range to prevent extreme coordinate values
105 if (x < -100000.0f || x > 100000.0f) x = 0.0f;
106 if (y < -100000.0f || y > 100000.0f) y = 0.0f;
107
108 int newID = AllocNodeID();
109
110 TaskNodeDefinition def;
111 def.NodeID = newID;
112 def.Type = type;
113 def.NodeName = GetNodeTypeLabel(type);
114
115 // EntryPoint is special
117 {
120 }
121
122 // Phase 24 FIX: Initialize DataPins for MathOp, GetBBValue, SetBBValue nodes
123 // Ensures data pins are rendered correctly with proper offsets
124 if (type == TaskNodeType::MathOp)
125 {
126 // Add input pins A and B
127 DataPinDefinition pinA;
128 pinA.PinName = "A";
130 pinA.PinType = VariableType::Float;
131 def.DataPins.push_back(pinA);
132
133 DataPinDefinition pinB;
134 pinB.PinName = "B";
136 pinB.PinType = VariableType::Float;
137 def.DataPins.push_back(pinB);
138
139 // Add output pin Result
140 DataPinDefinition pinResult;
141 pinResult.PinName = "Result";
144 def.DataPins.push_back(pinResult);
145
146 // Phase 24 Milestone 2: Initialize MathOpRef with default operands
147 // left = Const "0", operator = "+", right = Const "0"
148 def.mathOpRef.leftOperand.mode = MathOpOperand::Mode::Const;
149 def.mathOpRef.leftOperand.constValue = "0";
150 def.mathOpRef.mathOperator = "+";
151 def.mathOpRef.rightOperand.mode = MathOpOperand::Mode::Const;
152 def.mathOpRef.rightOperand.constValue = "0";
153 }
154 else if (type == TaskNodeType::GetBBValue)
155 {
156 // Phase 24 Milestone 3: GetBBValue outputs a data pin (Value)
157 DataPinDefinition pinValue;
158 pinValue.PinName = "Value";
160 pinValue.PinType = VariableType::None; // Type determined by selected variable
161 def.DataPins.push_back(pinValue);
162 }
163 else if (type == TaskNodeType::SetBBValue)
164 {
165 // Phase 24 Milestone 3: SetBBValue inputs a data pin (Value)
166 DataPinDefinition pinValue;
167 pinValue.PinName = "Value";
169 pinValue.PinType = VariableType::None; // Type determined by target variable
170 def.DataPins.push_back(pinValue);
171 }
172
173 // Persist the spawn position in Parameters so that redo (re-executing
174 // AddNodeCommand) restores the node at its original position rather than
175 // falling back to the default grid layout.
176 {
177 ParameterBinding bx, by;
179 bx.LiteralValue = TaskValue(x);
181 by.LiteralValue = TaskValue(y);
182 def.Parameters["__posX"] = bx;
183 def.Parameters["__posY"] = by;
184 }
185
186 // Editor-side node (tracks canvas position independently of the template)
187 VSEditorNode eNode;
188 eNode.nodeID = newID;
189 eNode.posX = x;
190 eNode.posY = y;
191 eNode.def = def;
192 m_editorNodes.push_back(eNode);
193
194 // Command adds the node to m_template.Nodes and rebuilds the lookup cache
196 std::unique_ptr<ICommand>(new AddNodeCommand(def)),
197 m_template);
198
199 m_dirty = true;
200 m_verificationDone = false;
201 return newID;
202}
203
204/**
205 * @brief Remove a node from the editor canvas and template graph.
206 *
207 * Deletes the node by ID from both the editor canvas state (m_editorNodes) and
208 * the template model (via DeleteNodeCommand). All execution and data connections
209 * involving this node are automatically removed by the command system.
210 *
211 * **Undo/Redo Integration**: The deletion is pushed to the undo stack as a
212 * DeleteNodeCommand, allowing Ctrl+Z to restore the node and its connections.
213 *
214 * **Link Rebuilding**: After node removal, RebuildLinks() is called to ensure
215 * all remaining connections are valid and ImNodes is synchronized with the new
216 * canvas state.
217 *
218 * @param nodeID The ID of the node to remove (must exist in m_editorNodes)
219 *
220 * @note Sets m_dirty = true and m_verificationDone = false to trigger verification
221 * @note Removing a node also removes all its incoming and outgoing connections
222 * @note If EntryPoint is removed, m_template.EntryPointID is reset by DeleteNodeCommand
223 *
224 * @see AddNode(), DeleteNodeCommand, RebuildLinks()
225 */
227{
228 // Remove from editor nodes (canvas-side)
229 m_editorNodes.erase(
230 std::remove_if(m_editorNodes.begin(), m_editorNodes.end(),
231 [nodeID](const VSEditorNode& n) { return n.nodeID == nodeID; }),
232 m_editorNodes.end());
233
234 // Command removes the node + all associated connections from m_template
236 std::unique_ptr<ICommand>(new DeleteNodeCommand(nodeID)),
237 m_template);
238
239 RebuildLinks();
240 m_dirty = true;
241 m_verificationDone = false;
242}
243
244/**
245 * @brief Create an execution pin connection (flow link) between two nodes.
246 *
247 * Connects an output execution pin on a source node to an input execution pin on
248 * a destination node. Execution connections control the **control flow** (order of
249 * execution) in the task graph.
250 *
251 * This method validates neither the source nor destination — validation is handled
252 * by CanCreateLink() or other connection validation logic in RenderCanvas().
253 *
254 * **Undo/Redo Integration**: The connection is pushed to the undo stack as an
255 * AddConnectionCommand, enabling Ctrl+Z to remove the link.
256 *
257 * **Link Rebuilding**: After creating the connection, RebuildLinks() synchronizes
258 * ImNodes with the new template state so the visual link appears on screen.
259 *
260 * **Pin Naming**: Pin names are semantic strings:
261 * - For exec-out: "Out", "Then", "Else", "Loop", "Loop Body", "Completed", etc.
262 * - For exec-in: "In" (most nodes) or "Loop" (While loops)
263 * - Dynamic pins (VSSequence/Switch): "Out_2", "Out_3", "Case_1", "Case_2", etc.
264 *
265 * @param srcNodeID ID of the source node (must be valid)
266 * @param srcPinName Name of the output execution pin (must match GetExecOutputPins)
267 * @param dstNodeID ID of the destination node (must be valid)
268 * @param dstPinName Name of the input execution pin (must match GetExecInputPins)
269 *
270 * @note Sets m_dirty = true and m_verificationDone = false to trigger verification
271 * @note The connection is stored in m_template.ExecConnections
272 * @note Pin names are case-sensitive and must exactly match node definitions
273 *
274 * @see ConnectData(), GetExecOutputPins(), GetExecInputPins(), AddConnectionCommand
275 */
277 const std::string& srcPinName,
278 int dstNodeID,
279 const std::string& dstPinName)
280{
281 ExecPinConnection conn;
282 conn.SourceNodeID = srcNodeID;
283 conn.SourcePinName = srcPinName;
284 conn.TargetNodeID = dstNodeID;
285 conn.TargetPinName = dstPinName;
286 // Push to undo stack so link creation can be reversed via Ctrl+Z.
287 // AddConnectionCommand::Execute() calls graph.ExecConnections.push_back().
289 std::unique_ptr<ICommand>(new AddConnectionCommand(conn)),
290 m_template);
291 RebuildLinks();
292 m_dirty = true;
293 m_verificationDone = false;
294}
295
296/**
297 * @brief Create a data pin connection (value link) between two nodes.
298 *
299 * Connects a data output pin on a source node to a data input pin on a destination
300 * node. Data connections carry **computed or retrieved values** (not control flow).
301 *
302 * **Data-Pure Nodes**: Phase 24 introduces data-pure nodes that have no execution
303 * pins but only data connections:
304 * - **GetBBValue**: Retrieves a blackboard variable -> outputs "Value"
305 * - **MathOp**: Computes arithmetic result -> inputs "A", "B"; outputs "Result"
306 *
307 * These nodes can be chained to build data flow networks independent of control flow.
308 *
309 * **Dynamic Data Pins**: Branch nodes have dynamic input pins (Pin-in) derived from
310 * their condition definitions. Condition names become pin names for data connections.
311 *
312 * **Undo/Redo Integration**: The connection is pushed as an AddDataConnectionCommand,
313 * enabling Ctrl+Z to remove the link.
314 *
315 * **Pin Naming**: Pin names are semantic strings:
316 * - Standard outputs: "Value" (GetBBValue), "Result" (MathOp)
317 * - Standard inputs: "Value" (SetBBValue), "A"/"B" (MathOp)
318 * - Dynamic pins (Branch): Condition names (e.g., "IsAlive", "HasAmmo")
319 *
320 * @param srcNodeID ID of the source node (must be valid)
321 * @param srcPinName Name of the data output pin (must match GetDataOutputPins or dynamic pins)
322 * @param dstNodeID ID of the destination node (must be valid)
323 * @param dstPinName Name of the data input pin (must match GetDataInputPins or dynamic pins)
324 *
325 * @note Sets m_dirty = true and m_verificationDone = false to trigger verification
326 * @note The connection is stored in m_template.DataConnections
327 * @note Pin names are case-sensitive and must exactly match node definitions
328 * @note Phase 24: Data-pure nodes enable functional data flow composition
329 *
330 * @see ConnectExec(), GetDataOutputPins(), GetDataInputPins(), AddDataConnectionCommand
331 */
333 const std::string& srcPinName,
334 int dstNodeID,
335 const std::string& dstPinName)
336{
337 DataPinConnection conn;
338 conn.SourceNodeID = srcNodeID;
339 conn.SourcePinName = srcPinName;
340 conn.TargetNodeID = dstNodeID;
341 conn.TargetPinName = dstPinName;
342 // Push to undo stack so data link creation can be reversed via Ctrl+Z.
343 // AddDataConnectionCommand::Execute() calls graph.DataConnections.push_back().
345 std::unique_ptr<ICommand>(new AddDataConnectionCommand(conn)),
346 m_template);
347 RebuildLinks();
348 m_dirty = true;
349 m_verificationDone = false;
350}
351
352} // namespace Olympe
UI-side registry of available atomic tasks with display metadata.
Wrapper around the graph blackboard entries for dropdown editors.
Registry of available condition types for Branch/While node dropdowns.
Runtime debug controller for ATS Visual Scripting (Phase 5).
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Defines MathOpOperand — operand references for MathOp nodes.
Hardcoded lists of math and comparison operators for dropdown editors.
ImNodes-based graph editor for ATS Visual Script graphs (Phase 5).
int32_t RootNodeID
ID of the root node (must exist in Nodes)
int32_t EntryPointID
ID of the EntryPoint node (for VS graphs)
void PushCommand(std::unique_ptr< ICommand > cmd, TaskGraphTemplate &graph)
Executes the command on graph, then pushes it onto the undo stack.
UndoRedoStack m_undoStack
Undo/Redo command stack for reversible graph editing operations.
TaskGraphTemplate m_template
The template currently being edited.
void RemoveNode(int nodeID)
Removes a node from the canvas.
void ConnectData(int srcNodeID, const std::string &srcPinName, int dstNodeID, const std::string &dstPinName)
Creates a data connection between two nodes.
void ConnectExec(int srcNodeID, const std::string &srcPinName, int dstNodeID, const std::string &dstPinName)
Creates an exec connection between two nodes.
int AddNode(TaskNodeType type, float x, float y)
Creates a new node on the canvas.
void RebuildLinks()
Rebuilds ImNodes exec/data link arrays from the template.
bool m_verificationDone
True once RunVerification() has been called at least once for the current graph.
std::vector< VSEditorNode > m_editorNodes
Editor nodes (mirrors m_template.Nodes + position/selection state)
< Provides AssetID and INVALID_ASSET_ID
const char * GetNodeTypeLabel(TaskNodeType type)
Returns a human-readable label for a TaskNodeType.
@ Float
Single-precision float.
@ None
Uninitialized / empty value.
@ Literal
Value is embedded directly in the template.
TaskNodeType
Identifies the role of a node in the task graph.
@ GetBBValue
Data node – reads a Blackboard key.
@ MathOp
Data node – arithmetic operation (+, -, *, /)
@ SetBBValue
Data node – writes a Blackboard key.
@ EntryPoint
Unique entry node for VS graphs (replaces Root)
constexpr int32_t NODE_INDEX_NONE
Sentinel value for "no node" in node index / ID fields.
@ Output
Value produced by the node.
@ Input
Value consumed by the node.
@ Const
Literal constant value.
#define SYSTEM_LOG