TuttleOFX  1
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/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