void Expression::updatePlug( ValuePlug *parentPlug, size_t childIndex, ValuePlug *plug ) { if( parentPlug->children().size() > childIndex ) { // See if we can reuse the existing plug Plug *existingChildPlug = parentPlug->getChild<Plug>( childIndex ); if( ( existingChildPlug->direction() == Plug::In && existingChildPlug->getInput<Plug>() == plug ) || ( existingChildPlug->direction() == Plug::Out && plug->getInput<Plug>() == existingChildPlug ) ) { return; } } // Existing plug not OK, so we need to create one. First we must remove all // plugs from childIndex onwards, so that when we add the new plug it gets // the right index. removeChildren( parentPlug, childIndex ); // Finally we can add the plug we need. PlugPtr childPlug = plug->createCounterpart( "p0", parentPlug->direction() ); childPlug->setFlags( Plug::Dynamic, true ); parentPlug->addChild( childPlug ); if( childPlug->direction() == Plug::In ) { childPlug->setInput( plug ); } else { plug->setInput( childPlug ); } }
bool DotNodeGadget::drop( const DragDropEvent &event ) { if( dotNode()->inPlug<Plug>() ) { // We've already got our plugs set up - StandardNodeGadget // behaviour will take care of everything. return false; } Plug *plug = runTimeCast<Plug>( event.data.get() ); if( !plug ) { return false; } Gaffer::UndoScope undoEnabler( node()->ancestor<ScriptNode>() ); dotNode()->setup( plug ); if( plug->direction() == Plug::In ) { plug->setInput( dotNode()->outPlug<Plug>() ); } else { dotNode()->inPlug<Plug>()->setInput( plug ); } return true; }
void ScriptingKeyframesBool::set_key (void) { PROFILER(SCRIPTING); const std::vector<PlugBase*> &outgoing = _out.outgoing_connections(); if (outgoing.size() > 0) { // get first connected plug Plug<DTboolean> *outplug = static_cast<Plug<DTboolean>*>(*(outgoing.begin())); // get the value of the first connected plug DTboolean val = outplug->value_without_compute(); // clear any existing key clear_key(); // add the keyframe keyframe k; k._time = _t; k._value = val; k._id = _id++; _keyframes.push_back(k); std::sort(_keyframes.begin(), _keyframes.end()); } }
void Plug::removeOutputs() { for( OutputContainer::iterator it = m_outputs.begin(); it!=m_outputs.end(); ) { Plug *p = *it++; p->setInput( 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; }
void BoxIO::setupPromotedPlug() { Plug *toPromote = m_direction == Plug::In ? inPlugInternal() : outPlugInternal(); if( toPromote && parent<Box>() ) { Plug *promoted = PlugAlgo::promoteWithName( toPromote, namePlug()->getValue() ); namePlug()->setValue( promoted->getName() ); } }
std::vector<NoduleLayout::GadgetKey> NoduleLayout::layoutOrder() { typedef pair<int, GadgetKey> SortItem; vector<SortItem> toSort; // Add any plugs which should be visible for( PlugIterator plugIt( m_parent.get() ); !plugIt.done(); ++plugIt ) { Plug *plug = plugIt->get(); if( boost::starts_with( plug->getName().string(), "__" ) ) { continue; } if( !::visible( plug, m_section ) ) { continue; } toSort.push_back( SortItem( index( plug, toSort.size() ), plug ) ); } // Then any custom gadgets specified by the metadata vector<InternedString> metadata; Metadata::registeredValues( m_parent.get(), metadata ); boost::regex customGadgetRegex( "noduleLayout:customGadget:(.+):gadgetType" ); for( vector<InternedString>::const_iterator it = metadata.begin(), eIt = metadata.end(); it != eIt; ++it ) { boost::cmatch match; if( !boost::regex_match( it->c_str(), match, customGadgetRegex ) ) { continue; } const InternedString name = match[1].str(); if( !::visible( m_parent.get(), name, m_section ) ) { continue; } toSort.push_back( SortItem( index( m_parent.get(), name, toSort.size() ), name ) ); } // Sort and return the result sort( toSort.begin(), toSort.end() ); vector<GadgetKey> result; result.reserve( toSort.size() ); for( vector<SortItem>::const_iterator it = toSort.begin(), eIt = toSort.end(); it != eIt; ++it ) { result.push_back( it->second ); } return result; }
void ArnoldShader::loadShader( const std::string &shaderName, bool keepExistingValues ) { IECoreArnold::UniverseBlock arnoldUniverse( /* writable = */ false ); const AtNodeEntry *shader = AiNodeEntryLookUp( AtString( shaderName.c_str() ) ); if( !shader ) { throw Exception( str( format( "Shader \"%s\" not found" ) % shaderName ) ); } Plug *parametersPlug = this->parametersPlug()->source<Plug>(); if( !keepExistingValues ) { parametersPlug->clearChildren(); if( Plug *out = outPlug() ) { removeChild( out ); } } const bool isLightShader = AiNodeEntryGetType( shader ) == AI_NODE_LIGHT; namePlug()->setValue( AiNodeEntryGetName( shader ) ); int aiOutputType = AI_TYPE_POINTER; string type = "ai:light"; if( !isLightShader ) { const CompoundData *metadata = ArnoldShader::metadata(); const StringData *shaderTypeData = static_cast<const StringData*>( metadata->member<IECore::CompoundData>( "shader" )->member<IECore::Data>( "shaderType" ) ); if( shaderTypeData ) { type = "ai:" + shaderTypeData->readable(); } else { type = "ai:surface"; } if( type == "ai:surface" ) { aiOutputType = AiNodeEntryGetOutputType( shader ); } } if( !keepExistingValues && type == "ai:lightFilter" ) { attributeSuffixPlug()->setValue( shaderName ); } typePlug()->setValue( type ); ParameterHandler::setupPlugs( shader, parametersPlug ); ParameterHandler::setupPlug( "out", aiOutputType, this, Plug::Out ); }
void Expression::removeChildren( ValuePlug *parentPlug, size_t startChildIndex ) { // Remove backwards, because children() is a vector and // it's therefore cheaper to remove from the end. for( int i = (int)(parentPlug->children().size() ) - 1; i >= (int)startChildIndex; --i ) { Plug *toRemove = parentPlug->getChild<Plug>( i ); toRemove->removeOutputs(); parentPlug->removeChild( toRemove ); } }
void ScriptNode::deleteNodes( Node *parent, const Set *filter, bool reconnect ) { parent = parent ? parent : this; // because children are stored as a vector, it's // much more efficient to delete those at the end before // those at the beginning. int i = (int)(parent->children().size()) - 1; while( i >= 0 ) { Node *node = parent->getChild<Node>( i ); if( node && ( !filter || filter->contains( node ) ) ) { // reconnect the inputs and outputs as though the node was disabled DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node ); if( reconnect && dependencyNode ) { for( OutputPlugIterator it( node ); it != it.end(); ++it ) { Plug *inPlug = dependencyNode->correspondingInput( it->get() ); if ( !inPlug ) { continue; } Plug *srcPlug = inPlug->getInput<Plug>(); if ( !srcPlug ) { continue; } // record this plug's current outputs, and reconnect them. This is a copy of (*it)->outputs() rather // than a reference, as reconnection can modify (*it)->outputs()... Plug::OutputContainer outputs = (*it)->outputs(); for ( Plug::OutputContainer::const_iterator oIt = outputs.begin(); oIt != outputs.end(); ) { Plug *dstPlug = *oIt; if ( dstPlug && dstPlug->acceptsInput( srcPlug ) && this->isAncestorOf( dstPlug ) ) { oIt++; dstPlug->setInput( srcPlug ); } else { oIt++; } } } } parent->removeChild( node ); } i--; } }
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() ); } } }
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 ) ); } } } }
void CompoundNumericPlug<T>::ungang() { for( size_t i = 1, e = children().size(); i < e; ++i ) { Plug *child = getChild( i ); if( const Plug *input = child->getInput<Plug>() ) { if( input->parent<Plug>() == this ) { child->setInput( 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 ) ); } } } }
static void loadCoshaderParameter( Gaffer::CompoundPlug *parametersPlug, const std::string &name ) { Plug *existingPlug = parametersPlug->getChild<Plug>( name ); if( existingPlug && existingPlug->typeId() == Plug::staticTypeId() ) { return; } PlugPtr plug = new Plug( name, Plug::In, Plug::Default | Plug::Dynamic ); if( existingPlug && existingPlug->getInput<Plug>() ) { plug->setInput( existingPlug->getInput<Plug>() ); } parametersPlug->setChild( name, plug ); }
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 ) ) ); } } } }
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(); }
void BoxIO::plugInputChanged( Plug *plug ) { // An input has changed either on this node or on // the parent box node. This gives us the opportunity // to discover our promoted plug and connect to its // signals. Plug *promoted = nullptr; if( m_direction == Plug::In && plug == inPlugInternal() ) { promoted = promotedPlug(); } else if( m_direction == Plug::Out && plug == promotedPlug() ) { promoted = plug; } if( promoted ) { m_promotedPlugNameChangedConnection = promoted->nameChangedSignal().connect( boost::bind( &BoxIO::promotedPlugNameChanged, this, ::_1 ) ); m_promotedPlugParentChangedConnection = promoted->parentChangedSignal().connect( boost::bind( &BoxIO::promotedPlugParentChanged, this, ::_1 ) ); } // Detect manual setups created by legacy scripts from before // we added the pass-through, and fix them to include a pass-through. if( m_direction == Plug::Out && plug == outPlugInternal() && plug->getInput() == inPlugInternal() && !passThroughPlugInternal() ) { setupPassThrough(); } // If a connection has been made to our passThrough plug // for the first time, then we also want to create an enabled // plug for the Box and connect to it. if( plug == passThroughPlugInternal() && passThroughPlugInternal()->getInput() ) { setupBoxEnabledPlug(); } }
static Plug *loadClosureParameter( const OSLQuery::Parameter *parameter, Gaffer::CompoundPlug *parent ) { const string name = plugName( parameter ); Plug *existingPlug = parent->getChild<Plug>( name ); if( existingPlug && existingPlug->typeId() == Plug::staticTypeId() ) { return existingPlug; } PlugPtr plug = new Plug( name, parent->direction(), Plug::Default | Plug::Dynamic ); transferConnectionOrValue( existingPlug, plug.get() ); parent->setChild( name, plug ); return plug; }
static boost::python::tuple outputs( Plug &p ) { const Plug::OutputContainer &o = p.outputs(); boost::python::list l; for( Plug::OutputContainer::const_iterator it=o.begin(); it!=o.end(); it++ ) { l.append( PlugPtr( *it ) ); } return boost::python::tuple( l ); }
void OSLShader::loadShader( const std::string &shaderName, bool keepExistingValues ) { const char *searchPath = getenv( "OSL_SHADER_PATHS" ); OSLQuery query; if( !query.open( shaderName, searchPath ? searchPath : "" ) ) { throw Exception( query.error() ); } loadShaderParameters( query, parametersPlug(), keepExistingValues ); if( query.shadertype() == "shader" ) { CompoundPlug *existingOut = getChild<CompoundPlug>( "out" ); if( !existingOut || existingOut->typeId() != CompoundPlug::staticTypeId() ) { CompoundPlugPtr outPlug = new CompoundPlug( "out", Plug::Out, Plug::Default | Plug::Dynamic ); setChild( "out", outPlug ); } loadShaderParameters( query, getChild<CompoundPlug>( "out" ), keepExistingValues ); } else { Plug *existingOut = getChild<Plug>( "out" ); if( !existingOut || existingOut->typeId() != Plug::staticTypeId() ) { PlugPtr outPlug = new Plug( "out", Plug::Out, Plug::Default | Plug::Dynamic ); setChild( "out", outPlug ); } } namePlug()->setValue( shaderName ); typePlug()->setValue( "osl:" + query.shadertype() ); m_metadata = NULL; }
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() ); } } }
void Scene::removeEdge(PlugEdge* edge) { Plug* fromPlug = edge->getStartPlug(); Plug* toPlug = edge->getEndPlug(); QPair<Plug*, Plug*> edgeKey(fromPlug, toPlug); #ifdef QT_DEBUG Q_ASSERT(m_edges.contains(edgeKey)); #else if(!m_edges.contains(edgeKey)){ return; } #endif // unregister from the connected plugs fromPlug->removeEdge(edge); toPlug->removeEdge(edge); // remove the edge from the Scene's register m_edges.remove(edgeKey); // remove the edge from its group EdgeGroup* edgeGroup = edge->getGroup(); edgeGroup->removeEdge(edge); // if the group is now empty, we can only delete it if the other group in the pair is also empty EdgeGroupPair* edgeGroupPair = edgeGroup->getEdgeGroupPair(); if(edgeGroupPair->isEmpty()){ uint firstHash = edgeGroupPair->getFirstGroup()->getHash(); uint secondHash = edgeGroupPair->getSecondGroup()->getHash(); Q_ASSERT(m_edgeGroups.contains(firstHash)); Q_ASSERT(m_edgeGroups.contains(secondHash)); m_edgeGroups.remove(firstHash); m_edgeGroups.remove(secondHash); m_edgeGroupPairs.remove(edgeGroupPair); delete edgeGroupPair; // also deletes the EdgeGroups edgeGroupPair = nullptr; } // lastly, remove the QGraphicsItem from the scene, thereby taking possession of the last pointer to the edge removeItem(edge); // delete the edge from memory (automatically deletes all Qt-children as well) edge->deleteLater(); // emit signals emit fromPlug->getNode()->outputDisconnected(fromPlug, toPlug); emit toPlug->getNode()->inputDisconnected(toPlug, fromPlug); }
ArnoldMeshLight::ArnoldMeshLight( const std::string &name ) : GafferScene::FilteredSceneProcessor( name, IECore::PathMatcher::NoMatch ) { // ArnoldAttributesNode. This hides the objects from the majority // of ray types, since we don't want to add the poor sampling of the // object on top of the nice sampling of the light. The only visibility // option we don't turn off is camera visibility - instead we promote // so the user can decide whether or not the mesh should be visible in // the render. ArnoldAttributesPtr attributes = new ArnoldAttributes( "__attributes" ); attributes->inPlug()->setInput( inPlug() ); attributes->filterPlug()->setInput( filterPlug() ); for( CompoundDataPlug::MemberPlugIterator it( attributes->attributesPlug() ); !it.done(); ++it ) { if( boost::ends_with( (*it)->getName().string(), "Visibility" ) && (*it)->getName() != "cameraVisibility" ) { (*it)->enabledPlug()->setValue( true ); (*it)->valuePlug<BoolPlug>()->setValue( false ); } } addChild( attributes ); Plug *internalCameraVisibilityPlug = attributes->attributesPlug()->getChild<Plug>( "cameraVisibility" ); PlugPtr cameraVisibilityPlug = internalCameraVisibilityPlug->createCounterpart( "cameraVisibility", Plug::In ); addChild( cameraVisibilityPlug ); internalCameraVisibilityPlug->setInput( cameraVisibilityPlug ); // Shader node. This loads the Arnold mesh_light shader. ArnoldShaderPtr shader = new ArnoldShader( "__shader" ); shader->loadShader( "mesh_light" ); addChild( shader ); PlugPtr parametersPlug = shader->parametersPlug()->createCounterpart( "parameters", Plug::In ); addChild( parametersPlug ); for( PlugIterator srcIt( parametersPlug.get() ), dstIt( shader->parametersPlug() ); !srcIt.done(); ++srcIt, ++dstIt ) { (*dstIt)->setInput( *srcIt ); // We don't need the parameters to be dynamic, because we create the // plugs in our constructor when calling `loadShader()`. (*srcIt)->setFlags( Plug::Dynamic, false ); } // ShaderAssignment node. This assigns the mesh_light shader // to the objects chosen by the filter. ShaderAssignmentPtr shaderAssignment = new ShaderAssignment( "__shaderAssignment" ); shaderAssignment->inPlug()->setInput( attributes->outPlug() ); shaderAssignment->filterPlug()->setInput( filterPlug() ); shaderAssignment->shaderPlug()->setInput( shader->outPlug() ); addChild( shaderAssignment ); // Set node. This adds the objects into the __lights set, // so they will be output correctly to the renderer. SetPtr set = new Set( "__set" ); set->inPlug()->setInput( shaderAssignment->outPlug() ); set->filterPlug()->setInput( filterPlug() ); set->namePlug()->setValue( "__lights" ); set->modePlug()->setValue( Set::Add ); addChild( set ); // Default lights Set node. BoolPlugPtr defaultLightPlug = new BoolPlug( "defaultLight", Plug::In, true ); addChild( defaultLightPlug ); SetPtr defaultLightsSet = new Set( "__defaultLightsSet" ); defaultLightsSet->inPlug()->setInput( set->outPlug() ); defaultLightsSet->filterPlug()->setInput( filterPlug() ); defaultLightsSet->enabledPlug()->setInput( defaultLightPlug.get() ); defaultLightsSet->namePlug()->setValue( "defaultLights" ); defaultLightsSet->modePlug()->setValue( Set::Add ); addChild( defaultLightsSet ); // Switch for enabling/disabling SwitchPtr enabledSwitch = new Switch( "__switch" ); enabledSwitch->setup( inPlug() ); enabledSwitch->inPlugs()->getChild<ScenePlug>( 0 )->setInput( inPlug() ); enabledSwitch->inPlugs()->getChild<ScenePlug>( 1 )->setInput( defaultLightsSet->outPlug() ); enabledSwitch->indexPlug()->setValue( 1 ); enabledSwitch->enabledPlug()->setInput( enabledPlug() ); addChild( enabledSwitch ); outPlug()->setInput( enabledSwitch->outPlug() ); // We don't need to serialise the connection because we make // it upon construction. /// \todo Can we just do this in the SceneProcessor base class? outPlug()->setFlags( Plug::Serialisable, false ); }
void OSLShader::loadShader( const std::string &shaderName, bool keepExistingValues ) { Plug *existingOut = outPlug(); if( shaderName.empty() ) { parametersPlug()->clearChildren(); namePlug()->setValue( "" ); typePlug()->setValue( "" ); if( existingOut ) { existingOut->clearChildren(); } return; } const char *searchPath = getenv( "OSL_SHADER_PATHS" ); OSLQuery query; if( !query.open( shaderName, searchPath ? searchPath : "" ) ) { throw Exception( query.geterror() ); } const bool outPlugHadChildren = existingOut ? existingOut->children().size() : false; if( !keepExistingValues ) { // If we're not preserving existing values then remove all existing // parameter plugs - the various plug creators above know that if a // plug exists then they should preserve its values. parametersPlug()->clearChildren(); if( existingOut ) { existingOut->clearChildren(); } } m_metadata = NULL; namePlug()->setValue( shaderName ); typePlug()->setValue( std::string( "osl:" ) + query.shadertype().c_str() ); const IECore::CompoundData *metadata = OSLShader::metadata(); const IECore::CompoundData *parameterMetadata = NULL; if( metadata ) { parameterMetadata = metadata->member<IECore::CompoundData>( "parameter" ); } loadShaderParameters( query, parametersPlug(), parameterMetadata ); if( !existingOut || existingOut->typeId() != Plug::staticTypeId() ) { PlugPtr outPlug = new Plug( "out", Plug::Out, Plug::Default | Plug::Dynamic ); if( existingOut ) { // We had an out plug but it was the wrong type (we used // to use a CompoundPlug before that was deprecated). Move // over any existing child plugs onto our replacement. for( PlugIterator it( existingOut ); !it.done(); ++it ) { outPlug->addChild( *it ); } } setChild( "out", outPlug ); } if( query.shadertype() == "shader" ) { loadShaderParameters( query, outPlug(), parameterMetadata ); } else { outPlug()->clearChildren(); } if( static_cast<bool>( outPlug()->children().size() ) != outPlugHadChildren ) { // OSLShaderUI registers a dynamic metadata entry which depends on whether or // not the plug has children, so we must notify the world that the value will // have changed. Metadata::plugValueChangedSignal()( staticTypeId(), "out", "nodule:type", outPlug() ); } }
static NodePtr node( Plug &p ) { return p.node(); }
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(); } }
void Reference::load( const std::string &fileName ) { ScriptNode *script = scriptNode(); if( !script ) { throw IECore::Exception( "Reference::load called without ScriptNode" ); } // if we're doing a reload, then we want to maintain any values and // connections that our external plugs might have. but we also need to // get those existing plugs out of the way during the load, so that the // incoming plugs don't get renamed. std::map<std::string, Plug *> previousPlugs; for( PlugIterator it( this ); it != it.end(); ++it ) { Plug *plug = it->get(); if( isReferencePlug( plug ) ) { previousPlugs[plug->getName()] = plug; plug->setName( "__tmp__" + plug->getName().string() ); } } for( PlugIterator it( userPlug() ); it != it.end(); ++it ) { Plug *plug = it->get(); previousPlugs[plug->relativeName( this )] = plug; plug->setName( "__tmp__" + plug->getName().string() ); } // if we're doing a reload, then we also need to delete all our child // nodes to make way for the incoming nodes. int i = (int)(children().size()) - 1; while( i >= 0 ) { if( Node *node = getChild<Node>( i ) ) { removeChild( node ); } i--; } // load the reference. we use continueOnError=true to get everything possible // loaded, but if any errors do occur we throw an exception at the end of this // function. this means that the caller is still notified of errors via the // exception mechanism, but we leave ourselves in the best state possible for // the case where ScriptNode::load( continueOnError = true ) will ignore the // exception that we throw. const bool errors = script->executeFile( fileName, this, /* continueOnError = */ true ); fileNamePlug()->setValue( fileName ); // transfer connections and values from the old plugs onto the corresponding new ones. for( std::map<std::string, Plug *>::const_iterator it = previousPlugs.begin(), eIt = previousPlugs.end(); it != eIt; ++it ) { Plug *oldPlug = it->second; Plug *newPlug = descendant<Plug>( it->first ); if( newPlug ) { try { if( newPlug->direction() == Plug::In && oldPlug->direction() == Plug::In ) { if( Plug *oldInput = oldPlug->getInput<Plug>() ) { newPlug->setInput( oldInput ); } else { ValuePlug *oldValuePlug = runTimeCast<ValuePlug>( oldPlug ); ValuePlug *newValuePlug = runTimeCast<ValuePlug>( newPlug ); if( oldValuePlug && newValuePlug ) { newValuePlug->setFrom( oldValuePlug ); } } } else if( newPlug->direction() == Plug::Out && oldPlug->direction() == Plug::Out ) { for( Plug::OutputContainer::const_iterator oIt = oldPlug->outputs().begin(), oeIt = oldPlug->outputs().end(); oIt != oeIt; ) { Plug *outputPlug = *oIt; ++oIt; // increment now because the setInput() call invalidates our iterator. outputPlug->setInput( newPlug ); } } } catch( const std::exception &e ) { msg( Msg::Warning, boost::str( boost::format( "Loading \"%s\" onto \"%s\"" ) % fileName % getName().c_str() ), e.what() ); } } // remove the old plug now we're done with it. oldPlug->parent<GraphComponent>()->removeChild( oldPlug ); } // make the loaded plugs non-dynamic, because we don't want them // to be serialised in the script the reference is in - the whole // point is that they are referenced. for( RecursivePlugIterator it( this ); it != it.end(); ++it ) { if( isReferencePlug( it->get() ) ) { (*it)->setFlags( Plug::Dynamic, false ); } } if( errors ) { throw Exception( boost::str( boost::format( "Error loading reference \"%s\"" ) % fileName ) ); } }
void ScriptNode::deleteNodes( Node *parent, const Set *filter, bool reconnect ) { parent = parent ? parent : this; // because children are stored as a vector, it's // much more efficient to delete those at the end before // those at the beginning. int i = (int)(parent->children().size()) - 1; while( i >= 0 ) { Node *node = parent->getChild<Node>( i ); if( node && ( !filter || filter->contains( node ) ) ) { // reconnect the inputs and outputs as though the node was disabled DependencyNode *dependencyNode = IECore::runTimeCast<DependencyNode>( node ); if( reconnect && dependencyNode ) { for( RecursiveOutputPlugIterator it( node ); !it.done(); ++it ) { Plug *inPlug = nullptr; try { inPlug = dependencyNode->correspondingInput( it->get() ); } catch( const std::exception &e ) { msg( IECore::Msg::Warning, boost::str( boost::format( "correspondingInput error while deleting - cannot reconnect \"%s\"" ) % it->get()->fullName() ), e.what() ); } if ( !inPlug ) { continue; } Plug *srcPlug = inPlug->getInput(); if ( !srcPlug ) { continue; } // record this plug's current outputs, and reconnect them. This is a copy of (*it)->outputs() rather // than a reference, as reconnection can modify (*it)->outputs()... Plug::OutputContainer outputs = (*it)->outputs(); for ( Plug::OutputContainer::const_iterator oIt = outputs.begin(); oIt != outputs.end(); ++oIt ) { Plug *dstPlug = *oIt; if ( dstPlug && dstPlug->acceptsInput( srcPlug ) && this->isAncestorOf( dstPlug ) ) { dstPlug->setInput( srcPlug ); } } } } parent->removeChild( node ); } i--; } }
void Plug::parentChanging( Gaffer::GraphComponent *newParent ) { if( getFlags( Dynamic ) ) { // When a dynamic plug is removed from a node, we // need to propagate dirtiness based on that. We // must call DependencyNode::affects() now, while the // plug is still a child of the node, but we push // scope so that the emission of plugDirtiedSignal() // is deferred until parentChanged() when the operation // is complete. It is essential that exceptions don't // prevent us getting to parentChanged() where we pop // scope, so propateDirtiness() takes care of handling // exceptions thrown by DependencyNode::affects(). pushDirtyPropagationScope(); if( node() ) { propagateDirtinessForParentChange( this ); } } // This method manages the connections between plugs when // additional child plugs are added or removed. We only // want to react to these changes when they are first made - // after this our own actions will have been recorded in the // undo buffer anyway and will be undone/redone automatically. // So here we early out if we're in such an Undo/Redo situation. ScriptNode *scriptNode = ancestor<ScriptNode>(); scriptNode = scriptNode ? scriptNode : ( newParent ? newParent->ancestor<ScriptNode>() : NULL ); if( scriptNode && ( scriptNode->currentActionStage() == Action::Undo || scriptNode->currentActionStage() == Action::Redo ) ) { return; } // Now we can take the actions we need to based on the new parent // we're getting. if( !newParent ) { // We're losing our parent - remove all our connections first. // this must be done here (rather than in a parentChangedSignal() slot) // because we need a current parent for the operation to be undoable. setInput( 0 ); // Deal with outputs whose parent is an output of our parent. // For these we actually remove the destination plug itself, // so that the parent plugs may remain connected. if( Plug *oldParent = parent<Plug>() ) { for( OutputContainer::iterator it = m_outputs.begin(); it!=m_outputs.end(); ) { Plug *output = *it++; Plug *outputParent = output->parent<Plug>(); if( outputParent && outputParent->getInput<Plug>() == oldParent ) { // We're removing the child precisely so that the parent connection // remains valid, so we can block its updateInputFromChildInputs() call. assert( outputParent->m_skipNextUpdateInputFromChildInputs == false ); ScopedAssignment<bool> blocker( outputParent->m_skipNextUpdateInputFromChildInputs, true ); outputParent->removeChild( output ); } } } // Remove any remaining output connections. removeOutputs(); } else if( Plug *newParentPlug = IECore::runTimeCast<Plug>( newParent ) ) { // we're getting a new parent - update its input connection from // all the children including the pending one. newParentPlug->updateInputFromChildInputs( this ); // and add a new child plug to any of its outputs to maintain // the output connections. const OutputContainer &outputs = newParentPlug->outputs(); for( OutputContainer::const_iterator it = outputs.begin(), eIt = outputs.end(); it != eIt; ++it ) { Plug *output = *it; if( output->acceptsChild( this ) ) { PlugPtr outputChildPlug = createCounterpart( getName(), direction() ); { // We're adding the child so that the parent connection remains valid, // but the parent connection wouldn't be considered valid until the // child has both been added and had its input connected. We therefore // block the call to updateInputFromChildInputs() to keep the parent // connection intact. assert( output->m_skipNextUpdateInputFromChildInputs == false ); ScopedAssignment<bool> blocker( output->m_skipNextUpdateInputFromChildInputs, true ); output->addChild( outputChildPlug ); } outputChildPlug->setInput( this, /* setChildInputs = */ true, /* updateParentInput = */ false ); } } } }