Пример #1
0
    def testSetNodePosition(self):

        s = Gaffer.ScriptNode()
        s["n"] = Gaffer.Node()

        g = GafferUI.GraphGadget(s)

        self.assertFalse(g.hasNodePosition(s["n"]))
        g.setNodePosition(s["n"], IECore.V2f(-100, 2000))
        self.assertEqual(g.getNodePosition(s["n"]), IECore.V2f(-100, 2000))
        self.assertTrue(g.hasNodePosition(s["n"]))
Пример #2
0
    def testEnabledException(self):

        s = Gaffer.ScriptNode()

        s["n"] = GafferTest.AddNode()

        s["e"] = Gaffer.Expression()
        s["e"].setExpression("parent['n']['enabled'] = undefinedVariable")

        g = GafferUI.GraphGadget(s)
        self.assertTrue(g.nodeGadget(s["n"]) is not None)
Пример #3
0
    def testConnectSwitch(self):

        s = Gaffer.ScriptNode()
        s["n"] = GafferTest.AddNode()
        s["s"] = Gaffer.SwitchComputeNode()

        g = GafferUI.GraphGadget(s)
        g.getLayout().connectNode(g, s["s"], Gaffer.StandardSet([s["n"]]))

        self.assertTrue(isinstance(s["s"]["in"][0], Gaffer.IntPlug))
        self.assertTrue(s["s"]["in"][0].getInput().isSame(s["n"]["sum"]))
Пример #4
0
    def testUnconnectedNodes(self):

        s = Gaffer.ScriptNode()

        s["one"] = LayoutNode()
        s["two"] = LayoutNode()

        g = GafferUI.GraphGadget(s)
        g.getLayout().layoutNodes(g)

        self.assertNoOverlaps(g)
Пример #5
0
	def testConnectionsForNestedPlugs( self ) :

		script = Gaffer.ScriptNode()

		script["n"] = NestedPlugTestNode()
		script["n"]["c"] = Gaffer.Plug()
		script["n"]["c"]["i"] = Gaffer.IntPlug()

		script["n2"] = NestedPlugTestNode()
		script["n2"]["c"] = Gaffer.Plug(  direction = Gaffer.Plug.Direction.Out )
		script["n2"]["c"]["o"] = Gaffer.IntPlug( direction = Gaffer.Plug.Direction.Out )

		script["n"]["c"]["i"].setInput( script["n2"]["c"]["o"] )

		s = Gaffer.StandardSet( script.children() )
		g = GafferUI.GraphGadget( script, s )

		c = g.connectionGadget( script["n"]["c"]["i"] )
		self.failUnless( c )
		self.failUnless( c.srcNodule().plug().isSame( script["n2"]["c"]["o"] ) )
		self.failUnless( c.dstNodule().plug().isSame( script["n"]["c"]["i"] ) )

		s.remove( script["n2"] )

		self.failUnless( g.nodeGadget( script["n2"] ) is None )

		c = g.connectionGadget( script["n"]["c"]["i"] )
		self.failUnless( c )
		self.failUnless( c.srcNodule() is None )
		self.failUnless( c.dstNodule().plug().isSame( script["n"]["c"]["i"] ) )

		s.add( script["n2"] )

		self.failUnless( g.nodeGadget( script["n2"] ) )

		c = g.connectionGadget( script["n"]["c"]["i"] )
		self.failUnless( c )
		self.failUnless( c.srcNodule().plug().isSame( script["n2"]["c"]["o"] ) )
		self.failUnless( c.dstNodule().plug().isSame( script["n"]["c"]["i"] ) )

		s.remove( script["n"] )

		self.failUnless( g.nodeGadget( script["n"] ) is None )

		self.failUnless( g.connectionGadget( script["n"]["c"]["i"] ) is None )

		s.add( script["n"] )

		self.failUnless( g.nodeGadget( script["n"] ) )

		c = g.connectionGadget( script["n"]["c"]["i"] )
		self.failUnless( c )
		self.failUnless( c.srcNodule().plug().isSame( script["n2"]["c"]["o"] ) )
		self.failUnless( c.dstNodule().plug().isSame( script["n"]["c"]["i"] ) )
Пример #6
0
	def testNodeGadgetMetadataChanges( self ) :

		s = Gaffer.ScriptNode()
		g = GafferUI.GraphGadget( s )

		s["n1"] = GafferTest.AddNode()
		s["n2"] = GafferTest.AddNode()
		s["n2"]["op1"].setInput( s["n1"]["sum"] )

		def assertBothVisible() :

			ng1 = g.nodeGadget( s["n1"] )
			ng2 = g.nodeGadget( s["n2"] )
			c = g.connectionGadget( s["n2"]["op1"] )
			self.assertTrue( isinstance( ng1, GafferUI.StandardNodeGadget ) )
			self.assertTrue( isinstance( ng2, GafferUI.StandardNodeGadget ) )
			self.assertTrue( isinstance( c, GafferUI.StandardConnectionGadget ) )
			self.assertTrue( c.srcNodule().isSame( ng1.nodule( s["n1"]["sum"] ) ) )
			self.assertTrue( c.dstNodule().isSame( ng2.nodule( s["n2"]["op1"] ) ) )

		assertBothVisible()

		Gaffer.Metadata.registerValue( s["n1"], "nodeGadget:type", "" )

		def assertN1Hidden() :

			ng1 = g.nodeGadget( s["n1"] )
			ng2 = g.nodeGadget( s["n2"] )
			c = g.connectionGadget( s["n2"]["op1"] )
			self.assertTrue( ng1 is None )
			self.assertTrue( isinstance( ng2, GafferUI.StandardNodeGadget ) )
			self.assertTrue( isinstance( c, GafferUI.StandardConnectionGadget ) )
			self.assertTrue( c.srcNodule() is None )
			self.assertTrue( c.dstNodule().isSame( ng2.nodule( s["n2"]["op1"] ) ) )

		assertN1Hidden()

		Gaffer.Metadata.registerValue( s["n2"], "nodeGadget:type", "" )

		def assertBothHidden() :

			self.assertTrue( g.nodeGadget( s["n1"] ) is None )
			self.assertTrue( g.nodeGadget( s["n2"] ) is None )
			self.assertTrue( g.connectionGadget( s["n2"]["op1"] ) is None )

		assertBothHidden()

		Gaffer.Metadata.registerValue( s["n2"], "nodeGadget:type", "GafferUI::StandardNodeGadget" )

		assertN1Hidden()

		Gaffer.Metadata.registerValue( s["n1"], "nodeGadget:type", "GafferUI::StandardNodeGadget" )

		assertBothVisible()
Пример #7
0
	def testCanPositionNodeWithinBackdrop( self ) :

		s = Gaffer.ScriptNode()
		s["b"] = Gaffer.Backdrop()
		s["n"] = Gaffer.Node()

		g = GafferUI.GraphGadget( s )
		backdropBound = g.nodeGadget( s["b"] ).transformedBound( g )
		fallbackPosition = IECore.V2f( backdropBound.center().x, backdropBound.center().y )

		g.getLayout().positionNode( g, s["n"], fallbackPosition )
		self.assertEqual( g.getNodePosition( s["n"] ), fallbackPosition )
Пример #8
0
    def testNoDuplicatePositionPlugsAfterPasting(self):

        script = Gaffer.ScriptNode()
        script["n"] = Gaffer.Node()

        g = GafferUI.GraphGadget(script)

        script.execute(
            script.serialise(script, Gaffer.StandardSet([script["n"]])))

        self.assertTrue("__uiPosition" in script["n1"])
        self.assertFalse("__uiPosition1" in script["n1"])
Пример #9
0
    def testDisabledNodulesForPromotedPlugs(self):

        s = Gaffer.ScriptNode()
        g = GafferUI.GraphGadget(s)

        s["b"] = Gaffer.Box()
        s["b"]["n"] = self.NodulePositionNode()

        boxGadget = g.nodeGadget(s["b"])

        p = Gaffer.PlugAlgo.promote(s["b"]["n"]["op2"])
        self.assertEqual(boxGadget.nodule(p), None)
Пример #10
0
	def testChangeNodeGadgetForUnviewedNode( self ) :

		s = Gaffer.ScriptNode()
		s["b"] = Gaffer.Box()
		s["b"]["n"] = Gaffer.Node()

		g = GafferUI.GraphGadget( s )
		self.assertIsNotNone( g.nodeGadget( s["b"] ) )
		self.assertIsNone( g.nodeGadget( s["b"]["n"] ) )

		Gaffer.Metadata.registerValue( s["b"]["n"], "nodeGadget:type", "GafferUI::AuxiliaryNodeGadget" )
		self.assertIsNone( g.nodeGadget( s["b"]["n"] ) )
    def get_hierarchy_nodes(startnode, scriptnode, type_filter=HierarchyTask):
        connected = Gaffer.StandardSet()
        graphgadget = GafferUI.GraphGadget(scriptnode)
        if graphgadget is not None:
            connected.add([g.node() for g in graphgadget.connectedNodeGadgets(
                startnode,
                Gaffer.Plug.Direction.Out,
                sys.maxint
            ) if isinstance(g.node(), type_filter)])

        if isinstance(startnode, type_filter):
            connected.add(startnode)
        return connected
Пример #12
0
	def testRemoveNonNodulePlug( self ) :

		s = Gaffer.ScriptNode()

		s["n"] = Gaffer.Node()
		s["n"]["p"] = Gaffer.Plug()
		Gaffer.Metadata.registerPlugValue( s["n"]["p"], "nodule:type", "" )

		g = GafferUI.GraphGadget( s )
		self.assertTrue( g.nodeGadget( s["n"] ).nodule( s["n"]["p"] ) is None )

		# Once upon a time, this would crash.
		del s["n"]["p"]
Пример #13
0
    def testNodulePositions(self):

        s = Gaffer.ScriptNode()
        g = GafferUI.GraphGadget(s)

        s["a"] = GafferTest.AddNode()
        s["n"] = self.NodulePositionNode()
        s["r"] = GafferTest.AddNode()

        s["n"]["op1"].setInput(s["a"]["sum"])
        s["r"]["op1"].setInput(s["n"]["sum"])

        box = Gaffer.Box.create(s, Gaffer.StandardSet([s["n"]]))

        boxGadget = g.nodeGadget(box)

        self.assertEqual(
            boxGadget.connectionTangent(boxGadget.nodule(box["op1"])),
            imath.V3f(-1, 0, 0))
        self.assertEqual(
            boxGadget.connectionTangent(boxGadget.nodule(box["sum"])),
            imath.V3f(1, 0, 0))

        # Now test that a copy/paste of the box maintains the tangents in the copy.

        s2 = Gaffer.ScriptNode()
        g2 = GafferUI.GraphGadget(s2)

        s2.execute(s.serialise())

        box2 = s2[box.getName()]
        boxGadget2 = g2.nodeGadget(box2)

        self.assertEqual(
            boxGadget2.connectionTangent(boxGadget2.nodule(box2["op1"])),
            imath.V3f(-1, 0, 0))
        self.assertEqual(
            boxGadget2.connectionTangent(boxGadget2.nodule(box2["sum"])),
            imath.V3f(1, 0, 0))
Пример #14
0
	def testNoFilter( self ) :

		s = Gaffer.ScriptNode()
		s["n1"] = Gaffer.Node()

		g = GafferUI.GraphGadget( s )

		self.assertTrue( g.getRoot().isSame( s ) )
		self.assertTrue( g.getFilter() is None )
		self.assertTrue( g.nodeGadget( s["n1"] ) )

		s["n2"] = Gaffer.Node()
		self.assertTrue( g.nodeGadget( s["n1"] ) )
    def testIgnoreConnectionsFromSelf(self):

        script = Gaffer.ScriptNode()
        script["n"] = Gaffer.Node()
        script["n"]["p1"] = Gaffer.Plug(flags=Gaffer.Plug.Flags.Default
                                        | Gaffer.Plug.Flags.Dynamic)
        script["n"]["p2"] = Gaffer.Plug(flags=Gaffer.Plug.Flags.Default
                                        | Gaffer.Plug.Flags.Dynamic)
        script["n"]["p2"].setInput(script["n"]["p1"])

        g = GafferUI.GraphGadget(script)
        self.assertFalse(g.auxiliaryConnectionsGadget().hasConnection(
            script["n"], script["n"]))
Пример #16
0
	def testLayoutAccessors( self ) :

		s = Gaffer.ScriptNode()
		g = GafferUI.GraphGadget( s )
		l = g.getLayout()
		self.assertTrue( isinstance( l, GafferUI.StandardGraphLayout ) )

		l2 = GafferUI.StandardGraphLayout()
		g.setLayout( l2 )
		self.assertTrue( g.getLayout().isSame( l2 ) )

		g.setLayout( l )
		self.assertTrue( g.getLayout().isSame( l ) )
Пример #17
0
	def testRemovedNodesDontHaveGadgets( self ) :

		s = Gaffer.ScriptNode()
		g = GafferUI.GraphGadget( s )

		n = GafferTest.AddNode()
		s["add1"] = n

		self.failUnless( g.nodeGadget( n ) is not None )

		s.deleteNodes( filter = Gaffer.StandardSet( [ n ] ) )

		self.failUnless( g.nodeGadget( n ) is None )
Пример #18
0
	def testCreateWithFilter( self ) :

		script = Gaffer.ScriptNode()

		script["add1"] = GafferTest.AddNode()
		script["add2"] = GafferTest.AddNode()

		nodeFilter = Gaffer.StandardSet( [ script["add2"] ] )

		g = GafferUI.GraphGadget( script, nodeFilter )

		self.failIf( g.nodeGadget( script["add1"] ) )
		self.failUnless( g.nodeGadget( script["add2"] ) )
Пример #19
0
	def __init__( self, scriptNode, **kw ) :

		# We want to disable precise navigation motions as they interfere
		# with our keyboard shortcuts and aren't that useful in the graph
		viewportGadget = GafferUI.ViewportGadget()
		viewportGadget.setPreciseMotionAllowed( False )
		viewportGadget.setMaxPlanarZoom( imath.V2f( 25 ) )

		self.__gadgetWidget = GafferUI.GadgetWidget(
			gadget = viewportGadget,
			bufferOptions = set( [
				GafferUI.GLWidget.BufferOptions.Double,
			] ),
		)

		GafferUI.Editor.__init__( self, self.__gadgetWidget, scriptNode, **kw )

		graphGadget = GafferUI.GraphGadget( self.scriptNode() )
		self.__rootChangedConnection = graphGadget.rootChangedSignal().connect( Gaffer.WeakMethod( self.__rootChanged ) )

		self.__gadgetWidget.getViewportGadget().setPrimaryChild( graphGadget )
		self.__gadgetWidget.getViewportGadget().setDragTracking( GafferUI.ViewportGadget.DragTracking.XDragTracking | GafferUI.ViewportGadget.DragTracking.YDragTracking )
		self.__frame( scriptNode.selection() )

		self.__gadgetWidget.buttonPressSignal().connect( Gaffer.WeakMethod( self.__buttonPress ), scoped = False )
		self.__gadgetWidget.keyPressSignal().connect( Gaffer.WeakMethod( self.__keyPress ), scoped = False )
		self.__gadgetWidget.buttonDoubleClickSignal().connect( Gaffer.WeakMethod( self.__buttonDoubleClick ), scoped = False )
		self.dragEnterSignal().connect( Gaffer.WeakMethod( self.__dragEnter ), scoped = False )
		self.dragLeaveSignal().connect( Gaffer.WeakMethod( self.__dragLeave ), scoped = False )
		self.dropSignal().connect( Gaffer.WeakMethod( self.__drop ), scoped = False )
		self.__gadgetWidget.getViewportGadget().preRenderSignal().connect( Gaffer.WeakMethod( self.__preRender ), scoped = False )

		with GafferUI.ListContainer( borderWidth = 8, spacing = 0 ) as overlay :
			with GafferUI.ListContainer(
				GafferUI.ListContainer.Orientation.Horizontal,
				parenting = {
					"verticalAlignment" : GafferUI.VerticalAlignment.Top,
				}
			) :
				GafferUI.Spacer( imath.V2i( 1 ) )
				GafferUI.MenuButton(
					image = "annotations.png", hasFrame = False,
					menu = GafferUI.Menu(
						Gaffer.WeakMethod( self.__annotationsMenu ),
						title = "Annotations"
					)
				)

		self.__gadgetWidget.addOverlay( overlay )

		self.__nodeMenu = None
    def testNoExtraPlugsAfterCopyPaste(self):

        script = Gaffer.ScriptNode()
        script["b"] = Gaffer.Backdrop()
        script["n"] = Gaffer.Node()

        graphGadget = GafferUI.GraphGadget(script)
        backdropGadget = graphGadget.nodeGadget(script["b"])
        self.assertIsInstance(backdropGadget, GafferUI.BackdropNodeGadget)
        backdropGadget.frame([script["n"]])

        script.execute(
            script.serialise(filter=Gaffer.StandardSet([script["b"]])))
        self.assertEqual(script["b1"].keys(), script["b"].keys())
Пример #21
0
	def testConnectNodes( self ) :

		s = Gaffer.ScriptNode()

		s["add1"] = GafferTest.AddNode()
		s["add2"] = GafferTest.AddNode()
		s["add3"] = GafferTest.AddNode()

		s["add3"]["op1"].setInput( s["add2"]["sum"] )

		g = GafferUI.GraphGadget( s )
		g.getLayout().connectNodes( g, Gaffer.StandardSet( [ s["add3"], s["add2"] ] ), Gaffer.StandardSet( [ s["add1"] ] ) )

		self.assertTrue( s["add2"]["op1"].getInput().isSame( s["add1"]["sum"] ) )
Пример #22
0
    def testUpstreamNodeGadgets(self):

        script = Gaffer.ScriptNode()

        # a -> b -> c -> e -> f
        #           ^
        #           |
        #			d

        script["a"] = GafferTest.AddNode()
        script["b"] = GafferTest.AddNode()
        script["c"] = GafferTest.AddNode()
        script["d"] = GafferTest.AddNode()
        script["e"] = GafferTest.AddNode()
        script["f"] = GafferTest.AddNode()

        script["b"]["op1"].setInput(script["a"]["sum"])
        script["c"]["op1"].setInput(script["b"]["sum"])
        script["c"]["op2"].setInput(script["d"]["sum"])

        script["e"]["op1"].setInput(script["c"]["sum"])
        script["f"]["op1"].setInput(script["e"]["sum"])

        g = GafferUI.GraphGadget(script)

        u = [
            x.node().relativeName(script)
            for x in g.upstreamNodeGadgets(script["c"])
        ]

        self.assertEqual(len(u), 3)
        self.assertEqual(set(u), set(["a", "b", "d"]))

        u = [
            x.node().relativeName(script)
            for x in g.upstreamNodeGadgets(script["f"])
        ]
        self.assertEqual(len(u), 5)
        self.assertEqual(set(u), set(["a", "b", "d", "c", "e"]))

        # filtered nodes should be ignored

        g.setFilter(Gaffer.StandardSet([script["f"], script["e"],
                                        script["a"]]))

        u = [
            x.node().relativeName(script)
            for x in g.upstreamNodeGadgets(script["f"])
        ]
        self.assertEqual(u, ["e"])
Пример #23
0
	def testFilterIsChildSet( self ) :

		s = Gaffer.ScriptNode()
		s["n1"] = Gaffer.Node()

		g = GafferUI.GraphGadget( s, Gaffer.ChildSet( s ) )
		self.assertTrue( g.nodeGadget( s["n1"] ) )

		l = len( g )

		s["n2"] = Gaffer.Node()
		self.assertTrue( g.nodeGadget( s["n2"] ) )

		self.assertEqual( len( g ), l + 1 )
Пример #24
0
    def testInsertDot(self):

        s = Gaffer.ScriptNode()
        s["n1"] = GafferTest.AddNode()
        s["n2"] = GafferTest.AddNode()
        s["n2"]["op1"].setInput(s["n1"]["sum"])

        s["d"] = Gaffer.Dot()

        g = GafferUI.GraphGadget(s)
        g.getLayout().connectNode(g, s["d"], Gaffer.StandardSet([s["n1"]]))

        self.assertTrue(s["d"]["out"].source().isSame(s["n1"]["sum"]))
        self.assertTrue(s["n2"]["op1"].getInput().isSame(s["d"]["out"]))
    def get_required_tasks(startnode, scriptnode):
        graphgadget = GafferUI.GraphGadget(scriptnode)
        assert graphgadget, "We need a proper graphgadget."

        def _reduce_hierarchy_levels(nodes):
            """ Reduces unnecessary hierarchy levels for those cases that there's
            only a single entry in a Tuple or List. In these cases the nesting
            does not add any useful information and can be ignored.
            """
            if isinstance(nodes, List):
                while len(nodes) == 1:
                    nodes = nodes[0]

            elif isinstance(nodes, Tuple):
                while (len(nodes)) == 1:
                    nodes = nodes[0]
                if not isinstance(nodes, Tuple):
                    nodes = Tuple(nodes)

            return nodes

        def _get_nodes(current):
            required_tasks = List([])
            downstream_nodes = tuple([g.node() for g in graphgadget.connectedNodeGadgets(
                current,
                Gaffer.Plug.Direction.Out,
                1
            )])

            # Sorting by the x position is the expected behaviour for serial execution.
            # We assume that the x ordering of downstream nodes is the determining
            # factor for execution order.
            downstream_nodes = sorted(downstream_nodes, key=lambda node: graphgadget.getNodePosition(node).x)

            for node in downstream_nodes:
                if isinstance(node, Gaffer.Dot):
                    required_tasks.append(_reduce_hierarchy_levels(_get_nodes(node)))
                elif isinstance(node, Serial):
                    required_tasks.append(_reduce_hierarchy_levels(Tuple(_get_nodes(node))))
                elif isinstance(node, Parallel):
                    required_tasks.append(_reduce_hierarchy_levels(List(_get_nodes(node))))
                elif isinstance(node, (HierarchyTask, JobtronautTask)):
                    required_tasks.append(node.getName())

            return _reduce_hierarchy_levels(required_tasks)

        required_tasks = _get_nodes(startnode)

        # Make sure that we don't end up with a single required task without any wrapping List or Tuple
        return required_tasks if isinstance(required_tasks, (List, Tuple)) else [required_tasks]
Пример #26
0
    def testPositionNodeFallback(self):

        s = Gaffer.ScriptNode()

        s["node1"] = LayoutNode()
        s["node2"] = LayoutNode()

        g = GafferUI.GraphGadget(s)
        g.setNodePosition(s["node1"], imath.V2f(10, 0))

        g.getLayout().positionNode(g, s["node2"], imath.V2f(-10, -20))

        self.assertEqual(g.getNodePosition(s["node1"]), imath.V2f(10, 0))
        self.assertEqual(g.getNodePosition(s["node2"]), imath.V2f(-10, -20))
Пример #27
0
	def testNodulePositionsForPromotedPlugs( self ) :

		s = Gaffer.ScriptNode()
		g = GafferUI.GraphGadget( s )

		s["b"] = Gaffer.Box()
		s["b"]["n"] = self.NodulePositionNode()

		boxGadget = g.nodeGadget( s["b"] )

		p1 = Gaffer.PlugAlgo.promote( s["b"]["n"]["op1"] )
		p2 = Gaffer.PlugAlgo.promote( s["b"]["n"]["sum"] )

		self.assertEqual( boxGadget.noduleTangent( boxGadget.nodule( p1 ) ), IECore.V3f( -1, 0, 0 ) )
		self.assertEqual( boxGadget.noduleTangent( boxGadget.nodule( p2 ) ), IECore.V3f( 1, 0, 0 ) )
Пример #28
0
	def testRemovedNodesDontHaveConnections( self ) :

		s = Gaffer.ScriptNode()

		n = GafferTest.AddNode()
		s["add1"] = n
		s["add2"] = GafferTest.AddNode()

		s["add1"]["op1"].setInput( s["add2"]["sum"] )

		g = GafferUI.GraphGadget( s )

		s.deleteNodes( filter = Gaffer.StandardSet( [ s["add1"] ] ) )

		self.failIf( g.connectionGadget( n["op1"] ) )
Пример #29
0
	def testConnectNodeInStream( self ) :

		s = Gaffer.ScriptNode()

		s["a1"] = GafferScene.ShaderAssignment()
		s["a2"] = GafferScene.ShaderAssignment()
		s["a3"] = GafferScene.ShaderAssignment()

		s["a2"]["in"].setInput( s["a1"]["out"] )

		g = GafferUI.GraphGadget( s )
		g.getLayout().connectNode( g, s["a3"], Gaffer.StandardSet( [ s["a1"] ] ) )

		self.assertTrue( s["a3"]["in"].getInput().isSame( s["a1"]["out"] ) )
		self.assertTrue( s["a2"]["in"].getInput().isSame( s["a3"]["out"] ) )
Пример #30
0
    def testSeparateNetworks(self):

        s = Gaffer.ScriptNode()

        s["top1"] = LayoutNode()
        s["bottom1"] = LayoutNode()
        s["bottom1"]["top0"].setInput(s["top1"]["bottom0"])

        s["top2"] = LayoutNode()
        s["bottom2"] = LayoutNode()
        s["bottom2"]["top0"].setInput(s["top2"]["bottom0"])

        g = GafferUI.GraphGadget(s)
        g.getLayout().layoutNodes(g)

        self.assertNoOverlaps(g)