Vuo 2.4.4
Loading...
Searching...
No Matches
VuoCompilerNodeClass.cc
Go to the documentation of this file.
1
10#include <sstream>
11#include "VuoCompiler.hh"
16#include "VuoCompilerNode.hh"
20#include "VuoCompilerPort.hh"
25#include "VuoFileUtilities.hh"
26#include "VuoGenericType.hh"
27#include "VuoJsonUtilities.hh"
28#include "VuoNode.hh"
29#include "VuoNodeClass.hh"
30#include "VuoPort.hh"
31#include "VuoStringUtilities.hh"
32
33
39{
40 INPUT_DATA_ABSENT = 1 << 0,
41 INPUT_DATA_PRESENT = 1 << 1,
42 OUTPUT_DATA_ABSENT = 1 << 2,
43 OUTPUT_DATA_PRESENT = 1 << 3,
44 INPUT_EVENT_ABSENT = 1 << 4,
45 INPUT_EVENT_PRESENT = 1 << 5,
46 OUTPUT_EVENT_ABSENT = 1 << 6,
47 OUTPUT_EVENT_PRESENT = 1 << 7,
48 OUTPUT_TRIGGER_ABSENT = 1 << 8,
49 OUTPUT_TRIGGER_PRESENT = 1 << 9,
50 INSTANCE_DATA_ABSENT = 1 << 10,
51 INSTANCE_DATA_PRESENT = 1 << 11
52};
53
54
58VuoCompilerNodeClass::VuoCompilerNodeClass(string className, Module *module)
59 : VuoBaseDetail<VuoNodeClass>("VuoCompilerNodeClass with dummy base", new VuoNodeClass(className, vector<string>(), vector<string>())),
60 VuoCompilerModule(getBase(), module)
61{
62 instanceDataClass = NULL;
63 initFunction = NULL;
64 finiFunction = NULL;
65 callbackStartFunction = NULL;
66 callbackUpdateFunction = NULL;
67 callbackStopFunction = NULL;
68 _isSubcomposition = false;
69
70 parse();
71}
72
80 : VuoBaseDetail<VuoNodeClass>("VuoCompilerNodeClass with substantial base", new VuoNodeClass(
81 compilerNodeClass->getBase()->getClassName(),
82 compilerNodeClass->getBase()->getRefreshPortClass(),
83 compilerNodeClass->getBase()->getInputPortClasses(),
84 compilerNodeClass->getBase()->getOutputPortClasses())),
85 VuoCompilerModule(getBase(), compilerNodeClass->module)
86{
87 getBase()->setDefaultTitle(compilerNodeClass->getBase()->getDefaultTitle());
88 getBase()->setDescription(compilerNodeClass->getBase()->getDescription());
89 getBase()->setVersion(compilerNodeClass->getBase()->getVersion());
90 getBase()->setKeywords(compilerNodeClass->getBase()->getKeywords());
91 getBase()->setNodeSet(compilerNodeClass->getBase()->getNodeSet());
92#if VUO_PRO
93 getBase()->setPro(compilerNodeClass->getBase()->isPro());
94 setDependsOnPro(compilerNodeClass->dependsOnPro());
95#endif
97 getBase()->setDeprecated(compilerNodeClass->getBase()->getDeprecated());
98 getBase()->setCompiler(this);
99
100 this->dependencies = compilerNodeClass->dependencies;
101 this->compatibleTargets = compilerNodeClass->compatibleTargets;
102 this->eventFunction = compilerNodeClass->eventFunction;
103 this->initFunction = compilerNodeClass->initFunction;
104 this->finiFunction = compilerNodeClass->finiFunction;
105 this->callbackStartFunction = compilerNodeClass->callbackStartFunction;
106 this->callbackUpdateFunction = compilerNodeClass->callbackUpdateFunction;
107 this->callbackStopFunction = compilerNodeClass->callbackStopFunction;
108 this->instanceDataClass = compilerNodeClass->instanceDataClass;
109 this->triggerDescriptions = compilerNodeClass->triggerDescriptions;
111 this->portsWithExplicitEventBlockingNone = compilerNodeClass->portsWithExplicitEventBlockingNone;
112 this->sourcePath = compilerNodeClass->sourcePath;
113 this->sourceCode = compilerNodeClass->sourceCode;
114 this->makeDependencies = compilerNodeClass->makeDependencies;
115 this->containedNodes = compilerNodeClass->containedNodes;
117
118 compilerNodeClass->getBase()->setInputPortClasses(vector<VuoPortClass *>());
119 compilerNodeClass->getBase()->setOutputPortClasses(vector<VuoPortClass *>());
120 compilerNodeClass->instanceDataClass = NULL;
121
122 _isSubcomposition = false;
123 updateSubcompositionStatus();
124}
125
130 : VuoBaseDetail<VuoNodeClass>("VuoCompilerNodeClass with existing base", baseNodeClass),
131 VuoCompilerModule(baseNodeClass, NULL)
132{
133 getBase()->setCompiler(this);
134
135 instanceDataClass = NULL;
136 eventFunction = NULL;
137 initFunction = NULL;
138 finiFunction = NULL;
139 callbackStartFunction = NULL;
140 callbackUpdateFunction = NULL;
141 callbackStopFunction = NULL;
142 _isSubcomposition = false;
143}
144
149{
150 delete instanceDataClass;
151
152 vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
153 for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
154 delete (*i)->getCompiler();
155
156 vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
157 for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
158 delete (*i)->getCompiler();
159}
160
164VuoNode * VuoCompilerNodeClass::newNode(string title, double x, double y)
165{
166 if (title.empty())
168
169 VuoNode *n = getBase()->newNode(title, x, y);
170 instantiateCompilerNode(n);
171 return n;
172}
173
178{
179 VuoNode *n = getBase()->newNode(nodeToCopyMetadataFrom);
180 instantiateCompilerNode(n);
181 return n;
182}
183
189void VuoCompilerNodeClass::instantiateCompilerNode(VuoNode *node)
190{
191 // Instantiate compiler ports.
192 vector<VuoPort *> inputPorts = node->getInputPorts();
193 vector<VuoPort *> outputPorts = node->getOutputPorts();
194 vector<VuoPort *> ports;
195 ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
196 ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
197
198 for (vector<VuoPort *>::iterator i = ports.begin(); i != ports.end(); ++i)
199 {
200 VuoPort *basePort = *i;
201 VuoCompilerPortClass *portClass = static_cast<VuoCompilerPortClass *>(basePort->getClass()->getCompiler());
202 basePort->setCompiler(portClass->newPort(basePort));
203 }
204
205 // Instantiate compiler node.
206 new VuoCompilerNode(node);
207}
208
213{
214 return (new VuoCompilerNodeClass(baseNodeClass))->getBase();
215}
216
221VuoNodeClass * VuoCompilerNodeClass::newNodeClass(string nodeClassName, Module * module)
222{
223 if (isNodeClass(module, nodeClassName))
224 {
225 VuoNodeClass *specializedNodeClass = VuoCompilerSpecializedNodeClass::newNodeClass(nodeClassName, module);
226 if (specializedNodeClass)
227 return specializedNodeClass;
228
229 VuoCompilerNodeClass *cnc = new VuoCompilerNodeClass(nodeClassName, module);
230
231 // Reconstruct, this time with a base VuoNodeClass containing actual (non-dummy) ports.
233 delete cnc;
234 return cnc2->getBase();
235 }
236
237 return nullptr;
238}
239
244bool VuoCompilerNodeClass::isNodeClass(Module *module, string moduleKey)
245{
246 return hasOriginalOrMangledGlobal("nodeEvent", module, moduleKey) ||
247 hasOriginalOrMangledGlobal("nodeInstanceEvent", module, moduleKey);
248}
249
253void VuoCompilerNodeClass::parse(void)
254{
256
257 parseEventFunction();
258 if (instanceDataClass)
259 {
260 parseInitFunction();
261 parseFiniFunction();
262 parseCallbackStartFunction();
263 parseCallbackUpdateFunction();
264 parseCallbackStopFunction();
265 }
266
267 // Add each non-generic port type as a dependency.
268 vector<VuoPortClass *> portClasses;
269 vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
270 vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
271 portClasses.insert(portClasses.end(), inputPortClasses.begin(), inputPortClasses.end());
272 portClasses.insert(portClasses.end(), outputPortClasses.begin(), outputPortClasses.end());
273 for (vector<VuoPortClass *>::iterator i = portClasses.begin(); i != portClasses.end(); ++i)
274 {
275 VuoType *type = static_cast<VuoCompilerPortClass *>((*i)->getCompiler())->getDataVuoType();
276 if (type && ! dynamic_cast<VuoGenericType *>(type))
277 {
278 string typeName = type->getModuleKey();
279 dependencies.insert(typeName);
280 string innermostTypeName = VuoType::extractInnermostTypeName(typeName);
281 dependencies.insert(innermostTypeName);
282 }
283 }
284
285 updateSubcompositionStatus();
286}
287
291set<string> VuoCompilerNodeClass::globalsToRename(void)
292{
293 set<string> globals = VuoCompilerModule::globalsToRename();
294 globals.insert("nodeEvent");
295 globals.insert("nodeInstanceEvent");
296 globals.insert("nodeInstanceInit");
297 globals.insert("nodeInstanceFini");
298 globals.insert("nodeInstanceTriggerStart");
299 globals.insert("nodeInstanceTriggerUpdate");
300 globals.insert("nodeInstanceTriggerStop");
301 return globals;
302}
303
307void VuoCompilerNodeClass::parseMetadata(void)
308{
310
311 json_object *nodeDetails = NULL;
312 if (json_object_object_get_ex(moduleDetails, "node", &nodeDetails))
313 {
315 getBase()->setDeprecated(VuoJsonUtilities::parseBool(nodeDetails, "isDeprecated"));
316
317#if VUO_PRO
318 setDependsOnPro(VuoJsonUtilities::parseBool(nodeDetails, "dependsOnPro"));
319#endif
320
321 json_object *triggersArray = NULL;
322 if (json_object_object_get_ex(nodeDetails, "triggers", &triggersArray))
323 triggerDescriptions = VuoCompilerTriggerDescription::parseFromJson(triggersArray);
324
325 json_object *nodesObj = NULL;
326 if (json_object_object_get_ex(nodeDetails, "nodes", &nodesObj))
327 {
328 json_object_object_foreach(nodesObj, nodeIdentifier, nodeClassNameObj)
329 {
330 string nodeClassName = json_object_get_string(nodeClassNameObj);
331 containedNodes.insert(make_pair(nodeIdentifier, nodeClassName));
332 }
333 }
334 }
335}
336
343void VuoCompilerNodeClass::parseEventFunction(void)
344{
345 eventFunction = parser->getFunction(nameForGlobal("nodeEvent"));
346 VuoNodeArgumentAcceptance acceptsInstanceData = INSTANCE_DATA_ABSENT;
347
348 if (! eventFunction)
349 {
350 eventFunction = parser->getFunction(nameForGlobal("nodeInstanceEvent"));
351 acceptsInstanceData = INSTANCE_DATA_PRESENT;
352
353 if (! eventFunction)
354 {
355 VUserLog("Error: Node class '%s' is missing function nodeEvent or nodeInstanceEvent.", getBase()->getClassName().c_str());
356 return;
357 }
358 }
359
360 parseParameters(eventFunction,
361 INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
362 OUTPUT_DATA_ABSENT | OUTPUT_DATA_PRESENT |
363 INPUT_EVENT_ABSENT | INPUT_EVENT_PRESENT |
364 OUTPUT_EVENT_ABSENT | OUTPUT_EVENT_PRESENT |
365 OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
366 acceptsInstanceData);
367}
368
375void VuoCompilerNodeClass::parseInitFunction(void)
376{
377 initFunction = parser->getFunction(nameForGlobal("nodeInstanceInit"));
378 if (! initFunction)
379 return;
380
381 parseParameters(initFunction,
382 INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
383 OUTPUT_DATA_ABSENT |
384 INPUT_EVENT_ABSENT |
385 OUTPUT_EVENT_ABSENT |
386 OUTPUT_TRIGGER_ABSENT |
387 INSTANCE_DATA_ABSENT);
388}
389
393void VuoCompilerNodeClass::parseFiniFunction(void)
394{
395 finiFunction = parser->getFunction(nameForGlobal("nodeInstanceFini"));
396}
397
407void VuoCompilerNodeClass::parseCallbackStartFunction(void)
408{
409 callbackStartFunction = parser->getFunction(nameForGlobal("nodeInstanceTriggerStart"));
410 if (! callbackStartFunction)
411 return;
412
413 parseParameters(callbackStartFunction,
414 INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
415 OUTPUT_DATA_ABSENT |
416 INPUT_EVENT_ABSENT |
417 OUTPUT_EVENT_ABSENT |
418 OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
419 INSTANCE_DATA_PRESENT);
420}
421
431void VuoCompilerNodeClass::parseCallbackUpdateFunction(void)
432{
433 callbackUpdateFunction = parser->getFunction(nameForGlobal("nodeInstanceTriggerUpdate"));
434 if (! callbackUpdateFunction)
435 return;
436
437 parseParameters(callbackUpdateFunction,
438 INPUT_DATA_ABSENT | INPUT_DATA_PRESENT |
439 OUTPUT_DATA_ABSENT |
440 INPUT_EVENT_ABSENT |
441 OUTPUT_EVENT_ABSENT |
442 OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
443 INSTANCE_DATA_PRESENT);
444}
445
449void VuoCompilerNodeClass::parseCallbackStopFunction(void)
450{
451 callbackStopFunction = parser->getFunction(nameForGlobal("nodeInstanceTriggerStop"));
452 if (! callbackStopFunction)
453 return;
454
455 parseParameters(callbackStopFunction,
456 INPUT_DATA_ABSENT |
457 OUTPUT_DATA_ABSENT |
458 INPUT_EVENT_ABSENT |
459 OUTPUT_EVENT_ABSENT |
460 OUTPUT_TRIGGER_ABSENT | OUTPUT_TRIGGER_PRESENT |
461 INSTANCE_DATA_PRESENT);
462}
463
471void VuoCompilerNodeClass::parseParameters(Function *function, unsigned long acceptanceFlags)
472{
473 vector<pair<Argument *, string> > annotatedArguments = parser->getAnnotatedArguments(function);
474
475 vector<VuoCompilerNodeArgumentClass *> inputArgumentClasses;
476 vector<VuoCompilerNodeArgumentClass *> outputArgumentClasses;
477 map<string, VuoType *> vuoTypeForArgumentName;
478 map<string, json_object *> detailsForArgumentName;
479 map<string, VuoCompilerPortClass *> portClassForArgumentName;
480 map<string, VuoCompilerDataClass *> dataClassForArgumentName;
481
482 bool sawInputData = false;
483 bool sawOutputData = false;
484 bool sawInputEvent = false;
485 bool sawOutputEvent = false;
486 bool sawOutputTrigger = false;
487 bool sawInstanceData = false;
488
489 // Create the node argument class for each annotated argument.
490 for (vector<pair<Argument *, string> >::iterator i = annotatedArguments.begin(); i != annotatedArguments.end(); ++i)
491 {
492 Argument *argument = i->first;
493 string annotation = i->second;
494 string argumentName = parser->getArgumentNameInSourceCode(argument->getName());
495
496 VuoCompilerNodeArgumentClass *argumentClass = NULL;
497 VuoPortClass *existingPortClass = NULL;
498 json_object *details = NULL;
499 VuoType *type = NULL;
500
501 if ((argumentClass = parseInputDataParameter(annotation, argument)) != NULL)
502 {
503 existingPortClass = getExistingPortClass(argumentClass, true);
504 if (! existingPortClass)
505 {
506 inputArgumentClasses.push_back(argumentClass);
507 dataClassForArgumentName[argumentName] = static_cast<VuoCompilerDataClass *>(argumentClass);
508 }
509
510 sawInputData = true;
511 }
512 else if ((argumentClass = parseOutputDataParameter(annotation, argument)) != NULL)
513 {
514 existingPortClass = getExistingPortClass(argumentClass, false);
515 if (! existingPortClass)
516 {
517 outputArgumentClasses.push_back(argumentClass);
518 dataClassForArgumentName[argumentName] = static_cast<VuoCompilerDataClass *>(argumentClass);
519 }
520
521 sawOutputData = true;
522 }
523 else if ((argumentClass = parseInputEventParameter(annotation, argument)) != NULL)
524 {
525 existingPortClass = getExistingPortClass(argumentClass, true);
526 if (! existingPortClass)
527 {
528 inputArgumentClasses.push_back(argumentClass);
529 portClassForArgumentName[argumentName] = static_cast<VuoCompilerPortClass *>(argumentClass);
530 }
531
532 sawInputEvent = true;
533 }
534 else if ((argumentClass = parseOutputEventParameter(annotation, argument)) != NULL)
535 {
536 existingPortClass = getExistingPortClass(argumentClass, false);
537 if (! existingPortClass)
538 {
539 outputArgumentClasses.push_back(argumentClass);
540 portClassForArgumentName[argumentName] = static_cast<VuoCompilerPortClass *>(argumentClass);
541 }
542
543 sawOutputEvent = true;
544 }
545 else if ((argumentClass = parseTriggerParameter(annotation, argument)) != NULL)
546 {
547 existingPortClass = getExistingPortClass(argumentClass, false);
548 if (! existingPortClass)
549 {
550 outputArgumentClasses.push_back(argumentClass);
551 portClassForArgumentName[argumentName] = static_cast<VuoCompilerPortClass *>(argumentClass);
552 }
553
554 sawOutputTrigger = true;
555 }
556 else if ((argumentClass = parseInstanceDataParameter(annotation, argument)) != NULL)
557 {
558 if (instanceDataClass)
559 existingPortClass = instanceDataClass->getBase();
560 else
561 instanceDataClass = static_cast<VuoCompilerInstanceDataClass *>(argumentClass);
562
563 sawInstanceData = true;
564 }
565 else if ((type = parseTypeParameter(annotation)) != NULL)
566 {
567 vuoTypeForArgumentName[argumentName] = type;
568 }
569 else if ((details = parseDetailsParameter(annotation)) != NULL)
570 {
571 detailsForArgumentName[argumentName] = details;
572 }
573
574 VuoCompilerNodeArgumentClass *argumentClassInNodeClass = (existingPortClass ? existingPortClass->getCompiler() : argumentClass);
575 if (argumentClassInNodeClass)
576 {
577 size_t argumentIndex = argument->getArgNo();
578 if (function == eventFunction)
579 argumentClassInNodeClass->setIndexInEventFunction(argumentIndex);
580 else if (function == initFunction)
581 argumentClassInNodeClass->setIndexInInitFunction(argumentIndex);
582 else if (function == callbackStartFunction)
583 argumentClassInNodeClass->setIndexInCallbackStartFunction(argumentIndex);
584 else if (function == callbackUpdateFunction)
585 argumentClassInNodeClass->setIndexInCallbackUpdateFunction(argumentIndex);
586 else if (function == callbackStopFunction)
587 argumentClassInNodeClass->setIndexInCallbackStopFunction(argumentIndex);
588 }
589
590 if (existingPortClass)
591 delete argumentClass;
592 }
593
594 // Check that all required arguments and no disallowed arguments are present.
595 {
596 string functionName = function->getName().str();
597 string wronglyAbsentMessage = " is required in " + functionName;
598 string wronglyPresentMessage = " is not allowed in " + functionName;
599
600 if (sawInputData && ! (acceptanceFlags & INPUT_DATA_PRESENT))
601 VUserLog("Error: %s", ("VuoInputData" + wronglyPresentMessage).c_str());
602 if (sawOutputData && ! (acceptanceFlags & OUTPUT_DATA_PRESENT))
603 VUserLog("Error: %s", ("VuoOutputData" + wronglyPresentMessage).c_str());
604 if (sawInputEvent && ! (acceptanceFlags & INPUT_EVENT_PRESENT))
605 VUserLog("Error: %s", ("VuoInputEvent" + wronglyPresentMessage).c_str());
606 if (sawOutputEvent && ! (acceptanceFlags & OUTPUT_EVENT_PRESENT))
607 VUserLog("Error: %s", ("VuoOutputEvent" + wronglyPresentMessage).c_str());
608 if (sawOutputTrigger && ! (acceptanceFlags & OUTPUT_TRIGGER_PRESENT))
609 VUserLog("Error: %s", ("VuoOutputTrigger" + wronglyPresentMessage).c_str());
610 if (sawInstanceData && ! (acceptanceFlags & INSTANCE_DATA_PRESENT))
611 VUserLog("Error: %s", ("VuoInstanceData" + wronglyPresentMessage).c_str());
612
613 if (! sawInputData && ! (acceptanceFlags & INPUT_DATA_ABSENT))
614 VUserLog("Error: %s", ("VuoInputData" + wronglyAbsentMessage).c_str());
615 if (! sawOutputData && ! (acceptanceFlags & OUTPUT_DATA_ABSENT))
616 VUserLog("Error: %s", ("VuoOutputData" + wronglyAbsentMessage).c_str());
617 if (! sawInputEvent && ! (acceptanceFlags & INPUT_EVENT_ABSENT))
618 VUserLog("Error: %s", ("VuoInputEvent" + wronglyAbsentMessage).c_str());
619 if (! sawOutputEvent && ! (acceptanceFlags & OUTPUT_EVENT_ABSENT))
620 VUserLog("Error: %s", ("VuoOutputEvent" + wronglyAbsentMessage).c_str());
621 if (! sawOutputTrigger && ! (acceptanceFlags & OUTPUT_TRIGGER_ABSENT))
622 VUserLog("Error: %s", ("VuoOutputTrigger" + wronglyAbsentMessage).c_str());
623 if (! sawInstanceData && ! (acceptanceFlags & INSTANCE_DATA_ABSENT))
624 VUserLog("Error: %s", ("VuoInstanceData" + wronglyAbsentMessage).c_str());
625 }
626
627 // For each event portion of a data-and-event port, find the corresponding data portion. Rename the event portion to match.
628 map<string, VuoCompilerInputEventPortClass *> inputEventPortClassForDataClassName;
629 for (map<string, VuoCompilerPortClass *>::iterator i = portClassForArgumentName.begin(); i != portClassForArgumentName.end(); ++i)
630 {
631 string argumentName = i->first;
632 VuoCompilerInputEventPortClass *eventPortClass = dynamic_cast<VuoCompilerInputEventPortClass *>( i->second );
633
634 if (eventPortClass)
635 {
636 bool isDataInDetails = false;
637 string dataPortName = VuoJsonUtilities::parseString(detailsForArgumentName[argumentName], "data", "", &isDataInDetails);
638 if (isDataInDetails)
639 {
640 eventPortClass->getBase()->setName(dataPortName);
641 inputEventPortClassForDataClassName[dataPortName] = eventPortClass;
642 }
643 }
644 }
645 map<string, VuoCompilerOutputEventPortClass *> outputEventPortClassForDataClassName;
646 for (map<string, VuoCompilerPortClass *>::iterator i = portClassForArgumentName.begin(); i != portClassForArgumentName.end(); ++i)
647 {
648 string argumentName = i->first;
649 VuoCompilerOutputEventPortClass *eventPortClass = dynamic_cast<VuoCompilerOutputEventPortClass *>( i->second );
650
651 if (eventPortClass)
652 {
653 bool isDataInDetails = false;
654 string dataPortName = VuoJsonUtilities::parseString(detailsForArgumentName[argumentName], "data", "", &isDataInDetails);
655 if (isDataInDetails)
656 {
657 eventPortClass->getBase()->setName(dataPortName);
658 outputEventPortClassForDataClassName[dataPortName] = eventPortClass;
659 }
660 }
661 }
662
663 // Match up data classes with event port classes. Add event and trigger port classes to addedInputPortClasses/addedOutputPortClasses.
664 vector<VuoPortClass *> addedInputPortClasses;
665 for (vector<VuoCompilerNodeArgumentClass *>::iterator i = inputArgumentClasses.begin(); i != inputArgumentClasses.end(); ++i)
666 {
667 VuoCompilerInputDataClass *dataClass = dynamic_cast<VuoCompilerInputDataClass *>(*i);
668 if (dataClass)
669 {
670 VuoCompilerInputEventPortClass *eventPortClass = inputEventPortClassForDataClassName[dataClass->getBase()->getName()];
671
672 if (! eventPortClass)
673 {
674 eventPortClass = new VuoCompilerInputEventPortClass(dataClass->getBase()->getName());
675 eventPortClass->setDataClass(dataClass);
676 addedInputPortClasses.push_back(eventPortClass->getBase());
677 }
678 else
679 {
680 // Since setDataClass() changes the base, we need to remove the old base and add the new one.
681 vector<VuoPortClass *>::iterator oldBase = find(addedInputPortClasses.begin(), addedInputPortClasses.end(), eventPortClass->getBase());
682 eventPortClass->setDataClass(dataClass);
683 if (oldBase != addedInputPortClasses.end())
684 {
685 addedInputPortClasses.insert(oldBase,eventPortClass->getBase());
686 addedInputPortClasses.erase(oldBase);
687 }
688 }
689 }
690 else
691 addedInputPortClasses.push_back(((VuoCompilerPortClass *)(*i))->getBase());
692 }
693 vector<VuoPortClass *> addedOutputPortClasses;
694 for (vector<VuoCompilerNodeArgumentClass *>::iterator i = outputArgumentClasses.begin(); i != outputArgumentClasses.end(); ++i)
695 {
696 VuoCompilerOutputDataClass *dataClass = dynamic_cast<VuoCompilerOutputDataClass *>(*i);
697 if (dataClass)
698 {
699 VuoCompilerOutputEventPortClass *eventPortClass = outputEventPortClassForDataClassName[dataClass->getBase()->getName()];
700 if (! eventPortClass)
701 {
702 eventPortClass = new VuoCompilerOutputEventPortClass(dataClass->getBase()->getName());
703 eventPortClass->setDataClass(dataClass);
704 addedOutputPortClasses.push_back(eventPortClass->getBase());
705 }
706 else
707 {
708 // Since setDataClass() changes the base, we need to remove the old base and add the new one.
709 vector<VuoPortClass *>::iterator oldBase = find(addedOutputPortClasses.begin(), addedOutputPortClasses.end(), eventPortClass->getBase());
710 eventPortClass->setDataClass(dataClass);
711 if (oldBase != addedOutputPortClasses.end())
712 {
713 addedOutputPortClasses.insert(oldBase,eventPortClass->getBase());
714 addedOutputPortClasses.erase(oldBase);
715 }
716 }
717 }
718 else
719 addedOutputPortClasses.push_back(((VuoCompilerPortClass *)(*i))->getBase());
720 }
721
722 // Set the refresh port class if it hasn't been already (from a previous function).
723 if (! getBase()->getRefreshPortClass()->hasCompiler())
724 {
725 VuoPortClass *refreshPortClass = (new VuoCompilerInputEventPortClass("refresh"))->getBase();
726
727 // Remove the existing dummy refresh port class.
728 vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
729 inputPortClasses.erase(inputPortClasses.begin());
730 getBase()->setInputPortClasses(inputPortClasses);
731
732 // Set the new refresh port class.
733 getBase()->setRefreshPortClass(refreshPortClass);
734 addedInputPortClasses.insert(addedInputPortClasses.begin(), refreshPortClass);
735 }
736
737 // Append the port classes defined in this function to the node class's list of port classes.
738 vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
739 inputPortClasses.insert(inputPortClasses.end(), addedInputPortClasses.begin(), addedInputPortClasses.end());
740 getBase()->setInputPortClasses(inputPortClasses);
741 vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
742 outputPortClasses.insert(outputPortClasses.end(), addedOutputPortClasses.begin(), addedOutputPortClasses.end());
743 getBase()->setOutputPortClasses(outputPortClasses);
744
745 // Set the VuoType for each added port.
746 vector<VuoPortClass *> portClasses;
747 portClasses.insert(portClasses.end(), addedInputPortClasses.begin(), addedInputPortClasses.end());
748 portClasses.insert(portClasses.end(), addedOutputPortClasses.begin(), addedOutputPortClasses.end());
749 for (vector<VuoPortClass *>::iterator i = portClasses.begin(); i != portClasses.end(); ++i)
750 {
751 VuoCompilerEventPortClass *eventPortClass = dynamic_cast<VuoCompilerEventPortClass *>((*i)->getCompiler());
752 if (eventPortClass)
753 {
754 VuoCompilerDataClass *dataClass = eventPortClass->getDataClass();
755 if (dataClass)
756 {
757 string dataClassName = dataClass->getBase()->getName();
758 map<string, VuoType *>::iterator vuoTypeIter = vuoTypeForArgumentName.find(dataClassName);
759 if (vuoTypeIter != vuoTypeForArgumentName.end())
760 {
761 dataClass->setVuoType(vuoTypeIter->second);
762 vuoTypeForArgumentName.erase(vuoTypeIter);
763 }
764 }
765 }
766
767 VuoCompilerTriggerPortClass *triggerPortClass = dynamic_cast<VuoCompilerTriggerPortClass *>((*i)->getCompiler());
768 if (triggerPortClass)
769 {
770 string triggerName = triggerPortClass->getBase()->getName();
771 map<string, VuoType *>::iterator vuoTypeIter = vuoTypeForArgumentName.find(triggerName);
772 if (vuoTypeIter != vuoTypeForArgumentName.end())
773 {
774 triggerPortClass->setDataVuoType(vuoTypeIter->second);
775 vuoTypeForArgumentName.erase(vuoTypeIter);
776 }
777 }
778 }
779 for (map<string, VuoType *>::iterator i = vuoTypeForArgumentName.begin(); i != vuoTypeForArgumentName.end(); ++i)
780 delete i->second;
781
782 // Set the details for each added port.
783 for (map<string, json_object *>::iterator i = detailsForArgumentName.begin(); i != detailsForArgumentName.end(); ++i)
784 {
785 string argumentName = i->first;
786 json_object *details = i->second;
787
788 VuoCompilerPortClass *portClass = portClassForArgumentName[argumentName];
789 if (portClass)
790 portClass->setDetails(details);
791 else
792 {
793 VuoCompilerDataClass *dataClass = dataClassForArgumentName[argumentName];
794 if (dataClass)
795 dataClass->setDetails(details);
796 }
797 }
798
799 // Set the event-blocking behavior for each added input port.
800 for (vector<VuoPortClass *>::iterator i = addedInputPortClasses.begin(); i != addedInputPortClasses.end(); ++i)
801 {
802 VuoCompilerInputEventPortClass *eventPortClass = dynamic_cast<VuoCompilerInputEventPortClass *>( (*i)->getCompiler() );
803 bool isEventBlockingInDetails = false;
804 string eventBlockingStr = VuoJsonUtilities::parseString(eventPortClass->getDetails(), "eventBlocking", "", &isEventBlockingInDetails);
805 if (isEventBlockingInDetails)
806 {
807 VuoPortClass::EventBlocking eventBlocking;
808 if (eventBlockingStr == "none")
809 eventBlocking = VuoPortClass::EventBlocking_None;
810 else if (eventBlockingStr == "door")
811 eventBlocking = VuoPortClass::EventBlocking_Door;
812 else if (eventBlockingStr == "wall")
813 eventBlocking = VuoPortClass::EventBlocking_Wall;
814 else
815 {
816 VUserLog("Error: Unknown option for \"eventBlocking\": %s", eventBlockingStr.c_str());
817 continue;
818 }
819 eventPortClass->getBase()->setEventBlocking(eventBlocking);
820
821 if (eventBlocking == VuoPortClass::EventBlocking_None)
822 portsWithExplicitEventBlockingNone.insert(eventPortClass);
823 }
824 }
825
826 // Set the event-throttling behavior for each added trigger port.
827 for (vector<VuoPortClass *>::iterator i = addedOutputPortClasses.begin(); i != addedOutputPortClasses.end(); ++i)
828 {
829 VuoCompilerTriggerPortClass *triggerPortClass = dynamic_cast<VuoCompilerTriggerPortClass *>( (*i)->getCompiler() );
830 if (triggerPortClass)
831 {
832 bool isEventThrottlingInDetails = false;
833 string eventThrottlingStr = VuoJsonUtilities::parseString(triggerPortClass->getDetails(), "eventThrottling", "", &isEventThrottlingInDetails);
834 if (isEventThrottlingInDetails)
835 {
836 VuoPortClass::EventThrottling eventThrottling;
837 if (eventThrottlingStr == "enqueue")
839 else if (eventThrottlingStr == "drop")
840 eventThrottling = VuoPortClass::EventThrottling_Drop;
841 else
842 {
843 VUserLog("Error: Unknown option for \"throttling\": %s", eventThrottlingStr.c_str());
844 continue;
845 }
846 triggerPortClass->getBase()->setDefaultEventThrottling(eventThrottling);
847 }
848 }
849 }
850
851 // Update the port action status for each input port.
852 for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
853 {
854 VuoPortClass *portClass = *i;
855 VuoCompilerInputEventPortClass *eventPortClass = static_cast<VuoCompilerInputEventPortClass *>((*i)->getCompiler());
856 if (eventPortClass->getBase() != getBase()->getRefreshPortClass())
857 {
858 bool isPortActionInDetails = false;
859 bool hasPortAction = VuoJsonUtilities::parseBool(eventPortClass->getDetails(), "hasPortAction", false, &isPortActionInDetails);
860
861 if (isPortActionInDetails)
862 portClass->setPortAction(hasPortAction);
863 else if (! eventPortClass->getDataClass() ||
864 portsWithExplicitEventBlockingNone.find(eventPortClass) != portsWithExplicitEventBlockingNone.end())
865 portClass->setPortAction(true);
866 }
867 }
868
869 // Set whether each added input port's data is passed as an unlowered struct pointer.
870 for (VuoPortClass *portClass : addedInputPortClasses)
871 {
873 {
874 VuoCompilerInputEventPortClass *eventPortClass = static_cast<VuoCompilerInputEventPortClass *>(portClass->getCompiler());
875 VuoCompilerInputDataClass *dataClass = eventPortClass->getDataClass();
876
877 if (function == eventFunction)
878 {
879 Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInEventFunction() );
880 dataClass->setUnloweredStructPointerInEventFunction(firstParameterType);
881 }
882 else if (function == initFunction)
883 {
884 Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInInitFunction() );
885 dataClass->setUnloweredStructPointerInInitFunction(firstParameterType);
886 }
887 else if (function == callbackStartFunction)
888 {
889 Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInCallbackStartFunction() );
890 dataClass->setUnloweredStructPointerInCallbackStartFunction(firstParameterType);
891 }
892 else if (function == callbackUpdateFunction)
893 {
894 Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInCallbackUpdateFunction() );
895 dataClass->setUnloweredStructPointerInCallbackUpdateFunction(firstParameterType);
896 }
897 else if (function == callbackStopFunction)
898 {
899 Type *firstParameterType = function->getFunctionType()->getParamType( dataClass->getIndexInCallbackStopFunction() );
900 dataClass->setUnloweredStructPointerInCallbackStopFunction(firstParameterType);
901 }
902 }
903 }
904}
905
909VuoCompilerInputDataClass * VuoCompilerNodeClass::parseInputDataParameter(string annotation, Argument *a)
910{
911 if (annotation != "vuoInputData")
912 return NULL;
913
914 string argumentName = parser->getArgumentNameInSourceCode(a->getName());
915 return new VuoCompilerInputDataClass(argumentName);
916}
917
921VuoCompilerOutputDataClass * VuoCompilerNodeClass::parseOutputDataParameter(string annotation, Argument *a)
922{
923 if (annotation != "vuoOutputData")
924 return NULL;
925
926 string argumentName = parser->getArgumentNameInSourceCode(a->getName());
927 if (! a->getType()->isPointerTy())
928 {
929 VUserLog("Error: Output port data %s must be a pointer.", argumentName.c_str());
930 return NULL;
931 }
932
933 return new VuoCompilerOutputDataClass(argumentName);
934}
935
939VuoCompilerInputEventPortClass * VuoCompilerNodeClass::parseInputEventParameter(string annotation, Argument *a)
940{
941 if (! VuoStringUtilities::beginsWith(annotation, "vuoInputEvent"))
942 return NULL;
943
944 string argumentName = parser->getArgumentNameInSourceCode(a->getName());
945
946 return new VuoCompilerInputEventPortClass(argumentName);
947}
948
952VuoCompilerOutputEventPortClass * VuoCompilerNodeClass::parseOutputEventParameter(string annotation, Argument *a)
953{
954 if (! VuoStringUtilities::beginsWith(annotation, "vuoOutputEvent"))
955 return NULL;
956
957 string argumentName = parser->getArgumentNameInSourceCode(a->getName());
958 if (! a->getType()->isPointerTy())
959 {
960 VUserLog("Error: Output port %s must be a pointer.", argumentName.c_str());
961 return NULL;
962 }
963
964 return new VuoCompilerOutputEventPortClass(argumentName);
965}
966
970VuoCompilerTriggerPortClass * VuoCompilerNodeClass::parseTriggerParameter(string annotation, Argument *a)
971{
972 if (! VuoStringUtilities::beginsWith(annotation, "vuoOutputTrigger:"))
973 return NULL;
974
975 string argumentName = parser->getArgumentNameInSourceCode(a->getName());
976 if (! a->getType()->isPointerTy())
977 {
978 VUserLog("Error: Output trigger %s must be a pointer.", argumentName.c_str());
979 return NULL;
980 }
981
982 return new VuoCompilerTriggerPortClass(argumentName);
983}
984
988VuoCompilerInstanceDataClass * VuoCompilerNodeClass::parseInstanceDataParameter(string annotation, Argument *a)
989{
990 if (annotation != "vuoInstanceData")
991 return NULL;
992
993 string argumentName = parser->getArgumentNameInSourceCode(a->getName());
994 if (! a->getType()->isPointerTy())
995 {
996 VUserLog("Error: Node instance data %s must be a pointer.", argumentName.c_str());
997 return NULL;
998 }
999
1000 Type *instanceDataType = static_cast<PointerType *>(a->getType())->getElementType();
1001 return new VuoCompilerInstanceDataClass(argumentName, instanceDataType);
1002}
1003
1007VuoType * VuoCompilerNodeClass::parseTypeParameter(string annotation)
1008{
1009 if (! VuoStringUtilities::beginsWith(annotation, "vuoType:"))
1010 return NULL;
1011
1012 string typeName = VuoStringUtilities::substrAfter(annotation, "vuoType:");
1013
1014 VuoType *type;
1015 if (typeName == "void")
1016 {
1017 type = NULL;
1018 }
1019 else if (VuoGenericType::isGenericTypeName(typeName))
1020 {
1021 string innermostTypeName = VuoType::extractInnermostTypeName(typeName);
1022 vector<string> compatibleTypes;
1023 map<string, vector<string> >::iterator compatibleTypesIter = compatibleSpecializedForGenericTypeName.find(innermostTypeName);
1024 if (compatibleTypesIter != compatibleSpecializedForGenericTypeName.end())
1025 {
1026 string prefix = (VuoType::isListTypeName(typeName) ? VuoType::listTypeNamePrefix : "");
1027 vector<string> innermostCompatibleTypes = compatibleTypesIter->second;
1028 for (vector<string>::iterator i = innermostCompatibleTypes.begin(); i != innermostCompatibleTypes.end(); ++i)
1029 compatibleTypes.push_back(prefix + *i);
1030 }
1031
1032 type = new VuoGenericType(typeName, compatibleTypes);
1033 }
1034 else
1035 {
1036 type = new VuoType(typeName);
1037 }
1038 return type;
1039}
1040
1044json_object * VuoCompilerNodeClass::parseDetailsParameter(string annotation)
1045{
1046 if (! VuoStringUtilities::beginsWith(annotation, "vuoDetails:"))
1047 return NULL;
1048
1049 json_object *detailsObj = NULL;
1050 string details = VuoStringUtilities::substrAfter(annotation, "vuoDetails:");
1051 if (details.find_first_not_of(' ') != string::npos)
1052 {
1053 detailsObj = json_tokener_parse(details.c_str());
1054 if (! detailsObj)
1055 VUserLog("Error: Couldn't parse vuoDetails for `%s`: %s", getBase()->getClassName().c_str(), details.c_str());
1056 }
1057 return detailsObj;
1058}
1059
1064VuoPortClass * VuoCompilerNodeClass::getExistingPortClass(VuoCompilerNodeArgumentClass *argumentClass, bool isInput)
1065{
1066 string argumentName = argumentClass->getBase()->getName();
1067 VuoPortClass *existingInputPortClass = getInputPortClassWithName(argumentName);
1068 VuoPortClass *existingOutputPortClass = getOutputPortClassWithName(argumentName);
1069 if (existingInputPortClass || existingOutputPortClass)
1070 {
1071 if (! isInput && getBase()->getClassName() == VuoNodeClass::publishedInputNodeClassName)
1072 {
1073 if (! existingOutputPortClass)
1074 return NULL;
1075 }
1076 else if ((isInput && existingOutputPortClass) || (! isInput && existingInputPortClass))
1077 {
1078 VUserLog("Error: Port %s is declared as an input port in one function and an output port in another function.", argumentName.c_str());
1079 return NULL;
1080 }
1081
1082 VuoPortClass *existingPortClass = (existingInputPortClass ? existingInputPortClass : existingOutputPortClass);
1083 if (dynamic_cast<VuoCompilerDataClass *>(argumentClass))
1084 existingPortClass = dynamic_cast<VuoCompilerEventPortClass *>(existingPortClass->getCompiler())->getDataClass()->getBase();
1085
1086 return existingPortClass;
1087 }
1088 else
1089 {
1090 return NULL;
1091 }
1092}
1093
1098{
1099 return getBase()->getModuleKey() + ".vuonode";
1100}
1101
1107{
1110
1111 return moduleKey + ".vuonode";
1112}
1113
1119{
1120 string moduleKey = VuoCompiler::getModuleKeyForPath(fileName);
1121
1124
1125 return false;
1126}
1127
1139
1144{
1145 return eventFunction;
1146}
1147
1152{
1153 return initFunction;
1154}
1155
1160{
1161 return finiFunction;
1162}
1163
1168{
1169 return callbackStartFunction;
1170}
1171
1176{
1177 return callbackUpdateFunction;
1178}
1179
1184{
1185 return callbackStopFunction;
1186}
1187
1192{
1193 return parser->getFunction(nameForGlobal("compositionAddNodeMetadata"));
1194}
1195
1200{
1201 return parser->getFunction(nameForGlobal("compositionPerformDataOnlyTransmissions"));
1202}
1203
1208{
1209 return parser->getFunction(nameForGlobal("compositionSetPublishedInputPortValue"));
1210}
1211
1215Function * VuoCompilerNodeClass::getTriggerWorkerFunction(string portIdentifier)
1216{
1218}
1219
1223vector<VuoCompilerTriggerDescription *> VuoCompilerNodeClass::getTriggerDescriptions(void)
1224{
1225 return triggerDescriptions;
1226}
1227
1232{
1233 vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
1234 for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
1235 if ((*i)->getName() == portName)
1236 return *i;
1237
1238 return NULL;
1239}
1240
1245{
1246 vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
1247 for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
1248 if ((*i)->getName() == portName)
1249 return *i;
1250
1251 return NULL;
1252}
1253
1258{
1259 return instanceDataClass;
1260}
1261
1266{
1267 ostringstream documentation;
1268
1269 documentation << "/**" << endl;
1270
1271 string description = getBase()->getDescription();
1272 // Description is empty for nodes in the SDK's framework,
1273 // since the `descriptions/` folder is removed from each node set.
1274 if (!description.empty())
1275 {
1276 documentation << " * " << description << endl;
1277 documentation << " * " << endl;
1278 }
1279
1280 vector<VuoPortClass *> inputPortClasses = getBase()->getInputPortClasses();
1281 vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
1282
1283 vector< pair<string, VuoPortClass *> > portClasses;
1284 for (vector<VuoPortClass *>::iterator i = inputPortClasses.begin(); i != inputPortClasses.end(); ++i)
1285 {
1286 portClasses.push_back( make_pair("in", *i) );
1287 }
1288 for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
1289 {
1290 bool isTrigger = dynamic_cast<VuoCompilerTriggerPortClass *>((*i)->getCompiler());
1291 portClasses.push_back( make_pair(isTrigger ? "gen" : "out", *i) );
1292 }
1293
1294 for (vector< pair<string, VuoPortClass *> >::iterator i = portClasses.begin(); i != portClasses.end(); ++i)
1295 {
1296 string portKind = i->first;
1297 VuoPortClass *portClass = i->second;
1298 VuoCompilerPortClass *compilerPortClass = static_cast<VuoCompilerPortClass *>(portClass->getCompiler());
1299 VuoType *portType = compilerPortClass->getDataVuoType();
1300 string portTypeName = (portType ? portType->getModuleKey() : "event");
1301 string portClassName = portClass->getName();
1302 documentation << " * @param[" << portKind << "] " << portTypeName << " " << portClassName << endl;
1303 }
1304
1305 documentation << " */";
1306
1307 return documentation.str();
1308}
1309
1315{
1316 map<string, string>::iterator typeNameIter = defaultSpecializedForGenericTypeName.find(genericTypeName);
1317 if (typeNameIter != defaultSpecializedForGenericTypeName.end())
1318 return typeNameIter->second;
1319
1320 return "";
1321}
1322
1330{
1331 vector<string> keywords;
1332
1333 // Automatically add trigger-related keywords for nodes containing trigger ports.
1334 bool nodeHasTriggerPort = false;
1335 vector<VuoPortClass *> outputPortClasses = getBase()->getOutputPortClasses();
1336 for (vector<VuoPortClass *>::iterator i = outputPortClasses.begin(); i != outputPortClasses.end(); ++i)
1337 {
1338 if ((*i)->getPortType() == VuoPortClass::triggerPort)
1339 {
1340 nodeHasTriggerPort = true;
1341 break;
1342 }
1343 }
1344
1345 if (nodeHasTriggerPort)
1346 {
1347 keywords.push_back("bang");
1348 keywords.push_back("events");
1349 keywords.push_back("trigger");
1350 keywords.push_back("fire");
1351 }
1352
1353 bool nodeTitleBeginsWithSend = VuoStringUtilities::beginsWith(getBase()->getDefaultTitle(), "Send");
1354 bool nodeTitleBeginsWithReceive = VuoStringUtilities::beginsWith(getBase()->getDefaultTitle(), "Receive");
1355
1356 if (nodeTitleBeginsWithSend || nodeTitleBeginsWithReceive)
1357 {
1358 keywords.push_back("i/o");
1359 keywords.push_back("interface");
1360
1361 if (nodeTitleBeginsWithSend)
1362 {
1363 keywords.push_back("output");
1364 keywords.push_back("consumer");
1365 }
1366
1367 if (nodeTitleBeginsWithReceive)
1368 {
1369 keywords.push_back("input");
1370 keywords.push_back("provider");
1371 }
1372 }
1373
1374 if (VuoStringUtilities::beginsWith(getBase()->getClassName(), "vuo.type."))
1375 keywords.push_back("conversion");
1376
1377 if (isSubcomposition())
1378 keywords.push_back("subcomposition");
1379
1380 if (isLikelyImageFilter())
1381 keywords.push_back("filter");
1382
1384 keywords.push_back("generator");
1385
1387 keywords.push_back("transition");
1388
1389#if VUO_PRO
1390 if (getBase()->isPro())
1391 {
1392 keywords.push_back("premium");
1393 keywords.push_back("pro");
1394 }
1395#endif
1396
1397 return keywords;
1398}
1399
1405{
1406 return ((getImagePortCount(true) == 1) && ((getImagePortCount(false) == 1)));
1407}
1408
1414{
1415 return ((getImagePortCount(true) == 0) && ((getImagePortCount(false) == 1)));
1416}
1417
1423{
1424 return ((getImagePortCount(true) == 2) && ((getImagePortCount(false) == 1)));
1425}
1426
1435{
1436 int imagePortCount = 0;
1437
1438 auto portsToSearch = isInput ? getBase()->getInputPortClasses() : getBase()->getOutputPortClasses();
1439 for (VuoPortClass *p : portsToSearch)
1440 {
1441 string name = p->getName();
1442 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
1443 if (isInput && (name == "mask"))
1444 continue;
1445
1446 VuoCompilerPortClass *cpc = dynamic_cast<VuoCompilerPortClass *>(p->getCompiler());
1447 if (!cpc)
1448 continue;
1449 VuoType *dataType = cpc->getDataVuoType();
1450 if (!dataType)
1451 continue;
1452 if (dataType->getModuleKey() == "VuoImage")
1453 imagePortCount++;
1454 }
1455
1456 return imagePortCount;
1457}
1458
1463{
1464 return (instanceDataClass != NULL);
1465}
1466
1471{
1472 return _isSubcomposition;
1473}
1474
1475void VuoCompilerNodeClass::updateSubcompositionStatus()
1476{
1477 _isSubcomposition = parser && (getCompositionAddNodeMetadataFunction() != NULL);
1478}
1479
1484{
1485 string dir, file, ext;
1486 VuoFileUtilities::splitPath(getSourcePath(), dir, file, ext);
1487 return ext == "fs";
1488}
1489
1493set< pair<string, string> > VuoCompilerNodeClass::getContainedNodes(void)
1494{
1495 return containedNodes;
1496}