Vuo  2.0.3
VuoCommandChangeNode.cc
Go to the documentation of this file.
1 
10 #include "VuoCommandCommon.hh"
11 #include "VuoCommandRemove.hh"
12 #include "VuoCommandChangeNode.hh"
13 
14 #include "VuoCompilerCable.hh"
16 #include "VuoCompilerNode.hh"
17 #include "VuoEditor.hh"
20 #include "VuoGenericType.hh"
21 #include "VuoNodeClass.hh"
22 #include "VuoCompilerInputData.hh"
23 #include "VuoEditorComposition.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 
70 {
72 }
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 
110 void 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 
210 void 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 
300 void 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 
330 void 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 
590 void 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 }