<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2010-2013, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "UT/UT_StringMMPattern.h"

#include "IECore/CompoundData.h"
#include "IECore/CompoundParameter.h"
#include "IECore/MessageHandler.h"

#include "IECoreHoudini/Convert.h"
#include "IECoreHoudini/ToHoudiniAttribConverter.h"
#include "IECoreHoudini/ToHoudiniGeometryConverter.h"
#include "IECoreHoudini/ToHoudiniStringAttribConverter.h"

using namespace IECore;
using namespace IECoreHoudini;

IE_CORE_DEFINERUNTIMETYPED( ToHoudiniGeometryConverter );

ToHoudiniGeometryConverter::ToHoudiniGeometryConverter( const IECore::Object *object, const std::string &description )
	:	ToHoudiniConverter( description, ObjectTypeId )
{
	srcParameter()->setValue( const_cast<Object*>( object ) ); // safe because the object is const in doConversion

	m_nameParameter = new StringParameter(
		"name",
		"The name given to the converted primitive(s). If empty, primitives will be unnamed",
		""
	);

	m_attributeFilterParameter = new StringParameter(
		"attributeFilter",
		"A list of attribute names to convert, if they exist. Uses Houdini matching syntax.",
		"*"
	);

	m_convertStandardAttributesParameter = new BoolParameter(
		"convertStandardAttributes",
		"Performs automated conversion of standard PrimitiveVariables to Houdini Attributes (i.e. Pref->rest ; Cs->Cd ; s,t->uv)",
		true
	);

	parameters()->addParameter( m_nameParameter );
	parameters()->addParameter( m_attributeFilterParameter );
	parameters()->addParameter( m_convertStandardAttributesParameter );
}

ToHoudiniGeometryConverter::~ToHoudiniGeometryConverter()
{
}

BoolParameter *ToHoudiniGeometryConverter::convertStandardAttributesParameter()
{
	return m_convertStandardAttributesParameter;
}

const BoolParameter *ToHoudiniGeometryConverter::convertStandardAttributesParameter() const
{
	return m_convertStandardAttributesParameter;
}

StringParameter *ToHoudiniGeometryConverter::nameParameter()
{
	return m_nameParameter;
}

const StringParameter *ToHoudiniGeometryConverter::nameParameter() const
{
	return m_nameParameter;
}

StringParameter *ToHoudiniGeometryConverter::attributeFilterParameter()
{
	return m_attributeFilterParameter;
}

const StringParameter *ToHoudiniGeometryConverter::attributeFilterParameter() const
{
	return m_attributeFilterParameter;
}

bool ToHoudiniGeometryConverter::convert( GU_DetailHandle handle ) const
{
	ConstCompoundObjectPtr operands = parameters()->getTypedValidatedValue<CompoundObject>();
	GU_DetailHandleAutoWriteLock writeHandle( handle );

	GU_Detail *geo = writeHandle.getGdp();
	if ( !geo )
	{
		return false;
	}

	bool result = doConversion( srcParameter()->getValidatedValue(), geo );
	if ( result )
	{
		geo->incrementMetaCacheCount();
	}

	return result;
}

GA_Range ToHoudiniGeometryConverter::appendPoints( GA_Detail *geo, size_t numPoints ) const
{
	if ( !numPoints )
	{
		return GA_Range();
	}

	GA_OffsetList offsets;
	offsets.reserve( numPoints );

	for ( size_t i=0; i < numPoints; ++i )
	{
		offsets.append( geo->appendPoint() );
	}

	return GA_Range( geo->getPointMap(), offsets );
}

PrimitiveVariable ToHoudiniGeometryConverter::processPrimitiveVariable( const IECore::Primitive *primitive, const PrimitiveVariable &primVar ) const
{
	return primVar;
}

void ToHoudiniGeometryConverter::transferAttribs( GU_Detail *geo, const GA_Range &points, const GA_Range &prims ) const
{
	const Primitive *primitive = IECore::runTimeCast<const Primitive>( srcParameter()->getValidatedValue() );
	if ( primitive )
	{
		transferAttribValues( primitive, geo, points, prims );
	}

	setName( geo, prims );
}

void ToHoudiniGeometryConverter::setName( GU_Detail *geo, const GA_Range &prims ) const
{
	// add the name attribute based on the parameter
	const std::string &name = nameParameter()->getTypedValue();
	if ( name != "" && prims.isValid() )
	{
		ToHoudiniStringVectorAttribConverter::convertString( "name", name, geo, prims );
	}
}

void ToHoudiniGeometryConverter::transferAttribValues(
	const Primitive *primitive, GU_Detail *geo,
	const GA_Range &points, const GA_Range &prims,
	PrimitiveVariable::Interpolation vertexInterpolation,
	PrimitiveVariable::Interpolation primitiveInterpolation,
	PrimitiveVariable::Interpolation pointInterpolation,
	PrimitiveVariable::Interpolation detailInterpolation
) const
{
	GA_OffsetList offsets;
	if ( prims.isValid() )
	{
		const GA_PrimitiveList &primitives = geo->getPrimitiveList();
		for ( GA_Iterator it=prims.begin(); !it.atEnd(); ++it )
		{
			const GA_Primitive *prim = primitives.get( it.getOffset() );
			size_t numPrimVerts = prim->getVertexCount();
			for ( size_t v=0; v < numPrimVerts; v++ )
			{
				if ( prim->getTypeId() == GEO_PRIMPOLY )
				{
					offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) );
				}
				else
				{
					offsets.append( prim->getVertexOffset( v ) );
				}
			}
		}
	}

	GA_Range vertRange( geo->getVertexMap(), offsets );

	UT_String filter( attributeFilterParameter()->getTypedValue() );

	// match all the string variables to each associated indices variable
	/// \todo: replace all this logic with IECore::IndexedData once it exists...
	PrimitiveVariableMap stringsToIndices;
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if ( !primitive->isPrimitiveVariableValid( it->second ) )
		{
			IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." );
			filter += UT_String( " ^" + it->first );
			continue;
		}

		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data );
		if ( !converter )
		{
			continue;
		}

		if ( it->second.data->isInstanceOf( StringVectorDataTypeId ) )
		{
			std::string indicesVariableName = it->first + "Indices";
			PrimitiveVariableMap::const_iterator indices = primitive->variables.find( indicesVariableName );
			if ( indices != primitive->variables.end() && indices->second.data->isInstanceOf( IntVectorDataTypeId ) && primitive->isPrimitiveVariableValid( indices->second ) )
			{
				stringsToIndices[it->first] = indices->second;
				filter += UT_String( " ^" + indicesVariableName );
			}
		}
	}

	bool convertStandardAttributes = m_convertStandardAttributesParameter->getTypedValue();
	if ( convertStandardAttributes && UT_String( "s" ).multiMatch( filter ) && UT_String( "t" ).multiMatch( filter ) )
	{
		// convert s and t to uv
		PrimitiveVariableMap::const_iterator sPrimVar = primitive->variables.find( "s" );
		PrimitiveVariableMap::const_iterator tPrimVar = primitive->variables.find( "t" );
		if ( sPrimVar != primitive->variables.end() && tPrimVar != primitive->variables.end() )
		{
			if ( sPrimVar->second.interpolation == tPrimVar->second.interpolation )
			{
				const FloatVectorData *sData = runTimeCast<const FloatVectorData>( sPrimVar->second.data );
				const FloatVectorData *tData = runTimeCast<const FloatVectorData>( tPrimVar->second.data );
				if ( sData && tData )
				{
					const std::vector<float> &s = sData->readable();
					const std::vector<float> &t = tData->readable();

					std::vector<Imath::V3f> uvw;
					uvw.reserve( s.size() );
					for ( size_t i=0; i < s.size(); ++i )
					{
						uvw.push_back( Imath::V3f( s[i], 1 - t[i], 0 ) );
					}

					GA_Range range = vertRange;
					if ( sPrimVar->second.interpolation == pointInterpolation )
					{
						range = points;
					}

					ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( new V3fVectorData( uvw ) );
					converter->convert( "uv", geo, range );
					filter += " ^s ^t";
				}
			}
		}
	}

 	UT_StringMMPattern attribFilter;
	attribFilter.compile( filter );

	// add the primitive variables to the various GEO_AttribDicts based on interpolation type
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		UT_String varName( it->first );
		if ( !varName.multiMatch( attribFilter ) )
		{
			continue;
		}

		PrimitiveVariable primVar = processPrimitiveVariable( primitive, it->second );
		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( primVar.data );
		if ( !converter )
		{
			continue;
		}

		PrimitiveVariable::Interpolation interpolation = primVar.interpolation;

		if ( converter->isInstanceOf( (IECore::TypeId)ToHoudiniStringVectorAttribConverterTypeId ) )
		{
			PrimitiveVariableMap::const_iterator indices = stringsToIndices.find( it->first );
			if ( indices != stringsToIndices.end() )
			{
				ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter );
				PrimitiveVariable indicesPrimVar = processPrimitiveVariable( primitive, indices->second );
				stringVectorConverter->indicesParameter()->setValidatedValue( indicesPrimVar.data );
				interpolation = indices->second.interpolation;
			}
		}

		const std::string name = ( convertStandardAttributes ) ? processPrimitiveVariableName( it->first ) : it->first;

		if ( interpolation == detailInterpolation )
 		{
			// add detail attribs
			converter->convert( name, geo );
	 	}
		else if ( interpolation == pointInterpolation )
		{
			// add point attribs
			if ( name == "P" )
			{
				// special case for P
				transferP( runTimeCast<const V3fVectorData>( primVar.data ), geo, points );
			}
			else
			{
 				converter->convert( name, geo, points );
			}
		}
		else if ( interpolation == primitiveInterpolation )
		{
			// add primitive attribs
			converter->convert( name, geo, prims );
		}
		else if ( interpolation == vertexInterpolation )
		{
			// add vertex attribs
			converter->convert( name, geo, vertRange );
		}
	}

	// backwards compatibility with older data
	const StringData *nameData = primitive->blindData()->member<StringData>( "name" );
	if ( nameData && prims.isValid() )
	{
		ToHoudiniStringVectorAttribConverter::convertString( "name", nameData->readable(), geo, prims );
	}
}

void ToHoudiniGeometryConverter::transferP( const IECore::V3fVectorData *positions, GU_Detail *geo, const GA_Range &points ) const
{
	if ( !positions )
	{
		return;
	}

	const std::vector<Imath::V3f> &pos = positions->readable();

	size_t i = 0;
	for ( GA_Iterator it=points.begin(); !it.atEnd(); ++it, ++i )
	{
		geo->setPos3( it.getOffset(), IECore::convert<UT_Vector3>( pos[i] ) );
	}
}

const std::string ToHoudiniGeometryConverter::processPrimitiveVariableName( const std::string &name ) const
{
	/// \todo: This should probably be some formal static map. Make sure to update FromHoudiniGeometryConverter as well.
	if ( name == "Cs" )
	{
		return "Cd";
	}
	else if ( name == "Os" )
	{
		return "Alpha";
	}
	else if ( name == "Pref" )
	{
		return "rest";
	}
	else if ( name == "width" )
	{
		return "pscale";
	}

	return name;
}

/////////////////////////////////////////////////////////////////////////////////
// Factory
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverterPtr ToHoudiniGeometryConverter::create( const Object *object )
{
	const TypesToFnsMap *m = typesToFns();
	TypesToFnsMap::const_iterator it = m->find( Types( object->typeId() ) );
	if( it!=m->end() )
	{
		return it->second( object );
	}

	// no exact match, so check for base class matches
	for ( TypesToFnsMap::const_iterator it = m->begin(); it != m->end(); ++it )
	{
		if ( object->isInstanceOf( it->first.fromType ) )
		{
			return it->second( object );
		}
	}

	return 0;
}

void ToHoudiniGeometryConverter::registerConverter( IECore::TypeId fromType, CreatorFn creator )
{
	TypesToFnsMap *m = typesToFns();
	m->insert( TypesToFnsMap::value_type( Types( fromType ), creator ) );
}

ToHoudiniGeometryConverter::TypesToFnsMap *ToHoudiniGeometryConverter::typesToFns()
{
	static TypesToFnsMap *m = new TypesToFnsMap;
	return m;
}

void ToHoudiniGeometryConverter::supportedTypes( std::set<IECore::TypeId> &types )
{
	types.clear();

	const TypesToFnsMap *m = typesToFns();
	for ( TypesToFnsMap::const_iterator it=m->begin(); it != m->end(); it ++ )
	{
		types.insert( it->first.fromType );
	}
}

/////////////////////////////////////////////////////////////////////////////////
// Implementation of nested Types class
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverter::Types::Types( IECore::TypeId from ) : fromType( from )
{
}

bool ToHoudiniGeometryConverter::Types::operator < ( const Types &other ) const
{
	return fromType < other.fromType;
}
<commit_msg>Fixing factory function to search base classes in the correct order<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2010-2013, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "UT/UT_StringMMPattern.h"

#include "IECore/CompoundData.h"
#include "IECore/CompoundParameter.h"
#include "IECore/MessageHandler.h"

#include "IECoreHoudini/Convert.h"
#include "IECoreHoudini/ToHoudiniAttribConverter.h"
#include "IECoreHoudini/ToHoudiniGeometryConverter.h"
#include "IECoreHoudini/ToHoudiniStringAttribConverter.h"

using namespace IECore;
using namespace IECoreHoudini;

IE_CORE_DEFINERUNTIMETYPED( ToHoudiniGeometryConverter );

ToHoudiniGeometryConverter::ToHoudiniGeometryConverter( const IECore::Object *object, const std::string &description )
	:	ToHoudiniConverter( description, ObjectTypeId )
{
	srcParameter()->setValue( const_cast<Object*>( object ) ); // safe because the object is const in doConversion

	m_nameParameter = new StringParameter(
		"name",
		"The name given to the converted primitive(s). If empty, primitives will be unnamed",
		""
	);

	m_attributeFilterParameter = new StringParameter(
		"attributeFilter",
		"A list of attribute names to convert, if they exist. Uses Houdini matching syntax.",
		"*"
	);

	m_convertStandardAttributesParameter = new BoolParameter(
		"convertStandardAttributes",
		"Performs automated conversion of standard PrimitiveVariables to Houdini Attributes (i.e. Pref->rest ; Cs->Cd ; s,t->uv)",
		true
	);

	parameters()->addParameter( m_nameParameter );
	parameters()->addParameter( m_attributeFilterParameter );
	parameters()->addParameter( m_convertStandardAttributesParameter );
}

ToHoudiniGeometryConverter::~ToHoudiniGeometryConverter()
{
}

BoolParameter *ToHoudiniGeometryConverter::convertStandardAttributesParameter()
{
	return m_convertStandardAttributesParameter;
}

const BoolParameter *ToHoudiniGeometryConverter::convertStandardAttributesParameter() const
{
	return m_convertStandardAttributesParameter;
}

StringParameter *ToHoudiniGeometryConverter::nameParameter()
{
	return m_nameParameter;
}

const StringParameter *ToHoudiniGeometryConverter::nameParameter() const
{
	return m_nameParameter;
}

StringParameter *ToHoudiniGeometryConverter::attributeFilterParameter()
{
	return m_attributeFilterParameter;
}

const StringParameter *ToHoudiniGeometryConverter::attributeFilterParameter() const
{
	return m_attributeFilterParameter;
}

bool ToHoudiniGeometryConverter::convert( GU_DetailHandle handle ) const
{
	ConstCompoundObjectPtr operands = parameters()->getTypedValidatedValue<CompoundObject>();
	GU_DetailHandleAutoWriteLock writeHandle( handle );

	GU_Detail *geo = writeHandle.getGdp();
	if ( !geo )
	{
		return false;
	}

	bool result = doConversion( srcParameter()->getValidatedValue(), geo );
	if ( result )
	{
		geo->incrementMetaCacheCount();
	}

	return result;
}

GA_Range ToHoudiniGeometryConverter::appendPoints( GA_Detail *geo, size_t numPoints ) const
{
	if ( !numPoints )
	{
		return GA_Range();
	}

	GA_OffsetList offsets;
	offsets.reserve( numPoints );

	for ( size_t i=0; i < numPoints; ++i )
	{
		offsets.append( geo->appendPoint() );
	}

	return GA_Range( geo->getPointMap(), offsets );
}

PrimitiveVariable ToHoudiniGeometryConverter::processPrimitiveVariable( const IECore::Primitive *primitive, const PrimitiveVariable &primVar ) const
{
	return primVar;
}

void ToHoudiniGeometryConverter::transferAttribs( GU_Detail *geo, const GA_Range &points, const GA_Range &prims ) const
{
	const Primitive *primitive = IECore::runTimeCast<const Primitive>( srcParameter()->getValidatedValue() );
	if ( primitive )
	{
		transferAttribValues( primitive, geo, points, prims );
	}

	setName( geo, prims );
}

void ToHoudiniGeometryConverter::setName( GU_Detail *geo, const GA_Range &prims ) const
{
	// add the name attribute based on the parameter
	const std::string &name = nameParameter()->getTypedValue();
	if ( name != "" && prims.isValid() )
	{
		ToHoudiniStringVectorAttribConverter::convertString( "name", name, geo, prims );
	}
}

void ToHoudiniGeometryConverter::transferAttribValues(
	const Primitive *primitive, GU_Detail *geo,
	const GA_Range &points, const GA_Range &prims,
	PrimitiveVariable::Interpolation vertexInterpolation,
	PrimitiveVariable::Interpolation primitiveInterpolation,
	PrimitiveVariable::Interpolation pointInterpolation,
	PrimitiveVariable::Interpolation detailInterpolation
) const
{
	GA_OffsetList offsets;
	if ( prims.isValid() )
	{
		const GA_PrimitiveList &primitives = geo->getPrimitiveList();
		for ( GA_Iterator it=prims.begin(); !it.atEnd(); ++it )
		{
			const GA_Primitive *prim = primitives.get( it.getOffset() );
			size_t numPrimVerts = prim->getVertexCount();
			for ( size_t v=0; v < numPrimVerts; v++ )
			{
				if ( prim->getTypeId() == GEO_PRIMPOLY )
				{
					offsets.append( prim->getVertexOffset( numPrimVerts - 1 - v ) );
				}
				else
				{
					offsets.append( prim->getVertexOffset( v ) );
				}
			}
		}
	}

	GA_Range vertRange( geo->getVertexMap(), offsets );

	UT_String filter( attributeFilterParameter()->getTypedValue() );

	// match all the string variables to each associated indices variable
	/// \todo: replace all this logic with IECore::IndexedData once it exists...
	PrimitiveVariableMap stringsToIndices;
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		if ( !primitive->isPrimitiveVariableValid( it->second ) )
		{
			IECore::msg( IECore::MessageHandler::Warning, "ToHoudiniGeometryConverter", "PrimitiveVariable " + it->first + " is invalid. Ignoring." );
			filter += UT_String( " ^" + it->first );
			continue;
		}

		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( it->second.data );
		if ( !converter )
		{
			continue;
		}

		if ( it->second.data->isInstanceOf( StringVectorDataTypeId ) )
		{
			std::string indicesVariableName = it->first + "Indices";
			PrimitiveVariableMap::const_iterator indices = primitive->variables.find( indicesVariableName );
			if ( indices != primitive->variables.end() && indices->second.data->isInstanceOf( IntVectorDataTypeId ) && primitive->isPrimitiveVariableValid( indices->second ) )
			{
				stringsToIndices[it->first] = indices->second;
				filter += UT_String( " ^" + indicesVariableName );
			}
		}
	}

	bool convertStandardAttributes = m_convertStandardAttributesParameter->getTypedValue();
	if ( convertStandardAttributes && UT_String( "s" ).multiMatch( filter ) && UT_String( "t" ).multiMatch( filter ) )
	{
		// convert s and t to uv
		PrimitiveVariableMap::const_iterator sPrimVar = primitive->variables.find( "s" );
		PrimitiveVariableMap::const_iterator tPrimVar = primitive->variables.find( "t" );
		if ( sPrimVar != primitive->variables.end() && tPrimVar != primitive->variables.end() )
		{
			if ( sPrimVar->second.interpolation == tPrimVar->second.interpolation )
			{
				const FloatVectorData *sData = runTimeCast<const FloatVectorData>( sPrimVar->second.data );
				const FloatVectorData *tData = runTimeCast<const FloatVectorData>( tPrimVar->second.data );
				if ( sData && tData )
				{
					const std::vector<float> &s = sData->readable();
					const std::vector<float> &t = tData->readable();

					std::vector<Imath::V3f> uvw;
					uvw.reserve( s.size() );
					for ( size_t i=0; i < s.size(); ++i )
					{
						uvw.push_back( Imath::V3f( s[i], 1 - t[i], 0 ) );
					}

					GA_Range range = vertRange;
					if ( sPrimVar->second.interpolation == pointInterpolation )
					{
						range = points;
					}

					ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( new V3fVectorData( uvw ) );
					converter->convert( "uv", geo, range );
					filter += " ^s ^t";
				}
			}
		}
	}

 	UT_StringMMPattern attribFilter;
	attribFilter.compile( filter );

	// add the primitive variables to the various GEO_AttribDicts based on interpolation type
	for ( PrimitiveVariableMap::const_iterator it=primitive->variables.begin() ; it != primitive->variables.end(); it++ )
	{
		UT_String varName( it->first );
		if ( !varName.multiMatch( attribFilter ) )
		{
			continue;
		}

		PrimitiveVariable primVar = processPrimitiveVariable( primitive, it->second );
		ToHoudiniAttribConverterPtr converter = ToHoudiniAttribConverter::create( primVar.data );
		if ( !converter )
		{
			continue;
		}

		PrimitiveVariable::Interpolation interpolation = primVar.interpolation;

		if ( converter->isInstanceOf( (IECore::TypeId)ToHoudiniStringVectorAttribConverterTypeId ) )
		{
			PrimitiveVariableMap::const_iterator indices = stringsToIndices.find( it->first );
			if ( indices != stringsToIndices.end() )
			{
				ToHoudiniStringVectorAttribConverter *stringVectorConverter = IECore::runTimeCast<ToHoudiniStringVectorAttribConverter>( converter );
				PrimitiveVariable indicesPrimVar = processPrimitiveVariable( primitive, indices->second );
				stringVectorConverter->indicesParameter()->setValidatedValue( indicesPrimVar.data );
				interpolation = indices->second.interpolation;
			}
		}

		const std::string name = ( convertStandardAttributes ) ? processPrimitiveVariableName( it->first ) : it->first;

		if ( interpolation == detailInterpolation )
 		{
			// add detail attribs
			converter->convert( name, geo );
	 	}
		else if ( interpolation == pointInterpolation )
		{
			// add point attribs
			if ( name == "P" )
			{
				// special case for P
				transferP( runTimeCast<const V3fVectorData>( primVar.data ), geo, points );
			}
			else
			{
 				converter->convert( name, geo, points );
			}
		}
		else if ( interpolation == primitiveInterpolation )
		{
			// add primitive attribs
			converter->convert( name, geo, prims );
		}
		else if ( interpolation == vertexInterpolation )
		{
			// add vertex attribs
			converter->convert( name, geo, vertRange );
		}
	}

	// backwards compatibility with older data
	const StringData *nameData = primitive->blindData()->member<StringData>( "name" );
	if ( nameData && prims.isValid() )
	{
		ToHoudiniStringVectorAttribConverter::convertString( "name", nameData->readable(), geo, prims );
	}
}

void ToHoudiniGeometryConverter::transferP( const IECore::V3fVectorData *positions, GU_Detail *geo, const GA_Range &points ) const
{
	if ( !positions )
	{
		return;
	}

	const std::vector<Imath::V3f> &pos = positions->readable();

	size_t i = 0;
	for ( GA_Iterator it=points.begin(); !it.atEnd(); ++it, ++i )
	{
		geo->setPos3( it.getOffset(), IECore::convert<UT_Vector3>( pos[i] ) );
	}
}

const std::string ToHoudiniGeometryConverter::processPrimitiveVariableName( const std::string &name ) const
{
	/// \todo: This should probably be some formal static map. Make sure to update FromHoudiniGeometryConverter as well.
	if ( name == "Cs" )
	{
		return "Cd";
	}
	else if ( name == "Os" )
	{
		return "Alpha";
	}
	else if ( name == "Pref" )
	{
		return "rest";
	}
	else if ( name == "width" )
	{
		return "pscale";
	}

	return name;
}

/////////////////////////////////////////////////////////////////////////////////
// Factory
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverterPtr ToHoudiniGeometryConverter::create( const Object *object )
{
	const TypesToFnsMap *m = typesToFns();
	TypesToFnsMap::const_iterator it = m->find( Types( object->typeId() ) );
	if( it!=m->end() )
	{
		return it->second( object );
	}

	// no exact match, so check for base class matches
	const std::vector<IECore::TypeId> &bases = RunTimeTyped::baseTypeIds( object->typeId() );
	for ( std::vector<IECore::TypeId>::const_iterator it = bases.begin(); it != bases.end(); ++it )
	{
		TypesToFnsMap::const_iterator cIt = m->find( Types( *it ) );
		if ( cIt != m->end() )
		{
			return cIt->second( object );
		}
	}

	return 0;
}

void ToHoudiniGeometryConverter::registerConverter( IECore::TypeId fromType, CreatorFn creator )
{
	TypesToFnsMap *m = typesToFns();
	m->insert( TypesToFnsMap::value_type( Types( fromType ), creator ) );
}

ToHoudiniGeometryConverter::TypesToFnsMap *ToHoudiniGeometryConverter::typesToFns()
{
	static TypesToFnsMap *m = new TypesToFnsMap;
	return m;
}

void ToHoudiniGeometryConverter::supportedTypes( std::set<IECore::TypeId> &types )
{
	types.clear();

	const TypesToFnsMap *m = typesToFns();
	for ( TypesToFnsMap::const_iterator it=m->begin(); it != m->end(); it ++ )
	{
		types.insert( it->first.fromType );
	}
}

/////////////////////////////////////////////////////////////////////////////////
// Implementation of nested Types class
/////////////////////////////////////////////////////////////////////////////////

ToHoudiniGeometryConverter::Types::Types( IECore::TypeId from ) : fromType( from )
{
}

bool ToHoudiniGeometryConverter::Types::operator < ( const Types &other ) const
{
	return fromType < other.fromType;
}
<|endoftext|>


<commit_before>//
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2011-2012 Alessandro Tasora
// Copyright (c) 2013 Project Chrono
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file at the top level of the distribution
// and at http://projectchrono.org/license-chrono.txt.
//

#include <cmath>
#include <algorithm>

#include "physics/ChContactDEM.h"
#include "physics/ChSystemDEM.h"
#include "physics/ChBodyDEM.h"

// Memory leak debugger (must be included last).
#include "core/ChMemory.h"

namespace chrono {

using namespace collision;

// Initialize static members
double ChContactDEM::m_minSlipVelocity = 1e-4;
double ChContactDEM::m_characteristicVelocity = 1;

// Construct a new DEM contact between two models using the specified contact pair information.
ChContactDEM::ChContactDEM(collision::ChModelBulletBody*     mod1,
                           collision::ChModelBulletBody*     mod2,
                           const collision::ChCollisionInfo& cinfo)
{
  Reset(mod1, mod2, cinfo);
}

// Worker function for populating a new or reusing an exsting contact.
void ChContactDEM::Reset(collision::ChModelBulletBody*     mod1,
                         collision::ChModelBulletBody*     mod2,
                         const collision::ChCollisionInfo& cinfo)
{
  assert(cinfo.distance < 0);

  m_mod1 = mod1;
  m_mod2 = mod2;

  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  // Contact points, penetration, normal
  m_p1 = cinfo.vpA;
  m_p2 = cinfo.vpB;
  m_delta = -cinfo.distance;
  m_normal = cinfo.vN;

  // Contact plane
  ChVector<> Vx, Vy, Vz;
  cinfo.vN.DirToDxDyDz(Vx, Vy, Vz);
  m_contact_plane.Set_A_axis(Vx, Vy, Vz);

  // Contact points in local frames
  m_p1_loc = body1->Point_World2Body(m_p1);
  m_p2_loc = body2->Point_World2Body(m_p2);

  // Calculate contact force
  CalculateForce();
}

// Calculate the contact force to be applied to body2, based on the
// globally specified contact force model.
void ChContactDEM::CalculateForce()
{
  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  ChSystemDEM* sys = static_cast<ChSystemDEM*>(body1->GetSystem());

  double dT = sys->GetStep();
  bool use_mat_props = sys->UseMaterialProperties();
  ContactForceModel force_model = sys->GetContactForceModel();

  // Relative velocity at contact
  ChVector<> vel2 = body2->PointSpeedLocalToParent(m_p2_loc);
  ChVector<> vel1 = body1->PointSpeedLocalToParent(m_p1_loc);
  ChVector<> relvel = vel2 - vel1;
  double     relvel_n_mag = relvel.Dot(m_normal);
  ChVector<> relvel_n = relvel_n_mag * m_normal;
  ChVector<> relvel_t = relvel - relvel_n;
  double     relvel_t_mag = relvel_t.Length();

  // Calculate effective mass
  double m_eff = body1->GetMass() * body2->GetMass() / (body1->GetMass() + body2->GetMass());

  // Calculate effective contact radius
  //// TODO:  how can I get this with current collision system!?!?!?
  double R_eff = 1;

  // Calculate composite material properties
  ChCompositeMaterialDEM mat = ChMaterialSurfaceDEM::CompositeMaterial(body1->GetMaterialSurfaceDEM(), body2->GetMaterialSurfaceDEM());

  // Contact forces.
  // All models use the following formulas for normal and tangential forces:
  //     Fn = kn * delta_n - gn * v_n
  //     Ft = kt * delta_t - gt * v_t
  double kn;
  double kt;
  double gn;
  double gt;

  double delta_t = 0;

  switch (force_model) {

  case Hooke_history:
    delta_t = relvel_t_mag * dT;

  case Hooke:
    if (use_mat_props) {
      double tmp_k = (16.0 / 15) * std::sqrt(R_eff) * mat.E_eff;
      double v2 = m_characteristicVelocity * m_characteristicVelocity;
      double tmp_g = 1 + std::pow(CH_C_PI / std::log(mat.cr_eff), 2);
      kn = tmp_k * std::pow(m_eff * v2 / tmp_k, 1.0 / 5);
      kt = kn;
      gn = std::sqrt(4 * m_eff * kn / tmp_g);
      gt = gn;
    } else {
      kn = mat.kn;
      kt = mat.kt;
      gn = m_eff * mat.gn;
      gt = m_eff * mat.gt;
    }

    break;

  case Hertz_history:
    delta_t = relvel_t_mag * dT;

  case Hertz:
    if (use_mat_props) {
      double sqrt_Rd = std::sqrt(R_eff * m_delta);
      double Sn = 2 * mat.E_eff * sqrt_Rd;
      double St = 8 * mat.G_eff * sqrt_Rd;
      double loge = std::log(mat.cr_eff);
      double beta = loge / std::sqrt(loge * loge + CH_C_PI * CH_C_PI);
      kn = (2.0 / 3) * Sn;
      kt = St;
      gn = -2 * std::sqrt(5.0 / 6) * beta * std::sqrt(Sn * m_eff);
      gt = -2 * std::sqrt(5.0 / 6) * beta * std::sqrt(St * m_eff);
    } else {
      double tmp = R_eff * std::sqrt(m_delta);
      kn = tmp * mat.kn;
      kt = tmp * mat.kt;
      gn = tmp * m_eff * mat.gn;
      gt = tmp * m_eff * mat.gt;
    }

    break;
  }

  // Calculate the magnitudes of the normal and tangential contact forces
  double forceN = kn * m_delta - gn * relvel_n_mag;
  double forceT = kt * delta_t - gt * relvel_t_mag;

  // Coulomb law
  forceT = std::min<double>(forceT, mat.mu_eff * std::abs(forceN));

  // Accumulate normal and tangential forces
  m_force = forceN * m_normal;
  if (relvel_t_mag >= m_minSlipVelocity)
    m_force += (forceT / relvel_t_mag) * relvel_t;
}

// Include the contact force from this contact into the body forces and
// torques of the two bodies involved in this contact.  Recall that the
// contact force as calculated must be applied to body2 and inverted for
// body1.
// NOTE: SEE NEW VERSION BELOW...
void ChContactDEM::ConstraintsFbLoadForces(double factor)
{
  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  ChVector<> force1_loc = body1->Dir_World2Body(m_force);
  ChVector<> force2_loc = body2->Dir_World2Body(m_force);
  ChVector<> torque1_loc = Vcross(m_p1_loc, -force1_loc);
  ChVector<> torque2_loc = Vcross(m_p2_loc, force2_loc);

  body1->Variables().Get_fb().PasteSumVector(-m_force*factor, 0, 0);
  body1->Variables().Get_fb().PasteSumVector(torque1_loc*factor, 3, 0);

  body2->Variables().Get_fb().PasteSumVector(m_force*factor, 0, 0);
  body2->Variables().Get_fb().PasteSumVector(torque2_loc*factor, 3, 0);
}

// Apply contact forces to bodies (new version, for interfacing to ChTimestepper and ChIntegrable)
// Replaces ConstraintsFbLoadForces.
// Include the contact force from this contact into the body forces and
// torques of the two bodies involved in this contact.  Recall that the
// contact force as calculated must be applied to body2 and inverted for
// body1.
void  ChContactDEM::DemIntLoadResidual_F(ChVectorDynamic<>& R, const double c )
{
  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  ChVector<> force1_loc = body1->Dir_World2Body(m_force);
  ChVector<> force2_loc = body2->Dir_World2Body(m_force);
  ChVector<> torque1_loc = Vcross(m_p1_loc, -force1_loc);
  ChVector<> torque2_loc = Vcross(m_p2_loc, force2_loc);

  R.PasteSumVector(-m_force*c, body1->Variables().GetOffset() + 0, 0);
  R.PasteSumVector(torque1_loc*c, body1->Variables().GetOffset() + 3, 0);

  R.PasteSumVector(m_force*c, body2->Variables().GetOffset() + 0, 0);
  R.PasteSumVector(torque2_loc*c, body2->Variables().GetOffset() + 3, 0);
}

// Return the coordinate system for this contact (centered at point P2
// and oriented based on the contact plane and normal)
ChCoordsys<> ChContactDEM::GetContactCoords() const
{
  ChCoordsys<> mcsys;
  ChQuaternion<float> mrot = m_contact_plane.Get_A_quaternion();

  mcsys.rot.Set(mrot.e0, mrot.e1, mrot.e2, mrot.e3);
  mcsys.pos = m_p2;

  return mcsys;
}

} // end namespace chrono
<commit_msg>Fix sign in tangential force and put back in the check for pulling normal force.<commit_after>//
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2011-2012 Alessandro Tasora
// Copyright (c) 2013 Project Chrono
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file at the top level of the distribution
// and at http://projectchrono.org/license-chrono.txt.
//

#include <cmath>
#include <algorithm>

#include "physics/ChContactDEM.h"
#include "physics/ChSystemDEM.h"
#include "physics/ChBodyDEM.h"

// Memory leak debugger (must be included last).
#include "core/ChMemory.h"

namespace chrono {

using namespace collision;

// Initialize static members
double ChContactDEM::m_minSlipVelocity = 1e-4;
double ChContactDEM::m_characteristicVelocity = 1;

// Construct a new DEM contact between two models using the specified contact pair information.
ChContactDEM::ChContactDEM(collision::ChModelBulletBody*     mod1,
                           collision::ChModelBulletBody*     mod2,
                           const collision::ChCollisionInfo& cinfo)
{
  Reset(mod1, mod2, cinfo);
}

// Worker function for populating a new or reusing an exsting contact.
void ChContactDEM::Reset(collision::ChModelBulletBody*     mod1,
                         collision::ChModelBulletBody*     mod2,
                         const collision::ChCollisionInfo& cinfo)
{
  assert(cinfo.distance < 0);

  m_mod1 = mod1;
  m_mod2 = mod2;

  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  // Contact points, penetration, normal
  m_p1 = cinfo.vpA;
  m_p2 = cinfo.vpB;
  m_delta = -cinfo.distance;
  m_normal = cinfo.vN;

  // Contact plane
  ChVector<> Vx, Vy, Vz;
  cinfo.vN.DirToDxDyDz(Vx, Vy, Vz);
  m_contact_plane.Set_A_axis(Vx, Vy, Vz);

  // Contact points in local frames
  m_p1_loc = body1->Point_World2Body(m_p1);
  m_p2_loc = body2->Point_World2Body(m_p2);

  // Calculate contact force
  CalculateForce();
}

// Calculate the contact force to be applied to body2, based on the
// globally specified contact force model.
void ChContactDEM::CalculateForce()
{
  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  ChSystemDEM* sys = static_cast<ChSystemDEM*>(body1->GetSystem());

  double dT = sys->GetStep();
  bool use_mat_props = sys->UseMaterialProperties();
  ContactForceModel force_model = sys->GetContactForceModel();

  // Relative velocity at contact
  ChVector<> vel2 = body2->PointSpeedLocalToParent(m_p2_loc);
  ChVector<> vel1 = body1->PointSpeedLocalToParent(m_p1_loc);
  ChVector<> relvel = vel2 - vel1;
  double     relvel_n_mag = relvel.Dot(m_normal);
  ChVector<> relvel_n = relvel_n_mag * m_normal;
  ChVector<> relvel_t = relvel - relvel_n;
  double     relvel_t_mag = relvel_t.Length();

  // Calculate effective mass
  double m_eff = body1->GetMass() * body2->GetMass() / (body1->GetMass() + body2->GetMass());

  // Calculate effective contact radius
  //// TODO:  how can I get this with current collision system!?!?!?
  double R_eff = 1;

  // Calculate composite material properties
  ChCompositeMaterialDEM mat = ChMaterialSurfaceDEM::CompositeMaterial(body1->GetMaterialSurfaceDEM(), body2->GetMaterialSurfaceDEM());

  // Contact forces.
  // All models use the following formulas for normal and tangential forces:
  //     Fn = kn * delta_n - gn * v_n
  //     Ft = kt * delta_t - gt * v_t
  double kn;
  double kt;
  double gn;
  double gt;

  double delta_t = 0;

  switch (force_model) {

  case Hooke_history:
    delta_t = relvel_t_mag * dT;

  case Hooke:
    if (use_mat_props) {
      double tmp_k = (16.0 / 15) * std::sqrt(R_eff) * mat.E_eff;
      double v2 = m_characteristicVelocity * m_characteristicVelocity;
      double tmp_g = 1 + std::pow(CH_C_PI / std::log(mat.cr_eff), 2);
      kn = tmp_k * std::pow(m_eff * v2 / tmp_k, 1.0 / 5);
      kt = kn;
      gn = std::sqrt(4 * m_eff * kn / tmp_g);
      gt = gn;
    } else {
      kn = mat.kn;
      kt = mat.kt;
      gn = m_eff * mat.gn;
      gt = m_eff * mat.gt;
    }

    break;

  case Hertz_history:
    delta_t = relvel_t_mag * dT;

  case Hertz:
    if (use_mat_props) {
      double sqrt_Rd = std::sqrt(R_eff * m_delta);
      double Sn = 2 * mat.E_eff * sqrt_Rd;
      double St = 8 * mat.G_eff * sqrt_Rd;
      double loge = std::log(mat.cr_eff);
      double beta = loge / std::sqrt(loge * loge + CH_C_PI * CH_C_PI);
      kn = (2.0 / 3) * Sn;
      kt = St;
      gn = -2 * std::sqrt(5.0 / 6) * beta * std::sqrt(Sn * m_eff);
      gt = -2 * std::sqrt(5.0 / 6) * beta * std::sqrt(St * m_eff);
    } else {
      double tmp = R_eff * std::sqrt(m_delta);
      kn = tmp * mat.kn;
      kt = tmp * mat.kt;
      gn = tmp * m_eff * mat.gn;
      gt = tmp * m_eff * mat.gt;
    }

    break;
  }

  // Calculate the magnitudes of the normal and tangential contact forces
  double forceN = kn * m_delta - gn * relvel_n_mag;
  double forceT = kt * delta_t + gt * relvel_t_mag;

  // If the resulting force is negative, the two shapes are moving away from
  // each other so fast that no contact force is generated.
  if (forceN < 0) {
    forceN = 0;
    forceT = 0;
  }

  // Coulomb law
  forceT = std::min<double>(forceT, mat.mu_eff * std::abs(forceN));

  // Accumulate normal and tangential forces
  m_force = forceN * m_normal;
  if (relvel_t_mag >= m_minSlipVelocity)
    m_force -= (forceT / relvel_t_mag) * relvel_t;
}

// Include the contact force from this contact into the body forces and
// torques of the two bodies involved in this contact.  Recall that the
// contact force as calculated must be applied to body2 and inverted for
// body1.
// NOTE: SEE NEW VERSION BELOW...
void ChContactDEM::ConstraintsFbLoadForces(double factor)
{
  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  ChVector<> force1_loc = body1->Dir_World2Body(m_force);
  ChVector<> force2_loc = body2->Dir_World2Body(m_force);
  ChVector<> torque1_loc = Vcross(m_p1_loc, -force1_loc);
  ChVector<> torque2_loc = Vcross(m_p2_loc, force2_loc);

  body1->Variables().Get_fb().PasteSumVector(-m_force*factor, 0, 0);
  body1->Variables().Get_fb().PasteSumVector(torque1_loc*factor, 3, 0);

  body2->Variables().Get_fb().PasteSumVector(m_force*factor, 0, 0);
  body2->Variables().Get_fb().PasteSumVector(torque2_loc*factor, 3, 0);
}

// Apply contact forces to bodies (new version, for interfacing to ChTimestepper and ChIntegrable)
// Replaces ConstraintsFbLoadForces.
// Include the contact force from this contact into the body forces and
// torques of the two bodies involved in this contact.  Recall that the
// contact force as calculated must be applied to body2 and inverted for
// body1.
void  ChContactDEM::DemIntLoadResidual_F(ChVectorDynamic<>& R, const double c )
{
  ChBodyDEM* body1 = (ChBodyDEM*)m_mod1->GetBody();
  ChBodyDEM* body2 = (ChBodyDEM*)m_mod2->GetBody();

  ChVector<> force1_loc = body1->Dir_World2Body(m_force);
  ChVector<> force2_loc = body2->Dir_World2Body(m_force);
  ChVector<> torque1_loc = Vcross(m_p1_loc, -force1_loc);
  ChVector<> torque2_loc = Vcross(m_p2_loc, force2_loc);

  R.PasteSumVector(-m_force*c, body1->Variables().GetOffset() + 0, 0);
  R.PasteSumVector(torque1_loc*c, body1->Variables().GetOffset() + 3, 0);

  R.PasteSumVector(m_force*c, body2->Variables().GetOffset() + 0, 0);
  R.PasteSumVector(torque2_loc*c, body2->Variables().GetOffset() + 3, 0);
}

// Return the coordinate system for this contact (centered at point P2
// and oriented based on the contact plane and normal)
ChCoordsys<> ChContactDEM::GetContactCoords() const
{
  ChCoordsys<> mcsys;
  ChQuaternion<float> mrot = m_contact_plane.Get_A_quaternion();

  mcsys.rot.Set(mrot.e0, mrot.e1, mrot.e2, mrot.e3);
  mcsys.pos = m_p2;

  return mcsys;
}

} // end namespace chrono
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2014 Paul T. Bauman, Roy H. Stogner
// Copyright (C) 2010-2013 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

// This class
#include "grins/scalar_ode.h"

// GRINS
#include "grins/generic_ic_handler.h"
#include "grins/variable_name_defaults.h"

// libMesh
#include "libmesh/boundary_info.h"
#include "libmesh/parsed_fem_function.h"

namespace GRINS
{

  ScalarODE::ScalarODE( const std::string& physics_name, const GetPot& input )
    : Physics(physics_name, input), _order(1), _epsilon(1e-6)
  {
    this->read_input_options(input);

    this->_ic_handler = new GenericICHandler( physics_name, input );

    return;
  }

  ScalarODE::~ScalarODE()
  {
    return;
  }

  void ScalarODE::init_variables( libMesh::FEMSystem* system )
  {
    this->_scalar_ode_var = system->add_variable(_scalar_ode_var_name,
                                                 libMesh::Order(this->_order),
                                                 libMesh::SCALAR);
  }

  void ScalarODE::set_time_evolving_vars( libMesh::FEMSystem* system )
  {
    system->time_evolving(this->scalar_ode_var());

    // FIXME: this doesn't fit here at all, but it's the only time
    // we've clearly got a System to grab hold of with all it's
    // variables initialized.

    this->time_deriv_function.reset
      (new libMesh::ParsedFEMFunction<libMesh::Number>
       (*system, this->time_deriv_function_string));

    this->mass_residual_function.reset
      (new libMesh::ParsedFEMFunction<libMesh::Number>
       (*system, this->mass_residual_function_string));

    this->constraint_function.reset
      (new libMesh::ParsedFEMFunction<libMesh::Number>
       (*system, this->constraint_function_string));
  }

  void ScalarODE::read_input_options( const GetPot& input )
  {
    this->time_deriv_function_string =
      input("Physics/"+scalar_ode+"/time_deriv",
        std::string("0"));

    if (this->time_deriv_function_string == "0")
      std::cout << "Warning! Zero time_deriv function specified!" << std::endl;

    this->mass_residual_function_string =
      input("Physics/"+scalar_ode+"/mass_residual",
        std::string("0"));

    if (this->mass_residual_function_string == "0")
      std::cout << "Warning! Zero mass_residual function specified!" << std::endl;

    this->constraint_function_string =
      input("Physics/"+scalar_ode+"/constraint",
        std::string("0"));

    if (this->constraint_function_string == "0")
      std::cout << "Warning! Zero constraint function specified!" << std::endl;

    this->_epsilon = input("Physics/"+scalar_ode+"/epsilon", 1e-6);

    this->_order = input("Physics/"+scalar_ode+"/order", 1);

    _scalar_ode_var_name = input("Physics/VariableNames/scalar_ode",
                                 scalar_ode_var_name_default);
  }

  void ScalarODE::init_context( AssemblyContext& context )
  {
    mass_residual_function->init_context(context);
    time_deriv_function->init_context(context);
    constraint_function->init_context(context);
  }

  void ScalarODE::nonlocal_time_derivative(bool compute_jacobian,
				           AssemblyContext& context,
				           CachedValues& /* cache */ )
  {
    libMesh::DenseSubMatrix<libMesh::Number> &Kss =
            context.get_elem_jacobian(_scalar_ode_var, _scalar_ode_var); // R_{s},{s}

    libMesh::DenseSubVector<libMesh::Number> &Fs =
            context.get_elem_residual(_scalar_ode_var); // R_{s}

    const std::vector<libMesh::dof_id_type>& dof_indices =
      context.get_dof_indices(_scalar_ode_var);

    const libMesh::Number time_deriv =
      (*time_deriv_function)(context, libMesh::Point(0),
                             context.get_time());

    Fs(0) += time_deriv;

    if (compute_jacobian && context.elem_solution_derivative)
      {
        // FIXME: we should replace this hacky FDM with a hook to the
        // AD fparser stuff
        libMesh::DenseSubVector<libMesh::Number> &Us =
          const_cast<libMesh::DenseSubVector<libMesh::Number>&>
            (context.get_elem_solution(_scalar_ode_var)); // U_{s}

        const libMesh::Number s = Us(0);
        Us(0) = s + this->_epsilon;
        libMesh::Number time_deriv_jacobian =
          (*time_deriv_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s - this->_epsilon;
        time_deriv_jacobian -=
          (*time_deriv_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s;
        time_deriv_jacobian /= (2*this->_epsilon);

        libmesh_assert_equal_to (context.elem_solution_derivative, 1.0);

        Kss(0,0) += time_deriv_jacobian;
      }

    return;
  }

  void ScalarODE::nonlocal_mass_residual(bool compute_jacobian,
				         AssemblyContext& context,
				         CachedValues& /* cache */ )
  {
    libMesh::DenseSubMatrix<libMesh::Number> &Kss =
            context.get_elem_jacobian(_scalar_ode_var, _scalar_ode_var); // R_{s},{s}

    libMesh::DenseSubVector<libMesh::Number> &Fs =
            context.get_elem_residual(_scalar_ode_var); // R_{s}

    const std::vector<libMesh::dof_id_type>& dof_indices =
      context.get_dof_indices(_scalar_ode_var);

    const libMesh::Number mass_res =
      (*mass_residual_function)(context, libMesh::Point(0),
                                context.get_time());

    Fs(0) += mass_res;

    if (compute_jacobian && context.elem_solution_derivative)
      {
        // FIXME: we should replace this hacky FDM with a hook to the
        // AD fparser stuff
        libMesh::DenseSubVector<libMesh::Number> &Us =
          const_cast<libMesh::DenseSubVector<libMesh::Number>&>
            (context.get_elem_solution(_scalar_ode_var)); // U_{s}

        const libMesh::Number s = Us(0);
        Us(0) = s + this->_epsilon;
        libMesh::Number mass_residual_jacobian =
          (*mass_residual_function)(context, libMesh::Point(0),
                                    context.get_time());

        Us(0) = s - this->_epsilon;
        mass_residual_jacobian -=
          (*mass_residual_function)(context, libMesh::Point(0),
                                    context.get_time());

        Us(0) = s;
        mass_residual_jacobian /= (2*this->_epsilon);

        libmesh_assert_equal_to (context.elem_solution_derivative, 1.0);

        Kss(0,0) += mass_residual_jacobian;
      }

    return;
  }

  void ScalarODE::nonlocal_constraint(bool compute_jacobian,
				      AssemblyContext& context,
				      CachedValues& /* cache */ )
  {
    libMesh::DenseSubMatrix<libMesh::Number> &Kss =
            context.get_elem_jacobian(_scalar_ode_var, _scalar_ode_var); // R_{s},{s}

    libMesh::DenseSubVector<libMesh::Number> &Fs =
            context.get_elem_residual(_scalar_ode_var); // R_{s}

    const std::vector<libMesh::dof_id_type>& dof_indices =
      context.get_dof_indices(_scalar_ode_var);

    const libMesh::Number constraint =
      (*constraint_function)(context, libMesh::Point(0),
                             context.get_time());

    Fs(0) += constraint;

    if (compute_jacobian && context.elem_solution_derivative)
      {
        // FIXME: we should replace this hacky FDM with a hook to the
        // AD fparser stuff
        libMesh::DenseSubVector<libMesh::Number> &Us =
          const_cast<libMesh::DenseSubVector<libMesh::Number>&>
            (context.get_elem_solution(_scalar_ode_var)); // U_{s}

        const libMesh::Number s = Us(0);
        Us(0) = s + this->_epsilon;
        libMesh::Number constraint_jacobian =
          (*constraint_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s - this->_epsilon;
        constraint_jacobian -=
          (*constraint_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s;
        constraint_jacobian /= (2*this->_epsilon);

        libmesh_assert_equal_to (context.elem_solution_derivative, 1.0);

        Kss(0,0) += constraint_jacobian;
      }

    return;
  }

} // namespace GRINS
<commit_msg>Get rid of unused dof_indices in ScalarODE<commit_after>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2014 Paul T. Bauman, Roy H. Stogner
// Copyright (C) 2010-2013 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

// This class
#include "grins/scalar_ode.h"

// GRINS
#include "grins/generic_ic_handler.h"
#include "grins/variable_name_defaults.h"

// libMesh
#include "libmesh/boundary_info.h"
#include "libmesh/parsed_fem_function.h"

namespace GRINS
{

  ScalarODE::ScalarODE( const std::string& physics_name, const GetPot& input )
    : Physics(physics_name, input), _order(1), _epsilon(1e-6)
  {
    this->read_input_options(input);

    this->_ic_handler = new GenericICHandler( physics_name, input );

    return;
  }

  ScalarODE::~ScalarODE()
  {
    return;
  }

  void ScalarODE::init_variables( libMesh::FEMSystem* system )
  {
    this->_scalar_ode_var = system->add_variable(_scalar_ode_var_name,
                                                 libMesh::Order(this->_order),
                                                 libMesh::SCALAR);
  }

  void ScalarODE::set_time_evolving_vars( libMesh::FEMSystem* system )
  {
    system->time_evolving(this->scalar_ode_var());

    // FIXME: this doesn't fit here at all, but it's the only time
    // we've clearly got a System to grab hold of with all it's
    // variables initialized.

    this->time_deriv_function.reset
      (new libMesh::ParsedFEMFunction<libMesh::Number>
       (*system, this->time_deriv_function_string));

    this->mass_residual_function.reset
      (new libMesh::ParsedFEMFunction<libMesh::Number>
       (*system, this->mass_residual_function_string));

    this->constraint_function.reset
      (new libMesh::ParsedFEMFunction<libMesh::Number>
       (*system, this->constraint_function_string));
  }

  void ScalarODE::read_input_options( const GetPot& input )
  {
    this->time_deriv_function_string =
      input("Physics/"+scalar_ode+"/time_deriv",
        std::string("0"));

    if (this->time_deriv_function_string == "0")
      std::cout << "Warning! Zero time_deriv function specified!" << std::endl;

    this->mass_residual_function_string =
      input("Physics/"+scalar_ode+"/mass_residual",
        std::string("0"));

    if (this->mass_residual_function_string == "0")
      std::cout << "Warning! Zero mass_residual function specified!" << std::endl;

    this->constraint_function_string =
      input("Physics/"+scalar_ode+"/constraint",
        std::string("0"));

    if (this->constraint_function_string == "0")
      std::cout << "Warning! Zero constraint function specified!" << std::endl;

    this->_epsilon = input("Physics/"+scalar_ode+"/epsilon", 1e-6);

    this->_order = input("Physics/"+scalar_ode+"/order", 1);

    _scalar_ode_var_name = input("Physics/VariableNames/scalar_ode",
                                 scalar_ode_var_name_default);
  }

  void ScalarODE::init_context( AssemblyContext& context )
  {
    mass_residual_function->init_context(context);
    time_deriv_function->init_context(context);
    constraint_function->init_context(context);
  }

  void ScalarODE::nonlocal_time_derivative(bool compute_jacobian,
				           AssemblyContext& context,
				           CachedValues& /* cache */ )
  {
    libMesh::DenseSubMatrix<libMesh::Number> &Kss =
            context.get_elem_jacobian(_scalar_ode_var, _scalar_ode_var); // R_{s},{s}

    libMesh::DenseSubVector<libMesh::Number> &Fs =
            context.get_elem_residual(_scalar_ode_var); // R_{s}

    const libMesh::Number time_deriv =
      (*time_deriv_function)(context, libMesh::Point(0),
                             context.get_time());

    Fs(0) += time_deriv;

    if (compute_jacobian && context.elem_solution_derivative)
      {
        // FIXME: we should replace this hacky FDM with a hook to the
        // AD fparser stuff
        libMesh::DenseSubVector<libMesh::Number> &Us =
          const_cast<libMesh::DenseSubVector<libMesh::Number>&>
            (context.get_elem_solution(_scalar_ode_var)); // U_{s}

        const libMesh::Number s = Us(0);
        Us(0) = s + this->_epsilon;
        libMesh::Number time_deriv_jacobian =
          (*time_deriv_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s - this->_epsilon;
        time_deriv_jacobian -=
          (*time_deriv_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s;
        time_deriv_jacobian /= (2*this->_epsilon);

        libmesh_assert_equal_to (context.elem_solution_derivative, 1.0);

        Kss(0,0) += time_deriv_jacobian;
      }

    return;
  }

  void ScalarODE::nonlocal_mass_residual(bool compute_jacobian,
				         AssemblyContext& context,
				         CachedValues& /* cache */ )
  {
    libMesh::DenseSubMatrix<libMesh::Number> &Kss =
            context.get_elem_jacobian(_scalar_ode_var, _scalar_ode_var); // R_{s},{s}

    libMesh::DenseSubVector<libMesh::Number> &Fs =
            context.get_elem_residual(_scalar_ode_var); // R_{s}

    const libMesh::Number mass_res =
      (*mass_residual_function)(context, libMesh::Point(0),
                                context.get_time());

    Fs(0) += mass_res;

    if (compute_jacobian && context.elem_solution_derivative)
      {
        // FIXME: we should replace this hacky FDM with a hook to the
        // AD fparser stuff
        libMesh::DenseSubVector<libMesh::Number> &Us =
          const_cast<libMesh::DenseSubVector<libMesh::Number>&>
            (context.get_elem_solution(_scalar_ode_var)); // U_{s}

        const libMesh::Number s = Us(0);
        Us(0) = s + this->_epsilon;
        libMesh::Number mass_residual_jacobian =
          (*mass_residual_function)(context, libMesh::Point(0),
                                    context.get_time());

        Us(0) = s - this->_epsilon;
        mass_residual_jacobian -=
          (*mass_residual_function)(context, libMesh::Point(0),
                                    context.get_time());

        Us(0) = s;
        mass_residual_jacobian /= (2*this->_epsilon);

        libmesh_assert_equal_to (context.elem_solution_derivative, 1.0);

        Kss(0,0) += mass_residual_jacobian;
      }

    return;
  }

  void ScalarODE::nonlocal_constraint(bool compute_jacobian,
				      AssemblyContext& context,
				      CachedValues& /* cache */ )
  {
    libMesh::DenseSubMatrix<libMesh::Number> &Kss =
            context.get_elem_jacobian(_scalar_ode_var, _scalar_ode_var); // R_{s},{s}

    libMesh::DenseSubVector<libMesh::Number> &Fs =
            context.get_elem_residual(_scalar_ode_var); // R_{s}

    const libMesh::Number constraint =
      (*constraint_function)(context, libMesh::Point(0),
                             context.get_time());

    Fs(0) += constraint;

    if (compute_jacobian && context.elem_solution_derivative)
      {
        // FIXME: we should replace this hacky FDM with a hook to the
        // AD fparser stuff
        libMesh::DenseSubVector<libMesh::Number> &Us =
          const_cast<libMesh::DenseSubVector<libMesh::Number>&>
            (context.get_elem_solution(_scalar_ode_var)); // U_{s}

        const libMesh::Number s = Us(0);
        Us(0) = s + this->_epsilon;
        libMesh::Number constraint_jacobian =
          (*constraint_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s - this->_epsilon;
        constraint_jacobian -=
          (*constraint_function)(context, libMesh::Point(0),
                                 context.get_time());

        Us(0) = s;
        constraint_jacobian /= (2*this->_epsilon);

        libmesh_assert_equal_to (context.elem_solution_derivative, 1.0);

        Kss(0,0) += constraint_jacobian;
      }

    return;
  }

} // namespace GRINS
<|endoftext|>


<commit_before>#include <apollo/builtin_types.hpp>
#include <apollo/error.hpp>
#include <apollo/lapi.hpp>
#include <apollo/detail/light_key.hpp>

#include <boost/exception/info.hpp>
#include <boost/throw_exception.hpp>

namespace apollo {

static apollo::detail::light_key const msghKey = {};

APOLLO_API void set_error_msg_handler(lua_State* L)
{
    lua_rawsetp(L, LUA_REGISTRYINDEX, &msghKey);
}

APOLLO_API bool push_error_msg_handler(lua_State* L)
{
    lua_rawgetp(L, LUA_REGISTRYINDEX, &msghKey);
    if lua_isnil(L, -1) {
        lua_pop(L, 1);
        return false;
    }
    return true;
}

APOLLO_API void pcall(lua_State* L, int nargs, int nresults, int msgh)
{
    int const r = lua_pcall(L, nargs, nresults, msgh);
    if (r != LUA_OK) {
        auto lua_msg = from_stack(L, -1, std::string("(no error message)"));
        lua_pop(L, 1);
        BOOST_THROW_EXCEPTION(lua_api_error()
                              << errinfo::lua_state(L)
                              << errinfo::lua_msg(lua_msg)
                              << errinfo::lua_error_code(r)
                              << errinfo::msg("lua_pcall() failed"));
    }
}

APOLLO_API void pcall(lua_State* L, int nargs, int nresults)
{
    int const msgh = push_error_msg_handler(L) ? lua_absindex(L, -nargs - 2) : 0;
    if (msgh)
        lua_insert(L, msgh); // move beneath arguments and function

    auto cleanup = [L, msgh]() -> void {
        if (msgh)
            lua_remove(L, msgh);
    };

    try { pcall(L, nargs, nresults, msgh); }
    catch (...) {
        cleanup();
        throw;
    }
    cleanup();
}

} // namespace apollo
<commit_msg>Fix missing parantheses (macros...).<commit_after>#include <apollo/builtin_types.hpp>
#include <apollo/error.hpp>
#include <apollo/lapi.hpp>
#include <apollo/detail/light_key.hpp>

#include <boost/exception/info.hpp>
#include <boost/throw_exception.hpp>

namespace apollo {

static apollo::detail::light_key const msghKey = {};

APOLLO_API void set_error_msg_handler(lua_State* L)
{
    lua_rawsetp(L, LUA_REGISTRYINDEX, &msghKey);
}

APOLLO_API bool push_error_msg_handler(lua_State* L)
{
    lua_rawgetp(L, LUA_REGISTRYINDEX, &msghKey);
    if (lua_isnil(L, -1)) {
        lua_pop(L, 1);
        return false;
    }
    return true;
}

APOLLO_API void pcall(lua_State* L, int nargs, int nresults, int msgh)
{
    int const r = lua_pcall(L, nargs, nresults, msgh);
    if (r != LUA_OK) {
        auto lua_msg = from_stack(L, -1, std::string("(no error message)"));
        lua_pop(L, 1);
        BOOST_THROW_EXCEPTION(lua_api_error()
                              << errinfo::lua_state(L)
                              << errinfo::lua_msg(lua_msg)
                              << errinfo::lua_error_code(r)
                              << errinfo::msg("lua_pcall() failed"));
    }
}

APOLLO_API void pcall(lua_State* L, int nargs, int nresults)
{
    int const msgh = push_error_msg_handler(L) ? lua_absindex(L, -nargs - 2) : 0;
    if (msgh)
        lua_insert(L, msgh); // move beneath arguments and function

    auto cleanup = [L, msgh]() -> void {
        if (msgh)
            lua_remove(L, msgh);
    };

    try { pcall(L, nargs, nresults, msgh); }
    catch (...) {
        cleanup();
        throw;
    }
    cleanup();
}

} // namespace apollo
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <sys/types.h>
#include <unistd.h>

#include <cstdio>
#include <cstring>
#include <iostream>

#include "logging.h"
#include "stringprintf.h"
#include "utils.h"

namespace art {

LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int error)
    : data_(new LogMessageData(line, severity, error)) {
  const char* last_slash = strrchr(file, '/');
  data_->file = (last_slash == NULL) ? file : last_slash + 1;
}

void LogMessage::LogLine(const char* message) {
  std::ostream& os(std::cerr);
  os << "VDIWEFF"[data_->severity] << ' '
     << StringPrintf("%5d %5d", getpid(), ::art::GetTid()) << ' '
     << data_->file << ':' << data_->line_number << "] " << message << "\n" << std::flush;
}

}  // namespace art
<commit_msg>Make strace(1) output more readable by using a single write(2) for logging.<commit_after>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <sys/types.h>
#include <unistd.h>

#include <cstdio>
#include <cstring>
#include <iostream>

#include "logging.h"
#include "stringprintf.h"
#include "utils.h"

namespace art {

LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int error)
    : data_(new LogMessageData(line, severity, error)) {
  const char* last_slash = strrchr(file, '/');
  data_->file = (last_slash == NULL) ? file : last_slash + 1;
}

void LogMessage::LogLine(const char* message) {
  char severity = "VDIWEFF"[data_->severity];
  fprintf(stderr, "%c %5d %5d %s:%d] %s\n", severity, getpid(), ::art::GetTid(),
          data_->file, data_->line_number, message);
}

}  // namespace art
<|endoftext|>


<commit_before>/*
Copyright (c) 2012 Carsten Burstedde, Donna Calhoun
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "swirl_user.h"
#include <fclaw2d_forestclaw.h>
#include <fclaw2d_clawpatch.h>
#include <fc2d_clawpack5.h>
#include <fc2d_dummy.h>

/* Leave these in for demonstration purposes */
#include <fclaw2d_output_ascii.h>
#include <fclaw2d_regrid_default.h>

static fclaw2d_vtable_t vt;
static fc2d_clawpack5_vtable_t classic_claw;

void swirl_link_solvers(fclaw2d_domain_t *domain)
{
    fclaw2d_init_vtable(&vt);

    vt.problem_setup            = &fc2d_clawpack5_setprob;

    vt.patch_setup              = &swirl_patch_setup;
    vt.patch_initialize         = &swirl_patch_initialize;
    vt.patch_physical_bc        = &swirl_patch_physical_bc;
    vt.patch_single_step_update = &fc2d_clawpack5_update;  /* Includes b4step2 and src2 */

    vt.regrid_tag4refinement     = &swirl_patch_tag4refinement;
    vt.fort_tag4refinement      = &TAG4REFINEMENT;

    vt.regrid_tag4coarsening     = &swirl_patch_tag4coarsening;
    vt.fort_tag4coarsening      = &TAG4COARSENING;

    vt.write_header             = &fclaw2d_output_header_ascii;
    vt.fort_write_header        = &FCLAW2D_FORT_WRITE_HEADER;

    vt.patch_write_file         = &fclaw2d_output_patch_ascii;
    vt.fort_write_file          = &FCLAW2D_FORT_WRITE_FILE;

    fclaw2d_set_vtable(domain,&vt);

    /* Needed for the clawpack5 package */
    classic_claw.qinit = &QINIT;
    classic_claw.bc2 = &BC2;
    classic_claw.setaux = &SETAUX;
    classic_claw.setprob = &SETPROB;
    classic_claw.b4step2 = &B4STEP2;
    classic_claw.rpn2 = &RPN2;
    classic_claw.rpt2 = &RPT2;

    fc2d_clawpack5_set_vtable(&classic_claw);

}

void swirl_patch_setup(fclaw2d_domain_t *domain,
                          fclaw2d_patch_t *this_patch,
                          int this_block_idx,
                          int this_patch_idx)
{
    /* Dummy setup - use multiple libraries */
    fc2d_clawpack5_setaux(domain,this_patch,this_block_idx,this_patch_idx);
    fc2d_dummy_setup_patch(domain,this_patch,this_block_idx,this_patch_idx);
}

void swirl_patch_initialize(fclaw2d_domain_t *domain,
                            fclaw2d_patch_t *this_patch,
                            int this_block_idx,
                            int this_patch_idx)
{
    /* This is an example of how to call the initialization routines explicitly
       This routine can be replaced by setting the appropriate fclaw2d_vtable_t,
       entry above, or by calling fclaw2d_clawpack5_qinit(...) from here. */

    int mx,my,mbc,meqn, maux, maxmx, maxmy;
    double xlower,ylower,dx,dy;
    double *q, *aux;

    vt = fclaw2d_get_vtable(domain);

    fclaw2d_clawpatch_grid_data(domain,this_patch,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    fclaw2d_clawpatch_soln_data(domain,this_patch,&q,&meqn);
    fc2d_clawpack5_aux_data(domain,this_patch,&aux,&maux);

    /* Call to used defined, classic Clawpack (ver. 4.6)  'qinit' routine.
       Header is in the Clawpack package
    */
    maxmx = mx;
    maxmy = my;
    QINIT(&meqn,&mbc,&mx,&my,&xlower,&ylower,&dx,&dy,q,&maux,aux);
}

void swirl_patch_physical_bc(fclaw2d_domain *domain,
                             fclaw2d_patch_t *this_patch,
                             int this_block_idx,
                             int this_patch_idx,
                             double t,
                             double dt,
                             fclaw_bool intersects_bc[],
                             fclaw_bool time_interp)
{
    /* This calls bc2 in swirl/user_4.6;  that file isn't changed but
       is included to show that both the local version of bc2.f and the
       clawpack5 library code can be included */
    fc2d_clawpack5_bc2(domain,this_patch,this_block_idx,this_patch_idx,
                     t,dt,intersects_bc,time_interp);
}

int swirl_patch_tag4refinement(fclaw2d_domain_t *domain,
                               fclaw2d_patch_t *this_patch,
                               int blockno, int this_patch_idx,
                               int initflag)
{
    fclaw2d_vtable_t vt;
    int mx,my,mbc,meqn;
    double xlower,ylower,dx,dy;
    double *q;
    int tag_patch;
    const amr_options_t *amropt;
    double rt;

    amropt = get_domain_parms(domain);
    rt = amropt->refine_threshold;

    vt = fclaw2d_get_vtable(domain);

    fclaw2d_clawpatch_grid_data(domain,this_patch,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    fclaw2d_clawpatch_soln_data(domain,this_patch,&q,&meqn);

    tag_patch = 0;
    vt.fort_tag4refinement(&mx,&my,&mbc,&meqn,&xlower,&ylower,
                           &dx,&dy,&blockno, q,&rt,&initflag,
                           &tag_patch);
    return tag_patch;
}

int swirl_patch_tag4coarsening(fclaw2d_domain_t *domain,
                               fclaw2d_patch_t *fine_patches,
                               int blockno, int patchno)

{
    fclaw2d_vtable_t vt;

    int mx,my,mbc,meqn;
    double xlower,ylower,dx,dy;
    double *q[4];
    int tag_patch,igrid;
    double coarsen_threshold;
    fclaw2d_patch_t *patch0;

    patch0 = &fine_patches[0];

    const amr_options_t *amropt;
    amropt = get_domain_parms(domain);

    coarsen_threshold = amropt->coarsen_threshold;

    vt = fclaw2d_get_vtable(domain);

    fclaw2d_clawpatch_grid_data(domain,patch0,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    for (igrid = 0; igrid < 4; igrid++)
    {
        fclaw2d_clawpatch_soln_data(domain,&fine_patches[igrid],&q[igrid],&meqn);
    }
    tag_patch = 0;
    vt.fort_tag4coarsening(&mx,&my,&mbc,&meqn,&xlower,&ylower,&dx,&dy,
                           &blockno, q[0],q[1],q[2],q[3],
                           &coarsen_threshold, &tag_patch);
    return tag_patch;

}
<commit_msg>slightly modified<commit_after>/*
Copyright (c) 2012 Carsten Burstedde, Donna Calhoun
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "swirl_user.h"
#include <fclaw2d_forestclaw.h>
#include <fclaw2d_clawpatch.h>
#include <fc2d_clawpack5.h>
#include <fc2d_dummy.h>

/* Leave these in for demonstration purposes */
#include <fclaw2d_output_ascii.h>
#include <fclaw2d_regrid_default.h>

static fclaw2d_vtable_t vt;
static fc2d_clawpack5_vtable_t classic_claw;

void swirl_link_solvers(fclaw2d_domain_t *domain)
{
    fclaw2d_init_vtable(&vt);

    vt.problem_setup            = &fc2d_clawpack5_setprob;

    vt.patch_setup              = &swirl_patch_setup;
    vt.patch_initialize         = &swirl_patch_initialize;
    vt.patch_physical_bc        = &swirl_patch_physical_bc;
    vt.patch_single_step_update = &fc2d_clawpack5_update;  /* Includes b4step2 and src2 */

    vt.regrid_tag4refinement     = &swirl_patch_tag4refinement;
    vt.fort_tag4refinement      = &TAG4REFINEMENT;

    vt.regrid_tag4coarsening     = &swirl_patch_tag4coarsening;
    vt.fort_tag4coarsening      = &TAG4COARSENING;

    vt.write_header             = &fclaw2d_output_header_ascii;
    vt.fort_write_header        = &FCLAW2D_FORT_WRITE_HEADER;

    vt.patch_write_file         = &fclaw2d_output_patch_ascii;
    vt.fort_write_file          = &FCLAW2D_FORT_WRITE_FILE;

    fclaw2d_set_vtable(domain,&vt);

    /* Needed for the clawpack5 package */
    classic_claw.qinit = &QINIT;
    classic_claw.bc2 = &BC2;
    classic_claw.setaux = &SETAUX;
    classic_claw.setprob = &SETPROB;
    classic_claw.b4step2 = &B4STEP2;
    classic_claw.rpn2 = &RPN2;
    classic_claw.rpt2 = &RPT2;

    fc2d_clawpack5_set_vtable(&classic_claw);

}

void swirl_patch_setup(fclaw2d_domain_t *domain,
                          fclaw2d_patch_t *this_patch,
                          int this_block_idx,
                          int this_patch_idx)
{
    /* Dummy setup - use multiple libraries */
    fc2d_clawpack5_setaux(domain,this_patch,this_block_idx,this_patch_idx);
    fc2d_dummy_setup_patch(domain,this_patch,this_block_idx,this_patch_idx);
}

void swirl_patch_initialize(fclaw2d_domain_t *domain,
                            fclaw2d_patch_t *this_patch,
                            int this_block_idx,
                            int this_patch_idx)
{
    /* This is an example of how to call the initialization routines explicitly
       This routine can be replaced by setting the appropriate fclaw2d_vtable_t,
       entry above, or by calling fclaw2d_clawpack5_qinit(...) from here. */

    int mx,my,mbc,meqn, maux, maxmx, maxmy;
    double xlower,ylower,dx,dy;
    double *q, *aux;

    vt = fclaw2d_get_vtable(domain);

    fclaw2d_clawpatch_grid_data(domain,this_patch,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    fclaw2d_clawpatch_soln_data(domain,this_patch,&q,&meqn);
    fc2d_clawpack5_aux_data(domain,this_patch,&aux,&maux);

    /* Call to used defined, classic Clawpack (ver. 4.6)  'qinit' routine.
       Header is in the Clawpack package
    */
    maxmx = mx;
    maxmy = my;

    QINIT(&meqn,&mbc,&mx,&my,&xlower,&ylower,&dx,&dy,q,&maux,aux);
}

void swirl_patch_physical_bc(fclaw2d_domain *domain,
                             fclaw2d_patch_t *this_patch,
                             int this_block_idx,
                             int this_patch_idx,
                             double t,
                             double dt,
                             fclaw_bool intersects_bc[],
                             fclaw_bool time_interp)
{
    /* This calls bc2 in swirl/user_4.6;  that file isn't changed but
       is included to show that both the local version of bc2.f and the
       clawpack5 library code can be included */
    fc2d_clawpack5_bc2(domain,this_patch,this_block_idx,this_patch_idx,
                     t,dt,intersects_bc,time_interp);
}

int swirl_patch_tag4refinement(fclaw2d_domain_t *domain,
                               fclaw2d_patch_t *this_patch,
                               int blockno, int this_patch_idx,
                               int initflag)
{
    fclaw2d_vtable_t vt;
    int mx,my,mbc,meqn;
    double xlower,ylower,dx,dy;
    double *q;
    int tag_patch;
    const amr_options_t *amropt;
    double rt;

    amropt = get_domain_parms(domain);
    rt = amropt->refine_threshold;

    vt = fclaw2d_get_vtable(domain);

    fclaw2d_clawpatch_grid_data(domain,this_patch,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    fclaw2d_clawpatch_soln_data(domain,this_patch,&q,&meqn);

    tag_patch = 0;
    vt.fort_tag4refinement(&mx,&my,&mbc,&meqn,&xlower,&ylower,
                           &dx,&dy,&blockno, q,&rt,&initflag,
                           &tag_patch);
    return tag_patch;
}

int swirl_patch_tag4coarsening(fclaw2d_domain_t *domain,
                               fclaw2d_patch_t *fine_patches,
                               int blockno, int patchno)

{
    fclaw2d_vtable_t vt;

    int mx,my,mbc,meqn;
    double xlower,ylower,dx,dy;
    double *q[4];
    int tag_patch,igrid;
    double coarsen_threshold;
    fclaw2d_patch_t *patch0;

    patch0 = &fine_patches[0];

    const amr_options_t *amropt;
    amropt = get_domain_parms(domain);

    coarsen_threshold = amropt->coarsen_threshold;

    vt = fclaw2d_get_vtable(domain);

    fclaw2d_clawpatch_grid_data(domain,patch0,&mx,&my,&mbc,
                                &xlower,&ylower,&dx,&dy);

    for (igrid = 0; igrid < 4; igrid++)
    {
        fclaw2d_clawpatch_soln_data(domain,&fine_patches[igrid],&q[igrid],&meqn);
    }
    tag_patch = 0;
    vt.fort_tag4coarsening(&mx,&my,&mbc,&meqn,&xlower,&ylower,&dx,&dy,
                           &blockno, q[0],q[1],q[2],q[3],
                           &coarsen_threshold, &tag_patch);
    return tag_patch;

}
<|endoftext|>


<commit_before>/**
 * @file
 *
 * @brief Read key sets using yaml-cpp
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#include "read.hpp"
#include "log.hpp"
#include "yaml-cpp/yaml.h"

#include <kdb.hpp>
#include <kdblogger.h>
#include <kdbplugin.h>

#include <sstream>

using namespace std;
using namespace kdb;

namespace
{
/**
 * @brief This function converts a given number to an array base name.
 *
 * @param index This number specifies the index of the array entry.
 *
 * @return A string representing the given indices as Elektra array name.
 */
string indexToArrayBaseName (uintmax_t const index)
{
	size_t digits = 1;

	for (uintmax_t value = index; value > 9; digits++)
	{
		value /= 10;
	}

	return "#" + string (digits - 1, '_') + to_string (index);
}

/**
 * @brief This function creates a new key from the given parameters.
 *
 * @param name This string specifies the postfix of the name of the key produced by this function.
 * @param parent This key specifies the prefix of the name of the key produced by this function.
 *
 * @returns The function returns a new key that combines the name of the parent key and `name`.
 */
Key newKey (string const & name, Key const & parent)
{
	ELEKTRA_LOG_DEBUG ("Add new key with base name “%s”", name.c_str ());

	Key key{ parent.getFullName (), KEY_BINARY, KEY_END };
	key.addBaseName (name);

	return key;
}

/**
 * @brief This function creates a new array key from the given parameters.
 *
 * @param arrayKey This argument specifies the key that represents the root of the array.
 * @param index This parameter specifies the index of the array key this function creates.
 *
 * @returns The function returns a new key that is part of the array represented by `arrayKey`.
 */
Key newArrayKey (Key & arrayKey, uintmax_t const index)
{
	ELEKTRA_LOG_DEBUG ("Add new array element to array parent “%s”", arrayKey.getName ().c_str ());

	Key newKey{ arrayKey.getName (), KEY_BINARY, KEY_END };
	newKey.addBaseName (indexToArrayBaseName (index));
	arrayKey.setMeta ("array", newKey.getBaseName ());

	return newKey;
}

/**
 * @brief Add metadata saved in a YAML map to the specified key
 *
 * @param key This parameter saves the key to which this function should add the metadata stored in `node`.
 * @param node This YAML node stores a map containing metadata.
 */
void addMetadata (Key & key, YAML::Node const & node)
{
	for (auto & element : node)
	{
		auto metakey = element.first.as<string> ();
		auto metavalue = element.second.IsNull () ? "" : element.second.as<string> ();
		ELEKTRA_LOG_DEBUG ("Add metakey “%s: %s”", metakey.c_str (), metavalue.c_str ());
		key.setMeta (metakey, metavalue);
	}
}

/**
 * @brief Create a key containing a (possibly empty) value.
 *
 * @param node This YAML node stores the data that should be converted to a new `Key`.
 * @param name This text specifies the name of the key this function creates.
 *
 * @return A new key containing the data specified in `node`
 */
Key createLeafKey (YAML::Node const & node, string const & name)
{
	Key key{ name, KEY_BINARY, KEY_END };
	if (!node.IsNull ())
	{
		auto value = node.as<string> ();
		if (value == "true" || value == "false")
		{
			try
			{
				key.set<bool> (node.as<bool> ());
			}
			catch (YAML::BadConversion const &)
			{
				key.set<string> (value); // Save value as string, if `node` is a quoted scalar
			}
		}
		else
		{
			key.set<string> (value);
		}
	}
	if (node.Tag () == "tag:yaml.org,2002:binary")
	{
		ELEKTRA_LOG_DEBUG ("Set metadata type of key to binary");
		key.setMeta ("type", "binary");
	}
	ELEKTRA_LOG_DEBUG ("Add key “%s: %s”", key.getName ().c_str (),
			   key.getBinarySize () == 0 ? "NULL" : key.isBinary () ? "binary value!" : key.get<string> ().c_str ());
	return key;
}

/**
 * @brief Convert the key value of a YAML meta node to a key
 *
 * @param node This YAML meta node stores the data this function stores in the returned key
 * @param parent This key stores the prefix for the key name
 *
 * @return A key representing the key value stored in `node`
 */
Key convertMetaNodeToKey (YAML::Node const & node, Key & parent)
{
	auto key = node[0].IsNull () ? Key{ parent.getFullName (), KEY_BINARY, KEY_END } :
				       Key{ parent.getFullName (), KEY_VALUE, node[0].as<string> ().c_str (), KEY_END };
	ELEKTRA_LOG_DEBUG ("Add key “%s”: “%s”", key.getName ().c_str (),
			   key.getBinarySize () == 0 ? "NULL" : key.isString () ? key.getString ().c_str () : "binary value!");
	return key;
}

/**
 * @brief Convert a YAML node to a key set
 *
 * @param node This YAML node stores the data that should be added to the keyset `mappings`
 * @param mappings The key set where the YAML data will be stored
 * @param parent This key stores the prefix for the key name
 */
void convertNodeToKeySet (YAML::Node const & node, KeySet & mappings, Key & parent)
{
	if (node.Tag () == "!elektra/meta")
	{
		auto key = convertMetaNodeToKey (node, parent);
		mappings.append (key);
		addMetadata (key, node[1]);
	}
	else if (node.IsScalar () || node.IsNull ())
	{
		auto key = createLeafKey (node, parent.getFullName ());
		mappings.append (key);
	}
	else if (node.IsMap ())
	{
		for (auto element : node)
		{
			Key key = newKey (element.first.as<string> (), parent);
			convertNodeToKeySet (element.second, mappings, key);
		}
	}
	else if (node.IsSequence ())
	{
		uintmax_t index = 0;
		uintmax_t lastIndex = 0;
		parent.setMeta ("array", "");
		for (auto const & element : node)
		{
			if (lastIndex == UINTMAX_MAX)
			{
				Key key = newArrayKey (parent, lastIndex);
				throw std::overflow_error ("Unable to add element after “" + key.getName () + "” in array “" +
							   parent.getName () + "”");
			}
			Key key = newArrayKey (parent, index);
			convertNodeToKeySet (element, mappings, key);
			lastIndex = index++;
		}
		mappings.append (parent); // Update array metadata
	}
}
} // end namespace

/**
 * @brief Read a YAML file and add the resulting data to a given key set
 *
 * @param mappings The key set where the YAML data will be stored
 * @param parent This key stores the path to the YAML data file that should be read
 */
void yamlcpp::yamlRead (KeySet & mappings, Key & parent)
{
	YAML::Node config = YAML::LoadFile (parent.getString ());

	ELEKTRA_LOG_DEBUG ("Read file “%s”", parent.getString ().c_str ());

#ifdef HAVE_LOGGER
	ostringstream data;
	data << config;

	ELEKTRA_LOG_DEBUG ("Read Data:");
	ELEKTRA_LOG_DEBUG ("——————————");

	istringstream stream (data.str ());
	for (string line; std::getline (stream, line);)
	{
		ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
	}

	ELEKTRA_LOG_DEBUG ("——————————");
#endif

	Key parentWithoutValue{ parent.getName (), KEY_BINARY, KEY_END }; // We do **not** want to save the filename inside the read key set
	convertNodeToKeySet (config, mappings, parentWithoutValue);

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Converted keys:");
	logKeySet (mappings);
#endif
}
<commit_msg>YAML CPP: Use explicit imports in read code<commit_after>/**
 * @file
 *
 * @brief Read key sets using yaml-cpp
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#include "read.hpp"
#include "log.hpp"
#include "yaml-cpp/yaml.h"

#include <kdb.hpp>
#include <kdblogger.h>
#include <kdbplugin.h>

#include <sstream>

namespace
{

using std::istringstream;
using std::ostringstream;
using std::string;
using std::to_string;

using kdb::Key;
using kdb::KeySet;

/**
 * @brief This function converts a given number to an array base name.
 *
 * @param index This number specifies the index of the array entry.
 *
 * @return A string representing the given indices as Elektra array name.
 */
string indexToArrayBaseName (uintmax_t const index)
{
	size_t digits = 1;

	for (uintmax_t value = index; value > 9; digits++)
	{
		value /= 10;
	}

	return "#" + string (digits - 1, '_') + to_string (index);
}

/**
 * @brief This function creates a new key from the given parameters.
 *
 * @param name This string specifies the postfix of the name of the key produced by this function.
 * @param parent This key specifies the prefix of the name of the key produced by this function.
 *
 * @returns The function returns a new key that combines the name of the parent key and `name`.
 */
Key newKey (string const & name, Key const & parent)
{
	ELEKTRA_LOG_DEBUG ("Add new key with base name “%s”", name.c_str ());

	Key key{ parent.getFullName (), KEY_BINARY, KEY_END };
	key.addBaseName (name);

	return key;
}

/**
 * @brief This function creates a new array key from the given parameters.
 *
 * @param arrayKey This argument specifies the key that represents the root of the array.
 * @param index This parameter specifies the index of the array key this function creates.
 *
 * @returns The function returns a new key that is part of the array represented by `arrayKey`.
 */
Key newArrayKey (Key & arrayKey, uintmax_t const index)
{
	ELEKTRA_LOG_DEBUG ("Add new array element to array parent “%s”", arrayKey.getName ().c_str ());

	Key newKey{ arrayKey.getName (), KEY_BINARY, KEY_END };
	newKey.addBaseName (indexToArrayBaseName (index));
	arrayKey.setMeta ("array", newKey.getBaseName ());

	return newKey;
}

/**
 * @brief Add metadata saved in a YAML map to the specified key
 *
 * @param key This parameter saves the key to which this function should add the metadata stored in `node`.
 * @param node This YAML node stores a map containing metadata.
 */
void addMetadata (Key & key, YAML::Node const & node)
{
	for (auto & element : node)
	{
		auto metakey = element.first.as<string> ();
		auto metavalue = element.second.IsNull () ? "" : element.second.as<string> ();
		ELEKTRA_LOG_DEBUG ("Add metakey “%s: %s”", metakey.c_str (), metavalue.c_str ());
		key.setMeta (metakey, metavalue);
	}
}

/**
 * @brief Create a key containing a (possibly empty) value.
 *
 * @param node This YAML node stores the data that should be converted to a new `Key`.
 * @param name This text specifies the name of the key this function creates.
 *
 * @return A new key containing the data specified in `node`
 */
Key createLeafKey (YAML::Node const & node, string const & name)
{
	Key key{ name, KEY_BINARY, KEY_END };
	if (!node.IsNull ())
	{
		auto value = node.as<string> ();
		if (value == "true" || value == "false")
		{
			try
			{
				key.set<bool> (node.as<bool> ());
			}
			catch (YAML::BadConversion const &)
			{
				key.set<string> (value); // Save value as string, if `node` is a quoted scalar
			}
		}
		else
		{
			key.set<string> (value);
		}
	}
	if (node.Tag () == "tag:yaml.org,2002:binary")
	{
		ELEKTRA_LOG_DEBUG ("Set metadata type of key to binary");
		key.setMeta ("type", "binary");
	}
	ELEKTRA_LOG_DEBUG ("Add key “%s: %s”", key.getName ().c_str (),
			   key.getBinarySize () == 0 ? "NULL" : key.isBinary () ? "binary value!" : key.get<string> ().c_str ());
	return key;
}

/**
 * @brief Convert the key value of a YAML meta node to a key
 *
 * @param node This YAML meta node stores the data this function stores in the returned key
 * @param parent This key stores the prefix for the key name
 *
 * @return A key representing the key value stored in `node`
 */
Key convertMetaNodeToKey (YAML::Node const & node, Key & parent)
{
	auto key = node[0].IsNull () ? Key{ parent.getFullName (), KEY_BINARY, KEY_END } :
				       Key{ parent.getFullName (), KEY_VALUE, node[0].as<string> ().c_str (), KEY_END };
	ELEKTRA_LOG_DEBUG ("Add key “%s”: “%s”", key.getName ().c_str (),
			   key.getBinarySize () == 0 ? "NULL" : key.isString () ? key.getString ().c_str () : "binary value!");
	return key;
}

/**
 * @brief Convert a YAML node to a key set
 *
 * @param node This YAML node stores the data that should be added to the keyset `mappings`
 * @param mappings The key set where the YAML data will be stored
 * @param parent This key stores the prefix for the key name
 */
void convertNodeToKeySet (YAML::Node const & node, KeySet & mappings, Key & parent)
{
	if (node.Tag () == "!elektra/meta")
	{
		auto key = convertMetaNodeToKey (node, parent);
		mappings.append (key);
		addMetadata (key, node[1]);
	}
	else if (node.IsScalar () || node.IsNull ())
	{
		auto key = createLeafKey (node, parent.getFullName ());
		mappings.append (key);
	}
	else if (node.IsMap ())
	{
		for (auto element : node)
		{
			Key key = newKey (element.first.as<string> (), parent);
			convertNodeToKeySet (element.second, mappings, key);
		}
	}
	else if (node.IsSequence ())
	{
		uintmax_t index = 0;
		uintmax_t lastIndex = 0;
		parent.setMeta ("array", "");
		for (auto const & element : node)
		{
			if (lastIndex == UINTMAX_MAX)
			{
				Key key = newArrayKey (parent, lastIndex);
				throw std::overflow_error ("Unable to add element after “" + key.getName () + "” in array “" +
							   parent.getName () + "”");
			}
			Key key = newArrayKey (parent, index);
			convertNodeToKeySet (element, mappings, key);
			lastIndex = index++;
		}
		mappings.append (parent); // Update array metadata
	}
}
} // end namespace

/**
 * @brief Read a YAML file and add the resulting data to a given key set
 *
 * @param mappings The key set where the YAML data will be stored
 * @param parent This key stores the path to the YAML data file that should be read
 */
void yamlcpp::yamlRead (KeySet & mappings, Key & parent)
{
	YAML::Node config = YAML::LoadFile (parent.getString ());

	ELEKTRA_LOG_DEBUG ("Read file “%s”", parent.getString ().c_str ());

#ifdef HAVE_LOGGER
	ostringstream data;
	data << config;

	ELEKTRA_LOG_DEBUG ("Read Data:");
	ELEKTRA_LOG_DEBUG ("——————————");

	istringstream stream (data.str ());
	for (string line; std::getline (stream, line);)
	{
		ELEKTRA_LOG_DEBUG ("%s", line.c_str ());
	}

	ELEKTRA_LOG_DEBUG ("——————————");
#endif

	Key parentWithoutValue{ parent.getName (), KEY_BINARY, KEY_END }; // We do **not** want to save the filename inside the read key set
	convertNodeToKeySet (config, mappings, parentWithoutValue);

#ifdef HAVE_LOGGER
	ELEKTRA_LOG_DEBUG ("Converted keys:");
	logKeySet (mappings);
#endif
}
<|endoftext|>