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