Skip to content

Commit

Permalink
ScriptNode : Save "current frame" with script
Browse files Browse the repository at this point in the history
Also go to great pains to document that Gaffer's computations are not restricted to a single global time, and that this plug is for the user, not the coder.

Fixes #4.
  • Loading branch information
johnhaddon authored and andrewkaufman committed Feb 22, 2018
1 parent 0da7cc9 commit 9ebf43b
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 9 deletions.
35 changes: 30 additions & 5 deletions include/Gaffer/ScriptNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,38 @@ class ScriptNode : public Node
//@}

//! @name Computation context
/// The ScriptNode provides a default context for computations to be
/// performed in, and allows the user to define custom variables in
/// it via a plug. It also maps the value of fileNamePlug() into
/// the script:name variable.
///
/// The ScriptNode provides a default context that is
/// driven by plug values, so that it is serialised
/// with the script. This allows the user to :
///
/// - Set the frame and framesPerSecond variables
/// - Add arbitrary variables of their own
/// - Use a "script:name" variable generated from
/// the filename.
///
/// It is expected that all computations will use a context
/// derived from this default context, but note that this does
/// _not_ imply that there is a single global "current time".
/// Derived contexts may have their own frame and even framesPerSecond
/// values, and can be used in parallel with the default context
/// or any other context. This allows features like TimeWarp nodes
/// and UI elements which view a different frame than the default.
////////////////////////////////////////////////////////////////////
//@{
/// The default context - all computations should be performed
/// with this context, or one which inherits its variables.
/// with this context, or one derived from it.
Context *context();
const Context *context() const;
/// Drives the frame variable in the context.
///
/// > Warning : This exists primarily as a convenience for the
/// > user, so that the "current frame" is saved within the
/// > script file. To perform a computation at a particular time,
/// > create your own context rather than change the value of
/// > this plug.
FloatPlug *framePlug();
const FloatPlug *framePlug() const;
/// Drives the framesPerSecond variable in the context.
FloatPlug *framesPerSecondPlug();
const FloatPlug *framesPerSecondPlug() const;
Expand All @@ -230,6 +252,8 @@ class ScriptNode : public Node

//! @name Frame range
/// The ScriptNode defines the valid frame range using two numeric plugs.
/// \todo Perhaps these should also drive context variables? It might
/// be useful to use the frame range in expressions etc.
////////////////////////////////////////////////////////////////////
//@{
IntPlug *frameStartPlug();
Expand Down Expand Up @@ -297,6 +321,7 @@ class ScriptNode : public Node
ContextPtr m_context;

void plugSet( Plug *plug );
void contextChanged( const Context *context, const IECore::InternedString &name );

static size_t g_firstPlugIndex;

Expand Down
17 changes: 17 additions & 0 deletions python/GafferTest/ScriptNodeTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,23 @@ def testFramesPerSecond( self ) :
self.assertEqual( s2["framesPerSecond"].getValue(), 48.0 )
self.assertEqual( s2.context().getFramesPerSecond(), 48.0 )

def testFrame( self ) :

s = Gaffer.ScriptNode()
self.assertEqual( s["frame"].getValue(), 1 )
self.assertEqual( s.context().getFrame(), 1.0 )

s["frame"].setValue( 2.0 )
self.assertEqual( s.context().getFrame(), 2.0 )

s.context().setFrame( 4.0 )
self.assertEqual( s["frame"].getValue(), 4.0 )

s2 = Gaffer.ScriptNode()
s2.execute( s.serialise() )
self.assertEqual( s2["frame"].getValue(), 4.0 )
self.assertEqual( s2.context().getFrame(), 4.0 )

def testLineNumberForExecutionSyntaxError( self ) :

s = Gaffer.ScriptNode()
Expand Down
19 changes: 19 additions & 0 deletions python/GafferUI/ScriptNodeUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@

),

"frame" : (

"description",
"""
The current frame.
> Note : To perform a computation at a particular time,
> you should create your own context rather than change
> the value of this plug.
>
> ```
> with Gaffer.Context( script.context() ) as c :
> c.setFrame( f )
> ...
> ```
""",

),

"framesPerSecond" : (

"description",
Expand Down
38 changes: 34 additions & 4 deletions src/Gaffer/ScriptNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ ScriptNode::ScriptNode( const std::string &name )
frameRangePlug->addChild( frameEndPlug );
addChild( frameRangePlug );

addChild( new FloatPlug( "frame", Plug::In, 1.0f ) );
addChild( new FloatPlug( "framesPerSecond", Plug::In, 24.0f, 0.0f ) );
addChild( new CompoundDataPlug( "variables" ) );

Expand All @@ -265,6 +266,7 @@ ScriptNode::ScriptNode( const std::string &name )
m_selection->memberAcceptanceSignal().connect( boost::bind( &ScriptNode::selectionSetAcceptor, this, ::_1, ::_2 ) );

plugSetSignal().connect( boost::bind( &ScriptNode::plugSet, this, ::_1 ) );
m_context->changedSignal().connect( boost::bind( &ScriptNode::contextChanged, this, ::_1, ::_2 ) );
}

ScriptNode::~ScriptNode()
Expand Down Expand Up @@ -311,24 +313,34 @@ const IntPlug *ScriptNode::frameEndPlug() const
return getChild<ValuePlug>( g_firstPlugIndex + 2 )->getChild<IntPlug>( 1 );
}

FloatPlug *ScriptNode::framesPerSecondPlug()
FloatPlug *ScriptNode::framePlug()
{
return getChild<FloatPlug>( g_firstPlugIndex + 3 );
}

const FloatPlug *ScriptNode::framesPerSecondPlug() const
const FloatPlug *ScriptNode::framePlug() const
{
return getChild<FloatPlug>( g_firstPlugIndex + 3 );
}

FloatPlug *ScriptNode::framesPerSecondPlug()
{
return getChild<FloatPlug>( g_firstPlugIndex + 4 );
}

const FloatPlug *ScriptNode::framesPerSecondPlug() const
{
return getChild<FloatPlug>( g_firstPlugIndex + 4 );
}

CompoundDataPlug *ScriptNode::variablesPlug()
{
return getChild<CompoundDataPlug>( g_firstPlugIndex + 4 );
return getChild<CompoundDataPlug>( g_firstPlugIndex + 5 );
}

const CompoundDataPlug *ScriptNode::variablesPlug() const
{
return getChild<CompoundDataPlug>( g_firstPlugIndex + 4 );
return getChild<CompoundDataPlug>( g_firstPlugIndex + 5 );
}

bool ScriptNode::acceptsParent( const GraphComponent *potentialParent ) const
Expand Down Expand Up @@ -774,6 +786,10 @@ void ScriptNode::plugSet( Plug *plug )
{
frameStartPlug()->setValue( std::min( frameStartPlug()->getValue(), frameEndPlug()->getValue() ) );
}
else if( plug == framePlug() )
{
context()->setFrame( framePlug()->getValue() );
}
else if( plug == framesPerSecondPlug() )
{
context()->setFramesPerSecond( framesPerSecondPlug()->getValue() );
Expand All @@ -793,3 +809,17 @@ void ScriptNode::plugSet( Plug *plug )
context()->set( "script:name", fileName.stem().string() );
}
}

void ScriptNode::contextChanged( const Context *context, const IECore::InternedString &name )
{
if( name == "frame" )
{
framePlug()->setValue( context->getFrame() );
}
else if( name == "framesPerSecond" )
{
framesPerSecondPlug()->setValue( context->getFramesPerSecond() );
}
/// \todo Emit a warning if manual changes are made that
/// wouldn't be preserved across save/load.
}

0 comments on commit 9ebf43b

Please sign in to comment.