IECore::ObjectPtr CompoundParameterHandler::getState( const IECore::Parameter *parameter )
{
	const CompoundParameter *compoundParameter = static_cast<const CompoundParameter *>( parameter );
	CompoundObjectPtr result = new CompoundObject;
	
	const CompoundParameter::ParameterVector &childParameters = compoundParameter->orderedParameters();
	for( CompoundParameter::ParameterVector::const_iterator cIt=childParameters.begin(); cIt!=childParameters.end(); cIt++ )
	{
		ParameterHandlerPtr h = handler( *cIt, true );
		if( h )
		{
			ObjectPtr childState = h->getState( *cIt );
			if( childState )
			{
				result->members()[(*cIt)->name()] = childState;
			}
		}
	}
	
	if( result->members().size() )
	{
		return result;
	}
	
	return 0;
}
Ejemplo n.º 2
0
IECore::ConstCompoundObjectPtr Attributes::computeProcessedAttributes( const ScenePath &path, const Gaffer::Context *context, IECore::ConstCompoundObjectPtr inputAttributes ) const
{
	const CompoundDataPlug *ap = attributesPlug();
	if( !ap->children().size() )
	{
		return inputAttributes;
	}

	/// \todo You might think that we wouldn't have to check this again
	/// because the base class would have used processesAttributes()
	/// to avoid even calling this function. But that isn't the case for
	/// some reason.
	if( globalPlug()->getValue() )
	{
		return inputAttributes;
	}

	CompoundObjectPtr result = new CompoundObject;
	// Since we're not going to modify any existing members (only add new ones),
	// and our result becomes const on returning it, we can directly reference
	// the input members in our result without copying. Be careful not to modify
	// them though!
	result->members() = inputAttributes->members();

	ap->fillCompoundObject( result->members() );

	return result;
}
IECore::ObjectPtr ClassParameterHandler::getState( const IECore::Parameter *parameter )
{
	CompoundObjectPtr result = staticPointerCast<CompoundObject>( CompoundParameterHandler::getState( parameter ) );
	if( !result )
	{
		result = new CompoundObject;
	}
	
	IECorePython::ScopedGILLock gilLock;
	try
	{
		boost::python::object pythonParameter( ParameterPtr( const_cast<Parameter *>( parameter ) ) );
		boost::python::tuple classInfo = extract<boost::python::tuple>( pythonParameter.attr( "getClass" )( true ) );
		
		std::string className = extract<const char *>( classInfo[1] )();
		int classVersion = extract<int>( classInfo[2] )();
		std::string searchPathEnvVar = extract<const char *>( classInfo[3] )();
		
		result->members()["__className"] = new IECore::StringData( className );
		result->members()["__classVersion"] = new IECore::IntData( classVersion );
		result->members()["__searchPathEnvVar"] = new IECore::StringData( searchPathEnvVar );
	}
	catch( boost::python::error_already_set )
	{
		PyErr_Print();
	}
	catch( const std::exception &e )
	{
		msg( Msg::Error, "ClassParameterHandler::getState", e.what() );
	}
	return result;
}
Ejemplo n.º 4
0
static void userHeaderGenerator( CompoundObjectPtr header )
{
	uid_t uid = getuid();
	struct passwd *st = getpwuid( uid );
	if ( st )
	{
		header->members()["userName"] = new StringData( st->pw_name );
	}
	else
	{
		header->members()["userID"] = new IntData( uid );
	}
}
Ejemplo n.º 5
0
IECore::ConstCompoundObjectPtr SceneReader::computeAttributes( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	std::string fileName = fileNamePlug()->getValue();
	if( !fileName.size() )
	{
		return parent->attributesPlug()->defaultValue();
	}
	
	ConstSceneInterfacePtr s = SharedSceneInterfaces::get( fileName );
	s = s->scene( path );
	
	// read attributes
	
	SceneInterface::NameList nameList;
	s->attributeNames( nameList );
	
	CompoundObjectPtr result = new CompoundObject;
	
	for( SceneInterface::NameList::iterator it = nameList.begin(); it != nameList.end(); ++it )
	{
		// these internal attributes should be ignored:
		if( *it == SceneCache::animatedObjectTopologyAttribute )
		{
			continue;
		}
		if( *it == SceneCache::animatedObjectPrimVarsAttribute )
		{
			continue;
		}
		
		// the const cast is ok, because we're only using it to put the object into a CompoundObject that will
		// be treated as forever const after being returned from this function.
		result->members()[ std::string( *it ) ] = constPointerCast<Object>( s->readAttribute( *it, context->getFrame() / g_frameRate ) );
	}

	// read tags and turn them into attributes of the form "user:tag:tagName"
	
	nameList.clear();
	s->readTags( nameList, IECore::SceneInterface::LocalTag );
	for( SceneInterface::NameList::const_iterator it = nameList.begin(); it != nameList.end(); ++it )
	{
		if( it->string().compare( 0, 11, "ObjectType:" ) == 0 )
		{
			continue;
		}
		result->members()["user:tag:"+it->string()] = g_trueBoolData;
	}

	return result;
}
Ejemplo n.º 6
0
IECore::ConstCompoundObjectPtr ArnoldDisplacement::attributes() const
{
	CompoundObjectPtr result = new CompoundObject;
	if( !enabledPlug()->getValue() )
	{
		return result;
	}

	CompoundObject::ObjectMap &m = result->members();
	m = mapPlug()->attributes()->members();

	for( InternedString *n = g_mapInputAttributeNames; *n != InternedString(); ++n )
	{
		CompoundObject::ObjectMap::iterator it = m.find( *n );
		if( it != m.end() )
		{
			m[g_mapAttributeName] = it->second;
			m.erase( it );
			break;
		}
	}

	m[g_heightAttributeName] = new FloatData( heightPlug()->getValue() );
	m[g_paddingAttributeName] = new FloatData( paddingPlug()->getValue() );
	m[g_zeroValueAttributeName] = new FloatData( zeroValuePlug()->getValue() );
	m[g_autoBumpAttributeName] = new BoolData( autoBumpPlug()->getValue() );

	return result;
}
Ejemplo n.º 7
0
void ObjectWriter::doWrite( const CompoundObject *operands )
{
	IndexedIOPtr io = new FileIndexedIO( fileName(), IndexedIO::rootPath, IndexedIO::Exclusive | IndexedIO::Write);

	/// \todo Establish why we only accept CompoundData / Data here when HeaderGenerator::header(), for example,
	/// returns a CompoundObject

	// write the header
	CompoundDataPtr header = boost::static_pointer_cast<CompoundData>( m_headerParameter->getValue()->copy() );

	header->writable()["typeName"] = new StringData( object()->typeName() );

	if( const VisibleRenderable* visibleRenderable = runTimeCast<const VisibleRenderable>(object()) )
	{
		header->writable()["bound"] = new Box3fData( visibleRenderable->bound() );
	}

	CompoundObjectPtr genericHeader = HeaderGenerator::header();
	for ( CompoundObject::ObjectMap::const_iterator it = genericHeader->members().begin(); it != genericHeader->members().end(); it++ )
	{
		assert( it->second );
		if ( it->second->isInstanceOf( Data::staticTypeId() ) )
		{
			header->writable()[ it->first ] = boost::static_pointer_cast< Data >( it->second );
		}
	}

	((ObjectPtr)header)->save( io, "header" );

	// write the object
	object()->save( io, "object" );
}
Ejemplo n.º 8
0
AttributeCache::AttributeCache( const std::string &filename, IndexedIO::OpenMode mode )
{
	IndexedIOPtr io = IndexedIO::create(filename, IndexedIO::rootPath, mode );

	if ( mode == IndexedIO::Write || mode == IndexedIO::Append )
	{
		m_headersIO = io->subdirectory("headers", IndexedIO::CreateIfMissing );
		m_objectsIO = io->subdirectory("objects", IndexedIO::CreateIfMissing );

		CompoundObjectPtr header = HeaderGenerator::header();
		for ( CompoundObject::ObjectMap::const_iterator it = header->members().begin(); it != header->members().end(); it++ )
		{
			writeHeader( it->first, it->second.get() );
		}
	}
	if ( mode == IndexedIO::Read )
	{
		try
		{
			m_headersIO = io->subdirectory("headers");
			m_objectsIO = io->subdirectory("objects");
		}
		catch (IECore::Exception &e)
		{
			throw Exception("Not an AttributeCache file.");
		}
	}
}
Ejemplo n.º 9
0
IECore::ConstCompoundObjectPtr Displays::computeProcessedGlobals( const Gaffer::Context *context, IECore::ConstCompoundObjectPtr inputGlobals ) const
{
	const CompoundPlug *dsp = displaysPlug(); 
	if( !dsp->children().size() )
	{
		return inputGlobals;
	}
	
	CompoundObjectPtr result = inputGlobals->copy();
	
	// add our displays to the result
	for( InputCompoundPlugIterator it( dsp ); it != it.end(); it++ )
	{
		const CompoundPlug *displayPlug = *it;
		if( displayPlug->getChild<BoolPlug>( "active" )->getValue() )
		{
			std::string name = displayPlug->getChild<StringPlug>( "name" )->getValue();
			std::string type = displayPlug->getChild<StringPlug>( "type" )->getValue();
			std::string data = displayPlug->getChild<StringPlug>( "data" )->getValue();
			if( name.size() && type.size() && data.size() )
			{
				DisplayPtr d = new Display( name, type, data );
				displayPlug->getChild<CompoundDataPlug>( "parameters" )->fillCompoundData( d->parameters() );
				result->members()["display:" + name] = d;
			}
		}
	}
	
	return result;
}
Ejemplo n.º 10
0
IECore::ConstCompoundObjectPtr AttributeProcessor::computeProcessedAttributes( const ScenePath &path, const Gaffer::Context *context, IECore::ConstCompoundObjectPtr inputAttributes ) const
{
	if( inputAttributes->members().empty() )
	{
		return inputAttributes;
	}

	const std::string names = namesPlug()->getValue();
	const bool invert = invertNamesPlug()->getValue();

	CompoundObjectPtr result = new CompoundObject;
	for( CompoundObject::ObjectMap::const_iterator it = inputAttributes->members().begin(), eIt = inputAttributes->members().end(); it != eIt; ++it )
	{
		ConstObjectPtr attribute = it->second;
		if( matchMultiple( it->first, names ) != invert )
		{
			attribute = processAttribute( path, context, it->first, attribute.get() );
		}

		if( attribute )
		{
			result->members().insert(
				CompoundObject::ObjectMap::value_type(
					it->first,
					// cast is ok - result is const immediately on
					// returning from this function, and attribute will
					// therefore not be modified.
					boost::const_pointer_cast<Object>( attribute )
				)
			);
		}
	}

	return result;
}
Ejemplo n.º 11
0
SceneGadget::SceneGadget()
	:	Gadget( defaultName<SceneGadget>() ),
		m_paused( false ),
		m_renderer( IECoreScenePreview::Renderer::create( "OpenGL", IECoreScenePreview::Renderer::Interactive ) ),
		m_controller( nullptr, nullptr, m_renderer ),
		m_updateErrored( false ),
		m_renderRequestPending( false )
{
	typedef CompoundObject::ObjectMap::value_type Option;
	CompoundObjectPtr openGLOptions = new CompoundObject;
	openGLOptions->members().insert( {
		Option( "gl:primitive:wireframeColor", new Color4fData( Color4f( 0.2f, 0.2f, 0.2f, 1.0f ) ) ),
		Option( "gl:primitive:pointColor", new Color4fData( Color4f( 0.9f, 0.9f, 0.9f, 1.0f ) ) ),
		Option( "gl:primitive:pointWidth", new FloatData( 2.0f ) )
	} );
	setOpenGLOptions( openGLOptions.get() );

	m_controller.updateRequiredSignal().connect(
		boost::bind( &SceneGadget::requestRender, this )
	);

	visibilityChangedSignal().connect( boost::bind( &SceneGadget::visibilityChanged, this ) );

	setContext( new Context );
}
Ejemplo n.º 12
0
IECore::ConstCompoundObjectPtr SceneReader::computeAttributes( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	ConstSceneInterfacePtr s = scene( path );
	if( !s )
	{
		return parent->attributesPlug()->defaultValue();
	}

	// read attributes
	SceneInterface::NameList nameList;
	s->attributeNames( nameList );

	CompoundObjectPtr result = new CompoundObject;

	for( SceneInterface::NameList::iterator it = nameList.begin(); it != nameList.end(); ++it )
	{
		// these internal attributes should be ignored:
		if( *it == SceneCache::animatedObjectTopologyAttribute )
		{
			continue;
		}
		if( *it == SceneCache::animatedObjectPrimVarsAttribute )
		{
			continue;
		}

		// the const cast is ok, because we're only using it to put the object into a CompoundObject that will
		// be treated as forever const after being returned from this function.
		result->members()[ std::string( *it ) ] = boost::const_pointer_cast<Object>( s->readAttribute( *it, context->getTime() ) );
	}

	return result;
}
Ejemplo n.º 13
0
ConstCompoundObjectPtr SetVisualiser::computeProcessedAttributes( const ScenePath &path, const Gaffer::Context *context, ConstCompoundObjectPtr inputAttributes ) const
{
	CompoundObjectPtr result = new CompoundObject;

	// Since we're not going to modify any existing members (only add a new one),
	// and our result becomes const on returning it, we can directly reference
	// the input members in our result without copying. Be careful not to modify
	// them though!
	result->members() = inputAttributes->members();

	ConstCompoundDataPtr outSetsData = outSetsPlug()->getValue();
	const InternedStringVectorData *setNamesData = outSetsData->member<InternedStringVectorData>( "names" );
	const Color3fVectorData *setColorsData = outSetsData->member<Color3fVectorData>( "colors" );

	int matchResult = PathMatcher::ExactMatch;
	if( includeInheritedPlug()->getValue() )
	{
		matchResult |= PathMatcher::AncestorMatch;
	}

	ConstCompoundDataPtr targetSets = SceneAlgo::sets( inPlug(), setNamesData->readable() );
	std::vector<Color3f> shaderColors;

	size_t index = 0;
	for( auto &setName : setNamesData->readable() )
	{
		const PathMatcherData *pathMatchData = targetSets->member<const PathMatcherData>( setName );
		if( pathMatchData->readable().match( path ) & matchResult )
		{
			shaderColors.push_back( setColorsData->readable()[ index ] );
		}
		// We need to pass our colors to the shader as a fixed size array
		if( shaderColors.size() == g_maxShaderColors )
		{
			break;
		}
		++index;
	}

	// Avoids shader compilation errors as its expecting g_maxShaderColors elements
	const size_t numColorsUsed = shaderColors.size();
	shaderColors.resize( g_maxShaderColors );

	result->members()["gl:surface"] = stripeShader( stripeWidthPlug()->getValue(), numColorsUsed, shaderColors );

	return result;
}
Ejemplo n.º 14
0
void CopyChannels::compute( Gaffer::ValuePlug *output, const Gaffer::Context *context ) const
{
	if( output == mappingPlug() )
	{
		const string channelMatchPatterns = channelsPlug()->getValue();

		CompoundObjectPtr result = new CompoundObject();
		StringVectorDataPtr channelNamesData = new StringVectorData;
		result->members()["__channelNames"] = channelNamesData;
		vector<string> &channelNames = channelNamesData->writable();
		size_t i = 0;
		for( ImagePlugIterator it( inPlugs() ); !it.done(); ++i, ++it )
		{
			/// \todo We need this check because an unconnected input
			/// has a default channelNames value of [ "R", "G", "B" ],
			/// when it should have an empty default instead. Fix
			/// the ImagePlug constructor and remove the check.
			if( !(*it)->getInput<Plug>() )
			{
				continue;
			}
			ConstStringVectorDataPtr inputChannelNamesData = (*it)->channelNamesPlug()->getValue();
			const vector<string> &inputChannelNames = inputChannelNamesData->readable();
			for( vector<string>::const_iterator cIt = inputChannelNames.begin(), ceIt = inputChannelNames.end(); cIt != ceIt; ++cIt )
			{
				if( i > 0 && !StringAlgo::matchMultiple( *cIt, channelMatchPatterns ) )
				{
					continue;
				}
				if( find( channelNames.begin(), channelNames.end(), *cIt ) == channelNames.end() )
				{
					channelNames.push_back( *cIt );
				}
				result->members()[*cIt] = new IntData( i );
			}
		}
		static_cast<CompoundObjectPlug *>( output )->setValue( result );
		return;
	}

	ImageProcessor::compute( output, context );
}
Ejemplo n.º 15
0
IECore::ConstCompoundObjectPtr SubTree::computeGlobals( const Gaffer::Context *context, const ScenePlug *parent ) const
{
	ConstCompoundObjectPtr inputGlobals = inPlug()->globalsPlug()->getValue();
	const CompoundData *inputSets = inputGlobals->member<CompoundData>( "gaffer:sets" );
	if( !inputSets )
	{
		return inputGlobals;
	}

	CompoundObjectPtr outputGlobals = inputGlobals->copy();
	CompoundDataPtr outputSets = new CompoundData;
	outputGlobals->members()["gaffer:sets"] = outputSets;

	std::string root = rootPlug()->getValue();
	if( !root.size() || root[root.size()-1] != '/' )
	{
		root += "/";
	}

	size_t prefixSize = root.size() - 1; // number of characters to remove from front of each declaration
	if( includeRootPlug()->getValue() && prefixSize )
	{
		size_t lastSlashButOne = root.rfind( "/", prefixSize-1 );
		if( lastSlashButOne != string::npos )
		{
			prefixSize = lastSlashButOne;
		}
	}

	for( CompoundDataMap::const_iterator it = inputSets->readable().begin(), eIt = inputSets->readable().end(); it != eIt; ++it )
	{
		/// \todo This could be more efficient if PathMatcher exposed the internal nodes,
		/// and allowed sharing between matchers. Then we could just pick the subtree within
		/// the matcher that we wanted.
		const PathMatcher &inputSet = static_cast<const PathMatcherData *>( it->second.get() )->readable();
		PathMatcher &outputSet = outputSets->member<PathMatcherData>( it->first, /* throwExceptions = */ false, /* createIfMissing = */ true )->writable();

		vector<string> inputPaths;
		inputSet.paths( inputPaths );
		for( vector<string>::const_iterator pIt = inputPaths.begin(), peIt = inputPaths.end(); pIt != peIt; ++pIt )
		{
			const string &inputPath = *pIt;
			if( inputPath.compare( 0, root.size(), root ) == 0 )
			{
				std::string outputPath( inputPath, prefixSize );
				outputSet.addPath( outputPath );
			}
		}
	}

	return outputGlobals;
}
Ejemplo n.º 16
0
IECore::ConstCompoundObjectPtr Grid::computeAttributes( const SceneNode::ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	if( path.size() == 1 )
	{
		CompoundObjectPtr result = new CompoundObject;

		result->members()["gl:curvesPrimitive:useGLLines"] = new BoolData( true );
		result->members()["gl:smoothing:lines"] = new BoolData( true );

		ShaderPtr shader = new Shader( "Constant", "gl:surface" );
		shader->parameters()["Cs"] = new Color3fData( Color3f( 1 ) );
		result->members()["gl:surface"] = shader;

		return result;
	}
	else if( path.size() == 2 )
	{
		float pixelWidth = 1.0f;
		if( path.back() == g_gridLinesName )
		{
			pixelWidth = gridPixelWidthPlug()->getValue();
		}
		else if( path.back() == g_centerLinesName )
		{
			pixelWidth = centerPixelWidthPlug()->getValue();
		}
		else if( path.back() == g_borderLinesName )
		{
			pixelWidth = borderPixelWidthPlug()->getValue();
		}

		CompoundObjectPtr result = new CompoundObject;
		result->members()["gl:curvesPrimitive:glLineWidth"] = new FloatData( pixelWidth );

		return result;
	}
	return outPlug()->attributesPlug()->defaultValue();
}
Ejemplo n.º 17
0
CompoundObjectPtr AttributeCache::readHeader( )
{
	CompoundObjectPtr dict = new CompoundObject();

	IndexedIO::EntryIDList directories;
	m_headersIO->entryIds( directories, IndexedIO::Directory );

	for (IndexedIO::EntryIDList::const_iterator it = directories.begin(); it != directories.end(); ++it)
	{
		ObjectPtr data = Object::load( m_headersIO, *it );
		dict->members()[ *it ] = data;
	}

	return dict;
}
Ejemplo n.º 18
0
static void unameHeaderGenerator( CompoundObjectPtr header )
{
	struct utsname name;

	if ( !uname( &name ) )
	{
		CompoundDataPtr compound = new CompoundData();
		compound->writable()["systemName"] = new StringData( name.sysname );
		compound->writable()["nodeName"] = new StringData( name.nodename );
		compound->writable()["systemRelease"] = new StringData( name.release );
		compound->writable()["systemVersion"] = new StringData( name.version );
		compound->writable()["machineName"] = new StringData( name.machine );
		header->members()["host"] = compound;
	}
}
Ejemplo n.º 19
0
CompoundObjectPtr AttributeCache::read( const ObjectHandle &obj )
{
	CompoundObjectPtr dict = new CompoundObject();

	IndexedIO::EntryIDList directories;
	IndexedIOPtr object = m_objectsIO->subdirectory( obj );
	object->entryIds( directories, IndexedIO::Directory );

	for (IndexedIO::EntryIDList::const_iterator it = directories.begin(); it != directories.end(); ++it)
	{
		ObjectPtr data = Object::load( object, *it );
		dict->members()[ *it ] = data;
	}

	return dict;
}
Ejemplo n.º 20
0
static void timeStampHeaderGenerator( CompoundObjectPtr header )
{
	time_t tm;
	time( &tm );

	/// ctime_r manpage suggest that 26 characters should be enough in all cases
	char buf[27];
	std::string strTime ( ctime_r( &tm, &buf[0] ) );

	assert( strTime.length() <= 26 );

	/// Remove the newline at the end
	boost::algorithm::trim_right( strTime );

	header->members()["timeStamp"] = new StringData( strTime );
}
Ejemplo n.º 21
0
IECore::ConstCompoundObjectPtr Prune::computeGlobals( const Gaffer::Context *context, const ScenePlug *parent ) const
{	
	ConstCompoundObjectPtr inputGlobals = inPlug()->globalsPlug()->getValue();
	const CompoundData *inputSets = inputGlobals->member<CompoundData>( "gaffer:sets" );
	if( !inputSets )
	{
		return inputGlobals;
	}

	CompoundObjectPtr outputGlobals = inputGlobals->copy();
	CompoundDataPtr outputSets = new CompoundData;
	outputGlobals->members()["gaffer:sets"] = outputSets;
	
	ContextPtr tmpContext = new Context( *Context::current() );
	Context::Scope scopedContext( tmpContext );
	ScenePath path;

	for( CompoundDataMap::const_iterator it = inputSets->readable().begin(), eIt = inputSets->readable().end(); it != eIt; ++it )
	{
		/// \todo This could be more efficient if PathMatcher exposed the internal nodes,
		/// and allowed sharing between matchers. Then we could do a really lightweight copy
		/// and just trim out the nodes we didn't want.
		const PathMatcher &inputSet = static_cast<const PathMatcherData *>( it->second.get() )->readable();
		PathMatcher &outputSet = outputSets->member<PathMatcherData>( it->first, /* throwExceptions = */ false, /* createIfMissing = */ true )->writable();
		
		vector<string> inputPaths;
		inputSet.paths( inputPaths );
		for( vector<string>::const_iterator pIt = inputPaths.begin(), peIt = inputPaths.end(); pIt != peIt; ++pIt )
		{
			path.clear();
			ScenePlug::stringToPath( *pIt, path );
			
			tmpContext->set( ScenePlug::scenePathContextName, path );
			if( !(filterPlug()->getValue() & ( Filter::ExactMatch | Filter::AncestorMatch ) ) )
			{
				outputSet.addPath( *pIt );
			}
		}
	}
	
	return outputGlobals;
}
Ejemplo n.º 22
0
IECore::ConstCompoundObjectPtr ShaderAssignment::computeProcessedAttributes( const ScenePath &path, const Gaffer::Context *context, IECore::ConstCompoundObjectPtr inputAttributes ) const
{
	CompoundObjectPtr result = inputAttributes->copy();

	const Shader *shader = shaderPlug()->source<Plug>()->ancestor<Shader>();
	if( shader )
	{
		// Shader::state() returns a const object, so that in the future it may
		// come from a cached value. we're putting it into our result which, once
		// returned, will also be treated as const and cached. for that reason the
		// temporary const_cast needed to put it into the result is justified -
		// we never change the object and nor can anyone after it is returned.
		ObjectVectorPtr state = boost::const_pointer_cast<ObjectVector>( shader->state() );
		if( state->members().size() )
		{
			result->members()["shader"] = state;
		}
	}

	return result;
}
Ejemplo n.º 23
0
IECore::ConstCompoundObjectPtr AttributeProcessor::computeProcessedAttributes( const ScenePath &path, const Gaffer::Context *context, IECore::ConstCompoundObjectPtr inputAttributes ) const
{
	if( inputAttributes->members().empty() )
	{
		return inputAttributes;
	}
		
	/// \todo See todos about name matching in PrimitiveVariableProcessor.
	typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
	std::string namesValue = namesPlug()->getValue();
	Tokenizer names( namesValue, boost::char_separator<char>( " " ) );
		
	bool invert = invertNamesPlug()->getValue();

	CompoundObjectPtr result = new CompoundObject;
	IECore::PrimitiveVariableMap::iterator next;
	for( CompoundObject::ObjectMap::const_iterator it = inputAttributes->members().begin(), eIt = inputAttributes->members().end(); it != eIt; ++it )
	{
		ConstObjectPtr attribute = it->second;
		bool found = std::find( names.begin(), names.end(), it->first.string() ) != names.end();
		if( found != invert )
		{
			attribute = processAttribute( path, context, it->first, attribute.get() );
		}
		if( attribute )
		{
			result->members().insert(
				CompoundObject::ObjectMap::value_type(
					it->first,
					// cast is ok - result is const immediately on
					// returning from this function, and attribute will
					// therefore not be modified.
					constPointerCast<Object>( attribute )
				)
			);
		}
	}
	
	return result;
}
Ejemplo n.º 24
0
IECore::ConstCompoundObjectPtr GafferScene::SceneAlgo::globalAttributes( const IECore::CompoundObject *globals )
{
	static const std::string prefix( "attribute:" );

	CompoundObjectPtr result = new CompoundObject;

	CompoundObject::ObjectMap::const_iterator it, eIt;
	for( it = globals->members().begin(), eIt = globals->members().end(); it != eIt; ++it )
	{
		if( !boost::starts_with( it->first.c_str(), "attribute:" ) )
		{
			continue;
		}
		// Cast is justified because we don't modify the data, and will return it
		// as const from this function.
		result->members()[it->first.string().substr( prefix.size() )] = boost::const_pointer_cast<Object>(
			it->second
		);
	}

	return result;
}
Ejemplo n.º 25
0
IECore::ConstCompoundObjectPtr AppleseedShaderAdaptor::computeAttributes( const ScenePath &path, const Gaffer::Context *context, const GafferScene::ScenePlug *parent ) const
{
	ConstCompoundObjectPtr inputAttributes = inPlug()->attributesPlug()->getValue();

	/// \todo Drop support for "osl:shader" assignments. They were never desirable, and the ShaderAssignment node
	/// no longer makes them.
	const ShaderNetwork *shaderNetwork = inputAttributes->member<const ShaderNetwork>( g_oslShaderAttributeName );
	if( !shaderNetwork )
	{
		shaderNetwork = inputAttributes->member<const ShaderNetwork>( g_oslSurfaceAttributeName );
	}
	if( !shaderNetwork )
	{
		return inputAttributes;
	}

	const Shader *outputShader = shaderNetwork->outputShader();
	if( !outputShader )
	{
		return inputAttributes;
	}

	OSLQuery::Parameter *firstOutput = firstOutputParameter( outputShader->getName() );

	// Build an adaptor network

	ShaderNetworkPtr adaptedNetwork;
	if( firstOutput && firstOutput->isclosure )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { materialHandle, g_bsdfParameterName } }
		);
		adaptedNetwork->setOutput( materialHandle );

	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeColor )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );
		adaptedNetwork->addConnection(
			{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { emissionHandle, "Color" } }
		);

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);

		adaptedNetwork->setOutput( materialHandle );
	}
	else if( firstOutput && ( firstOutput->type == TypeDesc::TypeFloat || firstOutput->type == TypeDesc::TypeInt ) )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		InternedString colorBuildHandle = adaptedNetwork->addShader( "colorBuild", std::move( colorBuild ) );
		for( const auto &channel : { "R", "G", "B" } )
		{
			adaptedNetwork->addConnection(
				{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { colorBuildHandle, channel } }
			);
		}

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );
		adaptedNetwork->addConnection(
			{ { colorBuildHandle, "ColorOut" }, { emissionHandle, "Color" } }
		);

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);
		adaptedNetwork->setOutput( materialHandle );
	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeVector )
	{
		adaptedNetwork = shaderNetwork->copy();
		ShaderPtr vectorSplit = new Shader( "vector/as_vector_split", "osl:shader" );
		InternedString vectorSplitHandle = adaptedNetwork->addShader( "vectorSplit", std::move( vectorSplit ) );
		adaptedNetwork->addConnection(
			{ { adaptedNetwork->getOutput().shader, firstOutput->name.string() }, { vectorSplitHandle, "Vector" } }
		);

		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		InternedString colorBuildHandle = adaptedNetwork->addShader( "colorBuild", std::move( colorBuild ) );
		for( const auto &c : { make_pair( "X", "R" ), make_pair( "Y", "G" ), make_pair( "Z", "B" ) } )
		{
			adaptedNetwork->addConnection(
				{ { vectorSplitHandle, c.first }, { colorBuildHandle, c.second } }
			);
		}

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );
		adaptedNetwork->addConnection(
			{ { colorBuildHandle, "ColorOut" }, { emissionHandle, "Color" } }
		);

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);
		adaptedNetwork->setOutput( materialHandle );
	}
	else
	{
		// Shader has no output, or an output we can't map sensibly.
		// Make an "error" shader.
		adaptedNetwork = new ShaderNetwork;
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new Color3fData( Imath::Color3f( 1, 0, 0 ) );
		InternedString emissionHandle = adaptedNetwork->addShader( "emission", std::move( emission ) );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		InternedString materialHandle = adaptedNetwork->addShader( "material", std::move( material ) );
		adaptedNetwork->addConnection(
			{ { emissionHandle, "BSDF" }, { materialHandle, "BSDF" } }
		);
		adaptedNetwork->setOutput( materialHandle );
	}

	// Place the new network into the "osl:surface" attribute
	// and remove the "osl:shader" attribute.

	CompoundObjectPtr outputAttributes = new CompoundObject;
	outputAttributes->members() = inputAttributes->members(); // Shallow copy for speed - do not modify in place!
	outputAttributes->members()[g_oslSurfaceAttributeName] = adaptedNetwork;
	outputAttributes->members().erase( g_oslShaderAttributeName );

	return outputAttributes;
}
Ejemplo n.º 26
0
IECore::ConstCompoundObjectPtr AppleseedShaderAdaptor::computeAttributes( const ScenePath &path, const Gaffer::Context *context, const GafferScene::ScenePlug *parent ) const
{
	ConstCompoundObjectPtr inputAttributes = inPlug()->attributesPlug()->getValue();

	const ObjectVector *shaderNetwork = inputAttributes->member<const ObjectVector>( g_oslShaderAttributeName );
	if( !shaderNetwork || shaderNetwork->members().empty() )
	{
		return inputAttributes;
	}

	const Shader *rootShader = runTimeCast<const Shader>( shaderNetwork->members().back().get() );
	if( !rootShader )
	{
		return inputAttributes;
	}

	OSLQuery::Parameter *firstOutput = firstOutputParameter( rootShader->getName() );

	// Build an adapter network if we can.

	bool prependInputNetwork = true;
	vector<ShaderPtr> adapters;
	if( firstOutput && firstOutput->isclosure )
	{
		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		adapters.push_back( material );
	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeColor )
	{
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( emission );
		adapters.push_back( material );
	}
	else if( firstOutput && ( firstOutput->type == TypeDesc::TypeFloat || firstOutput->type == TypeDesc::TypeInt ) )
	{
		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		StringDataPtr colorLink = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		colorBuild->parameters()["R"] = colorLink;
		colorBuild->parameters()["G"] = colorLink;
		colorBuild->parameters()["B"] = colorLink;
		colorBuild->parameters()[g_handleParameterName] = new StringData( "adapterColorBuildHandle" );

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new StringData( "link:adapterColorBuildHandle.ColorOut" );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( colorBuild );
		adapters.push_back( emission );
		adapters.push_back( material );
	}
	else if( firstOutput && firstOutput->type == TypeDesc::TypeVector )
	{
		ShaderPtr vectorSplit = new Shader( "vector/as_vector_split", "osl:shader" );
		vectorSplit->parameters()["Vector"] = new StringData( "link:adapterInputHandle." + firstOutput->name.string() );
		vectorSplit->parameters()[g_handleParameterName] = new StringData( "adapterVectorSplit" );

		ShaderPtr colorBuild = new Shader( "color/as_color_build", "osl:shader" );
		colorBuild->parameters()["R"] = new StringData( "link:adapterVectorSplit.X" );
		colorBuild->parameters()["G"] = new StringData( "link:adapterVectorSplit.Y" );
		colorBuild->parameters()["B"] = new StringData( "link:adapterVectorSplit.Z" );
		colorBuild->parameters()[g_handleParameterName] = new StringData( "adapterColorBuildHandle" );

		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new StringData( "link:adapterColorBuildHandle.ColorOut" );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( vectorSplit );
		adapters.push_back( colorBuild );
		adapters.push_back( emission );
		adapters.push_back( material );
	}
	else
	{
		// Shader has no output, or an output we can't map sensibly.
		// Make an "error" shader.
		ShaderPtr emission = new Shader( "surface/as_emission_surface", "osl:shader" );
		emission->parameters()["Color"] = new Color3fData( Imath::Color3f( 1, 0, 0 ) );
		emission->parameters()[g_handleParameterName] = new StringData( "adapterEmissionHandle" );

		ShaderPtr material = new Shader( "material/as_material_builder", "osl:surface" );
		material->parameters()[g_bsdfParameterName] = new StringData( "link:adapterEmissionHandle.BSDF" );

		adapters.push_back( emission );
		adapters.push_back( material );

		prependInputNetwork = false; // We don't need the original shaders at all
	}

	// Make a new network with the adapter network
	// appended onto the input network

	ObjectVectorPtr adaptedNetwork = new ObjectVector();

	if( prependInputNetwork )
	{
		adaptedNetwork->members() = shaderNetwork->members(); // Shallow copy for speed - do not modify in place!

		ShaderPtr rootShaderCopy = rootShader->copy();
		rootShaderCopy->parameters()[g_handleParameterName] = new StringData( "adapterInputHandle" );;
		adaptedNetwork->members().back() = rootShaderCopy;
	}

	std::copy( adapters.begin(), adapters.end(), back_inserter( adaptedNetwork->members() ) );

	// Place the new network into the "osl:surface" attribute
	// and remove the "osl:shader" attribute.

	CompoundObjectPtr outputAttributes = new CompoundObject;
	outputAttributes->members() = inputAttributes->members(); // Shallow copy for speed - do not modify in place!
	outputAttributes->members()[g_oslSurfaceAttributeName] = adaptedNetwork;
	outputAttributes->members().erase( g_oslShaderAttributeName );

	return outputAttributes;
}
Ejemplo n.º 27
0
ObjectPtr EnvMapSampler::doOperation( const CompoundObject * operands )
{
	ImagePrimitivePtr image = static_cast<ImagePrimitive *>( imageParameter()->getValue() )->copy();
	Box2i dataWindow = image->getDataWindow();

	// find the rgb channels
	ConstFloatVectorDataPtr redData = image->getChannel<float>( "R" );
	ConstFloatVectorDataPtr greenData = image->getChannel<float>( "G" );
	ConstFloatVectorDataPtr blueData = image->getChannel<float>( "B" );
	if( !(redData && greenData && blueData) )
	{
		throw Exception( "Image does not contain valid RGB float channels." );
	}
	const vector<float> &red = redData->readable();
	const vector<float> &green = greenData->readable();
	const vector<float> &blue = blueData->readable();

	// get a luminance channel
	LuminanceOpPtr luminanceOp = new LuminanceOp();
	luminanceOp->inputParameter()->setValue( image );
	luminanceOp->copyParameter()->getTypedValue() = false;
	luminanceOp->removeColorPrimVarsParameter()->getTypedValue() = false;
	luminanceOp->operate();

	// do the median cut thing to get some samples
	MedianCutSamplerPtr sampler = new MedianCutSampler;
	sampler->imageParameter()->setValue( image );
	sampler->subdivisionDepthParameter()->setNumericValue( subdivisionDepthParameter()->getNumericValue() );
	ConstCompoundObjectPtr samples = boost::static_pointer_cast<CompoundObject>( sampler->operate() );
	const vector<V2f> &centroids = boost::static_pointer_cast<V2fVectorData>( samples->members().find( "centroids" )->second )->readable();
	const vector<Box2i> &areas = boost::static_pointer_cast<Box2iVectorData>( samples->members().find( "areas" )->second )->readable();

	// get light directions and colors from the samples
	V3fVectorDataPtr directionsData = new V3fVectorData;
	Color3fVectorDataPtr colorsData = new Color3fVectorData;
	vector<V3f> &directions = directionsData->writable();
	vector<Color3f> &colors = colorsData->writable();

	float radiansPerPixel = M_PI / (dataWindow.size().y + 1);
	float angleAtTop = ( M_PI - radiansPerPixel ) / 2.0f;

	for( unsigned i=0; i<centroids.size(); i++ )
	{
		const Box2i &area = areas[i];
		Color3f color( 0 );
		for( int y=area.min.y; y<=area.max.y; y++ )
		{
			int yRel = y - dataWindow.min.y;

			float angle = angleAtTop - yRel * radiansPerPixel;
			float weight = cosf( angle );
			int index = (area.min.x - dataWindow.min.x) + (dataWindow.size().x + 1 ) * yRel;
			for( int x=area.min.x; x<=area.max.x; x++ )
			{
				color[0] += weight * red[index];
				color[1] += weight * green[index];
				color[2] += weight * blue[index];
				index++;
			}
		}
		color /= red.size();
		colors.push_back( color );

		float phi = angleAtTop - (centroids[i].y - dataWindow.min.y) * radiansPerPixel;

		V3f direction;
		direction.y = sinf( phi );
		float r = cosf( phi );
		float theta = 2 * M_PI * lerpfactor( (float)centroids[i].x, (float)dataWindow.min.x, (float)dataWindow.max.x );
		direction.x = r * cosf( theta );
		direction.z = r * sinf( theta );

		directions.push_back( -direction ); // negated so we output the direction the light shines in
	}

	// return the result
	CompoundObjectPtr result = new CompoundObject;
	result->members()["directions"] = directionsData;
	result->members()["colors"] = colorsData;
	return result;
}
Ejemplo n.º 28
0
int ParameterisedHolder<BaseType>::knob_changed( DD::Image::Knob *knob )
{	
	if( knob==m_classSpecifierKnob || knob==m_classReloadKnob )
	{			
		// reload the class, or load a new class

		updateParameterised( knob==m_classReloadKnob );		
			
		// regenerate the knobs used to represent the parameters
				
		replaceKnobs();
		
		// update the version menu
		
		updateVersionChooser();

		return 1;
	}
	else if( knob==m_getParameterisedKnob )
	{
		// this is triggered by the FnParameterisedHolder.getParameterised implementation.
		// currently there's no way to get an Op * and call a method on it from
		// python, so we use the knob_changed() mechanism to simulate a function call by
		// shoving the result into g_getParameterisedResult for subsequent retrieval.
		
		g_getParameterisedResult = loadClass( false );
		if( g_getParameterisedResult )
		{
			ParameterisedInterface *parameterisedInterface = dynamic_cast<ParameterisedInterface *>( g_getParameterisedResult.get() );
			// apply current state
			ConstCompoundObjectPtr classSpecifier = runTimeCast<const CompoundObject>( m_classSpecifierKnob->getValue() );
			ConstObjectPtr handlerState = classSpecifier->member<Object>( "handlerState" );
			if( handlerState )
			{
				m_parameterHandler->setState( parameterisedInterface->parameters(), handlerState );
			}
			// get values directly from knobs as they haven't been stored at this point
			m_parameterHandler->setParameterValue( parameterisedInterface->parameters(), ParameterHandler::Knob );
		}
	
		return 1;
	}
	else if( knob==m_modifiedParametersKnob )
	{
		// this is triggered by the FnParameterisedHolder.classModificationContext() implementation.
		// as above, we use this method in lieu of being able to call a method on this class.
		
		// get the new handler state and store it so we have it for save/load copy/paste etc
		////////////////////////////////////////////////////////////////////////////////////
				
		ParameterisedInterface *inputParameterisedInterface = dynamic_cast<ParameterisedInterface *>( g_modifiedParametersInput.get() );
		ObjectPtr handlerState = m_parameterHandler->getState( inputParameterisedInterface->parameters() );
		CompoundObjectPtr classSpecifier = runTimeCast<CompoundObject>( m_classSpecifierKnob->getValue()->copy() );
		if( handlerState )
		{
			classSpecifier->members()["handlerState"] = handlerState;
		}
		else
		{
			classSpecifier->members().erase( "handlerState" );
		}
		// it seems that setting the value from inside knob_changed() doesn't emit a new knob_changed(), which
		// is fortunately what we want.
		m_classSpecifierKnob->setValue( classSpecifier ); 
		
		// apply the new state to the current parameterised object
		////////////////////////////////////////////////////////////////////////////////////
		
		ParameterisedInterface *parameterisedInterface = dynamic_cast<ParameterisedInterface *>( m_parameterised.get() );
		if( handlerState )
		{
			m_parameterHandler->setState( parameterisedInterface->parameters(), handlerState );
		}
		parameterisedInterface->parameters()->setValue( inputParameterisedInterface->parameters()->getValue() );
		
		// update the knobs using our newly updated parameterised object
		////////////////////////////////////////////////////////////////////////////////////
		
		replaceKnobs();
		setKnobValues();
		
		// forget the input 
		
		g_modifiedParametersInput = 0;
		
		return 1;
	}
	
	return BaseType::knob_changed( knob );
}
Ejemplo n.º 29
0
static void ieCoreHeaderGenerator( CompoundObjectPtr header )
{
	header->members()["ieCoreVersion"] = new StringData( versionString() );
}
Ejemplo n.º 30
0
IECore::ObjectPtr Group::computeMapping( const Gaffer::Context *context ) const
{
	/// \todo It might be more optimal to make our own Object subclass better tailored
	/// for passing the information we want.
	CompoundObjectPtr result = new CompoundObject();

	InternedStringVectorDataPtr childNamesData = new InternedStringVectorData();
	vector<InternedString> &childNames = childNamesData->writable();
	result->members()["__GroupChildNames"] = childNamesData;

	ObjectVectorPtr forwardMappings = new ObjectVector;
	result->members()["__GroupForwardMappings"] = forwardMappings;

	boost::regex namePrefixSuffixRegex( "^(.*[^0-9]+)([0-9]+)$" );
	boost::format namePrefixSuffixFormatter( "%s%d" );

	set<InternedString> allNames;
	for( ScenePlugIterator it( inPlugs() ); it != it.end(); ++it )
	{
		ConstInternedStringVectorDataPtr inChildNamesData = (*it)->childNames( ScenePath() );
		CompoundDataPtr forwardMapping = new CompoundData;
		forwardMappings->members().push_back( forwardMapping );

		const vector<InternedString> &inChildNames = inChildNamesData->readable();
		for( vector<InternedString>::const_iterator cIt = inChildNames.begin(), ceIt = inChildNames.end(); cIt!=ceIt; cIt++ )
		{
			InternedString name = *cIt;
			if( allNames.find( name ) != allNames.end() )
			{
				// uniqueify the name
				/// \todo This code is almost identical to code in GraphComponent::setName(),
				/// is there a sensible place it can be shared? The primary obstacle is that
				/// each use has a different method of storing the existing names.
				string prefix = name;
				int suffix = 1;

				boost::cmatch match;
				if( regex_match( name.value().c_str(), match, namePrefixSuffixRegex ) )
				{
					prefix = match[1];
					suffix = boost::lexical_cast<int>( match[2] );
				}

				do
				{
					name = boost::str( namePrefixSuffixFormatter % prefix % suffix );
					suffix++;
				} while( allNames.find( name ) != allNames.end() );
			}

			allNames.insert( name );
			childNames.push_back( name );
			forwardMapping->writable()[*cIt] = new InternedStringData( name );

			CompoundObjectPtr entry = new CompoundObject;
			entry->members()["n"] = new InternedStringData( *cIt );
			entry->members()["i"] = new IntData( it.base() - inPlugs()->children().begin() );
			result->members()[name] = entry;
		}
	}

	return result;
}