def testSignalThreadSafety(self): script = Gaffer.ScriptNode() script["random"] = Gaffer.Random() script["random"]["contextEntry"].setValue("collect:rootName") script["sphere"] = GafferScene.Sphere() script["sphere"]["radius"].setInput(script["random"]["outFloat"]) script["filter"] = GafferScene.PathFilter() script["filter"]["paths"].setValue(IECore.StringVectorData(["/sphere" ])) script["encapsulate"] = GafferScene.Encapsulate() script["encapsulate"]["in"].setInput(script["sphere"]["out"]) script["encapsulate"]["filter"].setInput(script["filter"]["out"]) script["collect"] = GafferScene.CollectScenes() script["collect"]["in"].setInput(script["encapsulate"]["out"]) script["collect"]["rootNames"].setValue( IECore.StringVectorData([str(x) for x in range(0, 100)])) script["fileName"].setValue( os.path.join(self.temporaryDirectory(), "test.gfr")) script.save() # This exposed a crash caused by non-threadsafe access to signals from Capsules. It # will throw if the subprocess crashes. subprocess.check_output([ "gaffer", "stats", script["fileName"].getValue(), "-scene", "collect" ])
def testCapsules(self): s = Gaffer.ScriptNode() s["sphere"] = GafferScene.Sphere("sphere") s["sphere1"] = GafferScene.Sphere("sphere1") s["group"] = GafferScene.Group("group") s["group"]["in"][0].setInput(s["sphere"]["out"]) s["group"]["in"][1].setInput(s["sphere1"]["out"]) s["path_filter"] = GafferScene.PathFilter("path_filter") s["path_filter"]["paths"].setValue(IECore.StringVectorData(['*'])) s["encapsulate"] = GafferScene.Encapsulate("encapsulate") s["encapsulate"]["in"].setInput(s["group"]["out"]) s["encapsulate"]["filter"].setInput(s["path_filter"]["out"]) s["duplicate"] = GafferScene.Duplicate("duplicate") s["duplicate"]["in"].setInput(s["encapsulate"]["out"]) s["duplicate"]["target"].setValue('group') s["duplicate"]["copies"].setValue(2) s["render"] = GafferAppleseed.AppleseedRender() s["render"]["in"].setInput(s["duplicate"]["out"]) s["render"]["mode"].setValue(s["render"].Mode.SceneDescriptionMode) projectFilename = self.temporaryDirectory() + "/test.appleseed" s["render"]["fileName"].setValue(projectFilename) s["render"]["task"].execute() reader = asr.ProjectFileReader() options = asr.ProjectFileReaderOptions.OmitReadingMeshFiles project = reader.read(projectFilename, appleseedProjectSchemaPath(), options) scene = project.get_scene() mainAssembly = scene.assemblies().get_by_name("assembly") # Check that we have 3 instances of 1 capsule. self.assertEqual(len(mainAssembly.assemblies()), 1) self.assertEqual(len(mainAssembly.assembly_instances()), 3) capsuleAssemblyName = mainAssembly.assemblies().keys()[0] capsuleAssembly = mainAssembly.assemblies()[capsuleAssemblyName] # Check that we have 2 instances of 1 sphere inside the capsule. self.assertEqual(len(capsuleAssembly.assemblies()), 1) self.assertEqual(len(capsuleAssembly.assembly_instances()), 2)
def testCapsuleHash( self ) : sphere = GafferScene.Sphere() group1 = GafferScene.Group() group1["in"][0].setInput( sphere["out"] ) group2 = GafferScene.Group() group2["in"][0].setInput( group1["out"] ) pathFilter = GafferScene.PathFilter() pathFilter["paths"].setValue( IECore.StringVectorData( [ "/group/group" ] ) ) encapsulate = GafferScene.Encapsulate() encapsulate["in"].setInput( group2["out"] ) encapsulate["filter"].setInput( pathFilter["out"] ) objectHashes = set() capsuleHashes = set() def assertHashesUnique( path ) : objectHash = encapsulate["out"].objectHash( path ) capsule = encapsulate["out"].object( path ) self.assertIsInstance( capsule, GafferScene.Capsule ) capsuleHash = capsule.hash() self.assertNotIn( objectHash, objectHashes ) self.assertNotIn( capsuleHash, capsuleHashes ) objectHashes.add( objectHash ) capsuleHashes.add( capsuleHash ) assertHashesUnique( "/group/group" ) sphere["radius"].setValue( 2 ) assertHashesUnique( "/group/group" ) sphere["name"].setValue( "bigSphere" ) assertHashesUnique( "/group/group" ) sphere["transform"]["translate"]["x"].setValue( 1 ) assertHashesUnique( "/group/group" ) pathFilter["paths"].setValue( IECore.StringVectorData( [ "/group" ] ) ) assertHashesUnique( "/group" ) encapsulate["in"].setInput( group1["out"] ) assertHashesUnique( "/group" )
def testRootObject( self ) : sphere = GafferScene.Sphere() sphere["radius"].setValue( 42 ) f = GafferScene.PathFilter() f["paths"].setValue( IECore.StringVectorData( [ "/sphere" ] ) ) encapsulate= GafferScene.Encapsulate() encapsulate["in"].setInput( sphere["out"] ) encapsulate["filter"].setInput( f["out"] ) unencapsulate = GafferScene.Unencapsulate() unencapsulate["in"].setInput( encapsulate["out"] ) unencapsulate["filter"].setInput( f["out"] ) # Test unencapsulating the object at the root of the capsule self.assertScenesEqual( sphere["out"], unencapsulate["out"] )
def testSetMemberAtRoot( self ) : sphere = GafferScene.Sphere() sphere["sets"].setValue( "A" ) group = GafferScene.Group() group["in"][0].setInput( sphere["out"] ) self.assertEqual( group["out"].set( "A" ).value.paths(), [ "/group/sphere" ] ) pathFilter = GafferScene.PathFilter() pathFilter["paths"].setValue( IECore.StringVectorData( [ "/group/sphere" ] ) ) encapsulate = GafferScene.Encapsulate() encapsulate["in"].setInput( group["out"] ) encapsulate["filter"].setInput( pathFilter["out"] ) self.assertEqual( encapsulate["out"].set( "A" ).value.paths(), [ "/group/sphere" ] )
def testEncapsulateDeformationBlur( self ) : s = Gaffer.ScriptNode() # Make a sphere where the red channel has the value of the current frame. s["sphere"] = GafferScene.Sphere() s["sphereFilter"] = GafferScene.PathFilter() s["sphereFilter"]["paths"].setValue( IECore.StringVectorData( [ "/sphere" ] ) ) s["frame"] = GafferTest.FrameNode() s["flat"] = GafferArnold.ArnoldShader() s["flat"].loadShader( "flat" ) s["flat"]["parameters"]["color"].setValue( imath.Color3f( 0 ) ) s["flat"]["parameters"]["color"]["r"].setInput( s["frame"]["output"] ) s["assignment"] = GafferScene.ShaderAssignment() s["assignment"]["in"].setInput( s["sphere"]["out"] ) s["assignment"]["shader"].setInput( s["flat"]["out"] ) s["assignment"]["filter"].setInput( s["sphereFilter"]["out"] ) # Put the sphere in a capsule. s["group"] = GafferScene.Group() s["group"]["in"][0].setInput( s["assignment"]["out"] ) s["groupFilter"] = GafferScene.PathFilter() s["groupFilter"]["paths"].setValue( IECore.StringVectorData( [ "/group" ] ) ) s["encapsulate"] = GafferScene.Encapsulate() s["encapsulate"]["in"].setInput( s["group"]["out"] ) s["encapsulate"]["filter"].setInput( s["groupFilter"]["out"] ) # Do a render at frame 1, with deformation blur off. s["outputs"] = GafferScene.Outputs() s["outputs"].addOutput( "beauty", IECoreScene.Output( os.path.join( self.temporaryDirectory(), "deformationBlurOff.exr" ), "exr", "rgba", { } ) ) s["outputs"]["in"].setInput( s["encapsulate"]["out"] ) s["options"] = GafferScene.StandardOptions() s["options"]["in"].setInput( s["outputs"]["out"] ) s["arnoldOptions"] = GafferArnold.ArnoldOptions() s["arnoldOptions"]["in"].setInput( s["options"]["out"] ) s["arnoldOptions"]["options"]["aaSamples"]["enabled"].setValue( True ) s["arnoldOptions"]["options"]["aaSamples"]["value"].setValue( 6 ) s["render"] = GafferArnold.ArnoldRender() s["render"]["in"].setInput( s["arnoldOptions"]["out"] ) s["render"]["task"].execute() # Do another render at frame 1, but with deformation blur on. s["options"]["options"]["deformationBlur"]["enabled"].setValue( True ) s["options"]["options"]["deformationBlur"]["value"].setValue( True ) s["options"]["options"]["shutter"]["enabled"].setValue( True ) s["options"]["options"]["shutter"]["value"].setValue( imath.V2f( -0.5, 0.5 ) ) s["outputs"]["outputs"][0]["fileName"].setValue( os.path.join( self.temporaryDirectory(), "deformationBlurOn.exr" ) ) s["render"]["task"].execute() # Check that the renders are the same. s["deformationOff"] = GafferImage.ImageReader() s["deformationOff"]["fileName"].setValue( os.path.join( self.temporaryDirectory(), "deformationBlurOff.exr" ) ) s["deformationOn"] = GafferImage.ImageReader() s["deformationOn"]["fileName"].setValue( os.path.join( self.temporaryDirectory(), "deformationBlurOn.exr" ) ) # The `maxDifference` is huge to account for noise and watermarks, but is still low enough to check what # we want, since if the Encapsulate was sampled at shutter open and not the frame, the difference would be # 0.5. self.assertImagesEqual( s["deformationOff"]["out"], s["deformationOn"]["out"], maxDifference = 0.25, ignoreMetadata = True )
def test( self ) : # - groupA # - group1 # - sphere # - cube # - group2 # - sphere # - cube # - sometimesCube # - group3 # - sphere # - cube # - group4 # - sphere # - cube box = Gaffer.Node() box["sphere"] = GafferScene.Sphere() box["sphere"]["sets"].setValue( "sphereSet" ) box["cube"] = GafferScene.Cube() box["cube"]["sets"].setValue( "cubeSet" ) box["sometimesCube"] = GafferScene.Cube() box["sometimesCube"]["name"].setValue( "sometimesCube" ) box["sometimesCube"]["sets"].setValue( "cubeSet" ) box["group"] = GafferScene.Group() box["group"]["in"][0].setInput( box["sphere"]["out"] ) box["group"]["in"][1].setInput( box["cube"]["out"] ) box["group"]["in"][1].setInput( box["sometimesCube"]["out"] ) box["e"] = Gaffer.Expression() box["e"].setExpression( inspect.cleandoc( """ n = context["collect:rootName"] i = int( n[-1] ) - 1 parent["sphere"]["radius"] = 1 + i * 0.1 parent["sphere"]["transform"]["translate"] = imath.V3f( 1 + i, 0, 0 ) parent["cube"]["transform"]["translate"] = imath.V3f( 0, 1 + i, 0 ) parent["sometimesCube"]["enabled"] = n == "group2" parent["group"]["transform"]["translate"] = imath.V3f( 0, 0, 1 + i ) """ ) ) collect = GafferScene.CollectScenes() collect["in"].setInput( box["group"]["out"] ) collect["rootNames"].setValue( IECore.StringVectorData( [ "group1", "group2", "group3", "group4" ] ) ) collect["sourceRoot"].setValue( "/group" ) groupA = GafferScene.Group() groupA["name"].setValue( "groupA" ) groupA["in"][0].setInput( collect["out"] ) encapsulateFilter = GafferScene.PathFilter() encapsulateFilter["paths"].setValue( IECore.StringVectorData( [ "/groupA/*" ] ) ) encapsulateCollect = GafferScene.Encapsulate() encapsulateCollect["in"].setInput( groupA["out"] ) encapsulateCollect["filter"].setInput( encapsulateFilter["out"] ) preEncapsulateFilter = GafferScene.PathFilter() preEncapsulateFilter["paths"].setValue( IECore.StringVectorData( [ "/group" ] ) ) preEncapsulate = GafferScene.Encapsulate() preEncapsulate["in"].setInput( box["group"]["out"] ) preEncapsulate["filter"].setInput( preEncapsulateFilter["out"] ) collectEncapsulate = GafferScene.CollectScenes() collectEncapsulate["in"].setInput( preEncapsulate["out"] ) collectEncapsulate["rootNames"].setValue( IECore.StringVectorData( [ "group1", "group2", "group3", "group4" ] ) ) collectEncapsulate["sourceRoot"].setValue( "/group" ) collectEncapsulateGroup = GafferScene.Group() collectEncapsulateGroup["name"].setValue( "groupA" ) collectEncapsulateGroup["in"][0].setInput( collectEncapsulate["out"] ) unencapsulateFilter = GafferScene.PathFilter() unencapsulate1 = GafferScene.Unencapsulate() unencapsulate1["in"].setInput( encapsulateCollect["out"] ) unencapsulate1["filter"].setInput( unencapsulateFilter["out"] ) unencapsulate2 = GafferScene.Unencapsulate() unencapsulate2["in"].setInput( collectEncapsulateGroup["out"] ) unencapsulate2["filter"].setInput( unencapsulateFilter["out"] ) # We can reverse the encapsulate by unencapsulating everything unencapsulateFilter["paths"].setValue( IECore.StringVectorData( [ "..." ] ) ) self.assertScenesEqual( groupA["out"], unencapsulate1["out"] ) # Unencapsulate should work the same whether the capsules come from before or after the collect self.assertScenesEqual( unencapsulate1["out"], unencapsulate2["out"] ) # Or just unencapsulate one thing unencapsulateFilter["paths"].setValue( IECore.StringVectorData( [ "/groupA/group3" ] ) ) self.assertScenesEqual( encapsulateCollect["out"], unencapsulate1["out"], pathsToPrune = [ "/groupA/group3" ] ) self.assertScenesEqual( groupA["out"], unencapsulate1["out"], pathsToPrune = [ "/groupA/group1", "/groupA/group2", "/groupA/group4" ] ) # Whichever place we encapsulate, we still get the same results, except that the capsule objects themselves # which weren't encapsulated will appear different ( because they were computed in different places, and # reference different source plugs self.assertScenesEqual( unencapsulate1["out"], unencapsulate2["out"], checks = self.allSceneChecks - { "object" } ) self.assertScenesEqual( unencapsulate1["out"], unencapsulate2["out"], pathsToPrune = [ "/groupA/group1", "/groupA/group2", "/groupA/group4" ] ) unencapsulateFilter["paths"].setValue( IECore.StringVectorData( [ "..." ] ) ) # Test modifying the hierarchy after making capsules by duplicating a location duplicate = GafferScene.Duplicate() duplicate["target"].setValue( "/groupA/group3" ) duplicate["in"].setInput( collectEncapsulateGroup["out"] ) unencapsulateDuplicated = GafferScene.Unencapsulate() unencapsulateDuplicated["in"].setInput( duplicate["out"] ) unencapsulateDuplicated["filter"].setInput( unencapsulateFilter["out"] ) # This copies group3 as group5 self.assertEqual( unencapsulateDuplicated["out"].fullTransform( "/groupA/group5/sphere" ), groupA["out"].fullTransform( "/groupA/group3/sphere" ) ) # Sanity check that groups do have unique transforms self.assertNotEqual( unencapsulateDuplicated["out"].fullTransform( "/groupA/group5/sphere" ), groupA["out"].fullTransform( "/groupA/group4/sphere" ) ) # This should be same result as copying group3 to group5 without any encapsulation preDuplicate = GafferScene.Duplicate() preDuplicate["target"].setValue( "/groupA/group3" ) preDuplicate["in"].setInput( groupA["out"] ) self.assertScenesEqual( unencapsulateDuplicated["out"], preDuplicate["out"] ) # Some tests where we merge an extra location into the scene amongst the capsules, # which should give the same result whether it's done before or after unencapsulating extraSphere = GafferScene.Sphere() extraSphere["name"].setValue( "extra" ) extraSphere["sets"].setValue( "sphereSet" ) extraSpherePostParent = GafferScene.Parent() extraSpherePostParent["in"].setInput( unencapsulate2["out"] ) extraSpherePostParent["children"][0].setInput( extraSphere["out"] ) extraSpherePreParent = GafferScene.Parent() extraSpherePreParent["in"].setInput( collectEncapsulateGroup["out"] ) extraSpherePreParent["children"][0].setInput( extraSphere["out"] ) unencapsulateAfter = GafferScene.Unencapsulate() unencapsulateAfter["in"].setInput( extraSpherePreParent["out"] ) unencapsulateAfter["filter"].setInput( unencapsulateFilter["out"] ) # Test parenting in a sphere at a the same level as a capsule extraSpherePostParent["parent"].setValue( "/groupA" ) extraSpherePreParent["parent"].setValue( "/groupA" ) self.assertScenesEqual( extraSpherePostParent["out"], unencapsulateAfter["out"], checks = self.allSceneChecks - { "childNames" } ) # Test a weird case: parenting the sphere under a capsule, so that when the capsule is expanded, # it gets merged with the children of the capsule. It's arguable that this shouldn't need to # work, and maybe there would be some extra optimizations available if it wasn't allowed, but for # the moment, it works extraSpherePostParent["parent"].setValue( "/groupA/group2" ) extraSpherePreParent["parent"].setValue( "/groupA/group2" ) self.assertScenesEqual( extraSpherePostParent["out"], unencapsulateAfter["out"], checks = self.allSceneChecks - { "childNames" } )
def test(self): # - groupA # - groupB # - sphere # - cube # - sphere sphere = GafferScene.Sphere() sphere["sets"].setValue("sphereSet") cube = GafferScene.Cube() cube["sets"].setValue("cubeSet") groupB = GafferScene.Group() groupB["in"][0].setInput(sphere["out"]) groupB["in"][1].setInput(cube["out"]) groupB["name"].setValue("groupB") groupA = GafferScene.Group() groupA["in"][0].setInput(groupB["out"]) groupA["in"][1].setInput(sphere["out"]) groupA["name"].setValue("groupA") # When there is no filter attached, the node # should be an exact pass-through. encapsulate = GafferScene.Encapsulate() encapsulate["in"].setInput(groupA["out"]) self.assertSceneHashesEqual(encapsulate["out"], groupA["out"], checks=self.allSceneChecks - {"sets"}) self.assertScenesEqual(encapsulate["out"], groupA["out"]) # The same goes if there is a filter but it # doesn't match anything. pathFilter = GafferScene.PathFilter() encapsulate["filter"].setInput(pathFilter["out"]) self.assertSceneHashesEqual(encapsulate["out"], groupA["out"], checks=self.allSceneChecks - {"sets"}) self.assertScenesEqual(encapsulate["out"], groupA["out"]) # Even when the filter does match something, the # unmatched paths should be unaffected, and the # globals and set names should be unchanged. pathFilter["paths"].setValue( IECore.StringVectorData(["/groupA/groupB"])) for path in ["/", "/groupA", "/groupA/sphere"]: self.assertPathHashesEqual(groupA["out"], path, encapsulate["out"], path) self.assertPathsEqual(groupA["out"], path, encapsulate["out"], path) for plugName in ["globals", "setNames"]: self.assertEqual(groupA["out"][plugName].hash(), encapsulate["out"][plugName].hash()) self.assertEqual(groupA["out"][plugName].getValue(), encapsulate["out"][plugName].getValue()) # And even for matched paths, the attributes, transform # and bound should be passed through unchanged. self.assertEqual(groupA["out"].attributesHash("/groupA/groupB"), encapsulate["out"].attributesHash("/groupA/groupB")) self.assertEqual(groupA["out"].attributes("/groupA/groupB"), encapsulate["out"].attributes("/groupA/groupB")) self.assertEqual(groupA["out"].transformHash("/groupA/groupB"), encapsulate["out"].transformHash("/groupA/groupB")) self.assertEqual(groupA["out"].transform("/groupA/groupB"), encapsulate["out"].transform("/groupA/groupB")) self.assertEqual(groupA["out"].boundHash("/groupA/groupB"), encapsulate["out"].boundHash("/groupA/groupB")) self.assertEqual(groupA["out"].bound("/groupA/groupB"), encapsulate["out"].bound("/groupA/groupB")) # But the children should all have been pruned away # and replaced by an appropriate Capsule. self.assertEqual(encapsulate["out"].childNames("/groupA/groupB"), IECore.InternedStringVectorData()) capsule = encapsulate["out"].object("/groupA/groupB") self.assertIsInstance(capsule, GafferScene.Capsule) self.assertEqual(capsule.scene(), groupA["out"]) self.assertEqual(capsule.root(), "/groupA/groupB") self.assertEqual(capsule.bound(), groupA["out"].bound("/groupA/groupB")) # And the sets should also have been pruned so they # don't include the objects beneath the capsule. self.assertEqual(encapsulate["out"].set("sphereSet").value.paths(), ["/groupA/sphere"]) self.assertEqual(encapsulate["out"].set("cubeSet").value.paths(), []) with six.assertRaisesRegex( self, Gaffer.ProcessException, "Encapsulate.out.childNames : Tried to access path \"/groupA/groupB/sphere\", but its ancestor has been converted to a capsule" ): encapsulate["out"].childNames("/groupA/groupB/sphere") with six.assertRaisesRegex( self, Gaffer.ProcessException, "Encapsulate.out.object : Tried to access path \"/groupA/groupB/sphere\", but its ancestor has been converted to a capsule" ): encapsulate["out"].object("/groupA/groupB/sphere") # As a double check, test that this setup works properly with Unencapsulate unencapsulateFilter = GafferScene.PathFilter() unencapsulate = GafferScene.Unencapsulate() unencapsulate["in"].setInput(encapsulate["out"]) unencapsulate["filter"].setInput(unencapsulateFilter["out"]) # We can reverse the encapsulate by unencapsulating everything unencapsulateFilter["paths"].setValue(IECore.StringVectorData(["..."])) self.assertScenesEqual(encapsulate["in"], unencapsulate["out"]) # Or by targetting just the path that was encapsulated unencapsulateFilter["paths"].setValue( IECore.StringVectorData(["/groupA/groupB"])) self.assertScenesEqual(encapsulate["in"], unencapsulate["out"]) # But if we don't target that path, the scene stays encapsulated unencapsulateFilter["paths"].setValue( IECore.StringVectorData(["/groupA/"])) self.assertScenesEqual(encapsulate["out"], unencapsulate["out"])
def testCapsuleHash(self): sphere = GafferScene.Sphere() group1 = GafferScene.Group() group1["in"][0].setInput(sphere["out"]) group2 = GafferScene.Group() group2["in"][0].setInput(group1["out"]) pathFilter = GafferScene.PathFilter() pathFilter["paths"].setValue(IECore.StringVectorData(["/group/group"])) encapsulate = GafferScene.Encapsulate() encapsulate["in"].setInput(group2["out"]) encapsulate["filter"].setInput(pathFilter["out"]) objectHashes = set() capsuleHashes = set() def assertHashesUnique(path): objectHash = encapsulate["out"].objectHash(path) capsule = encapsulate["out"].object(path) self.assertIsInstance(capsule, GafferScene.Capsule) capsuleHash = capsule.hash() self.assertNotIn(objectHash, objectHashes) self.assertNotIn(capsuleHash, capsuleHashes) objectHashes.add(objectHash) capsuleHashes.add(capsuleHash) assertHashesUnique("/group/group") sphere["radius"].setValue(2) assertHashesUnique("/group/group") sphere["name"].setValue("bigSphere") assertHashesUnique("/group/group") sphere["transform"]["translate"]["x"].setValue(1) assertHashesUnique("/group/group") pathFilter["paths"].setValue(IECore.StringVectorData(["/group"])) assertHashesUnique("/group") encapsulate["in"].setInput(group1["out"]) assertHashesUnique("/group") # Test changing the filter or globals group2["in"][1].setInput(group1["out"]) options = GafferScene.CustomOptions() options["in"].setInput(group2["out"]) encapsulate["in"].setInput(options["out"]) pathFilter["paths"].setValue(IECore.StringVectorData(["/group/group"])) c = encapsulate["out"].object("/group/group") # Changing filter doesn't affect hash pathFilter["paths"].setValue( IECore.StringVectorData(["/group/group", "/group/group2"])) self.assertEqual(c, encapsulate["out"].object("/group/group")) # Changing globals shouldn't affect hash # \todo : But it currently does due to current issue with handling motion blur. Change this # assertNotEqual to an assertEqual after fixing shutter handling for capsules options["options"].addChild( Gaffer.NameValuePlug("test", IECore.IntData(10))) self.assertNotEqual(c, encapsulate["out"].object("/group/group"))
def test( self ) : # - groupA # - groupB # - sphere # - cube # - sphere sphere = GafferScene.Sphere() sphere["sets"].setValue( "sphereSet" ) cube = GafferScene.Cube() cube["sets"].setValue( "cubeSet" ) groupB = GafferScene.Group() groupB["in"][0].setInput( sphere["out"] ) groupB["in"][1].setInput( sphere["out"] ) groupB["name"].setValue( "groupB" ) groupA = GafferScene.Group() groupA["in"][0].setInput( groupB["out"] ) groupA["in"][1].setInput( sphere["out"] ) groupA["name"].setValue( "groupA" ) # When there is no filter attached, the node # should be an exact pass-through. encapsulate = GafferScene.Encapsulate() encapsulate["in"].setInput( groupA["out"] ) self.assertSceneHashesEqual( encapsulate["out"], groupA["out"], checks = self.allSceneChecks - { "sets" } ) self.assertScenesEqual( encapsulate["out"], groupA["out"] ) # The same goes if there is a filter but it # doesn't match anything. pathFilter = GafferScene.PathFilter() encapsulate["filter"].setInput( pathFilter["out"] ) self.assertSceneHashesEqual( encapsulate["out"], groupA["out"], checks = self.allSceneChecks - { "sets" } ) self.assertScenesEqual( encapsulate["out"], groupA["out"] ) # Even when the filter does match something, the # unmatched paths should be unaffected, and the # globals and set names should be unchanged. pathFilter["paths"].setValue( IECore.StringVectorData( [ "/groupA/groupB" ] ) ) for path in [ "/", "/groupA", "/groupA/sphere" ] : self.assertPathHashesEqual( groupA["out"], path, encapsulate["out"], path ) self.assertPathsEqual( groupA["out"], path, encapsulate["out"], path ) for plugName in [ "globals", "setNames" ] : self.assertEqual( groupA["out"][plugName].hash(), encapsulate["out"][plugName].hash() ) self.assertEqual( groupA["out"][plugName].getValue(), encapsulate["out"][plugName].getValue() ) # And even for matched paths, the attributes, transform # and bound should be passed through unchanged. self.assertEqual( groupA["out"].attributesHash( "/groupA/groupB" ), encapsulate["out"].attributesHash( "/groupA/groupB" ) ) self.assertEqual( groupA["out"].attributes( "/groupA/groupB" ), encapsulate["out"].attributes( "/groupA/groupB" ) ) self.assertEqual( groupA["out"].transformHash( "/groupA/groupB" ), encapsulate["out"].transformHash( "/groupA/groupB" ) ) self.assertEqual( groupA["out"].transform( "/groupA/groupB" ), encapsulate["out"].transform( "/groupA/groupB" ) ) self.assertEqual( groupA["out"].boundHash( "/groupA/groupB" ), encapsulate["out"].boundHash( "/groupA/groupB" ) ) self.assertEqual( groupA["out"].bound( "/groupA/groupB" ), encapsulate["out"].bound( "/groupA/groupB" ) ) # But the children should all have been pruned away # and replaced by an appropriate Capsule. self.assertEqual( encapsulate["out"].childNames( "/groupA/groupB" ), IECore.InternedStringVectorData() ) capsule = encapsulate["out"].object( "/groupA/groupB" ) self.assertIsInstance( capsule, GafferScene.Capsule ) self.assertEqual( capsule.scene(), groupA["out"] ) self.assertEqual( capsule.root(), "/groupA/groupB" ) self.assertEqual( capsule.bound(), groupA["out"].bound( "/groupA/groupB" ) ) # And the sets should also have been pruned so they # don't include the objects beneath the capsule. self.assertEqual( encapsulate["out"].set( "sphereSet" ).value.paths(), [ "/groupA/sphere" ] ) self.assertEqual( encapsulate["out"].set( "cubeSet" ).value.paths(), [] )