<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2019, 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 <NAME> 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 "GafferScene/Orientation.h"

#include "Gaffer/StringPlug.h"

#include "IECoreScene/Primitive.h"

#include "IECore/AngleConversion.h"
#include "IECore/MatrixAlgo.h"

#include "OpenEXR/ImathEuler.h"
#include "OpenEXR/ImathMatrixAlgo.h"
#include "OpenEXR/ImathRandom.h"

#include <random>

using namespace std;
using namespace Imath;
using namespace IECore;
using namespace IECoreScene;
using namespace Gaffer;
using namespace GafferScene;

//////////////////////////////////////////////////////////////////////////
// Internal utilities
//
// We deliberately separate the actual conversion operations out from
// `computeProcessedObject()` so that the latter has a simple structure
// that matches `hashProcessedObject()` closesly. This makes it easier
// to verify correctness, and harder to introduce bugs. Perhaps the
// conversion functions might make sense as IECoreScene Algo at some
// point.
//////////////////////////////////////////////////////////////////////////

namespace
{

struct ViewSpec
{
	std::string name;
	PrimitiveVariable::Interpolation interpolation = PrimitiveVariable::Invalid;
	size_t size = 0;
};

template<typename T>
PrimitiveVariable::IndexedView<T> indexedView( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &name, bool deleteInputs, ViewSpec &spec )
{
	const auto it = inputPrimitive->variables.find( name );
	if( it == inputPrimitive->variables.end() )
	{
		throw IECore::Exception(
			boost::str( boost::format( "Primitive variable \"%s\" not found" ) % name )
		);
	}

	using DataType = IECore::TypedData<vector<T>>;
	const DataType *data = runTimeCast<const DataType>( it->second.data.get() );
	if( !data )
	{
		throw IECore::Exception(
			boost::str(
				boost::format( "Primitive variable \"%s\" has wrong type \"%s\" (wanted \"%s\")" )
					% name
					% it->second.data->typeName()
					% DataType::staticTypeName()
			)
		);
	}

	if( spec.name == "" )
	{
		// First variable found.
		spec.name = name;
		spec.interpolation = it->second.interpolation;
		spec.size = data->readable().size();
	}
	else
	{
		// Check that we match any previously found variables.
		if( data->readable().size() != spec.size )
		{
			throw IECore::Exception(
				boost::str(
					boost::format( "Primitive variable \"%s\" has wrong size (%d, but should be %d to match \"%s\")" )
						% name
						% data->readable().size()
						% spec.size
						% spec.name
				)
			);
		}
	}

	if( deleteInputs )
	{
		// Although the returned IndexedView doesn't maintain any ownership
		// of the data, it's OK to erase here because we get the IndexedView
		// from `inputPrimitive`, and we only erase from `outputPrimitive`.
		outputPrimitive->variables.erase( name );
	}

	return PrimitiveVariable::IndexedView<T>( it->second );
}

PrimitiveVariable inEuler( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &eulerName, const Imath::Eulerf::Order order, bool deleteInputs )
{
	if( eulerName == "" )
	{
		return PrimitiveVariable();
	}

	ViewSpec spec;
	auto view = indexedView<V3f>( inputPrimitive, outputPrimitive, eulerName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( view.size() );

	for( auto &eulerVec : view )
	{
		const Eulerf euler( degreesToRadians( eulerVec ), order, Eulerf::XYZLayout );
		quaternions.push_back( euler.toQuat() );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

PrimitiveVariable inQuaternion( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &quaternionName, bool deleteInputs, bool xyzw )
{
	if( quaternionName == "" )
	{
		return PrimitiveVariable();
	}

	ViewSpec spec;
	auto view = indexedView<Quatf>( inputPrimitive, outputPrimitive, quaternionName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( view.size() );

	for( auto &q : view )
	{
		if( xyzw )
		{
			quaternions.push_back( Quatf( q.v.z, V3f( q.r, q.v.x, q.v.y ) ) );
		}
		else
		{
			quaternions.push_back( q );
		}
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

PrimitiveVariable inAxisAngle( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &axisName, const std::string &angleName, bool deleteInputs )
{
	if( axisName == "" || angleName == "" )
	{
		return PrimitiveVariable();
	}

	ViewSpec spec;
	auto axisView = indexedView<V3f>( inputPrimitive, outputPrimitive, axisName, deleteInputs, spec );
	auto angleView = indexedView<float>( inputPrimitive, outputPrimitive, angleName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( axisView.size() );

	for( size_t i = 0, s = axisView.size(); i < s; ++i )
	{
		quaternions.push_back( Quatf().setAxisAngle( axisView[i], angleView[i] ) );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

/// Disable over-zealous GCC warning.
/// See https://stackoverflow.com/questions/21755206/how-to-get-around-gcc-void-b-4-may-be-used-uninitialized-in-this-funct
#ifdef BOOST_COMP_GNUC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif

PrimitiveVariable inAim( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &xAxisName, const std::string &yAxisName, const std::string &zAxisName, bool deleteInputs )
{
	ViewSpec spec;

	using OptionalVector = boost::optional<PrimitiveVariable::IndexedView<V3f>>;
	OptionalVector xAxis, yAxis, zAxis;

	if( xAxisName != "" )
	{
		xAxis = indexedView<V3f>( inputPrimitive, outputPrimitive, xAxisName, deleteInputs, spec );
	}
	if( yAxisName != "" )
	{
		yAxis = indexedView<V3f>( inputPrimitive, outputPrimitive, yAxisName, deleteInputs, spec );
	}
	if( zAxisName != "" )
	{
		zAxis = indexedView<V3f>( inputPrimitive, outputPrimitive, zAxisName, deleteInputs, spec );
	}

	if( !xAxis && !yAxis && !zAxis )
	{
		return PrimitiveVariable();
	}

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( spec.size );

	for( size_t i = 0; i < spec.size; ++i )
	{
		M44f m;
		if( xAxis && yAxis && zAxis )
		{
			m = matrixFromBasis( (*xAxis)[i], (*yAxis)[i], (*zAxis)[i], V3f( 0 ) );
		}
		else if( xAxis && yAxis )
		{
			const V3f &x = (*xAxis)[i];
			const V3f &y = (*yAxis)[i];
			m = matrixFromBasis( x, y, x.cross( y ), V3f( 0 ) );
		}
		else if( xAxis && zAxis )
		{
			const V3f &x = (*xAxis)[i];
			const V3f &z = (*zAxis)[i];
			m = matrixFromBasis( x, z.cross( x ), z, V3f( 0 ) );
		}
		else if( yAxis && zAxis )
		{
			const V3f &y = (*yAxis)[i];
			const V3f &z = (*zAxis)[i];
			m = matrixFromBasis( y.cross( z ), y, z, V3f( 0 ) );
		}
		else if( xAxis )
		{
			m = rotationMatrixWithUpDir( V3f( 1, 0, 0 ), (*xAxis)[i], V3f( 0, 1, 0 ) );
		}
		else if( yAxis )
		{
			m = rotationMatrixWithUpDir( V3f( 0, 1, 0 ), (*yAxis)[i], V3f( 0, 1, 0 ) );
		}
		else if( zAxis )
		{
			m = rotationMatrixWithUpDir( V3f( 0, 0, 1 ), (*zAxis)[i], V3f( 0, 1, 0 ) );
		}

		removeScalingAndShear( m );
		quaternions.push_back( extractQuat( m ) );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

#ifdef BOOST_COMP_GNUC
#pragma GCC diagnostic pop
#endif

PrimitiveVariable inMatrix( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &matrixName, bool deleteInputs )
{
	ViewSpec spec;
	auto matrixView = indexedView<M33f>( inputPrimitive, outputPrimitive, matrixName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( matrixView.size() );

	for( auto &m : matrixView )
	{
		quaternions.push_back( extractQuat( M44f( m, V3f( 0 ) ) ) );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

void outEuler( const PrimitiveVariable &orientations, Primitive *outputPrimitive, const std::string &eulerName, Imath::Eulerf::Order order )
{
	if( eulerName == "" )
	{
		return;<commit_msg>Orientation : Fix compilation with Clang<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2019, 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 <NAME> 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 "GafferScene/Orientation.h"

#include "Gaffer/StringPlug.h"

#include "IECoreScene/Primitive.h"

#include "IECore/AngleConversion.h"
#include "IECore/MatrixAlgo.h"

#include "OpenEXR/ImathEuler.h"
#include "OpenEXR/ImathMatrixAlgo.h"
#include "OpenEXR/ImathRandom.h"

#include <random>

using namespace std;
using namespace Imath;
using namespace IECore;
using namespace IECoreScene;
using namespace Gaffer;
using namespace GafferScene;

//////////////////////////////////////////////////////////////////////////
// Internal utilities
//
// We deliberately separate the actual conversion operations out from
// `computeProcessedObject()` so that the latter has a simple structure
// that matches `hashProcessedObject()` closesly. This makes it easier
// to verify correctness, and harder to introduce bugs. Perhaps the
// conversion functions might make sense as IECoreScene Algo at some
// point.
//////////////////////////////////////////////////////////////////////////

namespace
{

struct ViewSpec
{
	std::string name;
	PrimitiveVariable::Interpolation interpolation = PrimitiveVariable::Invalid;
	size_t size = 0;
};

template<typename T>
PrimitiveVariable::IndexedView<T> indexedView( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &name, bool deleteInputs, ViewSpec &spec )
{
	const auto it = inputPrimitive->variables.find( name );
	if( it == inputPrimitive->variables.end() )
	{
		throw IECore::Exception(
			boost::str( boost::format( "Primitive variable \"%s\" not found" ) % name )
		);
	}

	using DataType = IECore::TypedData<vector<T>>;
	const DataType *data = runTimeCast<const DataType>( it->second.data.get() );
	if( !data )
	{
		throw IECore::Exception(
			boost::str(
				boost::format( "Primitive variable \"%s\" has wrong type \"%s\" (wanted \"%s\")" )
					% name
					% it->second.data->typeName()
					% DataType::staticTypeName()
			)
		);
	}

	if( spec.name == "" )
	{
		// First variable found.
		spec.name = name;
		spec.interpolation = it->second.interpolation;
		spec.size = data->readable().size();
	}
	else
	{
		// Check that we match any previously found variables.
		if( data->readable().size() != spec.size )
		{
			throw IECore::Exception(
				boost::str(
					boost::format( "Primitive variable \"%s\" has wrong size (%d, but should be %d to match \"%s\")" )
						% name
						% data->readable().size()
						% spec.size
						% spec.name
				)
			);
		}
	}

	if( deleteInputs )
	{
		// Although the returned IndexedView doesn't maintain any ownership
		// of the data, it's OK to erase here because we get the IndexedView
		// from `inputPrimitive`, and we only erase from `outputPrimitive`.
		outputPrimitive->variables.erase( name );
	}

	return PrimitiveVariable::IndexedView<T>( it->second );
}

PrimitiveVariable inEuler( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &eulerName, const Imath::Eulerf::Order order, bool deleteInputs )
{
	if( eulerName == "" )
	{
		return PrimitiveVariable();
	}

	ViewSpec spec;
	auto view = indexedView<V3f>( inputPrimitive, outputPrimitive, eulerName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( view.size() );

	for( auto &eulerVec : view )
	{
		const Eulerf euler( degreesToRadians( eulerVec ), order, Eulerf::XYZLayout );
		quaternions.push_back( euler.toQuat() );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

PrimitiveVariable inQuaternion( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &quaternionName, bool deleteInputs, bool xyzw )
{
	if( quaternionName == "" )
	{
		return PrimitiveVariable();
	}

	ViewSpec spec;
	auto view = indexedView<Quatf>( inputPrimitive, outputPrimitive, quaternionName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( view.size() );

	for( auto &q : view )
	{
		if( xyzw )
		{
			quaternions.push_back( Quatf( q.v.z, V3f( q.r, q.v.x, q.v.y ) ) );
		}
		else
		{
			quaternions.push_back( q );
		}
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

PrimitiveVariable inAxisAngle( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &axisName, const std::string &angleName, bool deleteInputs )
{
	if( axisName == "" || angleName == "" )
	{
		return PrimitiveVariable();
	}

	ViewSpec spec;
	auto axisView = indexedView<V3f>( inputPrimitive, outputPrimitive, axisName, deleteInputs, spec );
	auto angleView = indexedView<float>( inputPrimitive, outputPrimitive, angleName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( axisView.size() );

	for( size_t i = 0, s = axisView.size(); i < s; ++i )
	{
		quaternions.push_back( Quatf().setAxisAngle( axisView[i], angleView[i] ) );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

/// Disable over-zealous GCC warning.
/// See https://stackoverflow.com/questions/21755206/how-to-get-around-gcc-void-b-4-may-be-used-uninitialized-in-this-funct
#if defined( __GNUC__ ) && !defined( __clang__ )
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif

PrimitiveVariable inAim( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &xAxisName, const std::string &yAxisName, const std::string &zAxisName, bool deleteInputs )
{
	ViewSpec spec;

	using OptionalVector = boost::optional<PrimitiveVariable::IndexedView<V3f>>;
	OptionalVector xAxis, yAxis, zAxis;

	if( xAxisName != "" )
	{
		xAxis = indexedView<V3f>( inputPrimitive, outputPrimitive, xAxisName, deleteInputs, spec );
	}
	if( yAxisName != "" )
	{
		yAxis = indexedView<V3f>( inputPrimitive, outputPrimitive, yAxisName, deleteInputs, spec );
	}
	if( zAxisName != "" )
	{
		zAxis = indexedView<V3f>( inputPrimitive, outputPrimitive, zAxisName, deleteInputs, spec );
	}

	if( !xAxis && !yAxis && !zAxis )
	{
		return PrimitiveVariable();
	}

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( spec.size );

	for( size_t i = 0; i < spec.size; ++i )
	{
		M44f m;
		if( xAxis && yAxis && zAxis )
		{
			m = matrixFromBasis( (*xAxis)[i], (*yAxis)[i], (*zAxis)[i], V3f( 0 ) );
		}
		else if( xAxis && yAxis )
		{
			const V3f &x = (*xAxis)[i];
			const V3f &y = (*yAxis)[i];
			m = matrixFromBasis( x, y, x.cross( y ), V3f( 0 ) );
		}
		else if( xAxis && zAxis )
		{
			const V3f &x = (*xAxis)[i];
			const V3f &z = (*zAxis)[i];
			m = matrixFromBasis( x, z.cross( x ), z, V3f( 0 ) );
		}
		else if( yAxis && zAxis )
		{
			const V3f &y = (*yAxis)[i];
			const V3f &z = (*zAxis)[i];
			m = matrixFromBasis( y.cross( z ), y, z, V3f( 0 ) );
		}
		else if( xAxis )
		{
			m = rotationMatrixWithUpDir( V3f( 1, 0, 0 ), (*xAxis)[i], V3f( 0, 1, 0 ) );
		}
		else if( yAxis )
		{
			m = rotationMatrixWithUpDir( V3f( 0, 1, 0 ), (*yAxis)[i], V3f( 0, 1, 0 ) );
		}
		else if( zAxis )
		{
			m = rotationMatrixWithUpDir( V3f( 0, 0, 1 ), (*zAxis)[i], V3f( 0, 1, 0 ) );
		}

		removeScalingAndShear( m );
		quaternions.push_back( extractQuat( m ) );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

#if defined( __GNUC__ ) && !defined( __clang__ )
#pragma GCC diagnostic pop
#endif

PrimitiveVariable inMatrix( const Primitive *inputPrimitive, Primitive *outputPrimitive, const std::string &matrixName, bool deleteInputs )
{
	ViewSpec spec;
	auto matrixView = indexedView<M33f>( inputPrimitive, outputPrimitive, matrixName, deleteInputs, spec );

	QuatfVectorDataPtr quaternionData = new QuatfVectorData;
	auto &quaternions = quaternionData->writable();
	quaternions.reserve( matrixView.size() );

	for( auto &m : matrixView )
	{
		quaternions.push_back( extractQuat( M44f( m, V3f( 0 ) ) ) );
	}

	return PrimitiveVariable( spec.interpolation, quaternionData );
}

void outEuler( const PrimitiveVariable &orientations, Primitive *outputPrimitive, const std::string &eulerName, Imath::Eulerf::Order order )
{
	if( eulerName == "" )
	{
		return;
{'fraction_non_alphanumeric': 0.15034044033596738, 'fraction_numerical': 0.006858506038467273, 'mean_word_length': 3.5217977528089888, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 71, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7225812', 'n_tokens_mistral': 6846, 'n_tokens_neox': 6419, 'n_words': 1859}