Skip to content

Commit

Permalink
Merge pull request #5912 from johnhaddon/loopImprovements
Browse files Browse the repository at this point in the history
Loop improvements
  • Loading branch information
murraystevenson authored Jun 20, 2024
2 parents ed515cd + f84cd46 commit 0244724
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 2 deletions.
6 changes: 6 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Fixes

- Cycles : Fixed rendering to the Catalogue using the batch Render node (#5905). Note that rendering a mixture of Catalogue and file outputs is still not supported, and in this case any file outputs will be ignored.
- CodeWidget : Fixed bug that could prevent changes from being committed while the completion menu was visible.
- Loop : Fixed handling of empty `indexVariable`. This now disables the Loop instead of creating an unnamed context variable.

API
---

- Loop : Added `nextIterationContext()` method.

API
---
Expand Down
4 changes: 4 additions & 0 deletions include/Gaffer/Loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class GAFFER_API Loop : public ComputeNode
Gaffer::Plug *correspondingInput( const Gaffer::Plug *output ) override;
const Gaffer::Plug *correspondingInput( const Gaffer::Plug *output ) const override;

/// Returns the context that will be used to evaluate `nextPlug()` in
/// the next iteration of the loop (relative to the current context).
ContextPtr nextIterationContext() const;

void affects( const Plug *input, DependencyNode::AffectedPlugsContainer &outputs ) const override;

protected :
Expand Down
48 changes: 48 additions & 0 deletions python/GafferTest/LoopTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,5 +307,53 @@ def testHashAliasingDeadlock( self ) :
# if not handled appropriately.
script["loop"]["out"].getValue()

def testEmptyLoopVariable( self ) :

loop = self.intLoop()

loopBody = GafferTest.AddNode()
loopBody["op1"].setInput( loop["previous"] )
loopBody["op2"].setValue( 2 )
loop["next"].setInput( loopBody["sum"] )
loop["iterations"].setValue( 4 )

self.assertEqual( loop["out"].getValue(), 8 )

loop["indexVariable"].setValue( "" )
self.assertEqual( loop["out"].getValue(), 0 )

def testNextLoopIteration( self ) :

loop = Gaffer.Loop()
self.assertIsNone( loop.nextIterationContext() )

loop = self.intLoop()
loop["next"].setInput( loop["previous"] )
loop["iterations"].setValue( 10 )

index = 0
context = Gaffer.Context()
while context is not None :
with context :
context = loop.nextIterationContext()
if context is not None :
self.assertEqual( context["loop:index"], index )
index += 1

self.assertEqual( index, loop["iterations"].getValue() )

loop["iterations"].setValue( 0 )
self.assertIsNone( loop.nextIterationContext() )

loop["iterations"].setValue( 2 )
self.assertIsNotNone( loop.nextIterationContext() )
loop["enabled"].setValue( False )
self.assertIsNone( loop.nextIterationContext() )

loop["enabled"].setValue( True )
self.assertIsNotNone( loop.nextIterationContext() )
loop["indexVariable"].setValue( "" )
self.assertIsNone( loop.nextIterationContext() )

if __name__ == "__main__":
unittest.main()
37 changes: 35 additions & 2 deletions src/Gaffer/Loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,39 @@ const Gaffer::Plug *Loop::correspondingInput( const Gaffer::Plug *output ) const
return output == outPlug() ? inPlug() : nullptr;
}

ContextPtr Loop::nextIterationContext() const
{
const Plug *in = inPlug();
if( !in )
{
return nullptr;
}

const Context *context = Context::current();
ContextAlgo::GlobalScope globalScope( context, in );

if( !enabledPlug()->getValue() )
{
return nullptr;
}

const IECore::InternedString indexVariable = indexVariablePlug()->getValue();
if( indexVariable.string().empty() )
{
return nullptr;
}

const int index = context->get<int>( indexVariable, -1 );
if( index + 1 < iterationsPlug()->getValue() )
{
ContextPtr result = new Context( *context );
result->set( indexVariable, index + 1 );
return result;
}

return nullptr;
}

void Loop::affects( const Plug *input, DependencyNode::AffectedPlugsContainer &outputs ) const
{
ComputeNode::affects( input, outputs );
Expand Down Expand Up @@ -354,7 +387,7 @@ const ValuePlug *Loop::sourcePlug( const ValuePlug *output, const Context *conte
if( ancestor == previousPlug() )
{
const int index = context->get<int>( indexVariable, 0 );
if( index >= 1 && enabledPlug()->getValue() )
if( index >= 1 && !indexVariable.string().empty() && enabledPlug()->getValue() )
{
sourceLoopIndex = index - 1;
return descendantPlug( nextPlug(), relativeName );
Expand All @@ -367,7 +400,7 @@ const ValuePlug *Loop::sourcePlug( const ValuePlug *output, const Context *conte
else if( ancestor == outPlug() )
{
const int iterations = iterationsPlug()->getValue();
if( iterations > 0 && enabledPlug()->getValue() )
if( iterations > 0 && !indexVariable.string().empty() && enabledPlug()->getValue() )
{
sourceLoopIndex = iterations - 1;
return descendantPlug( nextPlug(), relativeName );
Expand Down
7 changes: 7 additions & 0 deletions src/GafferModule/ContextProcessorBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ void setupLoop( Loop &n, const ValuePlug &plug )
n.setup( &plug );
}

ContextPtr nextIterationContextWrapper( Loop &loop )
{
IECorePython::ScopedGILRelease gilRelease;
return loop.nextIterationContext();
}

ContextPtr inPlugContext( const ContextProcessor &n )
{
IECorePython::ScopedGILRelease gilRelease;
Expand Down Expand Up @@ -189,6 +195,7 @@ void GafferModule::bindContextProcessor()

DependencyNodeClass<Loop>()
.def( "setup", &setupLoop )
.def( "nextIterationContext", &nextIterationContextWrapper )
;

DependencyNodeClass<ContextProcessor>()
Expand Down

0 comments on commit 0244724

Please sign in to comment.