TuttleOFX
1
|
00001 #include "Graph.hpp" 00002 #include "Node.hpp" 00003 #include "graph/ProcessGraph.hpp" 00004 00005 #include <tuttle/host/ofx/OfxhImageEffectPlugin.hpp> 00006 #include <tuttle/host/ofx/OfxhImageEffectNode.hpp> 00007 #include <tuttle/host/ofx/attribute/OfxhClipImage.hpp> 00008 #include <tuttle/host/graph/GraphExporter.hpp> 00009 00010 #include <boost/lexical_cast.hpp> 00011 #include <boost/exception/all.hpp> 00012 #include <boost/foreach.hpp> 00013 #include <boost/algorithm/string.hpp> 00014 00015 #include <iostream> 00016 #include <sstream> 00017 00018 namespace tuttle { 00019 namespace host { 00020 00021 Graph::Graph() 00022 {} 00023 00024 Graph::~Graph() 00025 {} 00026 00027 InputBufferWrapper Graph::createInputBuffer() 00028 { 00029 Node& node = createNode( "tuttle.inputbuffer" ); 00030 InputBufferWrapper nodeWrapper( node ); 00031 00032 return nodeWrapper; 00033 } 00034 00035 OutputBufferWrapper Graph::createOutputBuffer() 00036 { 00037 Node& node = createNode( "tuttle.outputbuffer" ); 00038 OutputBufferWrapper nodeWrapper( node ); 00039 00040 return nodeWrapper; 00041 } 00042 00043 Graph::Node& Graph::createNode( const std::string& pluginName ) 00044 { 00045 INode* node = tuttle::host::createNode( pluginName ); 00046 return addNode( *node ); 00047 } 00048 00049 Graph::Node& Graph::addNode( const NodeInit& node ) 00050 { 00051 return addNode( node.release() ); // transfer ownership 00052 } 00053 00054 Graph::Node& Graph::addNode( INode& node ) 00055 { 00056 std::stringstream uniqueName; 00057 uniqueName << node.getLabel() << "_" << ++_instanceCount[node.getLabel()]; 00058 node.setName( uniqueName.str() ); 00059 00060 std::string key( node.getName() ); // for constness 00061 _nodesMap.insert( key, &node ); // acquire the ownership 00062 addToInternalGraph( node ); 00063 00064 return node; 00065 } 00066 00067 std::vector<INode*> Graph::addNodes( const std::vector<NodeInit>& nodes ) 00068 { 00069 std::vector<INode*> nodePtrs; 00070 BOOST_FOREACH( const NodeInit& node, nodes ) 00071 { 00072 INode& newNode = addNode( node ); // tranfer nodes ownership to the graph 00073 nodePtrs.push_back( & newNode ); 00074 } 00075 return nodePtrs; 00076 } 00077 00078 std::vector<INode*> Graph::addConnectedNodes( const std::vector<NodeInit>& nodes ) 00079 { 00080 std::vector<INode*> nodePtrs = addNodes( nodes ); 00081 if( nodePtrs.size() > 1 ) 00082 connect( nodePtrs ); 00083 return nodePtrs; 00084 } 00085 00086 void Graph::renameNode( Graph::Node& node, const std::string& newUniqueName ) 00087 { 00088 // it's the same name, nothing to do. 00089 if( node.getName() == newUniqueName ) 00090 return; 00091 00092 TUTTLE_TLOG( TUTTLE_INFO, "Graph::renameNode: from: " << node.getName() << " -> to: " << newUniqueName ); 00093 { 00094 // check if newUniqueName is not already in the graph 00095 NodeMap::iterator itNew = _nodesMap.find( newUniqueName ); 00096 if( itNew != _nodesMap.end() ) 00097 { 00098 BOOST_THROW_EXCEPTION( exception::Value() 00099 << exception::user() + "New node name " + quotes(newUniqueName) + " already exists." ); 00100 } 00101 } 00102 NodeMap::iterator it = _nodesMap.find( node.getName() ); 00103 if( it == _nodesMap.end() ) 00104 { 00105 BOOST_THROW_EXCEPTION( exception::Value() 00106 << exception::user() + "Node " + quotes(node.getName()) + " is not in the graph." ); 00107 } 00108 00109 // warning: loose all connections !!! 00110 removeFromInternalGraph( node ); 00111 00112 // rename the key into the map 00113 NodeMap::auto_type n = _nodesMap.release( it ); 00114 n->setName( newUniqueName ); 00115 std::string key( newUniqueName ); // for constness 00116 _nodesMap.insert( key, n.release() ); 00117 00118 addToInternalGraph( node ); 00119 00120 node.setName( newUniqueName ); 00121 } 00122 00123 void Graph::addToInternalGraph( Node& node ) 00124 { 00125 //TUTTLE_TLOG( TUTTLE_INFO, "Graph::addToInternalGraph: " << node.getName() ); 00126 Vertex v( node.getName(), node ); 00127 _graph.addVertex( v ); 00128 } 00129 00130 void Graph::removeFromInternalGraph( Node& node ) 00131 { 00132 //TUTTLE_TLOG( TUTTLE_INFO, "Graph::removeFromInternalGraph: " << node.getName() ); 00133 const unsigned int id = _graph.getVertexDescriptor( node.getName() ); 00134 _graph.removeVertex( id ); 00135 } 00136 00137 void Graph::deleteNode( Node& node ) 00138 { 00139 NodeMap::iterator it = _nodesMap.find( node.getName() ); 00140 if( it == _nodesMap.end() ) 00141 { 00142 BOOST_THROW_EXCEPTION( exception::Value() 00143 << exception::user("Node not found.") ); 00144 } 00145 removeFromInternalGraph( node ); 00146 _nodesMap.erase( it ); // will delete the node 00147 } 00148 00149 std::size_t Graph::deleteUnconnectedNodes( const Node& node ) 00150 { 00151 std::vector<vertex_descriptor> toRemove = _graph.getUnconnectedVertices( _graph.getVertexDescriptor( node.getName() ) ); 00152 BOOST_FOREACH( const vertex_descriptor nodeId, toRemove ) 00153 { 00154 deleteNode( _graph.instance( nodeId ).getProcessNode() ); 00155 } 00156 return toRemove.size(); 00157 } 00158 00159 void Graph::clear() 00160 { 00161 _graph.clear(); 00162 _nodesMap.clear(); 00163 _instanceCount.clear(); 00164 } 00165 00166 void Graph::connect( const std::string& outNode, const std::string& inNode, const std::string& inAttr ) 00167 { 00168 connect( getNode(outNode), getNode(inNode).getAttribute(inAttr) ); 00169 } 00170 00171 void Graph::connect( const std::list<std::string>& nodes ) 00172 { 00173 typedef std::list<std::string>::const_iterator ConstIterator; 00174 if( nodes.size() <= 1 ) 00175 BOOST_THROW_EXCEPTION( exception::Logic() 00176 << exception::user( "Needs multiple nodes to connect them together." ) ); 00177 00178 ConstIterator itA = nodes.begin(), itB = itA; 00179 ++itB; 00180 ConstIterator itEnd = nodes.end(); 00181 for( ; 00182 itB != itEnd; 00183 ++itA, ++itB ) 00184 { 00185 this->connect( *itA, *itB ); 00186 } 00187 } 00188 00189 void Graph::connect( const Node& outNode, const Node& inNode ) 00190 { 00191 connect( outNode, inNode.getSingleInputAttribute() ); 00192 } 00193 00194 void Graph::connect( const std::list<Node*>& nodes ) 00195 { 00196 typedef std::list<Node*>::const_iterator ConstIterator; 00197 if( nodes.size() <= 1 ) 00198 BOOST_THROW_EXCEPTION( exception::Logic() 00199 << exception::user( "Needs multiple nodes to connect them together." ) ); 00200 00201 ConstIterator itA = nodes.begin(), itB = itA; 00202 ++itB; 00203 ConstIterator itEnd = nodes.end(); 00204 for( ; 00205 itB != itEnd; 00206 ++itA, ++itB ) 00207 { 00208 this->connect( **itA, **itB ); 00209 } 00210 } 00211 00212 void Graph::connect( const std::vector<Node*>& nodes ) 00213 { 00214 typedef std::vector<Node*>::const_iterator ConstIterator; 00215 if( nodes.size() <= 1 ) 00216 BOOST_THROW_EXCEPTION( exception::Logic() 00217 << exception::user( "Needs multiple clips to connect them together." ) ); 00218 00219 ConstIterator itA = nodes.begin(), itB = itA; 00220 ++itB; 00221 ConstIterator itEnd = nodes.end(); 00222 for( ; 00223 itB != itEnd; 00224 ++itA, ++itB ) 00225 { 00226 BOOST_ASSERT( *itA != NULL ); 00227 BOOST_ASSERT( *itB != NULL ); 00228 00229 this->connect( **itA, **itB ); 00230 } 00231 } 00232 00233 void Graph::connect( const Node& outNode, const Attribute& inAttr ) 00234 { 00235 _graph.connect( outNode.getName(), inAttr.getNode().getName(), inAttr.getName() ); 00236 } 00237 00238 void Graph::connect( const Attribute& outAttr, const Attribute& inAttr ) 00239 { 00240 _graph.connect( outAttr.getNode().getName(), inAttr.getNode().getName(), inAttr.getName() ); 00241 } 00242 00243 void Graph::unconnect( const Attribute& outAttr, const Attribute& inAttr ) 00244 { 00245 _graph.unconnect( outAttr.getNode().getName(), inAttr.getNode().getName(), inAttr.getName() ); 00246 } 00247 00248 namespace { 00249 template<class TGraph> 00250 inline void graphConnectClips( TGraph& graph ) 00251 { 00252 BOOST_FOREACH( typename TGraph::edge_descriptor ed, graph.getEdges() ) 00253 { 00254 typename TGraph::Edge& edge = graph.instance( ed ); 00255 typename TGraph::Vertex& vertexSource = graph.sourceInstance( ed ); 00256 typename TGraph::Vertex& vertexDest = graph.targetInstance( ed ); 00257 00258 //TUTTLE_TLOG( TUTTLE_INFO, "[connectClips] " << edge ); 00259 //TUTTLE_TLOG( TUTTLE_INFO, vertexSource << "->" << vertexDest ); 00260 00261 if( ! vertexDest.isFake() && ! vertexSource.isFake() ) 00262 { 00263 INode& sourceNode = vertexSource.getProcessNode(); 00264 INode& targetNode = vertexDest.getProcessNode(); 00265 targetNode.connect( sourceNode, targetNode.getAttribute( edge.getInAttrName() ) ); 00266 } 00267 } 00268 } 00269 } 00270 00271 void Graph::init() 00272 { 00273 graphConnectClips<InternalGraphImpl>( _graph ); 00274 } 00275 00276 void Graph::unconnect( const Node& node ) 00277 { 00278 _graph.clearVertex( _graph.getVertexDescriptor(node.getName()) ); 00279 } 00280 00281 void Graph::replaceNodeConnections( const Node& fromNode, const Node& toNode ) 00282 { 00283 BOOST_FOREACH( edge_descriptor e, _graph.getInEdges( _graph.getVertexDescriptor( fromNode.getName() ) ) ) 00284 { 00285 #ifdef DEBUG 00286 // The current node should have the declared input attribute. 00287 _graph.targetInstance(e).getProcessNode().getAttribute( _graph.instance(e).getInAttrName() ); 00288 #endif 00289 // Check that the new node has all needed attributes, before to start to modify the graph. 00290 toNode.getAttribute( _graph.instance(e).getInAttrName() ); 00291 } 00292 BOOST_FOREACH( edge_descriptor e, _graph.getInEdges( _graph.getVertexDescriptor( fromNode.getName() ) ) ) 00293 { 00294 // fromNode == targetInstance 00295 // So replace targetInstance with toNode 00296 connect( 00297 _graph.sourceInstance(e).getProcessNode(), 00298 toNode.getAttribute( _graph.instance(e).getInAttrName() ) ); 00299 } 00300 BOOST_FOREACH( edge_descriptor e, _graph.getOutEdges( _graph.getVertexDescriptor( fromNode.getName() ) ) ) 00301 { 00302 // fromNode == sourceInstance 00303 // So replace sourceInstance with toNode 00304 connect( 00305 toNode, 00306 _graph.targetInstance(e).getProcessNode().getAttribute( _graph.instance(e).getInAttrName() ) ); 00307 } 00308 unconnect( fromNode ); 00309 } 00310 00311 std::size_t Graph::getNbInputConnections( const Node& node ) const 00312 { 00313 return _graph.getInDegree( _graph.getVertexDescriptor( node.getName() ) ); 00314 } 00315 00316 std::size_t Graph::getNbOutputConnections( const Node& node ) const 00317 { 00318 return _graph.getOutDegree( _graph.getVertexDescriptor( node.getName() ) ); 00319 } 00320 00321 void Graph::setup() 00322 { 00323 const ComputeOptions options; 00324 const std::list<std::string> outputNodes; 00325 graph::ProcessGraph procGraph( options, *this, outputNodes, core().getMemoryCache() ); 00326 procGraph.setup(); 00327 } 00328 00329 void Graph::setupAtTime( const OfxTime time, const NodeListArg& outputNodes ) 00330 { 00331 const ComputeOptions options; 00332 graph::ProcessGraph procGraph( options, *this, outputNodes.getNodes(), core().getMemoryCache() ); 00333 procGraph.setupAtTime( time ); 00334 } 00335 00336 void Graph::computeGlobalHashAtTime( NodeHashContainer& outNodesHash, const OfxTime time, const NodeListArg& outputNodes ) 00337 { 00338 const ComputeOptions options; 00339 graph::ProcessGraph procGraph( options, *this, outputNodes.getNodes(), core().getMemoryCache() ); 00340 procGraph.setup(); 00341 procGraph.setupAtTime( time ); 00342 procGraph.computeHashAtTime( outNodesHash, time ); 00343 } 00344 00345 bool Graph::compute( const ComputeOptions& options ) 00346 { 00347 return compute( NodeListArg(), options ); 00348 } 00349 00350 bool Graph::compute( const NodeListArg& nodes, const ComputeOptions& options ) 00351 { 00352 const_cast<ComputeOptions&>(options).setReturnBuffers( false ); 00353 00354 memory::MemoryCache emptyMemoryCache; 00355 return compute( emptyMemoryCache, nodes, options ); 00356 } 00357 00358 bool Graph::compute( memory::IMemoryCache& memoryCache, const ComputeOptions& options ) 00359 { 00360 return compute( memoryCache, NodeListArg(), options ); 00361 } 00362 00363 bool Graph::compute( memory::IMemoryCache& memoryCache, const NodeListArg& nodes, const ComputeOptions& options ) 00364 { 00365 return compute( memoryCache, nodes, options, core().getMemoryCache() ); 00366 } 00367 00368 bool Graph::compute( memory::IMemoryCache& memoryCache, const NodeListArg& nodes, 00369 const ComputeOptions& options, memory::IMemoryCache& internMemoryCache ) 00370 { 00371 #ifdef TUTTLE_EXPORT_PROCESSGRAPH_DOT 00372 graph::exportAsDOT( "graph.dot", _graph ); 00373 #endif 00374 00375 graph::ProcessGraph procGraph( options, *this, nodes.getNodes(), internMemoryCache ); 00376 return procGraph.process( memoryCache ); 00377 } 00378 00379 std::vector<const Graph::Node*> Graph::getNodes() const 00380 { 00381 std::vector<const Graph::Node*> nodes; 00382 nodes.reserve( getNodesMap().size() ); 00383 for( NodeMap::const_iterator it = getNodesMap().cbegin(), itEnd = getNodesMap().cend(); 00384 it != itEnd; 00385 ++it ) 00386 { 00387 nodes.push_back( it->second ); 00388 } 00389 return nodes; 00390 } 00391 00392 std::vector<Graph::Node*> Graph::getNodes() 00393 { 00394 std::vector<Graph::Node*> nodes; 00395 nodes.reserve( getNodesMap().size() ); 00396 for( NodeMap::iterator it = getNodesMap().begin(), itEnd = getNodesMap().end(); 00397 it != itEnd; 00398 ++it ) 00399 { 00400 nodes.push_back( it->second ); 00401 } 00402 return nodes; 00403 } 00404 00405 std::vector<Graph::Node*> Graph::getConnectedNodes( const Node& node ) 00406 { 00407 std::vector<Node*> connectedNodes; 00408 std::vector<vertex_descriptor> toRemove = _graph.getConnectedVertices( _graph.getVertexDescriptor( node.getName() ) ); 00409 connectedNodes.reserve( toRemove.size() ); 00410 00411 BOOST_FOREACH( const vertex_descriptor nodeId, toRemove ) 00412 { 00413 connectedNodes.push_back( &_graph.instance( nodeId ).getProcessNode() ); 00414 } 00415 00416 return connectedNodes; 00417 } 00418 00419 std::vector<Graph::Node*> Graph::getUnconnectedNodes( const Node& node ) 00420 { 00421 std::vector<Node*> unconnectedNodes; 00422 std::vector<vertex_descriptor> toRemove = _graph.getUnconnectedVertices( _graph.getVertexDescriptor( node.getName() ) ); 00423 unconnectedNodes.reserve( toRemove.size() ); 00424 00425 BOOST_FOREACH( const vertex_descriptor nodeId, toRemove ) 00426 { 00427 unconnectedNodes.push_back( &_graph.instance( nodeId ).getProcessNode() ); 00428 } 00429 00430 return unconnectedNodes; 00431 } 00432 00433 std::vector<Graph::Node*> Graph::getNodesByContext( const std::string& context ) 00434 { 00435 std::vector<Node*> selectedNodes; 00436 for( NodeMap::iterator it = getNodesMap().begin(), itEnd = getNodesMap().end(); 00437 it != itEnd; 00438 ++it ) 00439 { 00440 try 00441 { 00442 /// @todo tuttle: use INode here ! 00443 ImageEffectNode& ie = it->second->asImageEffectNode(); 00444 00445 if( ie.getContext() == context ) 00446 selectedNodes.push_back( &ie ); 00447 } 00448 catch(...) 00449 { 00450 } 00451 } 00452 return selectedNodes; 00453 } 00454 00455 std::vector<Graph::Node*> Graph::getNodesByPlugin( const std::string& pluginId ) 00456 { 00457 std::vector<Node*> selectedNodes; 00458 for( NodeMap::iterator it = getNodesMap().begin(), itEnd = getNodesMap().end(); 00459 it != itEnd; 00460 ++it ) 00461 { 00462 try 00463 { 00464 /// @todo tuttle: use INode here ! 00465 ImageEffectNode& ie = it->second->asImageEffectNode(); 00466 00467 if( boost::iequals( ie.getPlugin().getIdentifier(), pluginId ) ) 00468 selectedNodes.push_back( &ie ); 00469 } 00470 catch(...) 00471 { 00472 } 00473 } 00474 return selectedNodes; 00475 } 00476 00477 void Graph::exportDot( const std::string& filename, const EDotExportLevel level ) const 00478 { 00479 switch( level ) 00480 { 00481 case eDotExportLevelSimple: 00482 graph::exportAsDOT( filename, _graph ); 00483 break; 00484 case eDotExportLevelDetailed: 00485 graph::exportAsDOT( filename, _graph ); 00486 break; 00487 } 00488 } 00489 00490 std::ostream& operator<<( std::ostream& os, const Graph& g ) 00491 { 00492 os << "Graph" << std::endl 00493 << g.getGraph(); 00494 return os; 00495 } 00496 00497 } 00498 } 00499