Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SceneAlgo : Add findAll() method #5452

Merged
merged 3 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
API
---

- SceneAlgo :
- Added `findAll()` method, for finding all scene locations matching a predicate.
- Added `findAllWithAttribute()` method, for finding all scene locations with a particular attribute.
- ThreadState : Added `process()` method.
- Process : Added const overload for `handleException()` method. The non-const version will be removed in future.

Expand Down
14 changes: 14 additions & 0 deletions include/GafferScene/SceneAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,20 @@ void filteredParallelTraverse( const ScenePlug *scene, const FilterPlug *filterP
template <class ThreadableFunctor>
void filteredParallelTraverse( const ScenePlug *scene, const IECore::PathMatcher &filter, ThreadableFunctor &f, const ScenePlug::ScenePath &root = ScenePlug::ScenePath() );

/// Searching
/// =========

/// Returns all the locations for which `predicate( scene, path )` returns `true`.
///
/// > Caution : The search is performed in parallel, so `predicate` must be safe to call
/// concurrently from multiple threads.
template<typename Predicate>
IECore::PathMatcher findAll( const ScenePlug *scene, Predicate &&predicate, const ScenePlug::ScenePath &root = ScenePlug::ScenePath() );

/// Returns all the locations which have a local attribute called `name`. If `value` is specified, then only
/// returns locations where the attribute has that value.
GAFFERSCENE_API IECore::PathMatcher findAllWithAttribute( const ScenePlug *scene, IECore::InternedString name, const IECore::Object *value = nullptr, const ScenePlug::ScenePath &root = ScenePlug::ScenePath() );

/// Globals
/// =======

Expand Down
25 changes: 25 additions & 0 deletions include/GafferScene/SceneAlgo.inl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include "Gaffer/Context.h"

#include "tbb/enumerable_thread_specific.h"
#include "tbb/parallel_for.h"

namespace GafferScene
Expand Down Expand Up @@ -179,6 +180,30 @@ void filteredParallelTraverse( const ScenePlug *scene, const IECore::PathMatcher
parallelTraverse( scene, ff, root );
}

template<typename Predicate>
IECore::PathMatcher findAll( const ScenePlug *scene, Predicate &&predicate, const ScenePlug::ScenePath &root )
{
tbb::enumerable_thread_specific<IECore::PathMatcher> threadResults;

auto f = [&] ( const ScenePlug *scene, const ScenePlug::ScenePath &path ) {
if( predicate( scene, path ) )
{
threadResults.local().addPath( path );
}
return true;
};

parallelTraverse( scene, f, root );

return threadResults.combine(
[] ( const IECore::PathMatcher &a, const IECore::PathMatcher &b ) {
IECore::PathMatcher c = a;
c.addPaths( b );
return c;
}
);
}

} // namespace SceneAlgo

} // namespace GafferScene
85 changes: 85 additions & 0 deletions python/GafferSceneTest/SceneAlgoTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,91 @@ def testValidateName( self ) :
with self.assertRaises( RuntimeError ) :
GafferScene.SceneAlgo.validateName( badName )

def testFindAll( self ) :

plane = GafferScene.Plane()

planeFilter = GafferScene.PathFilter()
planeFilter["paths"].setValue( IECore.StringVectorData( [ "/plane" ] ) )

sphere = GafferScene.Sphere()

instancer = GafferScene.Instancer()
instancer["in"].setInput( plane["out"] )
instancer["prototypes"].setInput( sphere["out"] )
instancer["filter"].setInput( planeFilter["out"] )

self.assertEqual(
GafferScene.SceneAlgo.findAll(
instancer["out"],
lambda scene, path : scene["transform"].getValue().translation().x > 0
),
IECore.PathMatcher( [
"/plane/instances/sphere/1",
"/plane/instances/sphere/3",
] )
)

self.assertEqual(
GafferScene.SceneAlgo.findAll(
instancer["out"],
lambda scene, path : scene["transform"].getValue().translation().x > 0,
root = "/not/a/location"
),
IECore.PathMatcher()
)

def testFindAllWithAttribute( self ) :

# /group
# /light1
# /light2

light1 = GafferSceneTest.TestLight()
light1["name"].setValue( "light1" )

group = GafferScene.Group()
group["in"][0].setInput( light1["out"] )

light2 = GafferSceneTest.TestLight()
light2["name"].setValue( "light2" )

parent = GafferScene.Parent()
parent["in"].setInput( group["out"] )
parent["children"][0].setInput( light2["out"] )
parent["parent"].setValue( "/" )

self.assertEqual(
GafferScene.SceneAlgo.findAllWithAttribute( parent["out"], "light:mute" ),
IECore.PathMatcher()
)

light1["mute"]["enabled"].setValue( True )
light1["mute"]["value"].setValue( True )

light2["mute"]["enabled"].setValue( True )
light2["mute"]["value"].setValue( False )

self.assertEqual(
GafferScene.SceneAlgo.findAllWithAttribute( parent["out"], "light:mute" ),
IECore.PathMatcher( [ "/group/light1", "/light2" ] )
)

self.assertEqual(
GafferScene.SceneAlgo.findAllWithAttribute( parent["out"], "light:mute", value = IECore.BoolData( True ) ),
IECore.PathMatcher( [ "/group/light1" ] )
)

self.assertEqual(
GafferScene.SceneAlgo.findAllWithAttribute( parent["out"], "light:mute", value = IECore.BoolData( False ) ),
IECore.PathMatcher( [ "/light2" ] )
)

self.assertEqual(
GafferScene.SceneAlgo.findAllWithAttribute( parent["out"], "light:mute", root = "/group" ),
IECore.PathMatcher( [ "/group/light1" ] )
)

def tearDown( self ) :

GafferSceneTest.SceneTestCase.tearDown( self )
Expand Down
20 changes: 20 additions & 0 deletions src/GafferScene/SceneAlgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,26 @@ IECore::MurmurHash GafferScene::SceneAlgo::matchingPathsHash( const PathMatcher
return IECore::MurmurHash( f.m_h1Accum, f.m_h2Accum );
}

//////////////////////////////////////////////////////////////////////////
// Searching
//////////////////////////////////////////////////////////////////////////

IECore::PathMatcher GafferScene::SceneAlgo::findAllWithAttribute( const ScenePlug *scene, IECore::InternedString name, const IECore::Object *value, const ScenePlug::ScenePath &root )
{
return findAll(
scene,
[&] ( const ScenePlug *scene, const ScenePlug::ScenePath &path ) {
ConstCompoundObjectPtr attributes = scene->attributesPlug()->getValue();
if( const Object *attribute = attributes->member<Object>( name ) )
{
return !value || attribute->isEqualTo( value );
}
return false;
},
root
);
}

//////////////////////////////////////////////////////////////////////////
// Globals
//////////////////////////////////////////////////////////////////////////
Expand Down
24 changes: 24 additions & 0 deletions src/GafferSceneModule/SceneAlgoBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,26 @@ IECore::MurmurHash matchingPathsHashWrapper2( const IECore::PathMatcher &filter,
return SceneAlgo::matchingPathsHash( filter, &scene );
}

IECore::PathMatcher findAllWrapper( const ScenePlug &scene, object predicate, const ScenePlug::ScenePath &root )
{
IECorePython::ScopedGILRelease gilRelease;
return SceneAlgo::findAll(
&scene,
[&] ( ConstScenePlugPtr scene, const ScenePlug::ScenePath &path ) {
const std::string pathString = ScenePlug::pathToString( path );
IECorePython::ScopedGILLock gilLock;
return predicate( boost::const_pointer_cast<ScenePlug>( scene ), pathString );
},
root
);
}

IECore::PathMatcher findAllWithAttributeWrapper( const ScenePlug &scene, InternedString name, const Object *value, const ScenePlug::ScenePath &root )
{
IECorePython::ScopedGILRelease gilRelease;
return SceneAlgo::findAllWithAttribute( &scene, name, value, root );
}

Imath::V2f shutterWrapper( const IECore::CompoundObject &globals, const ScenePlug &scene )
{
IECorePython::ScopedGILRelease r;
Expand Down Expand Up @@ -319,6 +339,10 @@ void bindSceneAlgo()
def( "matchingPaths", &matchingPathsWrapper4 );
def( "matchingPathsHash", &matchingPathsHashWrapper1, ( arg( "filter" ), arg( "scene" ), arg( "root" ) = "/" ) );
def( "matchingPathsHash", &matchingPathsHashWrapper2, ( arg( "filter" ), arg( "scene" ) ) );

def( "findAll", &findAllWrapper, ( arg( "scene" ), arg( "predicate" ), arg( "root" ) = "/" ) );
def( "findAllWithAttribute", &findAllWithAttributeWrapper, ( arg( "scene" ), arg( "name" ), arg( "value" ) = object(), arg( "root" ) = "/" ) );

def( "shutter", &shutterWrapper );
def( "setExists", &setExistsWrapper );
def(
Expand Down
Loading