Previously the cycle validator rejected any link whose source had a
vector index >= target's, which silently killed legitimate connections
between nodes added in the wrong drop order.
Switch to a DFS over source_ids: an edge from->to creates a cycle iff
`from` already (transitively) depends on `to`. topo_sort runs after
each topology change so the vector ends up in a consistent order
regardless of how nodes were inserted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs:
1. Dropped nodes were pushed to the end of the pipeline, but the Output
node already sat there from startup. The cycle validator and the
compiler only look for sources at indices strictly lower than the
target, so new nodes were invisible to the Output. Fix: insert
dropped nodes before the first Output; topo_sort also stable-moves
Output nodes to the back.
2. ColorEdit3 with default flags rendered RGB text inputs alongside the
swatch; clicking them dragged the node instead of opening the picker.
Fix: NoInputs + NoLabel leaves only the swatch (a single item), and
ed::Suspend/Resume wraps the call so the popup isn't clipped to the
node or captured by the canvas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous InvisibleButton captured mouse events, so you could drag
from the Functions palette into the canvas, but node dragging and
slider interaction inside the canvas stopped working.
Fix: watch the global drag-drop payload without an explicit target. When
the mouse releases LMB over the DAG window with a "DAG_NODE_TYPE"
payload active, queue an add at that canvas position. No button, no
capture.
Tests (compiled standalone with preprocessor defines):
- dag_compile: 6/6 asserts (empty, single gen, op chain, multi-source
blend, Output-driven fragColor, unconnected-Output fallback).
- dag_catalog: 8/8 asserts (uniqueness, per-kind input invariants,
exactly one Output, body_glsl present & returns, control param
indices valid).
Build with:
g++ -std=c++17 -Icpp/functions -DDAG_COMPILE_TEST cpp/functions/gfx/dag_compile.cpp cpp/functions/gfx/dag_catalog.cpp -o /tmp/dag_compile_test
g++ -std=c++17 -Icpp/functions -DDAG_CATALOG_TEST cpp/functions/gfx/dag_catalog.cpp -o /tmp/dag_catalog_test
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously SetNodePosition was called after EndNode every frame, which
reset any drag the user had done. Now the initial position is set once
per editor_uid (tracked in a static unordered_set), and the editor owns
the position afterwards. GetNodePosition at end-of-frame keeps step
state in sync for future persistence.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>