1818#include " pipeline/TransformNode.h"
1919
2020#include < QApplication>
21+ #include < QSet>
2122#include < QtDebug>
2223
2324namespace tomviz {
@@ -43,6 +44,49 @@ static pipeline::OutputPort* findCompatibleOutputPort(
4344 return nullptr ;
4445}
4546
47+ // / All nodes reachable downstream from @a start (inclusive), following output
48+ // / links. This is exactly the set Node::markStale() cascades over when @a
49+ // / start is marked stale, so it is the set whose state we must snapshot to be
50+ // / able to undo an eager insertion.
51+ static QSet<pipeline::Node*> downstreamClosure (pipeline::Node* start)
52+ {
53+ QSet<pipeline::Node*> result;
54+ if (!start) {
55+ return result;
56+ }
57+ QList<pipeline::Node*> stack;
58+ stack.append (start);
59+ while (!stack.isEmpty ()) {
60+ auto * node = stack.takeLast ();
61+ if (result.contains (node)) {
62+ continue ;
63+ }
64+ result.insert (node);
65+ for (auto * out : node->outputPorts ()) {
66+ for (auto * link : out->links ()) {
67+ if (link->to () && link->to ()->node ()) {
68+ stack.append (link->to ()->node ());
69+ }
70+ }
71+ }
72+ }
73+ return result;
74+ }
75+
76+ // / Snapshot the node and output-port states of @a nodes into @a deferred so
77+ // / they can be restored verbatim if an eager insertion is canceled. Must be
78+ // / called before the insertion mutates the pipeline.
79+ static void captureStates (const QSet<pipeline::Node*>& nodes,
80+ pipeline::DeferredLinkInfo& deferred)
81+ {
82+ for (auto * node : nodes) {
83+ deferred.nodeStates .append ({ node, node->state () });
84+ for (auto * out : node->outputPorts ()) {
85+ deferred.portStaleStates .append ({ out, out->isStale () });
86+ }
87+ }
88+ }
89+
4690// / Append a transform at the given targetPort, moving sink/group links to a
4791// / compatible output of the new transform. Sinks with no compatible output
4892// / on the new transform are left connected to @a targetPort.
@@ -80,17 +124,32 @@ static void appendTransformAtPort(
80124 }
81125}
82126
83- // / Deferred variant: adds node and input link only, returns deferred info
84- // / for the output links to be completed later.
127+ // / Deferred variant of appendTransformAtPort: performs the full append
128+ // / eagerly (so the preview shows the final topology) and returns the info
129+ // / needed to undo it if the user cancels.
85130static pipeline::DeferredLinkInfo appendTransformAtPortDeferred (
86131 pipeline::Pipeline* pip,
87132 pipeline::TransformNode* transform,
88133 pipeline::OutputPort* targetPort)
89134{
135+ // Snapshot the downstream subtree before mutating anything so a cancel can
136+ // restore it exactly (the sink moves below mark it stale).
137+ pipeline::DeferredLinkInfo deferred;
138+ captureStates (downstreamClosure (targetPort->node ()), deferred);
139+
90140 pip->addNode (transform);
91141 pip->createLink (targetPort, transform->inputPorts ()[0 ]);
92142
93- pipeline::DeferredLinkInfo deferred;
143+ // Move terminal (sink) links from targetPort onto the new transform's
144+ // compatible outputs now, instead of deferring it to commit, so the preview
145+ // shows the sinks already routed through the transform. Record each
146+ // original link so it can be recreated on cancel.
147+ struct Move
148+ {
149+ pipeline::Link* link;
150+ pipeline::OutputPort* newOut;
151+ };
152+ QList<Move> moves;
94153 for (auto * link : targetPort->links ()) {
95154 if (link->to ()->node () == transform) {
96155 continue ; // skip the link we just created
@@ -102,8 +161,13 @@ static pipeline::DeferredLinkInfo appendTransformAtPortDeferred(
102161 if (!newOut) {
103162 continue ;
104163 }
105- deferred.linksToBreak .append ({ targetPort, link->to () });
106- deferred.linksToCreate .append ({ newOut, link->to () });
164+ moves.append ({ link, newOut });
165+ }
166+ for (const auto & m : moves) {
167+ auto * sinkInput = m.link ->to ();
168+ deferred.linksToRestore .append ({ targetPort, sinkInput });
169+ pip->removeLink (m.link );
170+ pip->createLink (m.newOut , sinkInput);
107171 }
108172 return deferred;
109173}
@@ -140,7 +204,9 @@ static void insertTransformAtLink(
140204 pip->createLink (transform->outputPorts ()[0 ], toPort);
141205}
142206
143- // / Deferred variant for insert-at-link.
207+ // / Deferred variant of insertTransformAtLink: performs the full insertion
208+ // / eagerly (so the preview shows the transform in its final, inserted
209+ // / position) and returns the info needed to undo it if the user cancels.
144210static pipeline::DeferredLinkInfo insertTransformAtLinkDeferred (
145211 pipeline::Pipeline* pip,
146212 pipeline::TransformNode* transform,
@@ -149,12 +215,19 @@ static pipeline::DeferredLinkInfo insertTransformAtLinkDeferred(
149215 auto * fromPort = link->from ();
150216 auto * toPort = link->to ();
151217
218+ // Snapshot the downstream subtree before mutating anything so a cancel can
219+ // restore it exactly (createLink() below marks this subtree stale).
220+ pipeline::DeferredLinkInfo deferred;
221+ captureStates (downstreamClosure (toPort->node ()), deferred);
222+ deferred.linksToRestore .append ({ fromPort, toPort });
223+
224+ // Perform the full insertion now (break from->to, splice the transform in)
225+ // rather than only connecting the input and deferring the rest to commit.
226+ pip->removeLink (link);
152227 pip->addNode (transform);
153228 pip->createLink (fromPort, transform->inputPorts ()[0 ]);
229+ pip->createLink (transform->outputPorts ()[0 ], toPort);
154230
155- pipeline::DeferredLinkInfo deferred;
156- deferred.linksToBreak .append ({ fromPort, toPort });
157- deferred.linksToCreate .append ({ transform->outputPorts ()[0 ], toPort });
158231 return deferred;
159232}
160233
0 commit comments