Vuo  2.3.2
VuoRendererComposition.cc
Go to the documentation of this file.
1 
10 #include "VuoFileType.h"
11 
13 
14 #include "VuoCompiler.hh"
15 #include "VuoCompilerCable.hh"
17 #include "VuoCompilerDriver.hh"
18 #include "VuoCompilerException.hh"
22 #include "VuoCompilerIssue.hh"
24 #include "VuoCompilerType.hh"
25 #include "VuoComment.hh"
26 #include "VuoComposition.hh"
28 #include "VuoNodeClass.hh"
29 #include "VuoRendererComment.hh"
33 #include "VuoRendererSignaler.hh"
35 #include "VuoRendererFonts.hh"
37 
38 #include "VuoStringUtilities.hh"
39 
40 #include "VuoHeap.h"
41 
42 #include "VuoTextHtml.h"
43 #include "VuoUrl.h"
44 
45 #ifdef __APPLE__
46 #include <objc/message.h>
47 #endif
48 
49 int VuoRendererComposition::gridOpacity = 0;
50 VuoRendererComposition::GridType VuoRendererComposition::gridType = VuoRendererComposition::NoGrid;
51 const int VuoRendererComposition::minorGridLineSpacing = 15; // VuoRendererPort::portSpacing
53 const string VuoRendererComposition::deprecatedDefaultDescription = "This composition does...";
54 
65 VuoRendererComposition::VuoRendererComposition(VuoComposition *baseComposition, bool renderMissingAsPresent, bool enableCaching)
66  : VuoBaseDetail<VuoComposition>("VuoRendererComposition", baseComposition)
67 {
68  getBase()->setRenderer(this);
69 
70  VuoRendererFonts::getSharedFonts(); // Load the fonts now to avoid a delay later when rendering the first item in the composition.
71 
73  this->renderMissingAsPresent = renderMissingAsPresent;
74  this->renderNodeActivity = false;
75  this->renderPortActivity = false;
76  this->renderHiddenCables = false;
77  this->cachingEnabled = enableCaching;
78  this->publishedInputNode = createPublishedInputNode();
79  this->publishedOutputNode = createPublishedOutputNode();
80 
81  parser = NULL;
82 
84 
86 }
87 
92 {
93  set<VuoNode *> nodes = getBase()->getNodes();
94  foreach (VuoNode *node, nodes)
95  addNodeInCompositionToCanvas(node);
96 
97  vector<VuoPublishedPort *> publishedInputPorts = getBase()->getPublishedInputPorts();
98  foreach (VuoPublishedPort *publishedPort, publishedInputPorts)
100 
101  vector<VuoPublishedPort *> publishedOutputPorts = getBase()->getPublishedOutputPorts();
102  foreach (VuoPublishedPort *publishedPort, publishedOutputPorts)
103  createRendererForPublishedPortInComposition(publishedPort, false);
104 
105  set<VuoCable *> cables = getBase()->getCables();
106  foreach (VuoCable *cable, cables)
107  {
108  addCableInCompositionToCanvas(cable);
109 
110  if (cable->isPublishedInputCable())
111  cable->setFrom(publishedInputNode, cable->getFromPort());
112  if (cable->isPublishedOutputCable())
113  cable->setTo(publishedOutputNode, cable->getToPort());
114  }
115 
116  foreach (VuoComment *comment, getBase()->getComments())
117  addCommentInCompositionToCanvas(comment);
118 
120 
121  // Now that all renderer components have been created, calculate
122  // the final positions of collapsed "Make List" drawers.
123  foreach (VuoNode *node, nodes)
125 }
126 
131 {
132  if (transparent)
133  setBackgroundBrush(Qt::transparent);
134  else
135  setBackgroundBrush(VuoRendererColors::getSharedColors()->canvasFill());
136 }
137 
142 {
143  VuoRendererNode *rn = NULL;
144 
146  rn = new VuoRendererReadOnlyDictionary(baseNode, signaler);
147 
150 
153 
155  rn = new VuoRendererInputListDrawer(baseNode, signaler);
156 
157  else
158  rn = new VuoRendererNode(baseNode, signaler);
159 
160  return rn;
161 }
162 
173 void VuoRendererComposition::addNode(VuoNode *n, bool nodeShouldBeRendered, bool nodeShouldBeGivenUniqueIdentifier)
174 {
175  if (getBase()->hasCompiler() && nodeShouldBeGivenUniqueIdentifier)
177 
178  getBase()->addNode(n);
179 
180  if (nodeShouldBeRendered)
181  addNodeInCompositionToCanvas(n);
182 }
183 
192 void VuoRendererComposition::addNodeInCompositionToCanvas(VuoNode *n)
193 {
195 
196  if (renderMissingAsPresent)
197  rn->setMissingImplementation(false);
198 
200  addItem(rn);
201 
203 }
204 
211 {
212  getBase()->addCable(c);
213  addCableInCompositionToCanvas(c);
214 }
215 
221 void VuoRendererComposition::addCableInCompositionToCanvas(VuoCable *c)
222 {
223  VuoRendererCable *rc = (c->hasRenderer() ? c->getRenderer() : new VuoRendererCable(c));
224  addItem(rc);
225 
226  // The following VuoRendererCable::setFrom()/setTo() calls are unnecessary as far as the
227  // base cable is concerned, but forces the connected component renderings to update appropriately.
228  rc->setFrom(rc->getBase()->getFromNode(), rc->getBase()->getFromPort());
229  rc->setTo(rc->getBase()->getToNode(), rc->getBase()->getToPort());
230 
231  // Performance optimizations
232  rc->setCacheMode(getCurrentDefaultCacheMode());
233 }
234 
239 {
240  return new VuoRendererComment(baseComment, signaler);
241 }
242 
249 {
250  if (getBase()->hasCompiler())
252 
253  getBase()->addComment(c);
254  addCommentInCompositionToCanvas(c);
255 }
256 
262 void VuoRendererComposition::addCommentInCompositionToCanvas(VuoComment *c)
263 {
265  addItem(rc);
266 
267  // Performance optimizations
268  rc->setCacheMode(getCurrentDefaultCacheMode());
269 }
270 
275 {
276  rn->updateGeometry();
277  removeItem(rn);
278 
279  getBase()->removeNode(rn->getBase());
280 }
281 
286 {
287  rc->setFrom(NULL, NULL);
288  rc->setTo(NULL, NULL);
289 
290  rc->updateGeometry();
291  rc->removeFromScene();
292 
293  getBase()->removeCable(rc->getBase());
294 }
295 
300 {
301  rc->updateGeometry();
302  removeItem(rc);
303 
304  getBase()->removeComment(rc->getBase());
305 }
306 
310 QList<QGraphicsItem *> VuoRendererComposition::createAndConnectInputAttachments(VuoRendererNode *node, VuoCompiler *compiler, bool createButDoNotAdd)
311 {
312  QList<QGraphicsItem *> componentsAttached;
313  if (VuoStringUtilities::beginsWith(node->getBase()->getNodeClass()->getClassName(), "vuo.math.calculate"))
314  {
315  VuoPort *valuesPort = node->getBase()->getInputPortWithName("values");
316  set<VuoRendererInputAttachment *> attachments = valuesPort->getRenderer()->getAllUnderlyingUpstreamInputAttachments();
317  if (attachments.empty())
318  componentsAttached.append(createAndConnectDrawersToReadOnlyDictionaryInputPorts(node, compiler, createButDoNotAdd));
319  }
320 
321  componentsAttached.append(createAndConnectDrawersToListInputPorts(node, compiler, createButDoNotAdd));
322 
323  return componentsAttached;
324 }
325 
329 QList<QGraphicsItem *> VuoRendererComposition::createAndConnectDrawersToListInputPorts(VuoRendererNode *node, VuoCompiler *compiler, bool createButDoNotAdd)
330 {
331  QList<QGraphicsItem *> componentsAttached;
332  foreach (VuoPort *port, node->getBase()->getInputPorts())
333  {
334  VuoCompilerInputEventPort *inputEventPort = (port->hasCompiler()? dynamic_cast<VuoCompilerInputEventPort *>(port->getCompiler()) : NULL);
335  if (inputEventPort && VuoCompilerType::isListType(inputEventPort->getDataType()))
336  {
338  {
339  VuoRendererCable *cable = NULL;
340  VuoRendererNode *makeListNode = createAndConnectMakeListNode(node->getBase(), port, compiler, cable);
341 
342  if (cable)
343  componentsAttached.append(cable);
344 
345  if (makeListNode)
346  componentsAttached.append(makeListNode);
347 
348  if (!createButDoNotAdd)
349  {
350  addNode(makeListNode->getBase());
351  addCable(cable->getBase());
352  }
353  }
354  }
355  }
356 
357  return componentsAttached;
358 }
359 
363 QList<QGraphicsItem *> VuoRendererComposition::createAndConnectDrawersToReadOnlyDictionaryInputPorts(VuoRendererNode *node, VuoCompiler *compiler, bool createButDoNotAdd)
364 {
365  set<VuoRendererNode *> nodesToAdd;
366  set<VuoRendererCable *> cablesToAdd;
367  createAndConnectDictionaryAttachmentsForNode(node->getBase(), compiler, nodesToAdd, cablesToAdd);
368 
369  QList<QGraphicsItem *> componentsAttached;
370  foreach (VuoRendererNode *node, nodesToAdd)
371  componentsAttached.append(node);
372  foreach (VuoRendererCable *cable, cablesToAdd)
373  componentsAttached.append(cable);
374 
375  if (!createButDoNotAdd)
376  {
377  foreach (VuoRendererNode *node, nodesToAdd)
378  addNode(node->getBase());
379 
380  foreach (VuoRendererCable *cable, cablesToAdd)
381  addCable(cable->getBase());
382  }
383 
384  return componentsAttached;
385 }
386 
397  VuoRendererCable *&rendererCable)
398 {
399  VuoRendererNode *makeListRendererNode = NULL;
400  rendererCable = NULL;
401 
402  VuoCompilerInputEventPort *inputEventPort = static_cast<VuoCompilerInputEventPort *>(toPort->getCompiler());
403  VuoCompilerType *type = inputEventPort->getDataType();
404 
405  vector<string> itemInitialValues;
406  if (inputEventPort->getData())
407  {
408  string listInitialValue = inputEventPort->getData()->getInitialValue();
409  json_object *js = json_tokener_parse(listInitialValue.c_str());
410  if (json_object_get_type(js) == json_type_array)
411  {
412  int itemCount = json_object_array_length(js);
413  for (int i = 0; i < itemCount; ++i)
414  {
415  json_object *itemObject = json_object_array_get_idx(js, i);
416  string itemString = json_object_to_json_string_ext(itemObject, JSON_C_TO_STRING_PLAIN);
417  itemInitialValues.push_back(itemString);
418  }
419  }
420  json_object_put(js);
421  }
422 
423  unsigned long itemCount = (itemInitialValues.empty() ? 2 : itemInitialValues.size());
424  string nodeClassName = VuoCompilerMakeListNodeClass::getNodeClassName(itemCount, type);
425  VuoCompilerNodeClass *makeListNodeClass = compiler->getNodeClass(nodeClassName);
426 
427  VuoNode *makeListNode = makeListNodeClass->newNode();
428  makeListRendererNode = createRendererNode(makeListNode);
429 
430  vector<VuoPort *> itemPorts = makeListNode->getInputPorts();
431  for (size_t i = 0; i < itemInitialValues.size(); ++i)
432  {
434  VuoRendererPort *itemPort = itemPorts[portIndex]->getRenderer();
435  itemPort->setConstant(itemInitialValues[i]);
436  }
437 
438  VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(makeListNode->getOutputPorts().back()->getCompiler());
439  VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(toPort->getCompiler());
440  VuoCompilerCable *compilerCable = new VuoCompilerCable(makeListNode->getCompiler(), fromCompilerPort,
441  toNode->getCompiler(), toCompilerPort);
442  rendererCable = new VuoRendererCable(compilerCable->getBase());
443 
444  return makeListRendererNode;
445 }
446 
457  VuoCompiler *compiler,
458  set<VuoRendererNode *> &createdNodes,
459  set<VuoRendererCable *> &createdCables)
460 {
461  createdNodes.clear();
462  createdCables.clear();
463 
464  VuoPort *expressionInputPort = node->getInputPortWithName("expression");
465  VuoPort *valuesInputPort = node->getInputPortWithName("values");
466  if (!(expressionInputPort && valuesInputPort))
467  {
468  VUserLog("Error: Cannot create dictionary attachments for a node without 'expression' and 'values' input ports.");
469  return;
470  }
471 
472  // Assume for now that the dictionary should map strings to reals for use as variables in a VuoMathExpressionList.
473  const string dictionaryTypeName = "VuoDictionary_VuoText_VuoReal";
474  const string dictionaryClassName = "vuo.dictionary.make.VuoText.VuoReal";
475  const string dictionaryKeySourceTypeName = "VuoMathExpressionList";
476 
477  VuoCompilerPort *valuesInputPortCompiler = static_cast<VuoCompilerPort *>(valuesInputPort->getCompiler());
478  if (valuesInputPortCompiler->getDataVuoType()->getModuleKey() != dictionaryTypeName)
479  {
480  VUserLog("Error: Unexpected dictionary type required: %s", valuesInputPortCompiler->getDataVuoType()->getModuleKey().c_str());
481  return;
482  }
483 
484  VuoCompilerPort *expressionInputPortCompiler = static_cast<VuoCompilerPort *>(expressionInputPort->getCompiler());
485  if (expressionInputPortCompiler->getDataVuoType()->getModuleKey() != dictionaryKeySourceTypeName)
486  {
487  VUserLog("Error: Unexpected key source type encountered: %s", expressionInputPortCompiler->getDataVuoType()->getModuleKey().c_str());
488  return;
489  }
490 
491  // Extract the variable names from the math expressions.
492  VuoCompilerInputEventPort *expressionInputEventPort = static_cast<VuoCompilerInputEventPort *>(expressionInputPortCompiler);
493  string expressionConstant = expressionInputEventPort->getData()->getInitialValue();
494  vector<string> inputVariables = extractInputVariableListFromExpressionsConstant(expressionConstant, node->getNodeClass()->getClassName());
495  unsigned long itemCount = inputVariables.size();
496 
497  string keyListClassName = VuoCompilerMakeListNodeClass::buildNodeClassName(itemCount, "VuoText");
498  string valueListClassName = VuoCompilerMakeListNodeClass::buildNodeClassName(itemCount, "VuoReal");
499 
500  VuoCompilerNodeClass *keyListNodeClass = compiler->getNodeClass(keyListClassName);
501  VuoCompilerNodeClass *valueListNodeClass = compiler->getNodeClass(valueListClassName);
502  VuoCompilerNodeClass *dictionaryNodeClass = compiler->getNodeClass(dictionaryClassName);
503 
504  if (keyListNodeClass && valueListNodeClass && dictionaryNodeClass)
505  {
506  // Create and connect all base components before creating any renderer components so that createRendererNode()
507  // has all of the information it needs to create the appropriate renderer form for each node.
508  QPoint offset(-220, 50);
509  VuoNode *dictionaryNode = compiler->createNode(dictionaryNodeClass, "", node->getX()+offset.x(), node->getY()+offset.y());
510  VuoNode *keyListNode = compiler->createNode(keyListNodeClass, "", node->getX()+offset.x(), node->getY()+offset.y());
511  VuoNode *valueListNode = compiler->createNode(valueListNodeClass, "", node->getX()+offset.x(), node->getY()+offset.y());
512 
513  VuoCable *cableCarryingDictionary = (new VuoCompilerCable(dictionaryNode->getCompiler(),
514  static_cast<VuoCompilerPort *>(dictionaryNode->getOutputPortWithName("dictionary")->getCompiler()),
515  node->getCompiler(),
516  static_cast<VuoCompilerPort *>(valuesInputPort->getCompiler())))->getBase();
517 
518  VuoCable *cableCarryingKeys = (new VuoCompilerCable(keyListNode->getCompiler(),
519  static_cast<VuoCompilerPort *>(keyListNode->getOutputPortWithName("list")->getCompiler()),
520  dictionaryNode->getCompiler(),
521  static_cast<VuoCompilerPort *>(dictionaryNode->getInputPortWithName("keys")->getCompiler())))->getBase();
522 
523  VuoCable *cableCarryingValues = (new VuoCompilerCable(valueListNode->getCompiler(),
524  static_cast<VuoCompilerPort *>(valueListNode->getOutputPortWithName("list")->getCompiler()),
525  dictionaryNode->getCompiler(),
526  static_cast<VuoCompilerPort *>(dictionaryNode->getInputPortWithName("values")->getCompiler())))->getBase();
527 
528  // Set the variable names extracted from the math expressions.
529  vector<VuoPort *> keyPorts = keyListNode->getInputPorts();
530  for (size_t i = 0; i < itemCount; ++i)
531  {
532  VuoPort *keyPort = keyPorts[i + VuoNodeClass::unreservedInputPortStartIndex];
533  string key = "\"" + inputVariables[i] + "\"";
534 
535  VuoCompilerInputEventPort *keyEventPort = static_cast<VuoCompilerInputEventPort *>(keyPort->getCompiler());
536  keyEventPort->getData()->setInitialValue(key);
537  }
538 
539  createdNodes.insert(createRendererNode(dictionaryNode));
540  createdNodes.insert(createRendererNode(keyListNode));
541  createdNodes.insert(createRendererNode(valueListNode));
542 
543  createdCables.insert(new VuoRendererCable(cableCarryingDictionary));
544  createdCables.insert(new VuoRendererCable(cableCarryingKeys));
545  createdCables.insert(new VuoRendererCable(cableCarryingValues));
546  }
547 }
548 
555 vector<string> VuoRendererComposition::extractInputVariableListFromExpressionsConstant(string constant, string nodeClassName)
556 {
557  vector<string> inputVariables;
558 
559  json_object *js = json_tokener_parse(constant.c_str());
560  json_object *expressionsObject = NULL;
561 
562  if (json_object_object_get_ex(js, "inputVariables", &expressionsObject))
563  {
564  if (json_object_get_type(expressionsObject) == json_type_array)
565  {
566  int variableCount = json_object_array_length(expressionsObject);
567  for (int i = 0; i < variableCount; ++i)
568  {
569  json_object *itemObject = json_object_array_get_idx(expressionsObject, i);
570  if (json_object_get_type(itemObject) == json_type_string)
571  {
572  const char *variableName = json_object_get_string(itemObject);
573  if (nodeClassName == "vuo.math.calculate.list"
574  && (strcasecmp(variableName, "x") == 0
575  || strcasecmp(variableName, "i") == 0))
576  continue;
577 
578  inputVariables.push_back(json_object_get_string(itemObject));
579  }
580  }
581  }
582  }
583  json_object_put(js);
584 
585  return inputVariables;
586 }
587 
593 {
594  if (! publishedPort->hasCompiler())
595  return NULL;
596 
597  return new VuoRendererPublishedPort(publishedPort, !isPublishedInput);
598 }
599 
603 void VuoRendererComposition::addPublishedPort(VuoPublishedPort *publishedPort, bool isPublishedInput, VuoCompiler *compiler)
604 {
605  string name = publishedPort->getClass()->getName();
606  if (isPublishedInput)
607  {
608  VuoPublishedPort *existingPort = getBase()->getPublishedInputPortWithName(name);
609  if (! existingPort)
610  {
611  int index = getBase()->getPublishedInputPorts().size();
612  getBase()->addPublishedInputPort(publishedPort, index);
613  }
614  else if (publishedPort != existingPort)
615  VUserLog("Error: Unhandled published port name conflict.");
616  }
617  else // if (! isPublishedInput)
618  {
620  if (! existingPort)
621  {
622  int index = getBase()->getPublishedOutputPorts().size();
623  getBase()->addPublishedOutputPort(publishedPort, index);
624  }
625  else if (publishedPort != existingPort)
626  VUserLog("Error: Unhandled published port name conflict.");
627  }
628 }
629 
636 int VuoRendererComposition::removePublishedPort(VuoPublishedPort *publishedPort, bool isPublishedInput, VuoCompiler *compiler)
637 {
638  if (isPublishedInput)
639  {
640  int index = getBase()->getIndexOfPublishedPort(publishedPort, isPublishedInput);
641  if (index != -1)
643 
644  return index;
645  }
646  else
647  {
648  int index = getBase()->getIndexOfPublishedPort(publishedPort, isPublishedInput);
649  if (index != -1)
651 
652  return index;
653  }
654 }
655 
661 {
662  publishedPort->setName(getUniquePublishedPortName(name));
663 }
664 
670 VuoNode * VuoRendererComposition::createPublishedInputNode()
671 {
672  VuoNodeClass *dummyPublishedInputNodeClass = new VuoNodeClass(VuoNodeClass::publishedInputNodeClassName, vector<string>(), vector<string>());
673  return dummyPublishedInputNodeClass->newNode(VuoNodeClass::publishedInputNodeIdentifier);
674 }
675 
681 VuoNode * VuoRendererComposition::createPublishedOutputNode()
682 {
683  VuoNodeClass *dummyPublishedOutputNodeClass = new VuoNodeClass(VuoNodeClass::publishedOutputNodeClassName, vector<string>(), vector<string>());
684  return dummyPublishedOutputNodeClass->newNode(VuoNodeClass::publishedOutputNodeIdentifier);
685 }
686 
692 {
693  auto isNameAvailable = [this] (const string &name)
694  {
695  return (! name.empty() &&
696  name != "refresh" &&
697  getBase()->getPublishedInputPortWithName(name) == nullptr &&
698  getBase()->getPublishedOutputPortWithName(name) == nullptr);
699  };
700 
701  return VuoStringUtilities::formUniqueIdentifier(isNameAvailable, baseName);
702 }
703 
708 {
709  return (!port->getPublishedPorts().empty());
710 }
711 
716 vector<VuoRendererNode *> VuoRendererComposition::collapseTypecastNodes(void)
717 {
718  vector<VuoRendererNode *> typecastsCollapsed;
719 
720  foreach (VuoNode *node, getBase()->getNodes())
721  {
722  if (node->isTypecastNode())
723  {
724  VuoRendererTypecastPort *collapsedPort = collapseTypecastNode(node->getRenderer());
725  if (collapsedPort)
726  typecastsCollapsed.push_back(node->getRenderer());
727  }
728  }
729 
730  return typecastsCollapsed;
731 }
732 
738 {
739  // Don't try to collapse nodes that don't qualify as typecasts.
740  if (!rn || !rn->getBase()->isTypecastNode())
741  return NULL;
742 
745  return NULL;
746 
749 
750  // Don't try to re-collapse typecasts that are already collapsed.
751  VuoRendererPort *typecastParent = typecastInPort->getRenderer()->getTypecastParentPort();
752  if (typecastParent)
753  return NULL;
754 
755  vector<VuoCable *> outCables = typecastOutPort->getConnectedCables(true);
756  vector<VuoCable *> inCables = typecastInPort->getConnectedCables(true);
757 
758  // Don't try to collapse typecast nodes with incoming cables to the "refresh" port.
759  if ( ! rn->getBase()->getRefreshPort()->getConnectedCables(true).empty() )
760  return NULL;
761 
762  // Don't try to collapse typecast nodes outputting to multiple nodes, or without any output cables.
763  if (outCables.size() != 1)
764  return NULL;
765 
766  VuoCable *outCable = *(outCables.begin());
767 
768  // Don't try to collapse typecast nodes whose outgoing cable is event-only.
769  if (!(outCable->hasRenderer() && outCable->getRenderer()->effectivelyCarriesData()))
770  return NULL;
771 
772  // Don't try to collapse typecast nodes without any incoming data+event cables (including published ones).
773  VuoCable *incomingDataCable = NULL;
774  for (vector<VuoCable *>::iterator i = inCables.begin(); !incomingDataCable && (i != inCables.end()); ++i)
775  {
776  if ((*i)->hasRenderer() && (*i)->getRenderer()->effectivelyCarriesData())
777  incomingDataCable = *i;
778  }
779  if (! incomingDataCable)
780  return NULL;
781 
782  // Don't try to collapse typecast nodes that have published output ports.
783  if (outCable->isPublished())
784  return NULL;
785 
786  VuoNode *fromNode = incomingDataCable->getFromNode();
787  VuoPort *fromPort = incomingDataCable->getFromPort();
788 
789  VuoNode * toNode = outCable->getToNode();
790  VuoPort * toPort = outCable->getToPort();
791 
792  // Don't try to collapse typecast nodes with input or output cables that are not currently connected at both ends.
793  if (! (fromNode && fromPort && toNode && toPort))
794  return NULL;
795 
796  // Don't try to collapse typecast nodes with attached input drawers.
797  if (rn->getAttachedInputDrawers().size() > 0)
798  return NULL;
799 
800  // Don't try to collapse typecast nodes outputting to other typecasts.
801  if (toNode->isTypecastNode())
802  return NULL;
803 
804  // Don't try to collapse typecast nodes outputting to ports with multiple input cables.
805  if (toPort->getConnectedCables(false).size() > 1)
806  return NULL;
807 
808  // Don't try to collapse typecast nodes outputting to internal ports that have been published.
809  if (isPortPublished(toPort->getRenderer()))
810  return NULL;
811 
812  // Hide the typecast node.
813  VuoRendererNode * toRN = toNode->getRenderer();
814  rn->updateGeometry();
815  rn->setProxyNode(toRN);
816 
817  // Replace the target node's input port with a new typecast port.
818  VuoRendererPort * oldToRP = toPort->getRenderer();
820  oldToRP,
821  signaler);
822 
823  QGraphicsItem::CacheMode defaultCacheMode = getCurrentDefaultCacheMode();
824  foreach (VuoCable *cable, inCables)
825  {
826  if (cable->hasRenderer())
827  {
828  cable->getRenderer()->setCacheMode(QGraphicsItem::NoCache);
829  cable->getRenderer()->updateGeometry();
830  cable->getRenderer()->setCacheMode(defaultCacheMode);
831  }
832  }
833 
834  foreach (VuoCable *cable, outCables)
835  {
836  if (cable->hasRenderer())
837  {
838  cable->getRenderer()->setCacheMode(QGraphicsItem::NoCache);
839  cable->getRenderer()->updateGeometry();
840  cable->getRenderer()->setCacheMode(defaultCacheMode);
841  }
842  }
843 
844  tp->updateGeometry();
845  toRN->replaceInputPort(oldToRP, tp);
847  typecastInPort->getRenderer()->setTypecastParentPort(tp);
848  typecastInPort->getRenderer()->setParentItem(toRN);
849 
850  // Notify the base port of the change in renderer port, to reverse
851  // the change made within the VuoRendererPort constructor on behalf of any
852  // renderer port previously initialized for this base port.
853  tp->getBase()->setRenderer(tp);
854 
855  // The typecast port may have been added to a drawer,
856  // and may thus need to change the horizontal position of its name.
857  tp->updateNameRect();
858 
860 
861  return tp;
862 }
863 
868 {
869  foreach (VuoNode *node, getBase()->getNodes())
870  {
871  if (node->isTypecastNode())
872  {
875  }
876  }
877 }
878 
884 {
885  VuoPort * typecastInPort = typecastNode->getBase()->getInputPorts()[VuoNodeClass::unreservedInputPortStartIndex];
886  VuoRendererPort *typecastParent = typecastInPort->getRenderer()->getTypecastParentPort();
887 
888  if (typecastParent)
890 }
891 
896 {
897  VuoRendererNode *uncollapsedNode = typecast->getUncollapsedTypecastNode();
898  VuoPort *typecastInPort = uncollapsedNode->getBase()->getInputPorts()[VuoNodeClass::unreservedInputPortStartIndex];
899  VuoPort *typecastOutPort = uncollapsedNode->getBase()->getOutputPorts()[VuoNodeClass::unreservedOutputPortStartIndex];
900  vector<VuoCable *> outCables = typecastOutPort->getConnectedCables(false);
901  VuoCable * outCable = *(outCables.begin());
902  VuoRendererPort *uncollapsedToRP = typecast->getReplacedPort();
903  VuoRendererNode *toRN = outCable->getToNode()->getRenderer();
904 
905  QGraphicsItem::CacheMode defaultCacheMode = getCurrentDefaultCacheMode();
906  foreach (VuoCable *cable, typecastInPort->getConnectedCables(true))
907  {
908  if (cable->hasRenderer())
909  {
910  cable->getRenderer()->setCacheMode(QGraphicsItem::NoCache);
911  cable->getRenderer()->updateGeometry();
912  cable->getRenderer()->setCacheMode(defaultCacheMode);
913  }
914  }
915 
916  foreach (VuoCable *cable, typecastOutPort->getConnectedCables(true))
917  {
918  if (cable->hasRenderer())
919  {
920  cable->getRenderer()->setCacheMode(QGraphicsItem::NoCache);
921  cable->getRenderer()->updateGeometry();
922  cable->getRenderer()->setCacheMode(defaultCacheMode);
923  }
924  }
925 
926  typecast->updateGeometry();
927  uncollapsedNode->updateGeometry();
928 
929  typecastInPort->getRenderer()->setParentItem(uncollapsedNode);
930  uncollapsedNode->addInputPort(typecastInPort->getRenderer());
931 
932  typecastInPort->getRenderer()->setTypecastParentPort(NULL);
933  uncollapsedNode->setProxyNode(NULL);
934 
935  toRN->updateGeometry();
936  toRN->replaceInputPort(typecast, uncollapsedToRP);
938 
939  // Notify the base port of the change in renderer port, to reverse
940  // the change made within the VuoRendererPort constructor on behalf of any
941  // renderer port previously initialized for this base port.
942  uncollapsedToRP->getBase()->setRenderer(uncollapsedToRP);
943 
944  toRN->layoutConnectedInputDrawersAtAndAbovePort(uncollapsedToRP);
945 
946  return uncollapsedNode;
947 }
948 
953 {
954  return this->publishedInputNode;
955 }
956 
961 {
962  return this->publishedOutputNode;
963 }
964 
969 {
970  foreach (VuoNode *node, getBase()->getNodes())
971  {
972  vector<VuoPort *> inputPorts = node->getInputPorts();
973  for(vector<VuoPort *>::iterator inputPort = inputPorts.begin(); inputPort != inputPorts.end(); ++inputPort)
974  {
975  QGraphicsItem::CacheMode normalCacheMode = (*inputPort)->getRenderer()->cacheMode();
976  (*inputPort)->getRenderer()->setCacheMode(QGraphicsItem::NoCache);
977 
978  (*inputPort)->getRenderer()->updateGeometry();
979  (*inputPort)->getRenderer()->setEligibilityHighlight(VuoRendererColors::noHighlight);
980 
981  (*inputPort)->getRenderer()->setCacheMode(normalCacheMode);
982 
983  VuoRendererTypecastPort *typecastPort = dynamic_cast<VuoRendererTypecastPort *>((*inputPort)->getRenderer());
984  if (typecastPort)
985  {
986  QGraphicsItem::CacheMode normalCacheMode = typecastPort->getChildPort()->cacheMode();
987  typecastPort->getChildPort()->setCacheMode(QGraphicsItem::NoCache);
988 
989  typecastPort->getChildPort()->updateGeometry();
991 
992  typecastPort->getChildPort()->setCacheMode(normalCacheMode);
993  }
994  }
995 
996  vector<VuoPort *> outputPorts = node->getOutputPorts();
997  for(vector<VuoPort *>::iterator outputPort = outputPorts.begin(); outputPort != outputPorts.end(); ++outputPort)
998  {
999  QGraphicsItem::CacheMode normalCacheMode = (*outputPort)->getRenderer()->cacheMode();
1000  (*outputPort)->getRenderer()->setCacheMode(QGraphicsItem::NoCache);
1001 
1002  (*outputPort)->getRenderer()->updateGeometry();
1003  (*outputPort)->getRenderer()->setEligibilityHighlight(VuoRendererColors::noHighlight);
1004 
1005  (*outputPort)->getRenderer()->setCacheMode(normalCacheMode);
1006  }
1007 
1008 
1009  VuoRendererNode *rn = node->getRenderer();
1010  QGraphicsItem::CacheMode normalCacheMode = rn->cacheMode();
1011  rn->setCacheMode(QGraphicsItem::NoCache);
1012 
1013  rn->updateGeometry();
1015 
1016  rn->setCacheMode(normalCacheMode);
1017  }
1018 
1019  foreach (VuoCable *cable, getBase()->getCables())
1020  {
1021  VuoRendererCable *rc = cable->getRenderer();
1022  QGraphicsItem::CacheMode normalCacheMode = rc->cacheMode();
1023  rc->setCacheMode(QGraphicsItem::NoCache);
1024  rc->updateGeometry();
1025 
1027 
1028  rc->setCacheMode(normalCacheMode);
1029  }
1030 }
1031 
1036 {
1037  foreach (VuoNode *node, getBase()->getNodes())
1038  {
1039  node->getRenderer()->updateGeometry();
1040 
1041  foreach (VuoPort *port, node->getInputPorts())
1042  port->getRenderer()->updateGeometry();
1043 
1044  foreach (VuoPort *port, node->getOutputPorts())
1045  port->getRenderer()->updateGeometry();
1046  }
1047 
1048  foreach (VuoCable *cable, getBase()->getCables())
1049  cable->getRenderer()->updateGeometry();
1050 }
1051 
1058 {
1059  return this->renderNodeActivity;
1060 }
1061 
1068 {
1069  return (this->renderNodeActivity && this->renderPortActivity);
1070 }
1071 
1078 void VuoRendererComposition::drawBackground(QPainter *painter, const QRectF &rect)
1079 {
1080  QGraphicsScene::drawBackground(painter, rect);
1081 
1083  {
1084  painter->setRenderHint(QPainter::Antialiasing, true);
1085  painter->setPen(QPen(QColor(255,0,0,128),5));
1086  painter->drawRect(sceneRect());
1087  QVector<QPointF> points;
1088  points << QPointF(0,0);
1089  points << sceneRect().topLeft();
1090  points << sceneRect().center();
1091  points << sceneRect().bottomRight();
1092  foreach (QPointF p, points)
1093  {
1094  painter->setPen(QPen(QColor(255,0,0,128),5));
1095  painter->drawEllipse(p, 5, 5);
1096  painter->setPen(QColor(255,0,0,128));
1097  painter->drawText(p + QPointF(5,15) - (p == sceneRect().bottomRight() ? QPointF(70,20) : QPointF(0,0)), QString("(%1,%2)").arg(p.x()).arg(p.y()));
1098  }
1099  painter->setRenderHint(QPainter::Antialiasing, false);
1100  }
1101 
1102  // Draw grid.
1103  if (gridType != NoGrid)
1104  {
1106 
1107  qreal leftmostGridLine = quantizeToNearestGridLine(rect.topLeft(), gridSpacing).x();
1108  if (leftmostGridLine < rect.left())
1109  leftmostGridLine += gridSpacing;
1110  qreal topmostGridLine = quantizeToNearestGridLine(rect.topLeft(), gridSpacing).y();
1111  if (topmostGridLine < rect.top())
1112  topmostGridLine += gridSpacing;
1113 
1114  // Correct for the fact that VuoRendererNode::paint() starts painting at (-1,0) rather than (0,0).
1115  // @todo: Eliminate this correction after modifying VuoRendererNode::paint()
1116  // for https://b33p.net/kosada/node/10210 .
1117  const int nodeXAlignmentCorrection = -1;
1118 
1119  if (gridType == LineGrid)
1120  {
1121  QVector<QLineF> gridLines;
1122  for (qreal x = leftmostGridLine; x < rect.right(); x += gridSpacing)
1123  gridLines.append(QLineF(x + nodeXAlignmentCorrection, rect.top(), x + nodeXAlignmentCorrection, rect.bottom()));
1124  for (qreal y = topmostGridLine; y < rect.bottom(); y += gridSpacing)
1125  gridLines.append(QLineF(rect.left(), y, rect.right(), y));
1126 
1127  painter->setPen(QColor(128, 128, 128, VuoRendererComposition::gridOpacity * 32));
1128  painter->drawLines(gridLines);
1129  }
1130  else if (gridType == PointGrid)
1131  {
1132  painter->setRenderHint(QPainter::Antialiasing, true);
1133  painter->setPen(Qt::NoPen);
1134  painter->setBrush(QColor(128, 128, 128, VuoRendererComposition::gridOpacity * 128));
1135 
1136  for (qreal y = topmostGridLine; y < rect.bottom(); y += gridSpacing)
1137  for (qreal x = leftmostGridLine; x < rect.right(); x += gridSpacing)
1138  // Offset by a half-pixel to render a softer plus-like point (instead of a sharp pixel-aligned square).
1139  painter->drawEllipse(QPointF(x + nodeXAlignmentCorrection + .5, y + .5), 1.5,1.5);
1140  }
1141  }
1142 }
1143 
1150 void VuoRendererComposition::setRenderActivity(bool render, bool includePortActivity)
1151 {
1152  if ((this->renderNodeActivity == render) && (this->renderPortActivity == includePortActivity))
1153  return;
1154 
1155  this->renderNodeActivity = render;
1156  this->renderPortActivity = includePortActivity;
1157 
1158  if (render)
1159  {
1160  foreach (VuoNode *node, getBase()->getNodes())
1161  {
1163 
1164  if (includePortActivity)
1165  {
1166  foreach (VuoPort *port, node->getInputPorts())
1168 
1169  foreach (VuoPort *port, node->getOutputPorts())
1171  }
1172  }
1173 
1174  foreach (VuoCable *cable, getBase()->getCables())
1176  }
1177 
1180 }
1181 
1186 {
1187  return this->renderHiddenCables;
1188 }
1189 
1194 {
1195  setComponentCaching(QGraphicsItem::NoCache);
1196  this->renderHiddenCables = render;
1199 }
1200 
1205 void VuoRendererComposition::setComponentCaching(QGraphicsItem::CacheMode cacheMode)
1206 {
1207  // Nodes and ports
1208  foreach (VuoNode *node, getBase()->getNodes())
1209  {
1210  VuoRendererNode *rn = node->getRenderer();
1211  if (rn)
1212  rn->setCacheModeForNodeAndPorts(cacheMode);
1213  }
1214 
1215  // Cables
1216  set<VuoCable *> allCables = getBase()->getCables();
1217  foreach (VuoCable *cable, allCables)
1218  {
1219  VuoRendererCable *rc = cable->getRenderer();
1220  if (rc)
1221  rc->setCacheMode(cacheMode);
1222  }
1223 }
1224 
1231 {
1232  return ((cachingEnabled && !renderNodeActivity)? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache);
1233 }
1234 
1240 {
1241 #ifdef __APPLE__
1242  // [NSAutoreleasePool new];
1243  Class poolClass = objc_getClass("NSAutoreleasePool");
1244  ((void (*)(id, SEL))objc_msgSend)((id)poolClass, sel_getUid("new"));
1245 #endif
1246 }
1247 
1254 {
1255  foreach (VuoNode *node, getBase()->getNodes())
1256  {
1257  foreach (VuoPort *port, node->getInputPorts())
1258  {
1259  if (!port->hasRenderer())
1260  continue;
1261 
1262  VuoRendererPort *rp = port->getRenderer();
1264  {
1265  VuoUrl url = VuoUrl_makeFromString(rp->getConstantAsString().c_str());
1266  VuoRetain(url);
1267  string modifiedRelativeResourcePath = modifyResourcePathForBundle(url);
1268  rp->setConstant("\"" + modifiedRelativeResourcePath + "\"");
1269  VuoRelease(url);
1270  }
1271  }
1272  }
1273 }
1274 
1281 {
1282  map<VuoPort *, string> modifiedPathForPort;
1283  foreach (VuoNode *node, getBase()->getNodes())
1284  {
1285  foreach (VuoPort *port, node->getInputPorts())
1286  {
1287  if (!port->hasRenderer())
1288  continue;
1289 
1290  VuoRendererPort *rp = port->getRenderer();
1292  {
1293  VuoUrl url = VuoUrl_makeFromString(rp->getConstantAsString().c_str());
1294  VuoRetain(url);
1295  QString origRelativeResourcePath = url;
1296  string modifiedRelativeResourcePath = modifyResourcePathForNewDir(origRelativeResourcePath.toUtf8().constData(), newDir);
1297 
1298  if (modifiedRelativeResourcePath != origRelativeResourcePath.toUtf8().constData())
1299  modifiedPathForPort[port] = "\"" + modifiedRelativeResourcePath + "\"";
1300  VuoRelease(url);
1301  }
1302  }
1303  }
1304 
1305  return modifiedPathForPort;
1306 }
1307 
1313 {
1314  string iconURL = getBase()->getMetadata()->getIconURL();
1315  string quotedIconURL = "\"" + iconURL + "\"";
1316 
1317  VuoUrl url = VuoUrl_makeFromString(quotedIconURL.c_str());
1318  VuoRetain(url);
1319  bool isRelativePath = VuoUrl_isRelativePath(url);
1320  QString origRelativeResourcePath = url;
1321  VuoRelease(url);
1322 
1323  if (!isRelativePath)
1324  return iconURL;
1325 
1326  return modifyResourcePathForNewDir(origRelativeResourcePath.toUtf8().constData(), newDir);
1327 }
1328 
1339 void VuoRendererComposition::bundleResourceFiles(string targetResourceDir, bool tmpFilesOnly, QString bundledIconPath)
1340 {
1341  // Update relative URLs referenced within port constants.
1342  foreach (VuoNode *node, getBase()->getNodes())
1343  {
1344  foreach (VuoPort *port, node->getInputPorts())
1345  {
1346  if (!port->hasRenderer())
1347  continue;
1348 
1349  VuoRendererPort *rp = port->getRenderer();
1351  {
1352  VuoUrl url = VuoUrl_makeFromString(rp->getConstantAsString().c_str());
1353  VuoRetain(url);
1354  QString origRelativeResourcePath = url;
1355  QString modifiedRelativeResourcePath = modifyResourcePathForBundle(origRelativeResourcePath.toUtf8().constData()).c_str();
1356 
1357  string origRelativeDir, modifiedRelativeDir, file, ext;
1358  VuoFileUtilities::splitPath(origRelativeResourcePath.toUtf8().constData(), origRelativeDir, file, ext);
1359  VuoFileUtilities::splitPath(modifiedRelativeResourcePath.toUtf8().constData(), modifiedRelativeDir, file, ext);
1360  string resourceFileName = file;
1361  if (!ext.empty())
1362  {
1363  resourceFileName += ".";
1364  resourceFileName += ext;
1365  }
1366 
1367  QDir compositionDir(QDir(getBase()->getDirectory().c_str()).canonicalPath());
1368  QDir appDir(QDir(targetResourceDir.c_str()).canonicalPath());
1369 
1370  QString sourceFilePath = compositionDir.filePath(QDir(origRelativeDir.c_str()).filePath(resourceFileName.c_str()));
1371  if (!tmpFilesOnly || isTmpFile(sourceFilePath.toUtf8().constData()))
1372  {
1373  if (!modifiedRelativeDir.empty())
1374  appDir.mkpath(modifiedRelativeDir.c_str());
1375 
1376  QString targetFilePath = appDir.filePath(QDir(modifiedRelativeDir.c_str()).filePath(resourceFileName.c_str()));
1377 
1378  VDebugLog("Copying \"%s\" (from %s:%s)", url, node->getTitle().c_str(), port->getClass()->getName().c_str());
1379  VuoFileUtilities::copyDirectory(sourceFilePath.toStdString(), targetFilePath.toStdString());
1380 
1381  if (VuoFileType_isFileOfType(sourceFilePath.toUtf8().constData(), VuoFileType_Scene))
1382  bundleAuxiliaryFilesForSceneFile(sourceFilePath, targetFilePath);
1383  }
1384  VuoRelease(url);
1385  }
1386 
1387  }
1388  }
1389 
1390  // Update relative path to custom icon.
1391  string iconURL = getBase()->getMetadata()->getIconURL();
1392  string quotedIconURL = "\"" + iconURL + "\"";
1393 
1394  VuoUrl url = VuoUrl_makeFromString(quotedIconURL.c_str());
1395  VuoRetain(url);
1396  bool iconHasRelativePath = VuoUrl_isRelativePath(url);
1397  QString origRelativeResourcePath = url;
1398 
1399  if (iconHasRelativePath)
1400  {
1401  QString modifiedRelativeResourcePath = modifyResourcePathForBundle(origRelativeResourcePath.toUtf8().constData()).c_str();
1402 
1403  string origRelativeDir, modifiedRelativeDir, file, ext;
1404  VuoFileUtilities::splitPath(origRelativeResourcePath.toUtf8().constData(), origRelativeDir, file, ext);
1405  VuoFileUtilities::splitPath(modifiedRelativeResourcePath.toUtf8().constData(), modifiedRelativeDir, file, ext);
1406  string resourceFileName = file;
1407  if (!ext.empty())
1408  {
1409  resourceFileName += ".";
1410  resourceFileName += ext;
1411  }
1412 
1413  QDir compositionDir(QDir(getBase()->getDirectory().c_str()).canonicalPath());
1414  QDir appDir(QDir(targetResourceDir.c_str()).canonicalPath());
1415 
1416  QString sourceFilePath = compositionDir.filePath(QDir(origRelativeDir.c_str()).filePath(resourceFileName.c_str()));
1417  if (!tmpFilesOnly || isTmpFile(sourceFilePath.toUtf8().constData()))
1418  {
1419  if (!modifiedRelativeDir.empty())
1420  appDir.mkpath(modifiedRelativeDir.c_str());
1421 
1422  QString targetFilePath = (!bundledIconPath.isEmpty()? bundledIconPath :
1423  appDir.filePath(QDir(modifiedRelativeDir.c_str()).filePath(resourceFileName.c_str())));
1424 
1425  // Case: Icon is already in .icns format; copy it directly.
1426  if (QString(ext.c_str()).toLower() == "icns")
1427  {
1428  VDebugLog("Copying \"%s\" (app icon)", url);
1429  VuoFileUtilities::copyDirectory(sourceFilePath.toStdString(), targetFilePath.toStdString());
1430  }
1431  // Case: Icon is in some other format; convert it to an ".icns" file.
1432  else
1433  {
1434  VDebugLog("Converting \"%s\" (app icon)", url);
1435  QPixmap icon(sourceFilePath);
1436  QFile file(targetFilePath);
1437  file.open(QIODevice::WriteOnly);
1438  icon.save(&file, "ICNS");
1439  }
1440  }
1441  }
1442  VuoRelease(url);
1443 
1444  // @todo https://b33p.net/kosada/node/9205 : Published input port constants?
1445 }
1446 
1452 {
1453  const QString tmpDirPath = QDir(VuoFileUtilities::getTmpDir().c_str()).canonicalPath();
1454  QDir parentDir(QDir(QFileInfo(filePath.c_str()).dir()).canonicalPath());
1455 
1456  do
1457  {
1458  if (parentDir.canonicalPath() == tmpDirPath)
1459  return true;
1460  } while (parentDir.cdUp());
1461 
1462  return false;
1463 }
1464 
1469 string VuoRendererComposition::modifyResourcePathForBundle(string path)
1470 {
1471  if (VuoUrl_isRelativePath(path.c_str()))
1472  {
1473  // Replace parent-directory indicators ("../") so that resources
1474  // located within the parent (or ancestor) directory of the original composition will
1475  // still be copied into the exported app's "Resources" directory or a subdirectory thereof.
1476  const QString originalParentDirIndicator = "/../";
1477  const QString appBundleParentDirIndicator = "/VuoParentDir/";
1478 
1479  QString modifiedPath = QString("/").append(path.c_str());
1480 
1481  while (modifiedPath.contains(originalParentDirIndicator))
1482  modifiedPath.replace(originalParentDirIndicator, appBundleParentDirIndicator);
1483 
1484  modifiedPath.remove(0, 1); // Remove leading '/' added earlier
1485 
1486  return modifiedPath.toUtf8().constData();
1487  }
1488 
1489  else
1490  return path;
1491 }
1492 
1497 string VuoRendererComposition::modifyResourcePathForNewDir(string path, QDir newDir)
1498 {
1499  if (VuoUrl_isRelativePath(path.c_str()))
1500  {
1501  QDir compositionDir(QDir(getBase()->getDirectory().c_str()).canonicalPath());
1502  if (compositionDir.canonicalPath() == newDir.canonicalPath())
1503  return path;
1504 
1505  string origRelativeDir, file, ext;
1506  VuoFileUtilities::splitPath(path, origRelativeDir, file, ext);
1507  string resourceFileName = file;
1508  if (!ext.empty())
1509  {
1510  resourceFileName += ".";
1511  resourceFileName += ext;
1512  }
1513 
1514  QString sourceFilePath = compositionDir.filePath(QDir(origRelativeDir.c_str()).filePath(resourceFileName.c_str()));
1515  string modifiedPath = newDir.relativeFilePath(sourceFilePath).toUtf8().constData();
1516 
1517  return modifiedPath;
1518  }
1519 
1520  else
1521  return path;
1522 }
1523 
1528 void VuoRendererComposition::bundleAuxiliaryFilesForSceneFile(QString sourceFilePath, QString targetFilePath)
1529 {
1530  string sourceDirName, targetDirName, file, ext;
1531  VuoFileUtilities::splitPath(sourceFilePath.toUtf8().constData(), sourceDirName, file, ext);
1532  VuoFileUtilities::splitPath(targetFilePath.toUtf8().constData(), targetDirName, file, ext);
1533 
1534  // Bundle any file or folder in the same directory as the mesh file
1535  // whose name begins with the mesh file's basename.
1536  QDir sourceDir(sourceDirName.c_str());
1537  QStringList filesWithMatchingBaseName = QStringList() << QString(file.c_str()).append("*");
1538  sourceDir.setNameFilters(filesWithMatchingBaseName);
1539 
1540  foreach (QString auxiliaryFile, sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot))
1541  {
1542  QString sourceFile = QString(sourceDirName.c_str()) + QDir::separator() + auxiliaryFile;
1543  QString targetFile = QString(targetDirName.c_str()) + QDir::separator() + auxiliaryFile;
1544  if (!QFileInfo(targetFile).exists())
1545  {
1546  VDebugLog("Copying \"%s\"", QFileInfo(targetFile).fileName().toUtf8().constData());
1547  VuoFileUtilities::copyDirectory(sourceFile.toStdString(), targetFile.toStdString());
1548  }
1549  }
1550 
1551  // Bundle texture folders in the same directory as the mesh file.
1552  QStringList textureFolderNames = QStringList() << "Textures" << "_Textures";
1553  sourceDir.setNameFilters(textureFolderNames);
1554  foreach (QString textureFolderName, sourceDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
1555  {
1556  QString sourceTextureDir = QString(sourceDirName.c_str()) + QDir::separator() + textureFolderName;
1557  QString targetTextureDir = QString(targetDirName.c_str()) + QDir::separator() + textureFolderName;
1558  if (!QFileInfo(targetTextureDir).exists())
1559  {
1560  VDebugLog("Copying \"%s\"", QFileInfo(targetTextureDir).fileName().toUtf8().constData());
1561  VuoFileUtilities::copyDirectory(sourceTextureDir.toStdString(), targetTextureDir.toStdString());
1562  }
1563  }
1564 
1565  // @todo: Bundle texture folders in the parent directory of the mesh file.
1566  // See https://b33p.net/kosada/node/9390, https://b33p.net/kosada/node/9391.
1567 
1568  return;
1569 }
1570 
1576 {
1577  QDir dir(path.c_str());
1578  return dir.exists() && !VuoFileType_isFileOfType(path.c_str(), VuoFileType_App);
1579 }
1580 
1585 {
1586  VuoRendererComposition::gridOpacity = opacity;
1587 }
1588 
1593 {
1594  return VuoRendererComposition::gridOpacity;
1595 }
1596 
1601 {
1602  VuoRendererComposition::gridType = type;
1603 }
1604 
1609 QPoint VuoRendererComposition::quantizeToNearestGridLine(QPointF point, int gridSpacing)
1610 {
1611  return QPoint(floor((point.x()/(1.0*gridSpacing))+0.5)*gridSpacing,
1612  floor((point.y()/(1.0*gridSpacing))+0.5)*gridSpacing);
1613 }