Vuo 2.4.4
Loading...
Searching...
No Matches
VuoCommandChangeNode.cc
Go to the documentation of this file.
1
10#include "VuoCommandCommon.hh"
11#include "VuoCommandRemove.hh"
13
14#include "VuoCompilerCable.hh"
16#include "VuoCompilerNode.hh"
17#include "VuoEditor.hh"
20#include "VuoGenericType.hh"
21#include "VuoNodeClass.hh"
24#include "VuoEditorWindow.hh"
27#include "VuoRendererNode.hh"
29
35 : VuoCommandCommon(window)
36{
37 setText(QApplication::translate("VuoEditorWindow", "Change Node"));
38 this->window = window;
39 this->composition = window->getComposition();
40 this->revertedSnapshot = composition->takeSnapshot();
41 this->oldNode = oldNode;
42 this->newNode = newNode;
43
44 // Start of command content.
45 {
46 this->operationInvolvesGenericPort = false;
47 if (oldNode->hasGenericPort() || newNode->hasGenericPort())
48 this->operationInvolvesGenericPort = true;
49
50 createAllMappings();
51 removeStrandedAttachments();
52 swapNodes();
53 composition->createAndConnectInputAttachments(newNode, false);
54 diffInfo = VuoCommandCommon::addNodeReplacementToDiff(new VuoCompilerCompositionDiff(), oldNode, newNode, updatedPortForOriginalPort, composition);
55 }
56 // End of command content.
57
58 this->updatedSnapshot = composition->takeSnapshot();
59
60 setDescription("Change node %s %s -> %s",
61 oldNode->getBase()->hasCompiler() ? oldNode->getBase()->getCompiler()->getIdentifier().c_str() : "?",
62 oldNode->getBase()->getNodeClass()->getClassName().c_str(),
63 newNode->getBase()->getNodeClass()->getClassName().c_str());
64}
65
73
79{
81
82 window->resetCompositionWithSnapshot(updatedSnapshot);
83
84 if (operationInvolvesGenericPort)
85 composition->updateGenericPortTypes();
86
87 VuoCompilerCompositionDiff *diffInfoCopy = new VuoCompilerCompositionDiff(*diffInfo);
88 window->coalesceSnapshots(revertedSnapshot, updatedSnapshot, diffInfoCopy);
89}
90
95{
97
98 window->resetCompositionWithSnapshot(revertedSnapshot);
99
100 if (operationInvolvesGenericPort)
101 composition->updateGenericPortTypes();
102
103 VuoCompilerCompositionDiff *diffInfoCopy = new VuoCompilerCompositionDiff(*diffInfo);
104 window->coalesceSnapshots(updatedSnapshot, revertedSnapshot, diffInfoCopy);
105}
106
110void VuoCommandChangeNode::swapNodes()
111{
112 {
113 // If the original node is currently being rendered as collapsed typecast, uncollapse it.
114 if (oldNode->getProxyCollapsedTypecast())
115 composition->uncollapseTypecastNode(oldNode);
116
117 // Uncollapse typecasts attached to the original node.
118 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
119 composition->uncollapseTypecastNode(*i);
120
121 for (vector<pair<VuoPort *, VuoPublishedPort *> >::iterator i = revertedPublishedInternalExternalPortCombinations.begin(); i != revertedPublishedInternalExternalPortCombinations.end(); ++i)
122 {
123 bool unpublishIsolatedExternalPort = false;
124 VuoCommandCommon::unpublishInternalExternalPortCombination((*i).first, (*i).second, composition, unpublishIsolatedExternalPort);
125 }
126
127 // Swap nodes.
128 composition->replaceNode(oldNode, newNode->getBase());
129
130 // Restore port constants.
131 for (map<VuoPort *, string>::iterator i = constantValueForOriginalPort.begin(); i != constantValueForOriginalPort.end(); ++i)
132 {
133 VuoPort *oldInputPort = (*i).first;
134 VuoPort *newInputPort = updatedPortForOriginalPort[oldInputPort];
135 if (newInputPort)
136 composition->updatePortConstant(static_cast<VuoCompilerInputEventPort *>(newInputPort->getCompiler()), constantValueForOriginalPort[oldInputPort], false);
137 }
138
139 // Re-route cables.
140 for (set<VuoCable *>::iterator i = outgoingCables.begin(); i != outgoingCables.end(); ++i)
141 {
142 VuoCommandCommon::updateCable((*i)->getRenderer(),
143 updatedPortForOriginalPort[originalFromPortForCable[*i] ],
144 updatedPortForOriginalPort[originalToPortForCable[*i] ],
145 composition);
146
147 if (!cableCarriedData[*i] && (*i)->getRenderer()->effectivelyCarriesData() && (*i)->hasCompiler())
148 (*i)->getCompiler()->setAlwaysEventOnly(true);
149 }
150
151 for (set<VuoCable *>::iterator i = incomingCables.begin(); i != incomingCables.end(); ++i)
152 {
153 VuoCommandCommon::updateCable((*i)->getRenderer(),
154 updatedPortForOriginalPort[originalFromPortForCable[*i] ],
155 updatedPortForOriginalPort[originalToPortForCable[*i] ],
156 composition);
157
158 if (!cableCarriedData[*i] && (*i)->getRenderer()->effectivelyCarriesData() && (*i)->hasCompiler())
159 (*i)->getCompiler()->setAlwaysEventOnly(true);
160 }
161
162 // Re-publish published ports.
163 for (vector<pair<VuoPort *, VuoPublishedPort *> >::iterator i = revertedPublishedInternalExternalPortCombinations.begin(); i != revertedPublishedInternalExternalPortCombinations.end(); ++i)
164 {
165 if (updatedPortForOriginalPort[(*i).first])
166 {
167 bool forceEventOnlyPublication = !publishedConnectionCarriedData[(*i)];
168 VuoPublishedPort *updatedExternalPublishedPort = VuoCommandCommon::publishInternalExternalPortCombination(updatedPortForOriginalPort[(*i).first], (*i).second, forceEventOnlyPublication, composition);
169 updatedPublishedInternalExternalPortCombinations.push_back(make_pair(updatedPortForOriginalPort[(*i).first], updatedExternalPublishedPort));
170 publishedConnectionCarriedData[make_pair(updatedPortForOriginalPort[(*i).first], updatedExternalPublishedPort)] = publishedConnectionCarriedData[(*i)];
171 }
172 }
173
174 // Re-collapse typecasts onto the updated node.
175 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
176 {
177 if (updatedPortForOriginalPort[hostPortForTypecast[*i] ])
178 composition->collapseTypecastNode(*i);
179
180 else
181 {
182 composition->removeNode(*i);
183 foreach (VuoCable *cable, incomingCablesForTypecast[*i])
184 VuoCommandCommon::removeCable(cable->getRenderer(), composition);
185
186 for (vector<pair<VuoPort *, VuoPublishedPort *> >::iterator j = publishedInternalExternalPortCombinationsForTypecast[*i].begin(); j != publishedInternalExternalPortCombinationsForTypecast[*i].end(); ++j)
187 {
188 bool unpublishIsolatedExternalPort = false;
189 VuoCommandCommon::unpublishInternalExternalPortCombination((*j).first, (*j).second, composition, unpublishIsolatedExternalPort);
190 }
191 }
192 }
193
194 // Re-collapse the updated node, if applicable.
195 composition->collapseTypecastNode(newNode);
196
197 VuoRendererInputAttachment *newAttachment = dynamic_cast<VuoRendererInputAttachment *>(newNode);
198 if (newAttachment)
199 {
200 VuoNode *hostNode = newAttachment->getRenderedHostNode();
201 if (hostNode && hostNode->hasRenderer())
203 }
204 }
205}
206
210void VuoCommandChangeNode::createAllMappings()
211{
212 // Inventory the port constants and connected input cables associated with the old node, to be re-associated with the new node.
213 vector<VuoPort *> oldInputPorts = oldNode->getBase()->getInputPorts();
214 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
215 {
216 if ((*inputPort)->getRenderer()->getDataType())
217 constantValueForOriginalPort[*inputPort] = (*inputPort)->getRenderer()->getConstantAsString();
218
219 vector<VuoCable *> inputCables = (*inputPort)->getConnectedCables(true);
220 foreach(VuoCable *cable, inputCables)
221 {
222 if (cable->isPublished())
223 {
224 VuoPort *internalPublishedPort = cable->getToPort();
225 VuoPublishedPort *externalPublishedPort = dynamic_cast<VuoPublishedPort *>(cable->getFromPort());
226 this->revertedPublishedInternalExternalPortCombinations.push_back(make_pair(internalPublishedPort, externalPublishedPort));
227 publishedConnectionCarriedData[make_pair(internalPublishedPort, externalPublishedPort)] = cable->getRenderer()->effectivelyCarriesData();
228 }
229
230 else
231 {
232 this->incomingCables.insert(cable);
233 cableCarriedData[cable] = cable->getRenderer()->effectivelyCarriesData();
234 }
235 }
236
237 // Also inventory any typecasts attached to the old node, to be attached to the new node instead.
238 VuoRendererTypecastPort *typecastPort = dynamic_cast<VuoRendererTypecastPort *>((*inputPort)->getRenderer());
239 if (typecastPort)
240 {
241 VuoRendererNode *typecastNode = typecastPort->getUncollapsedTypecastNode();
242 this->collapsedTypecasts.push_back(typecastNode);
243
244 // Inventory the typecast's own input cables.
245 VuoPort *typecastInPort = typecastNode->getBase()->getInputPorts()[VuoNodeClass::unreservedInputPortStartIndex];
246
247 vector<VuoCable *> typecastInputCables = typecastInPort->getConnectedCables(true);
248 foreach (VuoCable *cable, typecastInputCables)
249 {
250 if (cable->isPublished())
251 {
252 VuoPort *internalPublishedPort = cable->getToPort();
253 VuoPublishedPort *externalPublishedPort = dynamic_cast<VuoPublishedPort *>(cable->getFromPort());
254 this->publishedInternalExternalPortCombinationsForTypecast[typecastNode].push_back(make_pair(internalPublishedPort, externalPublishedPort));
255 publishedConnectionCarriedData[make_pair(internalPublishedPort, externalPublishedPort)] = cable->getRenderer()->effectivelyCarriesData();
256 }
257
258 else
259 {
260 this->incomingCablesForTypecast[typecastNode].insert(cable);
261 originalFromPortForCable[cable] = cable->getFromPort();
262 originalToPortForCable[cable] = cable->getToPort();
263 cableCarriedData[cable] = cable->getRenderer()->effectivelyCarriesData();
264 }
265 }
266
267 hostPortForTypecast[typecastNode] = (*inputPort);
268 }
269 }
270
271 // Inventory the set of output cables connected to the old node, to be re-routed to the new node.
272 vector<VuoPort *> oldOutputPorts = oldNode->getBase()->getOutputPorts();
273 for(vector<VuoPort *>::iterator outputPort = oldOutputPorts.begin(); outputPort != oldOutputPorts.end(); ++outputPort)
274 {
275 vector<VuoCable *> outputCables = (*outputPort)->getConnectedCables(true);
276 foreach(VuoCable *cable, outputCables)
277 {
278 if (cable->isPublished())
279 {
280 VuoPort *internalPublishedPort = cable->getFromPort();
281 VuoPublishedPort *externalPublishedPort = dynamic_cast<VuoPublishedPort *>(cable->getToPort());
282 this->revertedPublishedInternalExternalPortCombinations.push_back(make_pair(internalPublishedPort, externalPublishedPort));
283 publishedConnectionCarriedData[make_pair(internalPublishedPort, externalPublishedPort)] = cable->getRenderer()->effectivelyCarriesData();
284 }
285
286 else
287 {
288 this->outgoingCables.insert(cable);
289 cableCarriedData[cable] = cable->getRenderer()->effectivelyCarriesData();
290 }
291 }
292 }
293
294 createAllPortMappings();
295}
296
300void VuoCommandChangeNode::createAllPortMappings()
301{
302 // First inventory the external cable endpoints that will not change.
303 for (set<VuoCable *>::iterator i = incomingCables.begin(); i != incomingCables.end(); ++i)
304 {
305 VuoCable *cable = (*i);
306
307 originalFromPortForCable[cable] = cable->getFromPort();
308 originalToPortForCable[cable] = cable->getToPort();
309 updatedPortForOriginalPort[cable->getFromPort()] = cable->getFromPort();
310 }
311
312 for (set<VuoCable *>::iterator i = outgoingCables.begin(); i != outgoingCables.end(); ++i)
313 {
314 VuoCable *cable = (*i);
315
316 originalFromPortForCable[cable] = cable->getFromPort();
317 originalToPortForCable[cable] = cable->getToPort();
318 updatedPortForOriginalPort[cable->getToPort()] = cable->getToPort();
319 }
320
321 // Now inventory the ports belonging to the swapped nodes.
322 createSwappedNodePortMappings();
323}
324
330void VuoCommandChangeNode::createSwappedNodePortMappings()
331{
332 set<VuoPort *> newPortsClaimed;
333
334 // First pass: Find ports with exact name and type matches
335 // Input ports
336 foreach (VuoRendererPort *oldPort, oldNode->getInputPorts())
337 {
338 string oldPortName = oldPort->getBase()->getClass()->getName();
339
340 // Find exact match-by-name.
341 VuoPort *newPort = newNode->getBase()->getInputPortWithName(oldPortName);
342 if (newPort)
343 {
344 // Check whether it's also an exact match-by-type.
345 VuoType *oldDataType = oldPort->getDataType();
346 VuoType *newDataType = newPort->getRenderer()->getDataType();
347 if ((!oldDataType) ||
348 ((oldDataType && newDataType) && (oldDataType->getModuleKey() == newDataType->getModuleKey())))
349 {
350 updatedPortForOriginalPort[oldPort->getBase()] = newPort;
351
352 if (oldDataType)
353 newPortsClaimed.insert(newPort);
354 }
355 }
356 }
357
358 // Output ports
359 foreach (VuoRendererPort *oldPort, oldNode->getOutputPorts())
360 {
361 string oldPortName = oldPort->getBase()->getClass()->getName();
362
363 // Find exact match-by-name.
364 VuoPort *newPort = newNode->getBase()->getOutputPortWithName(oldPortName);
365 if (newPort)
366 {
367 // Check whether it's also an exact match-by-type.
368 VuoType *oldDataType = oldPort->getDataType();
369 VuoType *newDataType = newPort->getRenderer()->getDataType();
370 if ((!oldDataType) ||
371 ((oldDataType && newDataType) && (oldDataType->getModuleKey() == newDataType->getModuleKey())))
372 {
373 updatedPortForOriginalPort[oldPort->getBase()] = newPort;
374
375 if (oldDataType)
376 newPortsClaimed.insert(newPort);
377 }
378 }
379 }
380
381 // Second pass: Find ports with matching types to accommodate data cables connected to the original ports
382 // Input ports
383 foreach (VuoRendererPort *oldPort, oldNode->getInputPorts())
384 {
385 if (!oldPort->effectivelyHasConnectedDataCable(true))
386 continue;
387
388 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
389 continue;
390
391 // Find the first available match-by-type (excluding event-only).
392 foreach (VuoRendererPort *newPort, newNode->getInputPorts())
393 {
394 // Skip ports that have already been claimed.
395 if (newPortsClaimed.find(newPort->getBase()) != newPortsClaimed.end())
396 continue;
397
398 VuoType *oldDataType = oldPort->getDataType();
399 VuoType *newDataType = newPort->getDataType();
400 if ((oldDataType && newDataType) && (oldDataType->getModuleKey() == newDataType->getModuleKey()))
401 {
402 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
403 newPortsClaimed.insert(newPort->getBase());
404 break;
405 }
406 }
407 }
408
409 // Output ports
410 foreach (VuoRendererPort *oldPort, oldNode->getOutputPorts())
411 {
412 if (!oldPort->effectivelyHasConnectedDataCable(true))
413 continue;
414
415 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
416 continue;
417
418 // Find the first available match-by-type (excluding event-only).
419 foreach (VuoRendererPort *newPort, newNode->getOutputPorts())
420 {
421 // Skip ports that have already been claimed.
422 if (newPortsClaimed.find(newPort->getBase()) != newPortsClaimed.end())
423 continue;
424
425 VuoType *oldDataType = oldPort->getDataType();
426 VuoType *newDataType = newPort->getDataType();
427 if ((oldDataType && newDataType) && (oldDataType->getModuleKey() == newDataType->getModuleKey()))
428 {
429 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
430 newPortsClaimed.insert(newPort->getBase());
431 break;
432 }
433 }
434 }
435
436 // Third pass: Match remaining data+event ports with data+event ports of identical name but any type,
437 // as long as the original port had only event-only cables connected.
438 // Input ports
439 foreach (VuoRendererPort *oldPort, oldNode->getInputPorts())
440 {
441 string oldPortName = oldPort->getBase()->getClass()->getName();
442
443 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
444 continue;
445
446 if ((oldPort->getBase()->getConnectedCables(true).size() > 0) && (!oldPort->effectivelyHasConnectedDataCable(true)))
447 {
448 // Find exact match-by-name.
449 VuoPort *newPort = newNode->getBase()->getInputPortWithName(oldPortName);
450 if (newPort)
451 updatedPortForOriginalPort[oldPort->getBase()] = newPort;
452 }
453 }
454
455 // Output ports
456 foreach (VuoRendererPort *oldPort, oldNode->getOutputPorts())
457 {
458 string oldPortName = oldPort->getBase()->getClass()->getName();
459
460 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
461 continue;
462
463 if ((oldPort->getBase()->getConnectedCables(true).size() > 0) && (!oldPort->effectivelyHasConnectedDataCable(true)))
464 {
465 // Find exact match-by-name.
466 VuoPort *newPort = newNode->getBase()->getOutputPortWithName(oldPortName);
467 if (newPort)
468 updatedPortForOriginalPort[oldPort->getBase()] = newPort;
469 }
470 }
471
472 // Fourth pass: Match remaining data+event ports with event-only ports if they have only
473 // event-only cables connected.
474 // Input ports
475 foreach (VuoRendererPort *oldPort, oldNode->getInputPorts())
476 {
477 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
478 continue;
479
480 if ((oldPort->getBase()->getConnectedCables(true).size() > 0) && (!oldPort->effectivelyHasConnectedDataCable(true)))
481 {
482 VuoRendererPort *newPort = composition->findDefaultPortForEventOnlyConnection(newNode, true);
483 if (newPort)
484 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
485 }
486 }
487
488 // Output ports
489 foreach (VuoRendererPort *oldPort, oldNode->getOutputPorts())
490 {
491 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
492 continue;
493
494 if ((oldPort->getBase()->getConnectedCables(true).size() > 0) && (!oldPort->effectivelyHasConnectedDataCable(true)))
495 {
496 VuoRendererPort *newPort = composition->findDefaultPortForEventOnlyConnection(newNode, false);
497 if (newPort)
498 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
499 }
500 }
501
502 // Fifth pass: Match remaining event-only ports with new event-only ports if possible.
503 // Input ports
504 foreach (VuoRendererPort *oldPort, oldNode->getInputPorts())
505 {
506 VuoType *oldDataType = oldPort->getDataType();
507 if (oldDataType)
508 continue;
509
510 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
511 continue;
512
513 foreach (VuoRendererPort *newPort, newNode->getInputPorts())
514 {
515 // Skip refresh ports.
516 if (newPort->getBase() == newNode->getBase()->getRefreshPort())
517 continue;
518
519 VuoType *newDataType = newPort->getDataType();
520 if (!newDataType)
521 {
522 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
523 break;
524 }
525 }
526 }
527
528 // Output ports
529 foreach (VuoRendererPort *oldPort, oldNode->getOutputPorts())
530 {
531 VuoType *oldDataType = oldPort->getDataType();
532 if (oldDataType)
533 continue;
534
535 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
536 continue;
537
538 foreach (VuoRendererPort *newPort, newNode->getOutputPorts())
539 {
540 // Skip refresh ports.
541 if (newPort->getBase() == newNode->getBase()->getRefreshPort())
542 continue;
543
544 VuoType *newDataType = newPort->getDataType();
545 if (!newDataType)
546 {
547 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
548 break;
549 }
550 }
551 }
552
553 // Sixth pass: Match any remaining event-only ports to the port in the new node that would receive
554 // a cable connection dropped onto the node header.
555 // Input ports
556 foreach (VuoRendererPort *oldPort, oldNode->getInputPorts())
557 {
558 VuoType *oldDataType = oldPort->getDataType();
559 if (oldDataType)
560 continue;
561
562 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
563 continue;
564
565 VuoRendererPort *newPort = composition->findDefaultPortForEventOnlyConnection(newNode, true);
566 if (newPort)
567 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
568 }
569
570 // Output ports
571 foreach (VuoRendererPort *oldPort, oldNode->getOutputPorts())
572 {
573 VuoType *oldDataType = oldPort->getDataType();
574 if (oldDataType)
575 continue;
576
577 if (updatedPortForOriginalPort.find(oldPort->getBase()) != updatedPortForOriginalPort.end())
578 continue;
579
580 VuoRendererPort *newPort = composition->findDefaultPortForEventOnlyConnection(newNode, false);
581 if (newPort)
582 updatedPortForOriginalPort[oldPort->getBase()] = newPort->getBase();
583 }
584}
585
590void VuoCommandChangeNode::removeStrandedAttachments()
591{
592 QList<QGraphicsItem *> strandedAttachments;
593 vector<VuoPort *> inputPorts = oldNode->getBase()->getInputPorts();
594 for(unsigned int i = 0; i < inputPorts.size(); ++i)
595 {
596 if (updatedPortForOriginalPort.find(inputPorts[i]) == updatedPortForOriginalPort.end())
597 {
598 set<VuoRendererInputAttachment *> portUpstreamAttachments = inputPorts[i]->getRenderer()->getAllUnderlyingUpstreamInputAttachments();
599 foreach (VuoRendererInputAttachment *attachment, portUpstreamAttachments)
600 strandedAttachments.append(attachment);
601 }
602 }
603
604 VuoCommandRemove removeAttachments(strandedAttachments, window, composition->getInputEditorManager(), "Delete", true);
605 removeAttachments.redo();
606}