Skip to content

Commit

Permalink
BoxIO : Add static utility methods
Browse files Browse the repository at this point in the history
These can be used to promote plugs via BoxIO, and to upgrade old Boxes which were created prior to the BoxIO era.
  • Loading branch information
johnhaddon committed Apr 18, 2017
1 parent 7ebbb17 commit 7250bfa
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 0 deletions.
24 changes: 24 additions & 0 deletions include/Gaffer/BoxIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace Gaffer
{

IE_CORE_FORWARDDECLARE( StringPlug )
IE_CORE_FORWARDDECLARE( Box )

/// Utility node for representing plug promotion
/// graphically in the NodeGraph. Note that this has
Expand Down Expand Up @@ -101,6 +102,29 @@ class BoxIO : public Node

Plug::Direction direction() const;

/// Static utility methods
/// ======================
///
/// Equivalent to `PlugAlgo::promote()`, but
/// inserting an intermediate BoxIO node where
/// relevant (based on querying nodule layout
/// metadata).
/// \undoable
static Plug *promote( Plug *plug );
/// Inserts intermediate BoxIO nodes for any
/// promoted plugs that require them (based
/// on querying nodule layout metadata). This
/// can be used to upgrade boxes that were
/// either authored in the pre-BoxIO era, or
/// were created by automated scripts that
/// are not BoxIO savvy.
/// \undoable
static void insert( Box *box );
/// Returns true if `insert( box )` would
/// do anything.
/// \undoable
static bool canInsert( const Box *box );

protected :

BoxIO( Plug::Direction direction, const std::string &name=defaultName<BoxIO>() );
Expand Down
46 changes: 46 additions & 0 deletions python/GafferTest/BoxInTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,5 +301,51 @@ def assertPostconditions() :
s.redo()
assertPostconditions()

def testPromote( self ) :

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

Gaffer.Metadata.registerValue( s["b"]["n"]["op2"], "nodule:type", "" )

Gaffer.BoxIO.promote( s["b"]["n"]["op1"] )
Gaffer.BoxIO.promote( s["b"]["n"]["op2"] )
Gaffer.BoxIO.promote( s["b"]["n"]["sum"] )

self.assertTrue( isinstance( s["b"]["n"]["op1"].getInput().node(), Gaffer.BoxIn ) )
self.assertTrue( s["b"]["n"]["op1"].source().node().isSame( s["b"] ) )
self.assertTrue( s["b"]["n"]["op2"].getInput().node().isSame( s["b"] ) )
self.assertEqual( len( s["b"]["n"]["sum"].outputs() ), 1 )
self.assertTrue( isinstance( s["b"]["n"]["sum"].outputs()[0].parent(), Gaffer.BoxOut ) )

def testInsert( self ) :

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

op1Promoted = Gaffer.PlugAlgo.promote( s["b"]["n"]["op1"] )
sumPromoted = Gaffer.PlugAlgo.promote( s["b"]["n"]["sum"] )
s["b"]["n"]["op2"].setInput( s["b"]["n"]["op1"].getInput() )
self.assertEqual( len( s["b"].children( Gaffer.BoxIO ) ), 0 )

self.assertEqual( Gaffer.BoxIO.canInsert( s["b"] ), True )
Gaffer.BoxIO.insert( s["b"] )

self.assertEqual( len( s["b"].children( Gaffer.BoxIn ) ), 1 )
self.assertEqual( len( s["b"].children( Gaffer.BoxOut ) ), 1 )

self.assertTrue( isinstance( s["b"]["n"]["op1"].getInput().node(), Gaffer.BoxIn ) )
self.assertTrue( isinstance( s["b"]["n"]["op2"].getInput().node(), Gaffer.BoxIn ) )
self.assertTrue( s["b"]["n"]["op1"].source().isSame( op1Promoted ) )
self.assertTrue( s["b"]["n"]["op2"].source().isSame( op1Promoted ) )

self.assertEqual( len( s["b"]["n"]["sum"].outputs() ), 1 )
self.assertTrue( isinstance( s["b"]["n"]["sum"].outputs()[0].parent(), Gaffer.BoxOut ) )
self.assertTrue( sumPromoted.source().isSame( s["b"]["n"]["sum"] ) )

self.assertEqual( Gaffer.BoxIO.canInsert( s["b"] ), False )

if __name__ == "__main__":
unittest.main()
184 changes: 184 additions & 0 deletions src/Gaffer/BoxIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
//////////////////////////////////////////////////////////////////////////

#include "boost/bind.hpp"
#include "boost/algorithm/string/replace.hpp"

#include "Gaffer/StringPlug.h"
#include "Gaffer/BoxIO.h"
Expand All @@ -43,6 +44,9 @@
#include "Gaffer/MetadataAlgo.h"
#include "Gaffer/PlugAlgo.h"
#include "Gaffer/ScriptNode.h"
#include "Gaffer/BoxIn.h"
#include "Gaffer/BoxOut.h"
#include "Gaffer/Box.h"

using namespace IECore;
using namespace Gaffer;
Expand Down Expand Up @@ -354,3 +358,183 @@ void BoxIO::promotedPlugParentChanged( GraphComponent *graphComponent )
}
}

//////////////////////////////////////////////////////////////////////////
// Static utilities
//////////////////////////////////////////////////////////////////////////

namespace
{

/// \todo Perhaps this could be moved to PlugAlgo and
/// (along with a matching canConnect()) be used to
/// address the todo in GraphBookmarksUI.__connection?
void connect( Plug *plug1, Plug *plug2 )
{
if( plug1->direction() == plug2->direction() )
{
throw IECore::Exception( "Ambiguous connection" );
}

if( plug1->direction() == Plug::In )
{
plug1->setInput( plug2 );
}
else
{
plug2->setInput( plug1 );
}
}

InternedString g_noduleTypeName( "nodule:type" );

bool hasNodule( const Plug *plug )
{
for( const Plug *p = plug; p; p = p->parent<Plug>() )
{
ConstStringDataPtr d = Metadata::plugValue<StringData>( p, g_noduleTypeName );
if( d && d->readable() == "" )
{
return false;
}
if( p != plug )
{
if( !d || d->readable() == "GafferUI::StandardNodule" )
{
return false;
}
}
}

return true;
}

Box *enclosingBox( Plug *plug )
{
Node *node = plug->node();
if( !node )
{
return NULL;
}
return node->parent<Box>();
}

std::string promotedName( const Plug *plug )
{
std::string result = plug->relativeName( plug->node() );
boost::replace_all( result, ".", "_" );
return result;
}

} // namespace

Plug *BoxIO::promote( Plug *plug )
{
Box *box = enclosingBox( plug );
if( !box || !hasNodule( plug ) )
{
return PlugAlgo::promote( plug );
}

BoxIOPtr boxIO;
if( plug->direction() == Plug::In )
{
boxIO = new BoxIn;
}
else
{
boxIO = new BoxOut;
}

box->addChild( boxIO );
boxIO->namePlug()->setValue( promotedName( plug ) );
boxIO->setup( plug );

connect( plug, boxIO->plug<Plug>() );
return boxIO->promotedPlug<Plug>();
}

bool BoxIO::canInsert( const Box *box )
{
for( PlugIterator it( box ); !it.done(); ++it )
{
const Plug *plug = it->get();
if( plug->direction() == Plug::In )
{
const Plug::OutputContainer &outputs = plug->outputs();
for( Plug::OutputContainer::const_iterator oIt = outputs.begin(), oeIt = outputs.end(); oIt != oeIt; ++oIt )
{
if( hasNodule( *oIt ) && !runTimeCast<BoxIn>( (*oIt)->node() ) )
{
return true;
}
}
}
else
{
const Plug *input = plug->getInput<Plug>();
if( input && hasNodule( input ) && !runTimeCast<const BoxOut>( input->node() ) )
{
return true;
}
}
}

return false;
}

void BoxIO::insert( Box *box )
{
// Must take a copy of children because adding a child
// would invalidate our PlugIterator.
GraphComponent::ChildContainer children = box->children();
for( PlugIterator it( children ); !it.done(); ++it )
{
Plug *plug = it->get();
if( plug->direction() == Plug::In )
{
std::vector<Plug *> outputsNeedingBoxIn;
const Plug::OutputContainer &outputs = plug->outputs();
for( Plug::OutputContainer::const_iterator oIt = outputs.begin(), oeIt = outputs.end(); oIt != oeIt; ++oIt )
{
if( hasNodule( *oIt ) && !runTimeCast<BoxIn>( (*oIt)->node() ) )
{
outputsNeedingBoxIn.push_back( *oIt );
}
}

if( outputsNeedingBoxIn.empty() )
{
continue;
}

BoxInPtr boxIn = new BoxIn;
boxIn->namePlug()->setValue( plug->getName() );
boxIn->setup( plug );
boxIn->inPlugInternal()->setInput( plug );
for( std::vector<Plug *>::const_iterator oIt = outputsNeedingBoxIn.begin(), oeIt = outputsNeedingBoxIn.end(); oIt != oeIt; ++oIt )
{
(*oIt)->setInput( boxIn->plug<Plug>() );
}

box->addChild( boxIn );
}
else
{
// Output plug

Plug *input = plug->getInput<Plug>();
if( !input || !hasNodule( input ) || runTimeCast<BoxOut>( input->node() ) )
{
continue;
}

BoxOutPtr boxOut = new BoxOut;
boxOut->namePlug()->setValue( plug->getName() );
boxOut->setup( plug );
boxOut->plug<Plug>()->setInput( input );
plug->setInput( boxOut->outPlugInternal() );
box->addChild( boxOut );
}
}

}
7 changes: 7 additions & 0 deletions src/GafferBindings/BoxIOBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "Gaffer/BoxIn.h"
#include "Gaffer/BoxOut.h"
#include "Gaffer/Plug.h"
#include "Gaffer/Box.h"

#include "GafferBindings/DependencyNodeBinding.h"
#include "GafferBindings/BoxIOBinding.h"
Expand Down Expand Up @@ -107,6 +108,12 @@ void GafferBindings::bindBoxIO()
.def( "setup", &BoxIO::setup, ( arg( "plug" ) = object() ) )
.def( "plug", &plug )
.def( "promotedPlug", &promotedPlug )
.def( "promote", &BoxIO::promote, return_value_policy<CastToIntrusivePtr>() )
.staticmethod( "promote" )
.def( "insert", &BoxIO::insert )
.staticmethod( "insert" )
.def( "canInsert", &BoxIO::canInsert )
.staticmethod( "canInsert" )
;

Serialisation::registerSerialiser( BoxIO::staticTypeId(), new BoxIOSerialiser );
Expand Down

0 comments on commit 7250bfa

Please sign in to comment.