void expand( Context *context, const PathMatcher &paths, bool expandAncestors ) { GafferScene::PathMatcherData *expandedPaths = const_cast<GafferScene::PathMatcherData *>( context->get<GafferScene::PathMatcherData>( g_expandedPathsName, nullptr ) ); if( !expandedPaths ) { expandedPaths = new GafferScene::PathMatcherData(); context->set( g_expandedPathsName, expandedPaths ); } GafferScene::PathMatcher &expanded = expandedPaths->writable(); bool needUpdate = false; if( expandAncestors ) { for( GafferScene::PathMatcher::RawIterator it = paths.begin(), eIt = paths.end(); it != eIt; ++it ) { needUpdate |= expanded.addPath( *it ); } } else { for( GafferScene::PathMatcher::Iterator it = paths.begin(), eIt = paths.end(); it != eIt; ++it ) { needUpdate |= expanded.addPath( *it ); } } if( needUpdate ) { // We modified the expanded paths in place to avoid unecessary copying, // so the context doesn't know they've changed. So we emit the changed // signal ourselves context->changedSignal()( context, g_expandedPathsName ); } }
MurmurHash SharedDataHolder<PathMatcher>::hash() const { IECore::MurmurHash result; HashStack stack; PathMatcher m = readable(); for( PathMatcher::RawIterator it = m.begin(), eIt = m.end(); it != eIt; ++it ) { // The iterator is recursive, so we use a stack to keep // track of where we are. Resize the stack to match our // current depth. The required size has the +1 because // we need a stack entry for the root item. size_t requiredStackSize = it->size() + 1; if( requiredStackSize > stack.size() ) { // Going a level deeper. stack.push( HashNodes() ); assert( stack.size() == requiredStackSize ); } else if( requiredStackSize < stack.size() ) { // Returning from recursion to the child nodes. // Output the hashes for the children we visited // and stored on the stack previously. popHashNodes( stack, requiredStackSize, result ); } stack.top().push_back( HashNode( it->size() ? it->back().c_str() : "", it.exactMatch() ) ); } popHashNodes( stack, 0, result ); return result; }
void GafferSceneTest::testPathMatcherRawIterator() { vector<InternedString> root; vector<InternedString> a = assign::list_of( "a" ); vector<InternedString> ab = assign::list_of( "a" )( "b" ); vector<InternedString> abc = assign::list_of( "a" )( "b" )( "c" ); PathMatcher m; PathMatcher::RawIterator it = m.begin(); GAFFERTEST_ASSERT( it == m.end() ); m.addPath( abc ); it = m.begin(); GAFFERTEST_ASSERT( *it == root ); GAFFERTEST_ASSERT( it.exactMatch() == false ); GAFFERTEST_ASSERT( it != m.end() ); ++it; GAFFERTEST_ASSERT( *it == a ); GAFFERTEST_ASSERT( it.exactMatch() == false ); GAFFERTEST_ASSERT( it != m.end() ); ++it; GAFFERTEST_ASSERT( *it == ab ); GAFFERTEST_ASSERT( it.exactMatch() == false ); GAFFERTEST_ASSERT( it != m.end() ); ++it; GAFFERTEST_ASSERT( *it == abc ); GAFFERTEST_ASSERT( it.exactMatch() == true ); GAFFERTEST_ASSERT( it != m.end() ); ++it; GAFFERTEST_ASSERT( it == m.end() ); }
IECore::PathMatcher SceneGadget::convertSelection( IECore::UIntVectorDataPtr ids ) const { CompoundDataMap parameters = { { "selection", ids } }; if( m_selectionMask ) { parameters["mask"] = m_selectionMask; } auto pathsData = static_pointer_cast<PathMatcherData>( m_renderer->command( "gl:querySelection", parameters ) ); PathMatcher result = pathsData->readable(); // Unexpanded locations are represented with // objects named __unexpandedChildren__ to allow // locations to have an object _and_ children. // We want to replace any such locations with their // parent location. const InternedString unexpandedChildren = "__unexpandedChildren__"; vector<InternedString> parent; PathMatcher toAdd; PathMatcher toRemove; for( PathMatcher::Iterator it = result.begin(), eIt = result.end(); it != eIt; ++it ) { if( it->size() && it->back() == unexpandedChildren ) { toRemove.addPath( *it ); parent.assign( it->begin(), it->end() - 1 ); toAdd.addPath( parent ); } } result.addPaths( toAdd ); result.removePaths( toRemove ); return result; }
void GafferSceneTest::testPathMatcherIteratorPrune() { vector<InternedString> root; vector<InternedString> abc = assign::list_of( "a" )( "b" )( "c" ); // Prune an empty iterator range. PathMatcher m; PathMatcher::Iterator it = m.begin(); GAFFERTEST_ASSERT( it == m.end() ); it.prune(); GAFFERTEST_ASSERT( it == m.end() ); // Prune the root iterator itself. m.addPath( root ); it = m.begin(); GAFFERTEST_ASSERT( *it == root ); GAFFERTEST_ASSERT( it != m.end() ); it.prune(); GAFFERTEST_ASSERT( *it == root ); GAFFERTEST_ASSERT( it != m.end() ); ++it; GAFFERTEST_ASSERT( it == m.end() ); // As above, but actually with some // descendants to be pruned. m.addPath( abc ); it = m.begin(); GAFFERTEST_ASSERT( *it == root ); GAFFERTEST_ASSERT( it != m.end() ); it.prune(); GAFFERTEST_ASSERT( *it == root ); GAFFERTEST_ASSERT( it != m.end() ); ++it; GAFFERTEST_ASSERT( it == m.end() ); }