Vuo 2.4.4
Loading...
Searching...
No Matches
VuoCompilerDriver.cc
Go to the documentation of this file.
1
10#include "VuoCable.hh"
11#include "VuoCompiler.hh"
12#include "VuoCompilerDriver.hh"
13#include "VuoCompilerCable.hh"
16#include "VuoCompilerNode.hh"
18#include "VuoCompilerType.hh"
19#include "VuoComposition.hh"
20#include "VuoNode.hh"
21#include "VuoNodeClass.hh"
22#include "VuoProtocol.hh"
23#include "VuoPublishedPort.hh"
24#include "VuoType.hh"
25
30{
31 map<string, string> drivers{
32 { VuoProtocol::imageFilter, "imageFilterDriver.vuo" },
33 { VuoProtocol::imageGenerator, "imageGeneratorDriver.vuo" },
34 { VuoProtocol::imageTransition, "imageTransitionDriver.vuo" },
35 };
36 string driverAsString = VuoFileUtilities::readFileToString(VuoFileUtilities::getVuoFrameworkPath() + "/Resources/" + drivers[protocolId]);
37 return new VuoCompilerDriver(compiler, driverAsString);
38}
39
46VuoCompilerDriver::VuoCompilerDriver(VuoCompiler *compiler, const string &driverAsCompositionString)
47{
48 this->compiler = compiler;
49 this->parser = VuoCompilerGraphvizParser::newParserFromCompositionString(driverAsCompositionString, compiler);
50 this->compositionString = driverAsCompositionString;
51}
52
61{
62 // For each published output port in driver, make sure the protocol has a published
63 // input port with a matching name and type.
64 vector<VuoPublishedPort *> driverBridgeInputs = parser->getPublishedOutputPorts();
65 for (vector<VuoPublishedPort *>::iterator i = driverBridgeInputs.begin(); i != driverBridgeInputs.end(); ++i)
66 {
67 VuoPublishedPort *driverBridgeInput = (*i);
68 string driverBridgeInputName = driverBridgeInput->getClass()->getName();
69 VuoType *driverBridgeInputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeInput->getCompiler())->getDataVuoType();
70
71 if (!protocol->hasInputPort(driverBridgeInputName) ||
72 (protocol->getTypeForInputPort(driverBridgeInputName) != driverBridgeInputType->getModuleKey()))
73 {
74 return false;
75 }
76 }
77
78 // For each published input port in driver, make sure the protocol has a published
79 // output port with a matching name and type.
80 vector<VuoPublishedPort *> driverBridgeOutputs = parser->getPublishedInputPorts();
81 for (vector<VuoPublishedPort *>::iterator i = driverBridgeOutputs.begin(); i != driverBridgeOutputs.end(); ++i)
82 {
83 VuoPublishedPort *driverBridgeOutput = (*i);
84 string driverBridgeOutputName = driverBridgeOutput->getClass()->getName();
85 VuoType *driverBridgeOutputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeOutput->getCompiler())->getDataVuoType();
86
87 if (!protocol->hasOutputPort(driverBridgeOutputName) ||
88 (protocol->getTypeForOutputPort(driverBridgeOutputName) != driverBridgeOutputType->getModuleKey()))
89 {
90 return false;
91 }
92 }
93
94 return true;
95}
96
104void VuoCompilerDriver::applyToComposition(VuoCompilerComposition *composition, VuoCompiler *compiler, bool canPublishedInputsBeEdited)
105{
106 // Copy the driver's contents so the original driver won't be affected.
108
109 // Add the (non-published) driver nodes to the composition.
110 vector<VuoNode *> nodes = parserCopy->getNodes();
111 for (vector<VuoNode *>::iterator node = nodes.begin(); node != nodes.end(); ++node)
112 {
113 // Give the node an identifier that is different from any nodes in the current composition
114 // and any `Make List` nodes that have just been replaced during a live-coding reload.
115 if ((*node)->hasCompiler())
116 {
117 string nodeIdentifierPrefix = (*node)->getCompiler()->getGraphvizIdentifierPrefix() + "_Driver";
118 composition->setUniqueGraphvizIdentifierForNode(*node, nodeIdentifierPrefix, nodeIdentifierPrefix);
119 }
120
121 composition->getBase()->addNode(*node);
122 }
123
124 // Add the (non-published) driver cables to the composition.
125 vector<VuoCable *> cables = parserCopy->getCables();
126 for (vector<VuoCable *>::iterator cable = cables.begin(); cable != cables.end(); ++cable)
127 if (! (*cable)->isPublished())
128 composition->getBase()->addCable(*cable);
129
130 // Bridge each of the driver's published outputs with the matching composition published input.
131 vector<VuoPublishedPort *> driverBridgeInputs = parserCopy->getPublishedOutputPorts();
132 for (vector<VuoPublishedPort *>::iterator i = driverBridgeInputs.begin(); i != driverBridgeInputs.end(); ++i)
133 {
134 VuoPublishedPort *driverBridgeInput = (*i);
135 string driverBridgeInputName = driverBridgeInput->getClass()->getName();
136 VuoType *driverBridgeInputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeInput->getCompiler())->getDataVuoType();
137
138 VuoPublishedPort *compositionPublishedInput = composition->getBase()->getPublishedInputPortWithName(driverBridgeInputName);
139 if (compositionPublishedInput)
140 {
141 VuoType *compositionPublishedInputType = static_cast<VuoCompilerPublishedPort *>(compositionPublishedInput->getCompiler())->getDataVuoType();
142 if (compositionPublishedInputType == driverBridgeInputType)
143 {
144 vector<VuoCable *> publishedInputOutgoingCables = compositionPublishedInput->getConnectedCables();
145 set<pair<VuoPort *, VuoNode *> > compositionConnectedPorts;
146 map<pair<VuoPort *, VuoNode *>, bool> eventOnlyConnection;
147
148 // Re-route the driver published output's incoming cables to each of the internal ports connected
149 // to the composition's corresponding published input.
150 for (vector<VuoCable *>::iterator i = publishedInputOutgoingCables.begin(); i != publishedInputOutgoingCables.end(); ++i)
151 {
152 VuoCable *cable = (*i);
153 VuoPort *toPort = cable->getToPort();
154 VuoNode *toNode = cable->getToNode();
155 compositionConnectedPorts.insert(make_pair(toPort, toNode));
156 eventOnlyConnection[make_pair(toPort, toNode)] = cable->getCompiler()->getAlwaysEventOnly();
157
158 // Remove the original outgoing cables from the composition's published input.
159 composition->getBase()->removeCable(cable);
160 }
161
162 // Remove the composition's published input.
163 int index = composition->getBase()->getIndexOfPublishedPort(compositionPublishedInput, true);
164 if (index != -1)
165 composition->getBase()->removePublishedInputPort(index);
166
167 vector<VuoCable *> driverBridgeInputIncomingCables = driverBridgeInput->getConnectedCables();
168 for (vector<VuoCable *>::iterator i = driverBridgeInputIncomingCables.begin(); i != driverBridgeInputIncomingCables.end(); ++i)
169 {
170 VuoCable *cable = (*i);
171 VuoPort *fromPort = cable->getFromPort();
172 VuoNode *fromNode = cable->getFromNode();
173
174 // For each of the driver published output's incoming cables, create new cables connecting
175 // that cable's 'From' port to each of the composition's internal ports originally connected
176 // to the composition published input.
177 for (set<pair<VuoPort *, VuoNode *> >::iterator i = compositionConnectedPorts.begin(); i != compositionConnectedPorts.end(); ++i)
178 {
179 VuoPort *toPort = i->first;
180 VuoNode *toNode = i->second;
181
182 VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(fromPort->getCompiler());
183 VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(toPort->getCompiler());
184 VuoCompilerCable *newCable = new VuoCompilerCable(fromNode->getCompiler(), fromCompilerPort, toNode->getCompiler(), toCompilerPort);
185 if (cable->getCompiler()->getAlwaysEventOnly() || eventOnlyConnection[(*i)])
186 newCable->setAlwaysEventOnly(true);
187
188 composition->getBase()->addCable(newCable->getBase());
189 }
190 }
191 }
192 }
193 }
194
195 // Bridge each of the driver's published inputs with the matching composition published output.
196 vector<VuoPublishedPort *> driverBridgeOutputs = parserCopy->getPublishedInputPorts();
197 for (vector<VuoPublishedPort *>::iterator i = driverBridgeOutputs.begin(); i != driverBridgeOutputs.end(); ++i)
198 {
199 VuoPublishedPort *driverBridgeOutput = (*i);
200 string driverBridgeOutputName = driverBridgeOutput->getClass()->getName();
201 VuoType *driverBridgeOutputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeOutput->getCompiler())->getDataVuoType();
202
203 VuoPublishedPort *compositionPublishedOutput = composition->getBase()->getPublishedOutputPortWithName(driverBridgeOutputName);
204 if (compositionPublishedOutput)
205 {
206 VuoType *compositionPublishedOutputType = static_cast<VuoCompilerPublishedPort *>(compositionPublishedOutput->getCompiler())->getDataVuoType();
207 if (compositionPublishedOutputType == driverBridgeOutputType)
208 {
209 vector<VuoCable *> publishedOutputIncomingCables = compositionPublishedOutput->getConnectedCables();
210 set<pair<VuoPort *, VuoNode *> > compositionConnectedPorts;
211 map<pair<VuoPort *, VuoNode *>, bool> eventOnlyConnection;
212
213 // Re-route the composition published output's incoming cables to each of the internal ports connected
214 // to the driver's corresponding published input.
215 for (vector<VuoCable *>::iterator i = publishedOutputIncomingCables.begin(); i != publishedOutputIncomingCables.end(); ++i)
216 {
217 VuoCable *cable = (*i);
218 VuoPort *fromPort = cable->getFromPort();
219 VuoNode *fromNode = cable->getFromNode();
220 compositionConnectedPorts.insert(make_pair(fromPort, fromNode));
221 eventOnlyConnection[make_pair(fromPort, fromNode)] = cable->getCompiler()->getAlwaysEventOnly();
222
223 // Remove the original outgoing cables from the composition's published input.
224 composition->getBase()->removeCable(cable);
225 }
226
227 // Remove the composition's published output.
228 int index = composition->getBase()->getIndexOfPublishedPort(compositionPublishedOutput, false);
229 if (index != -1)
230 composition->getBase()->removePublishedOutputPort(index);
231
232 vector<VuoCable *> driverBridgeOutputOutgoingCables = driverBridgeOutput->getConnectedCables();
233 for (vector<VuoCable *>::iterator i = driverBridgeOutputOutgoingCables.begin(); i != driverBridgeOutputOutgoingCables.end(); ++i)
234 {
235 VuoCable *cable = (*i);
236 VuoPort *toPort = cable->getToPort();
237 VuoNode *toNode = cable->getToNode();
238
239 // For each of the driver published input's outgoing cables, create new cables connecting
240 // that cable's 'To' port to each of the composition's internal ports originally connected
241 // to the composition published output.
242 for (set<pair<VuoPort *, VuoNode *> >::iterator i = compositionConnectedPorts.begin(); i != compositionConnectedPorts.end(); ++i)
243 {
244 VuoPort *fromPort = i->first;
245 VuoNode *fromNode = i->second;
246
247 VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(fromPort->getCompiler());
248 VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(toPort->getCompiler());
249 VuoCompilerCable *newCable = new VuoCompilerCable(fromNode->getCompiler(), fromCompilerPort, toNode->getCompiler(), toCompilerPort);
250 if (cable->getCompiler()->getAlwaysEventOnly() || eventOnlyConnection[(*i)])
251 newCable->setAlwaysEventOnly(true);
252
253 composition->getBase()->addCable(newCable->getBase());
254 }
255 }
256 }
257 }
258 }
259
260 // For each data published input in the composition that does not have a corresponding published output in the driver,
261 // add an `Allow Changes` or `Allow First Value` node with an incoming data cable from the published input port,
262 // an incoming event cable from the driver's `time` published output port, and an outgoing event cable to each
263 // internal input port connected to the published input port.
264 auto driverBridgeInput = std::find_if(driverBridgeInputs.begin(), driverBridgeInputs.end(),
265 [] (VuoPublishedPort *p) { return p->getClass()->getName() == "time"; });
266 if (driverBridgeInput != driverBridgeInputs.end())
267 {
268 vector<VuoCable *> driverBridgeInputIncomingCables = (*driverBridgeInput)->getConnectedCables();
269
270 vector<VuoPublishedPort *> compositionPublishedInputs = composition->getBase()->getPublishedInputPorts();
271 for (VuoPublishedPort *compositionPublishedInput : compositionPublishedInputs)
272 {
273 string name = compositionPublishedInput->getClass()->getName();
274 auto matchingDriverBridgeInput = std::find_if(driverBridgeInputs.begin(), driverBridgeInputs.end(),
275 [&name] (VuoPublishedPort *p) { return p->getClass()->getName() == name; });
276 if (matchingDriverBridgeInput != driverBridgeInputs.end())
277 continue;
278
279 VuoType *type = static_cast<VuoCompilerPublishedPort *>(compositionPublishedInput->getCompiler())->getDataVuoType();
280 if (! type)
281 continue;
282
283 vector<VuoCable *> publishedInputOutgoingCables = compositionPublishedInput->getConnectedCables();
284
285 // Workaround for data types that don't support `Allow Changes`: Connect an event cable from the driver's `time`
286 // published output port directly to each internal input port connected to the published port.
287 if (canPublishedInputsBeEdited && ! type->getCompiler()->supportsComparison())
288 {
289 for (VuoCable *driverCable : driverBridgeInputIncomingCables)
290 {
291 for (VuoCable *compositionCable : publishedInputOutgoingCables)
292 {
293 // Event cable: driver output port -> composition input port
294 VuoCompilerNode *fromCompilerNode = driverCable->getFromNode()->getCompiler();
295 VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(driverCable->getFromPort()->getCompiler());
296 VuoCompilerNode *toCompilerNode = compositionCable->getToNode()->getCompiler();
297 VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(compositionCable->getToPort()->getCompiler());
298 VuoCompilerCable *cable = new VuoCompilerCable(fromCompilerNode, fromCompilerPort, toCompilerNode, toCompilerPort);
299 cable->setAlwaysEventOnly(true);
300 composition->getBase()->addCable(cable->getBase());
301 }
302 }
303 continue;
304 }
305
306 // Node: Allow Changes or Allow First Value
307 string allowNodeClassName = (canPublishedInputsBeEdited ? "vuo.event.allowChanges2" : "vuo.event.allowFirstValue");
308 VuoCompilerNodeClass *allowNodeClass = compiler->getNodeClass(allowNodeClassName + "." + type->getModuleKey());
309 VuoNode *allowNode = compiler->createNode(allowNodeClass);
310 string nodeIdentifierPrefix = allowNode->getCompiler()->getGraphvizIdentifierPrefix() + "_Driver";
311 composition->setUniqueGraphvizIdentifierForNode(allowNode, nodeIdentifierPrefix, nodeIdentifierPrefix);
312 composition->getBase()->addNode(allowNode);
313
314 VuoCompilerPort *allowInputPort = static_cast<VuoCompilerPort *>(allowNode->getInputPorts().at(VuoNodeClass::unreservedInputPortStartIndex)->getCompiler());
315 VuoCompilerPort *allowOutputPort = static_cast<VuoCompilerPort *>(allowNode->getOutputPorts().at(VuoNodeClass::unreservedOutputPortStartIndex)->getCompiler());
316
317 {
318 // Data cable: composition published input port -> Allow node
319 VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPublishedPort *>(compositionPublishedInput->getCompiler());
320 VuoCompilerCable *cable = new VuoCompilerCable(nullptr, fromCompilerPort, allowNode->getCompiler(), allowInputPort);
321 composition->getBase()->addCable(cable->getBase());
322 }
323
324 for (VuoCable *driverCable : driverBridgeInputIncomingCables)
325 {
326 // Event cable: driver output port -> Allow node
327 VuoCompilerNode *fromCompilerNode = driverCable->getFromNode()->getCompiler();
328 VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(driverCable->getFromPort()->getCompiler());
329 VuoCompilerCable *cable = new VuoCompilerCable(fromCompilerNode, fromCompilerPort, allowNode->getCompiler(), allowInputPort);
330 cable->setAlwaysEventOnly(true);
331 composition->getBase()->addCable(cable->getBase());
332 }
333
334 for (VuoCable *compositionCable : publishedInputOutgoingCables)
335 {
336 // Event cable: Allow node -> composition input port
337 VuoCompilerNode *toCompilerNode = compositionCable->getToNode()->getCompiler();
338 VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(compositionCable->getToPort()->getCompiler());
339 VuoCompilerCable *cable = new VuoCompilerCable(allowNode->getCompiler(), allowOutputPort, toCompilerNode, toCompilerPort);
340 cable->setAlwaysEventOnly(true);
341 composition->getBase()->addCable(cable->getBase());
342 }
343 }
344 }
345}