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