Exemple #1
0
Gaffer::Node *DotNodeGadget::upstreamNode()
{
	Plug *plug = dotNode()->inPlug<Plug>();
	while( plug && runTimeCast<Dot>( plug->node() ) )
	{
		plug = plug->getInput<Plug>();
	}
	return plug ? plug->node() : NULL;
}
Exemple #2
0
void BoxIO::insert( Box *box )
{
	// Must take a copy of children because adding a child
	// would invalidate our PlugIterator.
	GraphComponent::ChildContainer children = box->children();
	for( PlugIterator it( children ); !it.done(); ++it )
	{
		Plug *plug = it->get();
		if( plug->direction() == Plug::In )
		{
			std::vector<Plug *> outputsNeedingBoxIn;
			const Plug::OutputContainer &outputs = plug->outputs();
			for( Plug::OutputContainer::const_iterator oIt = outputs.begin(), oeIt = outputs.end(); oIt != oeIt; ++oIt )
			{
				if( hasNodule( *oIt ) && !runTimeCast<BoxIO>( (*oIt)->node() ) )
				{
					outputsNeedingBoxIn.push_back( *oIt );
				}
			}

			if( outputsNeedingBoxIn.empty() )
			{
				continue;
			}

			BoxInPtr boxIn = new BoxIn;
			boxIn->namePlug()->setValue( plug->getName() );
			boxIn->setup( plug );
			box->addChild( boxIn );

			boxIn->inPlugInternal()->setInput( plug );
			for( std::vector<Plug *>::const_iterator oIt = outputsNeedingBoxIn.begin(), oeIt = outputsNeedingBoxIn.end(); oIt != oeIt; ++oIt )
			{
				(*oIt)->setInput( boxIn->plug() );
			}
		}
		else
		{
			// Output plug

			Plug *input = plug->getInput();
			if( !input || !hasNodule( input ) || runTimeCast<BoxIO>( input->node() ) )
			{
				continue;
			}

			BoxOutPtr boxOut = new BoxOut;
			boxOut->namePlug()->setValue( plug->getName() );
			boxOut->setup( plug );
			box->addChild( boxOut );

			boxOut->plug()->setInput( input );
			plug->setInput( boxOut->outPlugInternal() );
		}
	}

}
Exemple #3
0
void TaskNode::postTasks( const Context *context, Tasks &tasks ) const
{
	for( PlugIterator cIt( postTasksPlug() ); !cIt.done(); ++cIt )
	{
		Plug *source = (*cIt)->source();
		if( source != *cIt && source->direction() == Plug::Out )
		{
			if( TaskNodePtr n = runTimeCast<TaskNode>( source->node() ) )
			{
				tasks.push_back( Task( n, context ) );
			}
		}
	}
}
Exemple #4
0
void TaskNode::preTasks( const Context *context, Tasks &tasks ) const
{
	for( PlugIterator cIt( preTasksPlug() ); !cIt.done(); ++cIt )
	{
		Plug *source = (*cIt)->source<Plug>();
		if( source != *cIt )
		{
			if( TaskNodePtr n = runTimeCast<TaskNode>( source->node() ) )
			{
				tasks.push_back( Task( n, context ) );
			}
		}
	}
}
Exemple #5
0
void ExecutableNode::requirements( const Context *context, Tasks &requirements ) const
{
	for( PlugIterator cIt( requirementsPlug() ); cIt != cIt.end(); ++cIt )
	{
		Plug *p = (*cIt)->source<Plug>();
		if( p != *cIt )
		{
			if( ExecutableNode *n = runTimeCast<ExecutableNode>( p->node() ) )
			{
				/// \todo Can we not just reuse the context? Maybe we need to make
				/// the context in Task const?
				requirements.push_back( Task( n, new Context( *context ) ) );
			}
		}
	}
}
Exemple #6
0
		void emit()
		{
			// Because we hold a reference to the plugs via m_graph,
			// we may be the last owner. This means that when we clear
			// the graph below, those plugs may be destroyed, which can
			// trigger another dirty propagation as their child plugs are
			// removed etc.
			//
			// Additionally, emitting plugDirtiedSignal() can cause
			// ill-behaved code to trigger another dirty propagation
			// phase while we're emitting this one. This is explicitly
			// disallowed in the documentation for the Node class, but
			// unfortunately we can't control what the python interpreter
			// does - entering python via plugDirtiedSignal() can
			// trigger a garbage collection which might delete plugs
			// and trigger dirty propagation again as their children
			// and inputs are removed.
			//
			// We use the m_emitting flag to disable these unwanted
			// secondary propagations during emit(), since they're not
			// needed, and can cause crashes.

			ScopedAssignment<bool> scopedAssignment( m_emitting, true );

			std::vector<VertexDescriptor> sorted;
			try
			{
				topological_sort( m_graph, std::back_inserter( sorted ) );
			}
			catch( const std::exception &e )
			{
				IECore::msg( IECore::Msg::Error, "Plug dirty propagation", e.what() );
			}

			for( std::vector<VertexDescriptor>::const_iterator it = sorted.begin(), eIt = sorted.end(); it != eIt; ++it )
			{
				Plug *plug = m_graph[*it].get();
				plug->dirty();
				if( Node *node = plug->node() )
				{
					node->plugDirtiedSignal()( plug );
				}
			}

			m_graph.clear();
			m_plugs.clear();
		}
Exemple #7
0
static NodePtr node( Plug &p )
{
	return p.node();
}
Exemple #8
0
void PathFilter::plugDirtied( const Gaffer::Plug *plug )
{
	if( plug == pathsPlug() )
	{
		//\todo: share this logic with Switch::variesWithContext()
		Plug* sourcePlug = pathsPlug()->source();
		if( sourcePlug->direction() == Plug::Out && IECore::runTimeCast<const ComputeNode>( sourcePlug->node() ) )
		{
			// pathsPlug() is receiving data from a plug whose value is context varying, meaning
			// we need to use the intermediate pathMatcherPlug() in computeMatch() instead:

			m_pathMatcher = nullptr;
		}
		else
		{
			// pathsPlug() value is not context varying, meaning we can save on graph evaluations
			// by just precomputing it here and directly using it in computeMatch():

			ConstStringVectorDataPtr paths = pathsPlug()->getValue();
			m_pathMatcher = new PathMatcherData;
			m_pathMatcher->writable().init( paths->readable().begin(), paths->readable().end() );
		}
	}
}
Exemple #9
0
void DependencyNode::propagateDirtiness( Plug *plugToDirty )
{
	// we're not able to signal anything if there's no node, so just early out
	Node *node = plugToDirty->ancestor<Node>();
	if( !node )
	{
		return;
	}
	
	// we don't emit dirtiness immediately for each plug as we traverse the
	// dependency graph for two reasons :
	//
	// - we don't want to emit dirtiness for the same plug more than once
	// - we don't want to emit dirtiness while the graph may still be being
	//   rewired by slots connected to plugSetSignal() or plugInputChangedSignal()
	//
	// instead we collect all the dirty plugs in a container as we traverse
	// the graph and only when the traversal is complete do we emit the plugDirtiedSignal().
	//
	// the container used is stored per-thread as although it's illegal to be
	// monkeying with a script from multiple threads, it's perfectly legal to
	// be monkeying with a different script in each thread.
	
	DirtyPlugsContainer &dirtyPlugs = g_dirtyPlugsContainers.local();
	
	// if the container is currently empty then we are at the start of a traversal,
	// and will emit plugDirtiedSignal() and empty the container before returning
	// from this function. if the container isn't empty then we are mid-traversal
	// and will just add to it.
	const bool emit = dirtyPlugs.empty();

	Plug *p = plugToDirty;
	while( p )
	{
		dirtyPlugs.insert( p );
		p = p->parent<Plug>();
	}	
	
	// we only propagate dirtiness along leaf level plugs, because
	// they are the only plugs which can be the target of the affects(),
	// and compute() methods.
	if( !plugToDirty->isInstanceOf( (IECore::TypeId)CompoundPlugTypeId ) )
	{
		DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node );
		if( dependencyNode )
		{
			AffectedPlugsContainer affected;
			dependencyNode->affects( plugToDirty, affected );
			for( AffectedPlugsContainer::const_iterator it=affected.begin(); it!=affected.end(); it++ )
			{
				if( ( *it )->isInstanceOf( (IECore::TypeId)Gaffer::CompoundPlugTypeId ) )
				{
					// DependencyNode::affects() implementations are only allowed to place leaf plugs in the outputs,
					// so we helpfully report any mistakes.
					dirtyPlugs.clear();
					throw IECore::Exception( "Non-leaf plug " + (*it)->fullName() + " cannot be returned by affects()" );
				}
				// cast is ok - AffectedPlugsContainer only holds const pointers so that
				// affects() can be const to discourage implementations from having side effects.
				propagateDirtiness( const_cast<Plug *>( *it ) );
			}
		}
	
		for( Plug::OutputContainer::const_iterator it=plugToDirty->outputs().begin(), eIt=plugToDirty->outputs().end(); it!=eIt; ++it )
		{
			propagateDirtiness( *it );
		}		
	}
	
	if( emit )
	{
		for( size_t i = 0, e = dirtyPlugs.size(); i < e; ++i )
		{
			Plug *plug = dirtyPlugs.get<1>()[i];
			Node *node = plug->node();
			if( node )
			{
				node->plugDirtiedSignal()( plug );
			}
		}
		dirtyPlugs.clear();
	}
}
Exemple #10
0
BoxPtr Box::create( Node *parent, const Set *childNodes )
{
	BoxPtr result = new Box;
	parent->addChild( result );

	// it's pretty natural to call this function passing childNodes == ScriptNode::selection().
	// unfortunately nodes will be removed from the selection as we reparent
	// them, so we have to make a copy of childNodes so our iteration isn't befuddled by
	// the changing contents. we can use this opportunity to weed out anything in childNodes
	// which isn't a direct child of parent though.
	StandardSetPtr verifiedChildNodes = new StandardSet();
	for( NodeIterator nodeIt( parent ); nodeIt != nodeIt.end(); nodeIt++ )
	{
		if( childNodes->contains( nodeIt->get() ) )
		{
			verifiedChildNodes->add( *nodeIt );
		}
	}

	// when a node we're putting in the box has connections to
	// a node remaining outside, we need to reroute the connection
	// via an intermediate plug on the box. this mapping maps input
	// plugs (be they internal or external) to intermediate input plugs.
	typedef std::pair<const Plug *, Plug *> PlugPair;
	typedef std::map<const Plug *, Plug *> PlugMap;
	PlugMap plugMap;

	for( size_t i = 0, e = verifiedChildNodes->size(); i < e; i++ )
	{
		Node *childNode = static_cast<Node *>( verifiedChildNodes->member( i ) );
		// reroute any connections to external nodes
		for( RecursivePlugIterator plugIt( childNode ); plugIt != plugIt.end(); plugIt++ )
		{
			Plug *plug = plugIt->get();
			if( plug->direction() == Plug::In )
			{
				Plug *input = plug->getInput<Plug>();
				if( input && !verifiedChildNodes->contains( input->node() ) )
				{
					PlugMap::const_iterator mapIt = plugMap.find( input );
					if( mapIt == plugMap.end() )
					{
						PlugPtr intermediateInput = plug->createCounterpart( result->promotedCounterpartName( plug ), Plug::In );
						// we want intermediate inputs to appear on the same side of the node as the
						// equivalent internal plug, so we copy the relevant metadata over.
						copyMetadata( plug, intermediateInput.get() );
						intermediateInput->setFlags( Plug::Dynamic, true );
						result->addChild( intermediateInput );
						intermediateInput->setInput( input );
						mapIt = plugMap.insert( PlugPair( input, intermediateInput.get() ) ).first;
					}
					plug->setInput( mapIt->second );
					plugIt.prune();
				}
			}
			else
			{
				// take a copy of the outputs, because we might be modifying the
				// original as we iterate.
				Plug::OutputContainer outputs = plug->outputs();
				if( !outputs.empty() )
				{
					typedef Plug::OutputContainer::const_iterator OutputIterator;
					for( OutputIterator oIt = outputs.begin(), eIt = outputs.end(); oIt != eIt; oIt++ )
					{
						Plug *output = *oIt;
						const Node *outputNode = output->node();
						if( outputNode->parent<Node>() == parent && !verifiedChildNodes->contains( outputNode ) )
						{
							PlugMap::const_iterator mapIt = plugMap.find( plug );
							if( mapIt == plugMap.end() )
							{
								PlugPtr intermediateOutput = plug->createCounterpart( result->promotedCounterpartName( plug ), Plug::Out );
								copyMetadata( plug, intermediateOutput.get() );
								intermediateOutput->setFlags( Plug::Dynamic, true );
								result->addChild( intermediateOutput );
								intermediateOutput->setInput( plug );
								mapIt = plugMap.insert( PlugPair( plug, intermediateOutput.get() ) ).first;
							}
							output->setInput( mapIt->second );
						}
					}
					plugIt.prune();
				}
			}
		}
		// reparent the child under the Box. it's important that we do this after adding the intermediate
		// input plugs, so that when they are serialised and reloaded, the inputs to the box are set before
		// the inputs to the nodes inside the box - see GafferSceneTest.ShaderAssignmentTest.testAssignShaderFromOutsideBox
		// for a test case highlighting this necessity.
		result->addChild( childNode );
	}

	return result;
}