13 #include <graphviz/gvc.h>
50 dispatch_queue_t VuoCompilerGraphvizParser::graphvizQueue = dispatch_queue_create(
"org.vuo.compiler.graphviz", NULL);
87 partialParser.parse(composition);
92 map<string, VuoCompilerNodeClass *> compilerNodeClasses;
93 map<string, VuoCompilerType *> compilerTypes;
94 set<string> compilerProNodeClassNames;
96 auto addPortTypes = [&](
const vector<VuoPortClass *> &portClasses)
106 for (
auto nodeClass : partialParser.dummyNodeClassForName)
109 compilerNodeClasses[nodeClass.first] = compilerNodeClass;
111 if (compilerNodeClass)
118 for (
auto type : partialParser.typeForPublishedInputPort)
119 if (type.second !=
"event")
120 compilerTypes[type.second] = compiler->
getType(type.second);
122 for (
auto type : partialParser.typeForPublishedOutputPort)
123 if (type.second !=
"event")
124 compilerTypes[type.second] = compiler->
getType(type.second);
127 for (
auto nodeClass : partialParser.dummyNodeClassForName)
128 if (compiler->isProModule(nodeClass.first))
129 compilerProNodeClassNames.insert(nodeClass.first);
135 fullParser->parse(composition);
146 set<string> nodeClassNames;
153 parser.parse(composition);
155 for (
auto i : parser.dummyNodeClassForName)
156 nodeClassNames.insert(i.first);
170 return nodeClassNames;
190 VuoCompilerGraphvizParser::VuoCompilerGraphvizParser(
void) :
201 VuoCompilerGraphvizParser::VuoCompilerGraphvizParser(
VuoCompiler *compiler,
202 const map<string, VuoCompilerNodeClass *> &compilerNodeClasses,
203 const map<string, VuoCompilerType *> &compilerTypes,
204 const set<string> &compilerProNodeClassNames) :
206 compilerNodeClasses(compilerNodeClasses),
207 compilerTypes(compilerTypes),
208 compilerProNodeClassNames(compilerProNodeClassNames)
216 void VuoCompilerGraphvizParser::init(
void)
218 publishedInputNode =
nullptr;
219 publishedOutputNode =
nullptr;
220 manuallyFirableInputNode =
nullptr;
221 manuallyFirableInputPort =
nullptr;
232 void VuoCompilerGraphvizParser::parse(
const string &compositionAsStringOrig)
234 if (compositionAsStringOrig.empty())
239 string compositionAsString = std::regex_replace(compositionAsStringOrig, std::regex(
"(_\\w+_manuallyFirable)(\\s*[^=])"),
"$1=\"yes\"$2");
242 dispatch_sync(graphvizQueue, ^{
247 bool demandLoading =
false;
250 graph = agmemread((
char *)compositionAsString.c_str());
257 gvFreeContext(context);
260 agattr(graph, AGRAPH, (
char *)
"rankdir", (
char *)
"LR");
261 agattr(graph, AGRAPH, (
char *)
"ranksep", (
char *)
"0.75");
262 agattr(graph, AGNODE, (
char *)
"fontsize", (
char *)
"18");
263 agattr(graph, AGNODE, (
char *)
"shape", (
char *)
"Mrecord");
264 if (gvLayout(context, graph,
"dot"))
270 gvFreeContext(context);
276 makeDummyNodeClasses();
277 parsePublishedPortTypes();
282 gvFreeLayout(context, graph);
284 gvFreeContext(context);
294 makePublishedPorts();
295 setInputPortConstantValues();
296 setPublishedPortDetails();
297 setTriggerPortEventThrottling();
298 setManuallyFirableInputPort();
299 saveNodeDeclarations(compositionAsString);
303 gvFreeLayout(context, graph);
305 gvFreeContext(context);
317 void VuoCompilerGraphvizParser::makeDummyNodeClasses(
void)
319 map<string, bool> nodeClassNamesSeen;
320 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
322 char *nodeClassNameCstr = agget(n, (
char *)
"type");
323 if (! nodeClassNameCstr)
326 "Vuo couldn't parse the composition",
"A node lacks a 'type' attribute indicating the node class name.");
330 string nodeClassName = nodeClassNameCstr;
335 if (nodeClassNamesSeen[nodeClassName])
346 vector<string> inputPortClassNames;
347 vector<string> outputPortClassNames;
349 field_t *nodeInfo = (field_t *)ND_shape_info(n);
350 int numNodeInfoFields = nodeInfo->n_flds;
351 for (
int i = 0; i < numNodeInfoFields; i++)
353 field_t *nodeInfoField = nodeInfo->fld[i];
356 if (! nodeInfoField->id)
360 char * lr = strchr(nodeInfoField->lp->text,
'\\');
367 if (strcmp(nodeInfoField->id,
"refresh") == 0)
370 if (find(inputPortClassNames.begin(), inputPortClassNames.end(), nodeInfoField->id) == inputPortClassNames.end())
371 inputPortClassNames.push_back(nodeInfoField->id);
375 if (find(outputPortClassNames.begin(), outputPortClassNames.end(), nodeInfoField->id) == outputPortClassNames.end())
376 outputPortClassNames.push_back(nodeInfoField->id);
382 dummyNodeClassForName[nodeClassName] = nodeClass;
389 void VuoCompilerGraphvizParser::makeNodeClasses(
void)
391 for (map<string, VuoNodeClass *>::iterator i = dummyNodeClassForName.begin(), e = dummyNodeClassForName.end(); i != e; ++i)
393 string dummyNodeClassName = i->first;
400 nodeClassForName[dummyNodeClassName] = nodeClass->
getBase();
407 nodeClassForName[dummyNodeClassName] = dummyNodeClass;
410 if (compilerProNodeClassNames.find(dummyNodeClassName) != compilerProNodeClassNames.end())
411 dummyNodeClass->setPro(
true);
420 void VuoCompilerGraphvizParser::makeNodes(
void)
422 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
424 string nodeClassName = agget(n, (
char *)
"type");
429 char * pos = agget(n, (
char *)
"pos");
430 if (!(pos && sscanf(pos,
"%20lf,%20lf",&x,&y) == 2))
435 y = GD_bb(graph).UR.y - ND_coord(n).y;
438 string nodeName(agnameof(n));
441 field_t *nodeInfo = (field_t *)ND_shape_info(n);
442 int numNodeInfoFields = nodeInfo->n_flds;
443 for (
int i = 0; i < numNodeInfoFields; i++)
445 field_t *nodeInfoField = nodeInfo->fld[i];
446 if (! nodeInfoField->id)
450 VuoNodeClass *nodeClass = nodeClassForName[nodeClassName];
452 if (nodeForName[nodeName])
454 VUserLog(
"Error: More than one node with name '%s'.", nodeName.c_str());
463 node = nodeClass->
newNode(nodeTitle, x, y);
472 char * nodeTintColor = agget(n, (
char *)
"fillcolor");
476 char *nodeCollapsed = agget(n, (
char *)
"collapsed");
477 if (nodeCollapsed && strcmp(nodeCollapsed,
"true") == 0)
480 nodeForName[nodeName] = node;
485 publishedInputNode = node;
487 publishedOutputNode = node;
489 orderedNodes.push_back(node);
497 void VuoCompilerGraphvizParser::makeCables(
void)
499 map<string, bool> nodeNamesSeen;
500 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
502 for (Agedge_t *e = agfstedge(graph, n); e; e = agnxtedge(graph, e, n))
504 string fromNodeName(agnameof(agtail(e)));
505 string toNodeName(agnameof(aghead(e)));
506 string fromPortName(ED_tail_port(e).name);
507 string toPortName(ED_head_port(e).name);
509 if (nodeNamesSeen[fromNodeName] || nodeNamesSeen[toNodeName])
512 VuoNode *fromNode = nodeForName[fromNodeName];
513 VuoNode *toNode = nodeForName[toNodeName];
517 if (! toPort || ! fromPort)
537 if (! fromCompilerNode && fromNode != publishedInputNode)
539 if (! toCompilerNode && toNode != publishedOutputNode)
542 if (fromNode == publishedInputNode || toNode == publishedOutputNode)
544 publishedCablesInProgress[orderedCables.size()] = make_pair(cable, make_pair(fromPortName, toPortName));
545 orderedCables.push_back(NULL);
549 orderedCables.push_back(cable->
getBase());
552 char *eventOnlyAttribute = agget(e, (
char *)
"event");
553 if (eventOnlyAttribute && strcmp(eventOnlyAttribute,
"true") == 0)
556 char *hiddenAttribute = agget(e, (
char *)
"style");
557 if (hiddenAttribute && strcmp(hiddenAttribute,
"invis") == 0)
561 nodeNamesSeen[agnameof(n)] =
true;
568 void VuoCompilerGraphvizParser::makeComments(
void)
570 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
572 string typeName = agget(n, (
char *)
"type");
577 char * pos = agget(n, (
char *)
"pos");
578 if (!(pos && sscanf(pos,
"%20lf,%20lf",&x,&y) == 2))
583 y = GD_bb(graph).UR.y - ND_coord(n).y;
586 double widthVal, heightVal;
587 char *width = agget(n, (
char *)
"width");
588 if (!(width && sscanf(width,
"%20lf",&widthVal) == 1))
591 char *height = agget(n, (
char *)
"height");
592 if (!(height && sscanf(height,
"%20lf",&heightVal) == 1))
595 string commentName(agnameof(n));
597 string commentContent;
598 field_t *commentInfo = (field_t *)ND_shape_info(n);
599 int numCommentInfoFields = commentInfo->n_flds;
600 for (
int i = 0; i < numCommentInfoFields; i++)
602 field_t *commentInfoField = commentInfo->fld[i];
603 if (! commentInfoField->id)
607 if (commentForName[commentName])
609 VUserLog(
"Error: More than one comment with name '%s'.", commentName.c_str());
614 new VuoComment(commentContent, x, y, widthVal, heightVal) :
615 new VuoComment(commentContent, x, y))))->getBase();
616 char * commentTintColor = agget(n, (
char *)
"fillcolor");
617 if (commentTintColor)
620 commentForName[commentName] = comment;
625 orderedComments.push_back(comment);
632 void VuoCompilerGraphvizParser::parsePublishedPortTypes(
void)
634 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
636 string nodeClassName = agget(n, (
char *)
"type");
641 VuoNodeClass *nodeClass = dummyNodeClassForName[nodeClassName];
642 vector<VuoPortClass *> publishedPorts = isPublishedInputNode ?
648 string portName = port->getName();
650 parseAttributeOfPort(n, portName,
"type", typeName);
652 if (! typeName.empty())
654 if (isPublishedInputNode)
655 typeForPublishedInputPort[portName] = typeName;
657 typeForPublishedOutputPort[portName] = typeName;
668 void VuoCompilerGraphvizParser::makePublishedPorts(
void)
670 map<string, set<VuoCompilerPort *> > connectedPortsForPublishedInputPort;
671 map<string, set<VuoCompilerPort *> > connectedPortsForPublishedOutputPort;
672 for (map<
size_t, pair<
VuoCompilerCable *, pair<string, string> > >::iterator i = publishedCablesInProgress.begin(); i != publishedCablesInProgress.end(); ++i)
675 string fromPortName = i->second.second.first;
676 string toPortName = i->second.second.second;
681 connectedPortsForPublishedInputPort[fromPortName].insert(connectedPort);
686 connectedPortsForPublishedOutputPort[toPortName].insert(connectedPort);
691 auto inferPublishedPortTypes = [&](
VuoNode *publishedNode)
696 bool isPublishedInputNode = (publishedNode == publishedInputNode);
698 vector<VuoPortClass *> publishedPorts = isPublishedInputNode ?
699 publishedNode->getNodeClass()->getOutputPortClasses() :
700 publishedNode->getNodeClass()->getInputPortClasses();
704 string portName = port->getName();
705 string typeName = isPublishedInputNode ?
706 typeForPublishedInputPort[portName] :
707 typeForPublishedOutputPort[portName];
709 if (typeName.empty())
711 set<VuoCompilerPort *> connectedPorts = isPublishedInputNode ?
712 connectedPortsForPublishedInputPort[portName] :
713 connectedPortsForPublishedOutputPort[portName];
714 VuoType *type = inferTypeForPublishedPort(portName, connectedPorts);
719 if (isPublishedInputNode)
720 typeForPublishedInputPort[portName] = typeName;
722 typeForPublishedOutputPort[portName] = typeName;
727 inferPublishedPortTypes(publishedInputNode);
728 inferPublishedPortTypes(publishedOutputNode);
730 map<string, VuoPort *> publishedInputPortForName;
731 map<string, VuoPort *> publishedOutputPortForName;
732 for (
int i = 0; i < 2; ++i)
734 if ((i == 0 && ! publishedInputNode) || (i == 1 && ! publishedOutputNode))
737 vector<VuoPort *> basePorts;
750 for (
int j = startIndex; j < basePorts.size(); ++j)
752 string portName = basePorts[j]->getClass()->getName();
753 string typeName = (i == 0 ? typeForPublishedInputPort[portName] : typeForPublishedOutputPort[portName]);
754 VuoCompilerType *type = (typeName.empty() || typeName ==
"event" ? nullptr : compilerTypes[typeName]);
763 publishedInputPortForName[portName] = publishedPort->
getBase();
768 publishedOutputPortForName[portName] = publishedPort->
getBase();
774 for (map<
size_t, pair<
VuoCompilerCable *, pair<string, string> > >::iterator i = publishedCablesInProgress.begin(); i != publishedCablesInProgress.end(); ++i)
776 size_t index = i->first;
778 string fromPortName = i->second.second.first;
779 string toPortName = i->second.second.second;
782 cable->
getBase()->
setFrom(publishedInputNode, publishedInputPortForName[fromPortName]);
784 cable->
getBase()->
setTo(publishedOutputNode, publishedOutputPortForName[toPortName]);
786 orderedCables[index] = cable->
getBase();
793 void VuoCompilerGraphvizParser::setInputPortConstantValues(
void)
796 map<string, string> constantForPublishedInputPort;
797 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
799 string nodeClassName = agget(n, (
char *)
"type");
803 if (nodeForName[agnameof(n)] == publishedInputNode)
804 constantForPublishedInputPort = parsePortConstantValues(n);
808 for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
812 map<string, string>::iterator constantIter = constantForPublishedInputPort.find( publishedInputPort->
getClass()->
getName() );
813 if (constantIter != constantForPublishedInputPort.end())
818 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
820 string nodeClassName = agget(n, (
char *)
"type");
824 VuoNode *node = nodeForName[agnameof(n)];
826 map<string, string> constantForInputPort = parsePortConstantValues(n);
829 for (vector<VuoPort *>::iterator i = inputPorts.begin(); i != inputPorts.end(); ++i)
833 VuoPort *publishedInputPort = NULL;
835 for (vector<VuoCable *>::iterator j = connectedCables.begin(); j != connectedCables.end(); ++j)
847 bool hasConstant =
false;
850 if (publishedInputPort)
853 constant = constantForPublishedInputPort[ publishedInputPort->
getClass()->
getName() ];
859 map<string, string>::iterator constantIter = constantForInputPort.find(inputPort->
getClass()->
getName());
860 hasConstant = (constantIter != constantForInputPort.end());
862 constant = constantIter->second;
884 void VuoCompilerGraphvizParser::setPublishedPortDetails(
void)
886 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
888 string nodeClassName = agget(n, (
char *)
"type");
892 vector<string> detailKeys;
893 detailKeys.push_back(
"suggestedMin");
894 detailKeys.push_back(
"suggestedMax");
895 detailKeys.push_back(
"suggestedStep");
897 for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
901 for (vector<string>::iterator j = detailKeys.begin(); j != detailKeys.end(); ++j)
903 string detailKey = *j;
905 bool foundAttribute = parseAttributeOfPort(n, publishedPort->
getClass()->
getName(), detailKey, detailValue);
909 portClass->
setDetail(detailKey, detailValue);
920 void VuoCompilerGraphvizParser::setTriggerPortEventThrottling(
void)
922 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
924 string nodeClassName = agget(n, (
char *)
"type");
928 VuoNode *node = nodeForName[agnameof(n)];
931 for (vector<VuoPort *>::iterator i = outputPorts.begin(); i != outputPorts.end(); ++i)
936 string eventThrottlingStr;
937 parseAttributeOfPort(n, port->
getClass()->
getName(),
"eventThrottling", eventThrottlingStr);
939 if (eventThrottlingStr ==
"drop")
954 void VuoCompilerGraphvizParser::setManuallyFirableInputPort(
void)
956 for (
Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
958 string nodeClassName = agget(n, (
char *)
"type");
962 VuoNode *node = nodeForName[agnameof(n)];
967 bool hasAttribute = parseAttributeOfPort(n, port->
getClass()->
getName(),
"manuallyFirable", unused);
970 manuallyFirableInputNode = node;
971 manuallyFirableInputPort = port;
983 map<string, string> VuoCompilerGraphvizParser::parsePortConstantValues(
Agnode_t *n)
985 map<string, string> constantForInputPort;
987 field_t *nodeInfo = (field_t *)ND_shape_info(n);
988 int numNodeInfoFields = nodeInfo->n_flds;
989 for (
int i = 0; i < numNodeInfoFields; i++)
991 field_t *nodeInfoField = nodeInfo->fld[i];
992 char *inputPortName = nodeInfoField->id;
996 string constantValue;
997 if (parseAttributeOfPort(n, inputPortName,
"", constantValue))
998 constantForInputPort[inputPortName] = constantValue;
1001 return constantForInputPort;
1009 bool VuoCompilerGraphvizParser::parseAttributeOfPort(
Agnode_t *n,
string portName,
string suffix,
string &attributeValue)
1012 oss <<
"_" << portName;
1013 if (! suffix.empty())
1014 oss <<
"_" << suffix;
1015 char *attributeName = strdup(oss.str().c_str());
1017 char *rawAttributeValue = agget(n, attributeName);
1018 free(attributeName);
1025 if (rawAttributeValue && strcmp(rawAttributeValue,
""))
1037 void VuoCompilerGraphvizParser::checkPortClasses(
string nodeClassName, vector<VuoPortClass *> dummy, vector<VuoPortClass *> actual)
1039 for (vector<VuoPortClass *>::iterator i = dummy.begin(); i != dummy.end(); ++i)
1041 string dummyName = (*i)->getName();
1043 if (dummyName ==
"refresh")
1047 for (vector<VuoPortClass *>::iterator j = actual.begin(); j != actual.end(); ++j)
1049 if ((*j)->getName() == dummyName)
1057 VUserLog(
"Error: Couldn't find node %s's port '%s'.", nodeClassName.c_str(), dummyName.c_str());
1066 void VuoCompilerGraphvizParser::saveNodeDeclarations(
const string &compositionAsString)
1068 size_t nodesRemaining = nodeForName.size();
1071 for (vector<string>::iterator i = lines.begin(); i != lines.end() && nodesRemaining > 0; ++i)
1076 identifier += line[j];
1078 map<string, VuoNode *>::iterator nodeIter = nodeForName.find(identifier);
1079 if (nodeIter != nodeForName.end())
1081 VuoNode *node = nodeIter->second;
1094 return orderedNodes;
1103 return orderedCables;
1111 return orderedComments;
1120 return publishedInputPorts;
1128 return publishedOutputPorts;
1136 return manuallyFirableInputNode;
1144 return manuallyFirableInputPort;
1159 VuoType * VuoCompilerGraphvizParser::inferTypeForPublishedPort(
string name,
const set<VuoCompilerPort *> &connectedPorts)
1161 if (connectedPorts.empty() || name ==
"refresh")