Skip to content

Commit

Permalink
Switch : Add connectedInputs plug
Browse files Browse the repository at this point in the history
Fixes #1797.
  • Loading branch information
johnhaddon committed Dec 18, 2023
1 parent 772e296 commit 1c0ed64
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 2 deletions.
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Improvements
- Cache : Increased default computation cache size to 8Gb. Call `Gaffer.ValuePlug.setCacheMemoryLimit()` from a startup file to override this.
- Dispatcher : Reduced internal overhead of `dispatch()` call, with one benchmark showing around a 3x speedup.
- ScriptWindow : Added "Save" option to dialogue shown when closing a window containing unsaved changes.
- Switch : Added `connectedInputs` output plug.

Fixes
-----
Expand Down
4 changes: 4 additions & 0 deletions include/Gaffer/Switch.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "Gaffer/ArrayPlug.h"
#include "Gaffer/ComputeNode.h"
#include "Gaffer/NumericPlug.h"
#include "Gaffer/TypedObjectPlug.h"

namespace Gaffer
{
Expand Down Expand Up @@ -87,6 +88,9 @@ class GAFFER_API Switch : public ComputeNode
BoolPlug *enabledPlug() override;
const BoolPlug *enabledPlug() const override;

IntVectorDataPlug *connectedInputsPlug();
const IntVectorDataPlug *connectedInputsPlug() const;

Plug *correspondingInput( const Plug *output ) override;
const Plug *correspondingInput( const Plug *output ) const override;

Expand Down
40 changes: 40 additions & 0 deletions python/GafferTest/NameSwitchTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,45 @@ def testUnnamedRowsNeverMatch( self ) :
del c["selector"]
self.assertEqual( s["out"]["value"].getValue(), 1 )

def testConnectedInputs( self ) :

switch = Gaffer.NameSwitch()
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData() )

switch.setup( Gaffer.IntPlug() )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData() )

inputA = Gaffer.IntPlug()
switch["in"][0]["value"].setInput( inputA )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData( [ 0 ] ) )

switch["in"][1]["value"].setInput( inputA )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData( [ 0, 1 ] ) )

switch["in"][0]["value"].setInput( None )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData( [ 1 ] ) )

def testConnectedInputsWithPromotedInPlug( self ) :

box = Gaffer.Box()
box["switch"] = Gaffer.NameSwitch()
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData() )

box["switch"].setup( Gaffer.IntPlug() )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData() )

Gaffer.PlugAlgo.promote( box["switch"]["in"] )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData() )

inputA = Gaffer.IntPlug()
box["in"][0]["value"].setInput( inputA )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData( [ 0 ] ) )

box["in"][1]["value"].setInput( inputA )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData( [ 0, 1 ] ) )

box["in"][0]["value"].setInput( None )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData( [ 1 ] ) )

if __name__ == "__main__":
unittest.main()
44 changes: 42 additions & 2 deletions python/GafferTest/SwitchTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,14 @@ def testAffects( self ) :
# not affect the output (dependency is carried by
# the connection instead).
for plug in [ n["in"][0], n["in"][1] ] :
self.assertEqual( n.affects( plug ), [] )
self.assertEqual( n.affects( plug ), [ n["connectedInputs"] ] )

# Now the index is computed, the dependencies
# must be declared.
a = GafferTest.AddNode()
n["index"].setInput( a["sum"] )
for plug in [ n["in"][0], n["in"][1] ] :
self.assertEqual( n.affects( plug ), [ n["out"] ] )
self.assertEqual( n.affects( plug ), [ n["out"], n["connectedInputs"] ] )

self.assertEqual( n.affects( n["out"] ), [] )

Expand Down Expand Up @@ -562,6 +562,46 @@ def testInternalConnectionWithTypeConversionAndCanceller( self ) :
boolPlug.setValue( index )
self.assertTrue( switch["out"].getInput().isSame( switch["in"][index] ) )

def testConnectedInputs( self ) :

switch = Gaffer.Switch()
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData() )

switch.setup( Gaffer.IntPlug() )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData() )

inputA = Gaffer.IntPlug()
switch["in"][0].setInput( inputA )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData( [ 0 ] ) )

switch["in"][1].setInput( inputA )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData( [ 0, 1 ] ) )

switch["in"][0].setInput( None )
self.assertEqual( switch["connectedInputs"].getValue(), IECore.IntVectorData( [ 1 ] ) )

def testConnectedInputsWithPromotedInPlug( self ) :

box = Gaffer.Box()
box["switch"] = Gaffer.Switch()
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData() )

box["switch"].setup( Gaffer.IntPlug() )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData() )

Gaffer.PlugAlgo.promote( box["switch"]["in"] )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData() )

inputA = Gaffer.IntPlug()
box["in"][0].setInput( inputA )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData( [ 0 ] ) )

box["in"][1].setInput( inputA )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData( [ 0, 1 ] ) )

box["in"][0].setInput( None )
self.assertEqual( box["switch"]["connectedInputs"].getValue(), IECore.IntVectorData( [ 1 ] ) )

def setUp( self ) :

GafferTest.TestCase.setUp( self )
Expand Down
6 changes: 6 additions & 0 deletions python/GafferUI/NameSwitchUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@

],

"connectedInputs" : [

"layout:index", -3,

],

}

)
Expand Down
37 changes: 37 additions & 0 deletions python/GafferUI/SwitchUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
##########################################################################

import Gaffer
import GafferUI

from GafferUI.PlugValueWidget import sole

Gaffer.Metadata.registerNode(

Expand Down Expand Up @@ -98,6 +101,40 @@

],

"connectedInputs" : [

"description",
"""
The indices of the input array that have incoming connections.
> Tip : This can be used to drive a Wedge or Collect node so that
> they operate over each input in turn.
""",

"nodule:type", "",
"layout:section", "Advanced",
"plugValueWidget:type", "GafferUI.SwitchUI._ConnectedInputsPlugValueWidget",

],

}

)

class _ConnectedInputsPlugValueWidget( GafferUI.PlugValueWidget ) :

def __init__( self, plugs, **kw ) :

self.__textWidget = GafferUI.TextWidget( editable = False )
GafferUI.PlugValueWidget.__init__( self, self.__textWidget, plugs, **kw )

def _updateFromValues( self, values, exception ) :

value = sole( values )
self.__textWidget.setText(
", ".join( [ str( x ) for x in value ] )
if value is not None
else "---"
)

self.__textWidget.setErrored( exception is not None )
74 changes: 74 additions & 0 deletions src/Gaffer/Switch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,36 @@ using namespace Gaffer;
namespace
{

bool connectedIndividually( const ArrayPlug *array, size_t childIndex )
{
const Plug *child = array->getChild<Plug>( childIndex );
auto nameValuePlug = IECore::runTimeCast<const NameValuePlug>( child );
if( nameValuePlug )
{
// For NameSwitch, we only check connections to the `value`
// plug, because typically the `name` part isn't connected.
child = nameValuePlug->valuePlug();
if( !child )
{
return false;
}
}

const Plug *source = child->source();
if( source == child )
{
// No input.
return false;
}

// We do have an input connection, but it might just be
// that the entire input array was promoted. We don't consider
// an individual child to have a connection until it differs
// from the array's.

return !array->source()->isAncestorOf( source );
}

const IECore::InternedString g_inPlugsName( "in" );
const IECore::InternedString g_outPlugName( "out" );

Expand All @@ -70,6 +100,7 @@ Switch::Switch( const std::string &name)

addChild( new IntPlug( "index", Gaffer::Plug::In, 0, 0 ) );
addChild( new BoolPlug( "enabled", Gaffer::Plug::In, true ) );
addChild( new IntVectorDataPlug( "connectedInputs", Plug::Out ) );

childAddedSignal().connect( boost::bind( &Switch::childAdded, this, ::_2 ) );
plugSetSignal().connect( boost::bind( &Switch::plugSet, this, ::_1 ) );
Expand Down Expand Up @@ -169,6 +200,16 @@ const BoolPlug *Switch::enabledPlug() const
return getChild<BoolPlug>( g_firstPlugIndex + 1 );
}

IntVectorDataPlug *Switch::connectedInputsPlug()
{
return getChild<IntVectorDataPlug>( g_firstPlugIndex + 2 );
}

const IntVectorDataPlug *Switch::connectedInputsPlug() const
{
return getChild<IntVectorDataPlug>( g_firstPlugIndex + 2 );
}

void Switch::affects( const Plug *input, DependencyNode::AffectedPlugsContainer &outputs ) const
{
ComputeNode::affects( input, outputs );
Expand Down Expand Up @@ -204,6 +245,7 @@ void Switch::affects( const Plug *input, DependencyNode::AffectedPlugsContainer
{
outputs.push_back( output );
}
outputs.push_back( connectedInputsPlug() );
}
}
}
Expand Down Expand Up @@ -275,6 +317,21 @@ void Switch::hash( const ValuePlug *output, const Context *context, IECore::Murm
h = input->hash();
return;
}
else if( output == connectedInputsPlug() )
{
ComputeNode::hash( output, context, h );
if( auto in = inPlugs() )
{
for( int i = 0, s = in->children().size(); i < s; ++i )
{
if( connectedIndividually( in, i ) )
{
h.append( i );
}
}
}
return;
}

ComputeNode::hash( output, context, h );
}
Expand All @@ -286,6 +343,23 @@ void Switch::compute( ValuePlug *output, const Context *context ) const
output->setFrom( input );
return;
}
else if( output == connectedInputsPlug() )
{
IECore::IntVectorDataPtr resultData = new IECore::IntVectorData;
std::vector<int> &result = resultData->writable();
if( auto in = inPlugs() )
{
for( int i = 0, s = in->children().size(); i < s; ++i )
{
if( connectedIndividually( in, i ) )
{
result.push_back( i );
}
}
}
static_cast<IntVectorDataPlug *>( output )->setValue( resultData );
return;
}

ComputeNode::compute( output, context );
}
Expand Down

0 comments on commit 1c0ed64

Please sign in to comment.