TuttleOFX  1
TuttleOFX/libraries/tuttle/src/tuttle/host/Graph.cpp
Go to the documentation of this file.
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