Vuo 2.4.2
Loading...
Searching...
No Matches
VuoCompilerComposition.cc
Go to the documentation of this file.
1
10#include "VuoCable.hh"
12#include "VuoCompiler.hh"
13#include "VuoCompilerCable.hh"
14#include "VuoCompilerComment.hh"
16#include "VuoCompilerGraph.hh"
18#include "VuoCompilerIssue.hh"
19#include "VuoCompilerNode.hh"
24#include "VuoComposition.hh"
25#include "VuoGenericType.hh"
26#include "VuoNode.hh"
27#include "VuoNodeClass.hh"
28#include "VuoComment.hh"
29#include "VuoPublishedPort.hh"
30#include "VuoStringUtilities.hh"
31#include <sstream>
32
33const string VuoCompilerComposition::defaultGraphDeclaration = "digraph G\n";
35
41 : VuoBaseDetail<VuoComposition>("VuoCompilerComposition", baseComposition)
42{
43 getBase()->setCompiler(this);
44
45 graph = nullptr;
46 graphHash = 0;
47 manuallyFirableInputNode = nullptr;
48 manuallyFirableInputPort = nullptr;
49 module = nullptr;
50
51 if (parser)
52 {
53 vector<VuoNode *> nodes = parser->getNodes();
54 for (vector<VuoNode *>::iterator node = nodes.begin(); node != nodes.end(); ++node)
55 getBase()->addNode(*node);
56
57 vector<VuoCable *> cables = parser->getCables();
58 for (vector<VuoCable *>::iterator cable = cables.begin(); cable != cables.end(); ++cable)
59 getBase()->addCable(*cable);
60
61 vector<VuoPublishedPort *> publishedInputPorts = parser->getPublishedInputPorts();
62 for (int index = 0; index < publishedInputPorts.size(); ++index)
63 getBase()->addPublishedInputPort(publishedInputPorts.at(index), index);
64
65 vector<VuoPublishedPort *> publishedOutputPorts = parser->getPublishedOutputPorts();
66 for (int index = 0; index < publishedOutputPorts.size(); ++index)
67 getBase()->addPublishedOutputPort(publishedOutputPorts.at(index), index);
68
69 vector<VuoComment *> comments = parser->getComments();
70 for (vector<VuoComment *>::iterator comment = comments.begin(); comment != comments.end(); ++comment)
71 getBase()->addComment(*comment);
72
73 manuallyFirableInputNode = parser->getManuallyFirableInputNode();
74 manuallyFirableInputPort = parser->getManuallyFirableInputPort();
75
76 getBase()->setMetadata(parser->getMetadata(), true);
77
79
80 for (vector<VuoNode *>::iterator node = nodes.begin(); node != nodes.end(); ++node)
81 if ((*node)->hasCompiler())
82 nodeGraphvizIdentifierUsed[ (*node)->getCompiler()->getGraphvizIdentifier() ] = *node;
83 }
84}
85
90{
91 delete graph;
93}
94
101{
102 VuoCompilerGraphvizParser *parser = VuoCompilerGraphvizParser::newParserFromCompositionString(compositionGraphvizDeclaration, compiler);
103 VuoCompilerComposition *composition = new VuoCompilerComposition(new VuoComposition(), parser);
104 delete parser;
105 return composition;
106}
107
113 map<VuoCable *, VuoPort *> &cablesToTransferFromPort,
114 map<VuoCable *, VuoPort *> &cablesToTransferToPort,
115 set<VuoCable *> &cablesToRemove) const
116{
117 set<VuoCable *> cables = getBase()->getCables();
118 for (set<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
119 {
120 VuoCable *cable = *i;
121
122 bool foundMismatch = false;
123 if (cable->getFromNode() == oldNode)
124 {
125 VuoPort *oldPort = cable->getFromPort();
126 VuoPort *newPort = newNode->getOutputPortWithName( oldPort->getClass()->getName() );
127
128 if (portsMatch(oldPort, newPort))
129 cablesToTransferFromPort[cable] = newPort;
130 else
131 foundMismatch = true;
132 }
133 if (cable->getToNode() == oldNode)
134 {
135 VuoPort *oldPort = cable->getToPort();
136 VuoPort *newPort = newNode->getInputPortWithName( oldPort->getClass()->getName() );
137
138 if (portsMatch(oldPort, newPort))
139 cablesToTransferToPort[cable] = newPort;
140 else
141 foundMismatch = true;
142 }
143
144 if (foundMismatch)
145 {
146 cablesToRemove.insert(cable);
147
148 map<VuoCable *, VuoPort *>::iterator fromIter = cablesToTransferFromPort.find(cable);
149 if (fromIter != cablesToTransferFromPort.end())
150 cablesToTransferFromPort.erase(fromIter);
151
152 map<VuoCable *, VuoPort *>::iterator toIter = cablesToTransferToPort.find(cable);
153 if (toIter != cablesToTransferToPort.end())
154 cablesToTransferToPort.erase(toIter);
155 }
156 }
157}
158
167{
168 map<VuoCable *, VuoPort *> cablesToTransferFromPort;
169 map<VuoCable *, VuoPort *> cablesToTransferToPort;
170 set<VuoCable *> cablesToRemove;
171
172 getChangesToReplaceNode(oldNode, newNode, cablesToTransferFromPort, cablesToTransferToPort, cablesToRemove);
173
174 getBase()->removeNode(oldNode);
175 getBase()->addNode(newNode);
176
177 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
178 i->first->setFrom(newNode, i->second);
179 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
180 i->first->setTo(newNode, i->second);
181 for (set<VuoCable *>::iterator i = cablesToRemove.begin(); i != cablesToRemove.end(); ++i)
182 getBase()->removeCable(*i);
183}
184
189{
190 long currentHash = VuoCompilerGraph::getHash(this);
191 bool compositionChanged = (currentHash != graphHash);
192
193 bool shouldAddPublishedNodeImplementations = false;
194 if (! compositionChanged && compiler)
195 {
196 VuoCompilerNode *publishedInputNode = graph->getPublishedInputNode();
197 if (publishedInputNode && ! publishedInputNode->getBase()->getNodeClass()->getCompiler()->getEventFunction())
198 shouldAddPublishedNodeImplementations = true;
199 }
200
201 if (compositionChanged || shouldAddPublishedNodeImplementations)
202 {
203 delete graph;
204 graph = new VuoCompilerGraph(this, compiler);
205 graphHash = currentHash;
206 }
207
208 return graph;
209}
210
215{
216 delete graph;
217 graph = nullptr;
218 graphHash = 0;
219}
220
228{
230 checkForMissingTypes(issues);
232}
233
241{
242 set<VuoNode *> missingNodes;
243 set<VuoNode *> nodes = getBase()->getNodes();
244 for (set<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
245 {
246 VuoNode *node = *i;
247 if (! node->getNodeClass()->hasCompiler())
248 missingNodes.insert(node);
249 }
250
251 if (! missingNodes.empty())
252 {
253 // Assumes the details are always displayed in plain text. Doesn't use VuoCompilerIssue's formatting.
254 vector<string> uniqueNodeDetails;
255 bool missingProNode = false;
256 bool missingOldNode = false;
257 for (set<VuoNode *>::iterator i = missingNodes.begin(); i != missingNodes.end(); ++i)
258 {
259 VuoNode *node = *i;
260
261 string nodeDetail = node->getTitle() + " (" + (*i)->getNodeClass()->getClassName() + ")";
262
263#if VUO_PRO
264 if (node->getNodeClass()->isPro())
265 {
266 nodeDetail += " [Vuo Pro]";
267 missingProNode = true;
268 }
269#endif
270
271 if (node->getNodeClass()->getDescription().find("This node was updated or removed in Vuo 0.9 or earlier.") != string::npos)
272 {
273 nodeDetail += " [Vuo 0.9 or earlier]";
274 missingOldNode = true;
275 }
276
277 if (find(uniqueNodeDetails.begin(), uniqueNodeDetails.end(), nodeDetail) == uniqueNodeDetails.end())
278 uniqueNodeDetails.push_back(nodeDetail);
279 }
280 sort(uniqueNodeDetails.begin(), uniqueNodeDetails.end());
281 string details = "\n\n" + VuoStringUtilities::join(uniqueNodeDetails, "\n");
282
283 string hint;
284 string linkUrl;
285 string linkText;
286
287 if (missingProNode)
288 {
289 hint += "<p>Some of the non-installed nodes are Pro nodes. "
290 "%link to enable Pro nodes.</p>";
291 linkUrl = "https://vuo.org/buy";
292 linkText = "Upgrade to Vuo Pro";
293 }
294
295 if (missingOldNode)
296 {
297 hint += "<p>Some of the non-installed nodes were updated or removed in Vuo 0.9 or earlier. "
298 "To work with this composition in Vuo 1.0 or later, first make a "
299 "backup copy of the composition, then open the composition in Vuo 0.9 and save it. "
300 "This will automatically upgrade the composition so you can use it in Vuo 1.0 or later.</p>";
301 }
302
303 // If changing this text, also change VuoEditorWindow::hideBuildActivityIndicator() and VuoEditorWindow::displayExportErrorBox().
304 string summary = "Nodes not installed";
305
306 VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", "", summary, details);
307 issue.setNodes(missingNodes);
308 issue.setHint(hint);
309 issue.setLink(linkUrl, linkText);
310 if (issues)
311 {
312 issues->append(issue);
313 throw VuoCompilerException(issues, false);
314 }
315 else
316 throw VuoCompilerException(issue);
317 }
318}
319
326void VuoCompilerComposition::checkForMissingTypes(VuoCompilerIssues *issues)
327{
328 map<string, set<string> > missingTypes;
329 for (VuoNode *node : getBase()->getNodes())
330 {
331 if (! node->getNodeClass()->hasCompiler())
332 continue;
333
334 vector<VuoPort *> ports;
335 vector<VuoPort *> inputPorts = node->getInputPorts();
336 ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
337 vector<VuoPort *> outputPorts = node->getOutputPorts();
338 ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
339
340 for (VuoPort *port : ports)
341 {
342 VuoType *type = static_cast<VuoCompilerPort *>(port->getCompiler())->getDataVuoType();
343 if (type && ! type->hasCompiler() && ! dynamic_cast<VuoGenericType *>(type))
344 missingTypes[type->getModuleKey()].insert(node->getNodeClass()->getClassName());
345 }
346 }
347
348 if (! missingTypes.empty())
349 {
350 auto exceptionIssues = new VuoCompilerIssues;
351 for (auto i : missingTypes)
352 {
353 vector<string> nodeClassNames(i.second.begin(), i.second.end());
354 std::sort(nodeClassNames.begin(), nodeClassNames.end());
355
356 string summary = "Data type not installed";
357 string details = i.first + " — used by " + VuoStringUtilities::join(nodeClassNames, ", ");
358 string hint = "Check with the developer of the nodes that use this data type.";
359
360 VuoCompilerIssue issue(VuoCompilerIssue::Error, "opening composition", "", summary, details);
361 issue.setHint(hint);
362 exceptionIssues->append(issue);
363 if (issues)
364 issues->append(issue);
365 }
366
367 throw VuoCompilerException(exceptionIssues, true);
368 }
369}
370
378{
379 checkForEventFlowIssues(set<VuoCompilerCable *>(), issues);
380}
381
389void VuoCompilerComposition::checkForEventFlowIssues(set<VuoCompilerCable *> potentialCables, VuoCompilerIssues *issues)
390{
391 VuoCompilerGraph *graph;
392 if (! potentialCables.empty())
393 graph = new VuoCompilerGraph(this, nullptr, potentialCables);
394 else
395 graph = getCachedGraph();
396
397 graph->checkForInfiniteFeedback(issues);
398 graph->checkForDeadlockedFeedback(issues);
399
400 if (! potentialCables.empty())
401 delete graph;
402}
403
407set< set<VuoCompilerPort *> > VuoCompilerComposition::groupGenericPortsByType(void)
408{
409 set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts;
410
411 // Put the generic ports into sets by node, with additional sets for published input and output ports.
412 set< pair< vector<VuoPort *>, VuoNode *> > portsGroupedByNode;
413 set<VuoNode *> nodes = getBase()->getNodes();
414 for (set<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
415 {
416 VuoNode *node = *i;
417 if (! node->hasCompiler())
418 continue;
419
420 vector<VuoPort *> inputPorts = node->getInputPorts();
421 vector<VuoPort *> outputPorts = node->getOutputPorts();
422 vector<VuoPort *> ports;
423 ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
424 ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
425
426 portsGroupedByNode.insert( make_pair(ports, node) );
427 }
428 vector<VuoPublishedPort *> publishedInputPorts = getBase()->getPublishedInputPorts();
429 vector<VuoPublishedPort *> publishedOutputPorts = getBase()->getPublishedOutputPorts();
430 vector<VuoPort *> publishedInputBasePorts( publishedInputPorts.begin(), publishedInputPorts.end() );
431 vector<VuoPort *> publishedOutputBasePorts( publishedOutputPorts.begin(), publishedOutputPorts.end() );
432 portsGroupedByNode.insert( make_pair(publishedInputBasePorts, static_cast<VuoNode *>(NULL)) );
433 portsGroupedByNode.insert( make_pair(publishedOutputBasePorts, static_cast<VuoNode *>(NULL)) );
434
435 // Within each set of generic ports by node (or published ports), group the generic ports into sets by generic type.
436 for (set< pair< vector<VuoPort *>, VuoNode* > >::iterator i = portsGroupedByNode.begin(); i != portsGroupedByNode.end(); ++i)
437 {
438 vector<VuoPort *> ports = i->first;
439
440 map<string, set<VuoCompilerPort *> > genericPortsForType;
441 for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
442 {
443 VuoCompilerPort *port = static_cast<VuoCompilerPort *>((*j)->getCompiler());
444 VuoGenericType *genericType = dynamic_cast<VuoGenericType *>(port->getDataVuoType());
445
446 if (genericType)
447 {
448 string innermostGenericTypeName = VuoType::extractInnermostTypeName(genericType->getModuleKey());
449 genericPortsForType[innermostGenericTypeName].insert(port);
450 }
451 }
452
453 for (map<string, set<VuoCompilerPort *> >::iterator j = genericPortsForType.begin(); j != genericPortsForType.end(); ++j)
454 {
455 set<VuoCompilerPort *> genericPorts = j->second;
456 setsOfConnectedGenericPorts.insert(genericPorts);
457 }
458 }
459
460 // Merge sets of generic ports from different nodes that are connected through a data-carrying cable.
461 set<VuoCable *> cables = getBase()->getCables();
462 for (set<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
463 {
464 VuoCable *cable = *i;
465
466 if (! (cable->getFromPort() && cable->getFromPort()->hasCompiler() &&
467 cable->getToPort() && cable->getToPort()->hasCompiler() &&
468 cable->getCompiler()->carriesData()))
469 continue;
470
471 VuoCompilerPort *fromPort = static_cast<VuoCompilerPort *>(cable->getFromPort()->getCompiler());
472 VuoCompilerPort *toPort = static_cast<VuoCompilerPort *>(cable->getToPort()->getCompiler());
473 set< set<VuoCompilerPort *> >::iterator fromSetIter = setsOfConnectedGenericPorts.end(); // the set containing fromPort
474 set< set<VuoCompilerPort *> >::iterator toSetIter = setsOfConnectedGenericPorts.end(); // the set containing toPort
475 for (set< set<VuoCompilerPort *> >::iterator j = setsOfConnectedGenericPorts.begin(); j != setsOfConnectedGenericPorts.end(); ++j)
476 {
477 if (j->find(fromPort) != j->end())
478 fromSetIter = j;
479 if (j->find(toPort) != j->end())
480 toSetIter = j;
481 }
482 if (fromSetIter != setsOfConnectedGenericPorts.end() && toSetIter != setsOfConnectedGenericPorts.end() &&
483 fromSetIter != toSetIter)
484 {
485 set<VuoCompilerPort *> mergedSet;
486 mergedSet.insert(fromSetIter->begin(), fromSetIter->end());
487 mergedSet.insert(toSetIter->begin(), toSetIter->end());
488 setsOfConnectedGenericPorts.insert(mergedSet);
489 setsOfConnectedGenericPorts.erase(fromSetIter);
490 setsOfConnectedGenericPorts.erase(toSetIter);
491 }
492 }
493
494 return setsOfConnectedGenericPorts;
495}
496
504{
505 set< set<VuoCompilerPort *> > setsOfConnectedGenericPorts = groupGenericPortsByType();
506
507 // Give each set of connected generic ports a unique generic type.
508 set<string> usedTypeNames;
509 for (set< set<VuoCompilerPort *> >::iterator i = setsOfConnectedGenericPorts.begin(); i != setsOfConnectedGenericPorts.end(); ++i)
510 {
511 set<VuoCompilerPort *> connectedGenericPorts = *i;
512
513 // Find the smallest-numbered generic type within the set that isn't already used by another set.
514 // If no such type exists, create a fresh type.
515
516 vector<string> sortedTypeNames;
517 for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
518 {
519 VuoCompilerPort *port = *j;
520 VuoGenericType *genericTypeFromPort = static_cast<VuoGenericType *>(port->getDataVuoType());
521 VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
522 VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
523 if (genericTypeFromPort != genericTypeFromPortClass)
524 {
525 string typeName = VuoGenericType::extractInnermostTypeName( genericTypeFromPort->getModuleKey() );
526 sortedTypeNames.push_back(typeName);
527 }
528 }
530
531 string commonTypeName;
532 for (vector<string>::iterator j = sortedTypeNames.begin(); j != sortedTypeNames.end(); ++j)
533 {
534 string portType = *j;
535 if (usedTypeNames.find(portType) == usedTypeNames.end())
536 {
537 commonTypeName = portType;
538 break;
539 }
540 }
541
542 if (commonTypeName.empty())
543 commonTypeName = createFreshGenericTypeName();
544
545 usedTypeNames.insert(commonTypeName);
546
547 // Form the set of compatible types for each composition-level generic type by finding the intersection of
548 // the sets of compatible types for each node-class-level generic type.
549 vector<string> compatibleTypeNames;
550 for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
551 {
552 VuoCompilerPort *port = *j;
553 VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
554 VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
555 VuoGenericType::Compatibility compatibility;
556 vector<string> compatibleTypeNamesForPort = genericTypeFromPortClass->getCompatibleSpecializedTypes(compatibility);
557 vector<string> innermostCompatibleTypeNamesForPort;
558 for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
559 innermostCompatibleTypeNamesForPort.push_back( VuoType::extractInnermostTypeName(*k) );
560
561 if (! innermostCompatibleTypeNamesForPort.empty())
562 {
563 if (compatibleTypeNames.empty())
564 compatibleTypeNames = innermostCompatibleTypeNamesForPort;
565 else
566 {
567 for (int k = compatibleTypeNames.size() - 1; k >= 0; --k)
568 if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleTypeNames[k]) ==
569 innermostCompatibleTypeNamesForPort.end())
570 compatibleTypeNames.erase(compatibleTypeNames.begin() + k);
571 }
572 }
573 }
574
575 // Apply the composition-level generic type name to each port.
576 for (set<VuoCompilerPort *>::iterator j = connectedGenericPorts.begin(); j != connectedGenericPorts.end(); ++j)
577 {
578 VuoCompilerPort *port = *j;
579 VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(port->getBase()->getClass()->getCompiler());
580 VuoGenericType *genericTypeFromPortClass = static_cast<VuoGenericType *>(portClass->getDataVuoType());
581
582 string typeNameForPort = VuoGenericType::replaceInnermostGenericTypeName(genericTypeFromPortClass->getModuleKey(), commonTypeName);
583 vector<string> compatibleTypeNamesForPort;
584 string prefix = (VuoType::isListTypeName(typeNameForPort) ? VuoType::listTypeNamePrefix : "");
585 for (vector<string>::iterator k = compatibleTypeNames.begin(); k != compatibleTypeNames.end(); ++k)
586 compatibleTypeNamesForPort.push_back(prefix + *k);
587
588 VuoGenericType *commonTypeForPort = new VuoGenericType(typeNameForPort, compatibleTypeNamesForPort);
589 port->setDataVuoType(commonTypeForPort);
590 }
591 }
592
593 // Update the list of type suffixes that can't currently be used when creating fresh types.
594 for (map<unsigned int, bool>::iterator i = genericTypeSuffixUsed.begin(); i != genericTypeSuffixUsed.end(); ++i)
595 {
596 unsigned int suffix = i->first;
597 bool used = (usedTypeNames.find( VuoGenericType::createGenericTypeName(suffix) ) != usedTypeNames.end());
598 genericTypeSuffixUsed[suffix] = used;
599 }
600}
601
605string VuoCompilerComposition::createFreshGenericTypeName(void)
606{
607 for (unsigned int i = 1; ; ++i)
608 {
609 if (! genericTypeSuffixUsed[i])
610 {
611 genericTypeSuffixUsed[i] = true;
613 }
614 }
615}
616
628set<VuoPort *> VuoCompilerComposition::getCorrelatedGenericPorts(VuoNode *entryNode, VuoPort *entryPort, bool useOriginalType)
629{
631
632 auto getGenericTypeName = [&useOriginalType] (VuoNode *node, VuoPort *port)
633 {
634 VuoGenericType *genericType = nullptr;
635
636 if (useOriginalType)
637 {
638 if (node && node->hasCompiler())
639 {
640 VuoCompilerNodeClass *nodeClass = node->getNodeClass()->getCompiler();
641 VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(nodeClass);
642 if (specializedNodeClass)
643 genericType = dynamic_cast<VuoGenericType *>(specializedNodeClass->getOriginalPortType(port->getClass()));
644 }
645 }
646 else
647 {
648 VuoCompilerPort *compilerPort = static_cast<VuoCompilerPort *>(port->getCompiler());
649 genericType = dynamic_cast<VuoGenericType *>(compilerPort->getDataVuoType());
650 }
651
652 return genericType ? VuoType::extractInnermostTypeName(genericType->getModuleKey()) : "";
653 };
654
655 set<VuoPort *> correlatedPorts;
656 set<VuoNode *> nodesEnqueuedOrVisited;
657 list< pair<VuoNode *, string> > nodesToVisit;
658
659 string entryGenericType = getGenericTypeName(entryNode, entryPort);
660 if (! entryGenericType.empty())
661 {
662 nodesToVisit.push_back( make_pair(entryNode, entryGenericType) );
663 nodesEnqueuedOrVisited.insert(entryNode);
664 }
665
666 while (! nodesToVisit.empty())
667 {
668 VuoNode *currNode = nodesToVisit.front().first;
669 string currGenericType = nodesToVisit.front().second;
670 nodesToVisit.pop_front();
671
672 // Find all ports on the node that share (or would share if unspecialized) the same data type.
673
674 list<VuoPort *> correlatedPortsOnNode;
675 for (VuoPort *currPort : currNode->getInputPorts())
676 if (getGenericTypeName(currNode, currPort) == currGenericType)
677 correlatedPortsOnNode.push_back(currPort);
678 for (VuoPort *currPort : currNode->getOutputPorts())
679 if (getGenericTypeName(currNode, currPort) == currGenericType)
680 correlatedPortsOnNode.push_back(currPort);
681
682 correlatedPorts.insert(correlatedPortsOnNode.begin(), correlatedPortsOnNode.end());
683
684 // Follow all data-carrying cables from those ports to other nodes.
685
686 for (VuoPort *port : correlatedPortsOnNode)
687 {
688 for (VuoCable *cable : port->getConnectedCables())
689 {
690 if (! (cable->hasCompiler() && cable->getCompiler()->carriesData()))
691 continue;
692
693 VuoNode *otherNode = nullptr;
694 VuoPort *otherPort = nullptr;
695 if (currNode != cable->getFromNode())
696 {
697 otherNode = cable->getFromNode();
698 otherPort = cable->getFromPort();
699 }
700 else if (currNode != cable->getToNode())
701 {
702 otherNode = cable->getToNode();
703 otherPort = cable->getToPort();
704 }
705
706 if (! otherPort)
707 continue;
708
709 if (nodesEnqueuedOrVisited.find(otherNode) == nodesEnqueuedOrVisited.end())
710 {
711 string otherGenericType = getGenericTypeName(otherNode, otherPort);
712 if (! otherGenericType.empty())
713 {
714 nodesToVisit.push_back( make_pair(otherNode, otherGenericType) );
715 nodesEnqueuedOrVisited.insert(otherNode);
716 }
717 }
718 }
719 }
720 }
721
722 return correlatedPorts;
723}
724
729{
732 return trigger ? trigger->getBase() : NULL;
733}
734
739{
740 this->module = module;
741}
742
747{
748 Module *takenModule = module;
749 module = NULL;
750 return takenModule;
751}
752
759void VuoCompilerComposition::setUniqueGraphvizIdentifierForNode(VuoNode *node, const string &preferredIdentifier, const string &identifierPrefix)
760{
761 if (! node->hasCompiler())
762 return;
763
764 for (VuoNode *currNode : getBase()->getNodes())
765 if (currNode != node && currNode->hasCompiler())
766 nodeGraphvizIdentifierUsed[ currNode->getCompiler()->getGraphvizIdentifier() ] = currNode;
767
768 auto isIdentifierAvailable = [this, node] (const string &identifier)
769 {
770 auto it = nodeGraphvizIdentifierUsed.find(identifier);
771 if (it == nodeGraphvizIdentifierUsed.end())
772 return true;
773
774 return it->second == node;
775 };
776
777 string nonEmptyPreferredIdentifier = (! preferredIdentifier.empty() ? preferredIdentifier : node->getCompiler()->getGraphvizIdentifier());
778 string nonEmptyIdentifierPrefix = (! identifierPrefix.empty() ? identifierPrefix : node->getCompiler()->getGraphvizIdentifierPrefix());
779
780 string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable, nonEmptyPreferredIdentifier, nonEmptyIdentifierPrefix);
781
782 nodeGraphvizIdentifierUsed[uniqueIdentifier] = node;
783 node->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
784}
785
790{
791 nodeGraphvizIdentifierUsed.clear();
792}
793
801{
802 if (! comment->hasCompiler())
803 return;
804
805 for (VuoComment *currComment : getBase()->getComments())
806 if (currComment != comment && currComment->hasCompiler())
807 commentGraphvizIdentifierUsed[ currComment->getCompiler()->getGraphvizIdentifier() ] = currComment;
808
809 auto isIdentifierAvailable = [this, comment] (const string &identifier)
810 {
811 auto it = commentGraphvizIdentifierUsed.find(identifier);
812 if (it == commentGraphvizIdentifierUsed.end())
813 return true;
814
815 return it->second == comment;
816 };
817
818 string uniqueIdentifier = VuoStringUtilities::formUniqueIdentifier(isIdentifierAvailable,
821 commentGraphvizIdentifierUsed[uniqueIdentifier] = comment;
822 comment->getCompiler()->setGraphvizIdentifier(uniqueIdentifier);
823}
824
829{
830 commentGraphvizIdentifierUsed.clear();
831}
832
841{
842 this->manuallyFirableInputNode = nodeContainingPort;
843 this->manuallyFirableInputPort = portFiredInto;
844}
845
850{
851 return manuallyFirableInputNode;
852}
853
858{
859 return manuallyFirableInputPort;
860}
861
865string VuoCompilerComposition::getGraphvizDeclaration(VuoProtocol *activeProtocol, string header, string footer)
866{
867 return getGraphvizDeclarationForComponents(getBase()->getNodes(),
868 getBase()->getCables(),
869 getBase()->getComments(),
870 getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, true),
871 getBase()->getProtocolAwarePublishedPortOrder(activeProtocol, false),
872 header,
873 footer);
874}
875
880 set<VuoCable *> cableSet,
881 set<VuoComment *> commentSet,
882 vector<VuoPublishedPort *> publishedInputPorts,
883 vector<VuoPublishedPort *> publishedOutputPorts,
884 string header, string footer, double xPositionOffset, double yPositionOffset)
885{
886 // Sort nodes.
887 vector<VuoNode *> nodes;
888 for (set<VuoNode *>::iterator i = nodeSet.begin(); i != nodeSet.end(); ++i)
889 nodes.push_back(*i);
890
891 sort(nodes.begin(), nodes.end(), compareGraphvizIdentifiersOfNodes);
892
893 // Sort cables.
894 vector<VuoCable *> cables;
895 for (set<VuoCable *>::iterator i = cableSet.begin(); i != cableSet.end(); ++i)
896 cables.push_back(*i);
897
898 sort(cables.begin(), cables.end(), compareGraphvizIdentifiersOfCables);
899
900 // Sort comments.
901 vector<VuoComment *> comments;
902 for (set<VuoComment *>::iterator i = commentSet.begin(); i != commentSet.end(); ++i)
903 comments.push_back(*i);
904
905 sort(comments.begin(), comments.end(), compareGraphvizIdentifiersOfComments);
906
907 string compositionHeader = (! header.empty()? header : defaultGraphDeclaration);
908 string compositionFooter = (! footer.empty()? footer : "\n");
909
910 ostringstream output;
911 string nodeCommentSectionDivider = "\n";
912 if ((nodes.empty() && publishedInputPorts.empty() && publishedOutputPorts.empty()) ||
913 (comments.empty() && cables.empty()))
914 nodeCommentSectionDivider = "";
915
916 string commentCableSectionDivider = "\n";
917 if (comments.empty() || cables.empty())
918 commentCableSectionDivider = "";
919
920 // Print header
921 output << compositionHeader;
922 output << "{" << endl;
923
924 // Print nodes
925 for (vector<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
926 {
927 string nodeDeclaration = ((*i)->hasCompiler() ?
928 (*i)->getCompiler()->getGraphvizDeclaration(true, xPositionOffset, yPositionOffset,
929 manuallyFirableInputNode == *i ? manuallyFirableInputPort : nullptr) :
930 (*i)->getRawGraphvizDeclaration());
931 output << nodeDeclaration << endl;
932 }
933
934 // Print pseudo-nodes for published ports
935 if (! publishedInputPorts.empty())
936 {
938 for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
939 output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\r";
940 output << "\"";
941 for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
942 output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
943 output << "];" << endl;
944 }
945 if (! publishedOutputPorts.empty())
946 {
948 for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
949 output << "|<" << (*i)->getClass()->getName() << ">" << (*i)->getClass()->getName() << "\\l";
950 output << "\"";
951
952 for (vector<VuoPublishedPort *>::iterator i = publishedOutputPorts.begin(); i != publishedOutputPorts.end(); ++i)
953 output << static_cast<VuoCompilerPublishedPort *>((*i)->getCompiler())->getGraphvizAttributes();
954
955 output << "];" << endl;
956 }
957
958 output << nodeCommentSectionDivider;
959
960 // Print comments
961 for (vector<VuoComment *>::iterator i = comments.begin(); i != comments.end(); ++i)
962 {
963 string commentDeclaration = ((*i)->hasCompiler() ?
964 (*i)->getCompiler()->getGraphvizDeclaration(xPositionOffset, yPositionOffset) : "");
965 output << commentDeclaration << endl;
966 }
967
968 output << commentCableSectionDivider;
969
970 // Print cables
971 for (vector<VuoCable *>::iterator i = cables.begin(); i != cables.end(); ++i)
972 {
973 VuoCable *cable = *i;
974 output << cable->getCompiler()->getGraphvizDeclaration() << endl;
975 }
976
977 output << "}";
978 output << compositionFooter;
979
980 return output.str();
981}
982
989{
992
993 VuoCompilerCompatibility compositeTarget(nullptr);
994 for (auto node : getBase()->getNodes())
995 compositeTarget = compositeTarget.intersection( node->getNodeClass()->getCompiler()->getCompatibleTargets() );
996
997 return compositeTarget;
998}
999
1003bool VuoCompilerComposition::compareGraphvizIdentifiersOfNodes(VuoNode *lhs, VuoNode *rhs)
1004{
1005 string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : lhs->getRawGraphvizIdentifier());
1006 string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : rhs->getRawGraphvizIdentifier());
1007 return lhsIdentifier.compare(rhsIdentifier) < 0;
1008}
1009
1013bool VuoCompilerComposition::compareGraphvizIdentifiersOfCables(VuoCable *lhs, VuoCable *rhs)
1014{
1015 string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizDeclaration() : "");
1016 string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizDeclaration() : "");
1017 return lhsIdentifier.compare(rhsIdentifier) < 0;
1018}
1019
1023bool VuoCompilerComposition::compareGraphvizIdentifiersOfComments(VuoComment *lhs, VuoComment *rhs)
1024{
1025 string lhsIdentifier = (lhs->hasCompiler() ? lhs->getCompiler()->getGraphvizIdentifier() : "");
1026 string rhsIdentifier = (rhs->hasCompiler() ? rhs->getCompiler()->getGraphvizIdentifier() : "");
1027 return lhsIdentifier.compare(rhsIdentifier) < 0;
1028}
1029
1035{
1036 if (! oldPort || ! newPort)
1037 return false;
1038
1039 string oldPortName = oldPort->getClass()->getName();
1040 string newPortName = newPort->getClass()->getName();
1041 if (oldPortName == newPortName)
1042 {
1043 if (oldPort->hasCompiler() && newPort->hasCompiler())
1044 {
1045 VuoType *oldType = static_cast<VuoCompilerPort *>( oldPort->getCompiler() )->getDataVuoType();
1046 VuoType *newType = static_cast<VuoCompilerPort *>( newPort->getCompiler() )->getDataVuoType();
1047 if (oldType == newType)
1048 return true;
1049 }
1050 else
1051 return true;
1052 }
1053
1054 return false;
1055}
1056
1062{
1063 if (! oldPortClass || ! newPortClass)
1064 return false;
1065
1066 string oldPortName = oldPortClass->getName();
1067 string newPortName = newPortClass->getName();
1068 if (oldPortName == newPortName)
1069 {
1070 if (oldPortClass->hasCompiler() && newPortClass->hasCompiler())
1071 {
1072 VuoType *oldType = static_cast<VuoCompilerPortClass *>( oldPortClass->getCompiler() )->getDataVuoType();
1073 VuoType *newType = static_cast<VuoCompilerPortClass *>( newPortClass->getCompiler() )->getDataVuoType();
1074 if (oldType == newType)
1075 return true;
1076 }
1077 else
1078 return true;
1079 }
1080
1081 return false;
1082}