Skip to content

Commit

Permalink
Merge branch '1.3_maintenance' into 1.4_maintenance
Browse files Browse the repository at this point in the history
  • Loading branch information
johnhaddon committed Sep 26, 2024
2 parents a803390 + be7087f commit 327c881
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 1 deletion.
8 changes: 8 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Fixes
- Fixed partial image updates when an unrelated InteractiveRender was running (#6043).
- Fixed "colour tearing", where updates to some image channels became visible before updates to others.
- Fixed unnecessary texture updates when specific image tiles don't change.
- ArrayPlug :
- Fixed error when `resize()` removed plugs with input connections.
- Fixed error when `resize()` was used on an output plug.
- CreateViews : Fixed loading of files saved from Gaffer 1.5+.

1.4.13.0 (relative to 1.4.12.0)
========
Expand Down Expand Up @@ -818,6 +822,10 @@ Fixes
- Fixed partial image updates when an unrelated InteractiveRender was running (#6043).
- Fixed "colour tearing", where updates to some image channels became visible before updates to others.
- Fixed unnecessary texture updates when specific image tiles don't change.
- ArrayPlug :
- Fixed error when `resize()` removed plugs with input connections.
- Fixed error when `resize()` was used on an output plug.
- CreateViews : Fixed loading of files saved from Gaffer 1.5+.

1.3.16.8 (relative to 1.3.16.7)
========
Expand Down
14 changes: 14 additions & 0 deletions python/GafferImageTest/CreateViewsTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import imath
import inspect
import os
import pathlib

import IECore

Expand Down Expand Up @@ -224,5 +225,18 @@ def testInputToExpressionDrivingEnabledPlug( self ) :
# for this particular view.
self.assertFalse( script["constant"]["enabled"].getValue() )

def testLoadFromVersion1_5( self ) :

script = Gaffer.ScriptNode()
script["fileName"].setValue( pathlib.Path( __file__ ).parent / "scripts" / "createViews-1.5.0.0.gfr" )
script.load()

self.assertEqual( len( script["CreateViews"]["views"] ), 2 )
self.assertEqual( script["CreateViews"]["views"][0]["name"].getValue(), "left" )
self.assertEqual( script["CreateViews"]["views"][1]["name"].getValue(), "right" )
self.assertTrue( script["CreateViews"]["views"][0]["value"].getInput().isSame( script["Checkerboard"]["out"] ) )
self.assertTrue( script["CreateViews"]["views"][1]["value"].getInput().isSame( script["Checkerboard1"]["out"] ) )
self.assertEqual( script["CreateViews"]["out"].viewNames(), IECore.StringVectorData( [ "left", "right" ] ) )

if __name__ == "__main__":
unittest.main()
45 changes: 45 additions & 0 deletions python/GafferImageTest/scripts/createViews-1.5.0.0.gfr
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Gaffer
import GafferImage
import imath

Gaffer.Metadata.registerValue( parent, "serialiser:milestoneVersion", 1, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:majorVersion", 5, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:minorVersion", 0, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:patchVersion", 0, persistent=False )

__children = {}

parent["variables"].addChild( Gaffer.NameValuePlug( "image:catalogue:port", Gaffer.IntPlug( "value", defaultValue = 0, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ), "imageCataloguePort", Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) )
parent["variables"].addChild( Gaffer.NameValuePlug( "project:name", Gaffer.StringPlug( "value", defaultValue = 'default', flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ), "projectName", Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) )
parent["variables"].addChild( Gaffer.NameValuePlug( "project:rootDirectory", Gaffer.StringPlug( "value", defaultValue = '$HOME/gaffer/projects/${project:name}', flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ), "projectRootDirectory", Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) )
__children["openColorIO"] = GafferImage.OpenColorIOConfigPlug( "openColorIO", flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, )
parent.addChild( __children["openColorIO"] )
__children["defaultFormat"] = GafferImage.FormatPlug( "defaultFormat", defaultValue = GafferImage.Format( 1920, 1080, 1.000 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, )
parent.addChild( __children["defaultFormat"] )
__children["Checkerboard"] = GafferImage.Checkerboard( "Checkerboard" )
parent.addChild( __children["Checkerboard"] )
__children["Checkerboard"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["Checkerboard1"] = GafferImage.Checkerboard( "Checkerboard1" )
parent.addChild( __children["Checkerboard1"] )
__children["Checkerboard1"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["CreateViews"] = GafferImage.CreateViews( "CreateViews" )
parent.addChild( __children["CreateViews"] )
__children["CreateViews"]["views"].resize( 2 )
__children["CreateViews"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
parent["variables"]["imageCataloguePort"]["value"].setValue( 34907 )
Gaffer.Metadata.registerValue( parent["variables"]["imageCataloguePort"], 'readOnly', True )
Gaffer.Metadata.registerValue( parent["variables"]["projectName"]["name"], 'readOnly', True )
Gaffer.Metadata.registerValue( parent["variables"]["projectRootDirectory"]["name"], 'readOnly', True )
__children["Checkerboard"]["size"]["y"].setInput( __children["Checkerboard"]["size"]["x"] )
__children["Checkerboard"]["__uiPosition"].setValue( imath.V2f( -30.6000004, 10.4000006 ) )
__children["Checkerboard1"]["size"]["y"].setInput( __children["Checkerboard1"]["size"]["x"] )
__children["Checkerboard1"]["__uiPosition"].setValue( imath.V2f( -16.8000031, 10.5 ) )
__children["CreateViews"]["views"][0]["name"].setValue( 'left' )
__children["CreateViews"]["views"][0]["value"].setInput( __children["Checkerboard"]["out"] )
__children["CreateViews"]["views"][1]["name"].setValue( 'right' )
__children["CreateViews"]["views"][1]["value"].setInput( __children["Checkerboard1"]["out"] )
__children["CreateViews"]["__uiPosition"].setValue( imath.V2f( -23.8000011, 2.23593783 ) )


del __children

22 changes: 22 additions & 0 deletions python/GafferTest/ArrayPlugTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,22 @@ def testResize( self ) :
with self.assertRaises( RuntimeError ) :
p.resize( p.maxSize() + 1 )

def testRemoveInputDuringResize( self ) :

node = Gaffer.Node()
node["user"]["p"] = Gaffer.IntPlug()
node["user"]["array"] = Gaffer.ArrayPlug( element = Gaffer.IntPlug(), resizeWhenInputsChange = True )
node["user"]["array"].resize( 4 )
node["user"]["array"][2].setInput( node["user"]["p"] )

node["user"]["array"].resize( 1 )
self.assertEqual( len( node["user"]["array"] ), 1 )

def testResizeOutputPlug( self ) :

array = Gaffer.ArrayPlug( element = Gaffer.IntPlug( direction = Gaffer.Plug.Direction.Out ), direction = Gaffer.Plug.Direction.Out )
array.resize( 2 )

def testSerialisationUsesIndices( self ) :

s = Gaffer.ScriptNode()
Expand All @@ -515,5 +531,11 @@ def testSerialisationUsesIndices( self ) :
self.assertEqual( s2["n"]["in"][0].getInput(), s2["a"]["sum"] )
self.assertEqual( s2["n"]["in"][1].getInput(), s2["a"]["sum"] )

def testResizeWithoutExistingChildren( self ) :

p = Gaffer.ArrayPlug( name = "p" )
with self.assertRaisesRegex( RuntimeError, "Can't resize ArrayPlug `p` as it has no children" ) :
p.resize( 1 )

if __name__ == "__main__":
unittest.main()
27 changes: 26 additions & 1 deletion src/Gaffer/ArrayPlug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,39 @@ void ArrayPlug::resize( size_t size )
throw IECore::Exception( "Invalid size" );
}

if( size && !children().size() )
{
if( auto scriptNode = ancestor<ScriptNode>() )
{
if( scriptNode->isExecuting() )
{
// Needed to allow CreateViews nodes serialised from Gaffer 1.5
// to be loaded. In 1.5, CreateViews creates an ArrayPlug with
// an element prototype, and uses `resize()` in the
// serialisation. We can't resize here because we don't have a
// prototype, but as long as we don't error,
// `startup/GafferImage/createViewsCompatibility.py` will deal
// with the rest for us.
return;
}
}
throw IECore::Exception(
fmt::format(
"Can't resize ArrayPlug `{}` as it has no children",
fullName()
)
);
}

while( size > children().size() )
{
PlugPtr p = getChild<Plug>( 0 )->createCounterpart( getChild<Plug>( 0 )->getName(), Plug::In );
PlugPtr p = getChild<Plug>( 0 )->createCounterpart( getChild<Plug>( 0 )->getName(), direction() );
p->setFlags( Gaffer::Plug::Dynamic, true );
addChild( p );
MetadataAlgo::copyColors( getChild<Plug>( 0 ) , p.get() , /* overwrite = */ false );
}

Gaffer::Signals::BlockedConnection blockedInputChange( m_inputChangedConnection );
while( children().size() > size )
{
removeChild( children().back() );
Expand Down
56 changes: 56 additions & 0 deletions startup/GafferImage/createViewsCompatibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
##########################################################################
#
# Copyright (c) 2024, Cinesite VFX Ltd. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with
# the distribution.
#
# * Neither the name of John Haddon nor the names of
# any other contributors to this software may be used to endorse or
# promote products derived from this software without specific prior
# written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import Gaffer
import GafferImage

def __arrayPlugGetItem( originalGetItem ) :

def getItem( self, key ) :

if isinstance( key, int ) and key >= len( self ) :
if isinstance( self.parent(), GafferImage.CreateViews ) and self.getName() == "views" :
# Probably loading a serialisation from Gaffer 1.5+, where `resize()` would
# have added the array elements that we are missing here. Just add them manually.
if not len( self ) :
self.addChild( Gaffer.NameValuePlug( "", GafferImage.ImagePlug(), True, "view0", Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) )
self.resize( key + 1 )

return originalGetItem( self, key )

return getItem

Gaffer.ArrayPlug.__getitem__ = __arrayPlugGetItem( Gaffer.ArrayPlug.__getitem__ )

0 comments on commit 327c881

Please sign in to comment.