12 #include <graphviz/gvc.h>
49 dispatch_queue_t VuoCompilerGraphvizParser::graphvizQueue = dispatch_queue_create(
"org.vuo.compiler.graphviz", NULL);
86 for (set<string>::iterator i = nodeClassNames.begin(); i != nodeClassNames.end(); ++i)
99 set<string> nodeClassNames;
118 return nodeClassNames;
130 set<string> nodeClassNames;
131 for (map<string, VuoNodeClass *>::iterator i = parser.dummyNodeClassForName.begin(); i != parser.dummyNodeClassForName.end(); ++i)
132 nodeClassNames.insert(i->first);
134 return nodeClassNames;
158 VuoCompilerGraphvizParser::VuoCompilerGraphvizParser(
const string &compositionAsString,
VuoCompiler *compiler,
bool nodeClassNamesOnly)
160 if (compositionAsString.empty())
163 this->compiler = compiler;
164 publishedInputNode =
nullptr;
165 publishedOutputNode =
nullptr;
166 manuallyFirableInputNode =
nullptr;
167 manuallyFirableInputPort =
nullptr;
171 dispatch_sync(graphvizQueue, ^{
176 bool demandLoading =
false;
179 graph = agmemread((
char *)compositionAsString.c_str());
187 agraphattr(graph, (
char *)
"rankdir", (
char *)
"LR");
188 agraphattr(graph, (
char *)
"ranksep", (
char *)
"0.75");
189 agnodeattr(graph, (
char *)
"fontsize", (
char *)
"18");
190 agnodeattr(graph, (
char *)
"shape", (
char *)
"Mrecord");
191 gvLayout(context, graph,
"dot");
195 makeDummyNodeClasses();
203 if (! nodeClassNamesOnly)
209 makePublishedPorts();
210 setInputPortConstantValues();
211 setPublishedPortDetails();
212 setTriggerPortEventThrottling();
213 setManuallyFirableInputPort();
214 saveNodeDeclarations(compositionAsString);
218 gvFreeLayout(context, graph);
220 gvFreeContext(context);
232 void VuoCompilerGraphvizParser::makeDummyNodeClasses(
void)
234 map<string, bool> nodeClassNamesSeen;
235 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
237 char *nodeClassNameCstr = agget(n, (
char *)
"type");
238 if (! nodeClassNameCstr)
241 "Vuo couldn't parse the composition",
"A node lacks a 'type' attribute indicating the node class name.");
245 string nodeClassName = nodeClassNameCstr;
250 if (nodeClassNamesSeen[nodeClassName])
261 vector<string> inputPortClassNames;
262 vector<string> outputPortClassNames;
264 field_t *nodeInfo = (field_t *)ND_shape_info(n);
265 int numNodeInfoFields = nodeInfo->n_flds;
266 for (
int i = 0; i < numNodeInfoFields; i++)
268 field_t *nodeInfoField = nodeInfo->fld[i];
271 if (! nodeInfoField->id)
275 char * lr = strchr(nodeInfoField->lp->text,
'\\');
282 if (strcmp(nodeInfoField->id,
"refresh") == 0)
285 if (find(inputPortClassNames.begin(), inputPortClassNames.end(), nodeInfoField->id) == inputPortClassNames.end())
286 inputPortClassNames.push_back(nodeInfoField->id);
290 if (find(outputPortClassNames.begin(), outputPortClassNames.end(), nodeInfoField->id) == outputPortClassNames.end())
291 outputPortClassNames.push_back(nodeInfoField->id);
297 dummyNodeClassForName[nodeClassName] = nodeClass;
304 void VuoCompilerGraphvizParser::makeNodeClasses(
void)
306 for (map<string, VuoNodeClass *>::iterator i = dummyNodeClassForName.begin(), e = dummyNodeClassForName.end(); i != e; ++i)
308 string dummyNodeClassName = i->first;
317 nodeClassForName[dummyNodeClassName] = nodeClass->
getBase();
324 nodeClassForName[dummyNodeClassName] = dummyNodeClass;
327 dummyNodeClass->setPro(compiler->isProModule(dummyNodeClassName));
336 void VuoCompilerGraphvizParser::makeNodes(
void)
338 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
340 string nodeClassName = agget(n, (
char *)
"type");
345 char * pos = agget(n, (
char *)
"pos");
346 if (!(pos && sscanf(pos,
"%20lf,%20lf",&x,&y) == 2))
351 y = GD_bb(graph).UR.y - ND_coord(n).y;
354 string nodeName = n->name;
357 field_t *nodeInfo = (field_t *)ND_shape_info(n);
358 int numNodeInfoFields = nodeInfo->n_flds;
359 for (
int i = 0; i < numNodeInfoFields; i++)
361 field_t *nodeInfoField = nodeInfo->fld[i];
362 if (! nodeInfoField->id)
366 VuoNodeClass *nodeClass = nodeClassForName[nodeClassName];
368 if (nodeForName[nodeName])
370 VUserLog(
"Error: More than one node with name '%s'.", nodeName.c_str());
379 node = nodeClass->
newNode(nodeTitle, x, y);
388 char * nodeTintColor = agget(n, (
char *)
"fillcolor");
392 char *nodeCollapsed = agget(n, (
char *)
"collapsed");
393 if (nodeCollapsed && strcmp(nodeCollapsed,
"true") == 0)
396 nodeForName[nodeName] = node;
401 publishedInputNode = node;
403 publishedOutputNode = node;
405 orderedNodes.push_back(node);
413 void VuoCompilerGraphvizParser::makeCables(
void)
415 map<string, bool> nodeNamesSeen;
416 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
418 for (Agedge_t *e = agfstedge(graph, n); e; e = agnxtedge(graph, e, n))
420 string fromNodeName = e->tail->name;
421 string toNodeName = e->head->name;
422 string fromPortName = e->u.tail_port.name;
423 string toPortName = e->u.head_port.name;
425 if (nodeNamesSeen[fromNodeName] || nodeNamesSeen[toNodeName])
428 VuoNode *fromNode = nodeForName[fromNodeName];
429 VuoNode *toNode = nodeForName[toNodeName];
433 if (! toPort || ! fromPort)
441 fromCompilerPort = static_cast<VuoCompilerPort *>(fromPort->
getCompiler());
449 toCompilerPort = static_cast<VuoCompilerPort *>(toPort->
getCompiler());
453 if (! fromCompilerNode && fromNode != publishedInputNode)
455 if (! toCompilerNode && toNode != publishedOutputNode)
458 if (fromNode == publishedInputNode || toNode == publishedOutputNode)
460 publishedCablesInProgress[orderedCables.size()] = make_pair(cable, make_pair(fromPortName, toPortName));
461 orderedCables.push_back(NULL);
465 orderedCables.push_back(cable->
getBase());
468 char *eventOnlyAttribute = agget(e, (
char *)
"event");
469 if (eventOnlyAttribute && strcmp(eventOnlyAttribute,
"true") == 0)
472 char *hiddenAttribute = agget(e, (
char *)
"style");
473 if (hiddenAttribute && strcmp(hiddenAttribute,
"invis") == 0)
477 nodeNamesSeen[n->name] =
true;
484 void VuoCompilerGraphvizParser::makeComments(
void)
486 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
488 string typeName = agget(n, (
char *)
"type");
493 char * pos = agget(n, (
char *)
"pos");
494 if (!(pos && sscanf(pos,
"%20lf,%20lf",&x,&y) == 2))
499 y = GD_bb(graph).UR.y - ND_coord(n).y;
502 double widthVal, heightVal;
503 char *width = agget(n, (
char *)
"width");
504 if (!(width && sscanf(width,
"%20lf",&widthVal) == 1))
507 char *height = agget(n, (
char *)
"height");
508 if (!(height && sscanf(height,
"%20lf",&heightVal) == 1))
511 string commentName = n->name;
513 string commentContent;
514 field_t *commentInfo = (field_t *)ND_shape_info(n);
515 int numCommentInfoFields = commentInfo->n_flds;
516 for (
int i = 0; i < numCommentInfoFields; i++)
518 field_t *commentInfoField = commentInfo->fld[i];
519 if (! commentInfoField->id)
523 if (commentForName[commentName])
525 VUserLog(
"Error: More than one comment with name '%s'.", commentName.c_str());
530 new VuoComment(commentContent, x, y, widthVal, heightVal) :
531 new VuoComment(commentContent, x, y))))->getBase();
532 char * commentTintColor = agget(n, (
char *)
"fillcolor");
533 if (commentTintColor)
536 commentForName[commentName] = comment;
541 orderedComments.push_back(comment);
551 void VuoCompilerGraphvizParser::makePublishedPorts(
void)
553 map<string, set<VuoCompilerPort *> > connectedPortsForPublishedInputPort;
554 map<string, set<VuoCompilerPort *> > connectedPortsForPublishedOutputPort;
555 for (map<
size_t, pair<
VuoCompilerCable *, pair<string, string> > >::iterator i = publishedCablesInProgress.begin(); i != publishedCablesInProgress.end(); ++i)
558 string fromPortName = i->second.second.first;
559 string toPortName = i->second.second.second;
564 connectedPortsForPublishedInputPort[fromPortName].insert(connectedPort);
569 connectedPortsForPublishedOutputPort[toPortName].insert(connectedPort);
574 map<string, VuoType *> typeForPublishedInputPort;
575 map<string, VuoType *> typeForPublishedOutputPort;
576 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
578 string nodeClassName = agget(n, (
char *)
"type");
582 string nodeName = n->name;
583 VuoNode *node = nodeForName[nodeName];
584 if (node != publishedInputNode && node != publishedOutputNode)
588 for (vector<VuoPort *>::iterator i = publishedPorts.begin(); i != publishedPorts.end(); ++i)
590 string portName = (*i)->getClass()->getName();
592 string portTypeStr =
"";
593 parseAttributeOfPort(n, portName,
"type", portTypeStr);
594 if (portTypeStr.empty())
596 set<VuoCompilerPort *> connectedPorts = (node == publishedInputNode ?
597 connectedPortsForPublishedInputPort[portName] :
598 connectedPortsForPublishedOutputPort[portName]);
599 portType = inferTypeForPublishedPort(portName, connectedPorts);
601 else if (portTypeStr !=
"event")
604 if (portCompilerType)
605 portType = portCompilerType->
getBase();
608 if (node == publishedInputNode)
609 typeForPublishedInputPort[portName] = portType;
611 typeForPublishedOutputPort[portName] = portType;
615 map<string, VuoPort *> publishedInputPortForName;
616 map<string, VuoPort *> publishedOutputPortForName;
617 for (
int i = 0; i < 2; ++i)
619 if ((i == 0 && ! publishedInputNode) || (i == 1 && ! publishedOutputNode))
622 vector<VuoPort *> basePorts;
635 for (
int j = startIndex; j < basePorts.size(); ++j)
637 string portName = basePorts[j]->getClass()->getName();
638 VuoType *vuoType = (i == 0 ? typeForPublishedInputPort[portName] : typeForPublishedOutputPort[portName]);
646 publishedInputPortForName[portName] = publishedPort->
getBase();
647 publishedInputPorts.push_back( static_cast<VuoPublishedPort *>(publishedPort->
getBase()) );
651 publishedOutputPortForName[portName] = publishedPort->
getBase();
652 publishedOutputPorts.push_back( static_cast<VuoPublishedPort *>(publishedPort->
getBase()) );
657 for (map<
size_t, pair<
VuoCompilerCable *, pair<string, string> > >::iterator i = publishedCablesInProgress.begin(); i != publishedCablesInProgress.end(); ++i)
659 size_t index = i->first;
661 string fromPortName = i->second.second.first;
662 string toPortName = i->second.second.second;
665 cable->
getBase()->
setFrom(publishedInputNode, publishedInputPortForName[fromPortName]);
667 cable->
getBase()->
setTo(publishedOutputNode, publishedOutputPortForName[toPortName]);
669 orderedCables[index] = cable->
getBase();
676 void VuoCompilerGraphvizParser::setInputPortConstantValues(
void)
679 map<string, string> constantForPublishedInputPort;
680 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
682 string nodeClassName = agget(n, (
char *)
"type");
686 if (nodeForName[n->name] == publishedInputNode)
687 constantForPublishedInputPort = parsePortConstantValues(n);
691 for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
695 map<string, string>::iterator constantIter = constantForPublishedInputPort.find( publishedInputPort->
getClass()->
getName() );
696 if (constantIter != constantForPublishedInputPort.end())
697 static_cast<VuoCompilerPublishedPort *>( publishedInputPort->
getCompiler() )->setInitialValue( constantIter->second );
701 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
703 string nodeClassName = agget(n, (
char *)
"type");
707 VuoNode *node = nodeForName[n->name];
709 map<string, string> constantForInputPort = parsePortConstantValues(n);
712 for (vector<VuoPort *>::iterator i = inputPorts.begin(); i != inputPorts.end(); ++i)
716 VuoPort *publishedInputPort = NULL;
718 for (vector<VuoCable *>::iterator j = connectedCables.begin(); j != connectedCables.end(); ++j)
730 bool hasConstant =
false;
733 if (publishedInputPort)
736 constant = constantForPublishedInputPort[ publishedInputPort->
getClass()->
getName() ];
742 map<string, string>::iterator constantIter = constantForInputPort.find(inputPort->
getClass()->
getName());
743 hasConstant = (constantIter != constantForInputPort.end());
745 constant = constantIter->second;
767 void VuoCompilerGraphvizParser::setPublishedPortDetails(
void)
769 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
771 string nodeClassName = agget(n, (
char *)
"type");
775 vector<string> detailKeys;
776 detailKeys.push_back(
"suggestedMin");
777 detailKeys.push_back(
"suggestedMax");
778 detailKeys.push_back(
"suggestedStep");
780 for (vector<VuoPublishedPort *>::iterator i = publishedInputPorts.begin(); i != publishedInputPorts.end(); ++i)
784 for (vector<string>::iterator j = detailKeys.begin(); j != detailKeys.end(); ++j)
786 string detailKey = *j;
788 bool foundAttribute = parseAttributeOfPort(n, publishedPort->
getClass()->
getName(), detailKey, detailValue);
792 portClass->
setDetail(detailKey, detailValue);
803 void VuoCompilerGraphvizParser::setTriggerPortEventThrottling(
void)
805 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
807 string nodeClassName = agget(n, (
char *)
"type");
811 VuoNode *node = nodeForName[n->name];
814 for (vector<VuoPort *>::iterator i = outputPorts.begin(); i != outputPorts.end(); ++i)
819 string eventThrottlingStr;
820 parseAttributeOfPort(n, port->
getClass()->
getName(),
"eventThrottling", eventThrottlingStr);
822 if (eventThrottlingStr ==
"drop")
837 void VuoCompilerGraphvizParser::setManuallyFirableInputPort(
void)
839 for (Agnode_t *n = agfstnode(graph); n; n = agnxtnode(graph, n))
841 string nodeClassName = agget(n, (
char *)
"type");
845 VuoNode *node = nodeForName[n->name];
850 bool hasAttribute = parseAttributeOfPort(n, port->
getClass()->
getName(),
"manuallyFirable", unused);
853 manuallyFirableInputNode = node;
854 manuallyFirableInputPort = port;
866 map<string, string> VuoCompilerGraphvizParser::parsePortConstantValues(Agnode_t *n)
868 map<string, string> constantForInputPort;
870 field_t *nodeInfo = (field_t *)ND_shape_info(n);
871 int numNodeInfoFields = nodeInfo->n_flds;
872 for (
int i = 0; i < numNodeInfoFields; i++)
874 field_t *nodeInfoField = nodeInfo->fld[i];
875 char *inputPortName = nodeInfoField->id;
879 string constantValue;
880 if (parseAttributeOfPort(n, inputPortName,
"", constantValue))
881 constantForInputPort[inputPortName] = constantValue;
884 return constantForInputPort;
892 bool VuoCompilerGraphvizParser::parseAttributeOfPort(Agnode_t *n,
string portName,
string suffix,
string &attributeValue)
895 oss <<
"_" << portName;
896 if (! suffix.empty())
897 oss <<
"_" << suffix;
898 char *attributeName = strdup(oss.str().c_str());
900 char *rawAttributeValue = agget(n, attributeName);
908 if (rawAttributeValue && strcmp(rawAttributeValue,
""))
920 void VuoCompilerGraphvizParser::checkPortClasses(
string nodeClassName, vector<VuoPortClass *> dummy, vector<VuoPortClass *> actual)
922 for (vector<VuoPortClass *>::iterator i = dummy.begin(); i != dummy.end(); ++i)
924 string dummyName = (*i)->getName();
926 if (dummyName ==
"refresh")
930 for (vector<VuoPortClass *>::iterator j = actual.begin(); j != actual.end(); ++j)
932 if ((*j)->getName() == dummyName)
940 VUserLog(
"Error: Couldn't find node %s's port '%s'.", nodeClassName.c_str(), dummyName.c_str());
949 void VuoCompilerGraphvizParser::saveNodeDeclarations(
const string &compositionAsString)
951 size_t nodesRemaining = nodeForName.size();
954 for (vector<string>::iterator i = lines.begin(); i != lines.end() && nodesRemaining > 0; ++i)
959 identifier += line[j];
961 map<string, VuoNode *>::iterator nodeIter = nodeForName.find(identifier);
962 if (nodeIter != nodeForName.end())
964 VuoNode *node = nodeIter->second;
986 return orderedCables;
994 return orderedComments;
1003 return publishedInputPorts;
1011 return publishedOutputPorts;
1019 return manuallyFirableInputNode;
1027 return manuallyFirableInputPort;
1042 VuoType * VuoCompilerGraphvizParser::inferTypeForPublishedPort(
string name,
const set<VuoCompilerPort *> &connectedPorts)
1044 if (connectedPorts.empty() || name ==
"refresh")