Skip to content

Commit

Permalink
CreateViews : Provide elementPrototype to ArrayPlug
Browse files Browse the repository at this point in the history
Passing `nullptr` is no longer allowed except to support the loading of legacy files. Unfortunately, all client code that is adding views is doing it via `addChild( NameValuePlug( ... ) )` rather than `resize()` or `next()`, meaning the elements are not forced to match the prototype exactly. And all existing serialisations are doing the same. And because `NameValuePlug( "left", ... )` produces a plug with a default value of "left", the array elements being created are not homogeneous. That breaks the new ArrayPlug serialisation, which assumes that a simple `resize()` is sufficient to recreate all children.

We deal with this using a compatibility shim that detects the bad child additions and resets the default value while maintaining the current value.
  • Loading branch information
johnhaddon committed Aug 27, 2024
1 parent 83f865b commit 24d6d93
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 1 deletion.
51 changes: 51 additions & 0 deletions python/GafferImageTest/scripts/createViews-1.4.10.0.gfr
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Gaffer
import GafferImage
import imath

Gaffer.Metadata.registerValue( parent, "serialiser:milestoneVersion", 1, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:majorVersion", 4, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:minorVersion", 10, 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["CheckerboardLeft"] = GafferImage.Checkerboard( "CheckerboardLeft" )
parent.addChild( __children["CheckerboardLeft"] )
__children["CheckerboardLeft"].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"].addChild( Gaffer.NameValuePlug( "left", GafferImage.ImagePlug( "value", ), True, "view0", Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) )
__children["CreateViews"]["views"].addChild( Gaffer.NameValuePlug( "right", GafferImage.ImagePlug( "value", ), True, "view1", Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) )
__children["CreateViews"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["CheckerboardRight"] = GafferImage.Checkerboard( "CheckerboardRight" )
parent.addChild( __children["CheckerboardRight"] )
__children["CheckerboardRight"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
parent["variables"]["imageCataloguePort"]["value"].setValue( 37035 )
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["CheckerboardLeft"]["size"]["y"].setInput( __children["CheckerboardLeft"]["size"]["x"] )
__children["CheckerboardLeft"]["__uiPosition"].setValue( imath.V2f( -7.31522799, 2.82158232 ) )
__children["CreateViews"]["out"]["format"].setInput( __children["CreateViews"]["__switch"]["out"]["format"] )
__children["CreateViews"]["out"]["dataWindow"].setInput( __children["CreateViews"]["__switch"]["out"]["dataWindow"] )
__children["CreateViews"]["out"]["metadata"].setInput( __children["CreateViews"]["__switch"]["out"]["metadata"] )
__children["CreateViews"]["out"]["deep"].setInput( __children["CreateViews"]["__switch"]["out"]["deep"] )
__children["CreateViews"]["out"]["sampleOffsets"].setInput( __children["CreateViews"]["__switch"]["out"]["sampleOffsets"] )
__children["CreateViews"]["out"]["channelNames"].setInput( __children["CreateViews"]["__switch"]["out"]["channelNames"] )
__children["CreateViews"]["out"]["channelData"].setInput( __children["CreateViews"]["__switch"]["out"]["channelData"] )
__children["CreateViews"]["views"][0]["value"].setInput( __children["CheckerboardLeft"]["out"] )
__children["CreateViews"]["views"][1]["value"].setInput( __children["CheckerboardRight"]["out"] )
__children["CreateViews"]["__uiPosition"].setValue( imath.V2f( 0.366687864, -5.34316444 ) )
__children["CheckerboardRight"]["size"]["y"].setInput( __children["CheckerboardRight"]["size"]["x"] )
__children["CheckerboardRight"]["__uiPosition"].setValue( imath.V2f( 8.04854202, 2.82158256 ) )


del __children

7 changes: 6 additions & 1 deletion src/GafferImage/CreateViews.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ CreateViews::CreateViews( const std::string &name )
ArrayPlugPtr views = new ArrayPlug(
"views",
Plug::In,
nullptr,
new NameValuePlug(
/* nameDefault = */ "",
/* valuePlug = */ new ImagePlug(),
/* defaultEnabled = */ true,
/* name = */ "view0"
),
0,
std::numeric_limits<size_t>::max(),
Plug::Default,
Expand Down
62 changes: 62 additions & 0 deletions startup/GafferImage/createViewsCompatibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
##########################################################################
#
# 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 GafferImage

def __viewsChildAdded( parent, child ) :

if child["name"].defaultValue() :
# Legacy code and files create children with non-empty default values
# in the `name`` plug. This violates the uniformity of the array, and
# can no longer be serialised. Reset the default value while preserving
# the current value.
value = child["name"].getValue()
child["name"].setValue( "" )
child["name"].resetDefault()
child["name"].setValue( value )

def __initWrapper( originalInit ) :

def init( self, *args, **kw ) :

originalInit( self, *args, **kw )
self["views"].childAddedSignal().connect(
__viewsChildAdded, scoped = False
)

return init

GafferImage.CreateViews.__init__ = __initWrapper( GafferImage.CreateViews.__init__ )

0 comments on commit 24d6d93

Please sign in to comment.