<commit_before>
#pragma once

#include <ionCore.h>
#include <vector>

#include "IInterpolator.h"


namespace ion
{
	namespace Animation
	{

		int IntermediateToIndex(float const Mu);

		template <typename TPathNode>
		class CPath : public IPath<TPathNode>
		{

		protected:

			std::vector<TPathNode> Nodes;
			std::vector<f32> DistanceTable;
			f32 MuIncrement = 0;
			bool Looping = true;

			mutable SharedPtr<IInterpolator<TPathNode>> DefaultInterpolator = SharedFromNew(new CLinearInterpolator<TPathNode>());

		public:

			std::vector<TPathNode> const & GetNodes() const;
			std::vector<TPathNode> & GetNodes();

			void AddNode(TPathNode const & Node);

			void SetLooping(bool const looping);
			bool IsLooping() const;

			void SetDefaultInterpolator(SharedPtr<IInterpolator<TPathNode>> defaultInterpolator);
			SharedPtr<IInterpolator<TPathNode>> GetDefaultInterpolator() const;

			int SanitizeIndex(int Index) const;

			TPathNode const & GetNode(int const Index) const;
			TPathNode GetNodeInterpolated(float const Intermediate, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0) const;

			float BuildDistanceTable(float const Increment = 0.1f, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0);
			float GetIntermediateFromDistance(float const Distance, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0);
			TPathNode GetNodeFromDistance(float const Distance, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0);
			float GetTotalPathLength();

		};

		template <typename TPathNode>
		std::vector<TPathNode> const & CPath<TPathNode>::GetNodes() const
		{
			return Nodes;
		}

		template <typename TPathNode>
		std::vector<TPathNode> & CPath<TPathNode>::GetNodes()
		{
			return Nodes;
		}

		template <typename TPathNode>
		void CPath<TPathNode>::AddNode(TPathNode const & Node)
		{
			Nodes.push_back(Node);
			DistanceTable.clear();
		}

		template <typename TPathNode>
		void CPath<TPathNode>::SetLooping(bool const looping)
		{
			DistanceTable.clear();
			Looping = looping;
		}

		template <typename TPathNode>
		bool CPath<TPathNode>::IsLooping() const
		{
			return Looping;
		}

		template <typename TPathNode>
		void CPath<TPathNode>::SetDefaultInterpolator(SharedPtr<IInterpolator<TPathNode>> defaultInterpolator)
		{
			DefaultInterpolator = defaultInterpolator;
		}

		template <typename TPathNode>
		SharedPtr<IInterpolator<TPathNode>> CPath<TPathNode>::GetDefaultInterpolator() const
		{
			return DefaultInterpolator;
		}

		template <typename TPathNode>
		int CPath<TPathNode>::SanitizeIndex(int Index) const
		{
			if (Looping)
			{
				while (Index < 0)
					Index += (int) Nodes.size();

				if (Index >= static_cast<int>(Nodes.size()))
					Index %= (int) Nodes.size();
			}
			else
			{
				Index = Clamp<int>(Index, 0, (int) Nodes.size() - 1);
			}

			return Index;
		}

		template <typename TPathNode>
		TPathNode const & CPath<TPathNode>::GetNode(int const Index) const
		{
			return Nodes[SanitizeIndex(Index)];
		}

		template <typename TPathNode>
		TPathNode CPath<TPathNode>::GetNodeInterpolated(float const Mu, SharedPtr<IInterpolator<TPathNode>> Interpolator) const
		{
			if (! Interpolator)
				Interpolator = DefaultInterpolator;

			if (! Interpolator)
				return TPathNode();

			int const Index = SanitizeIndex(IntermediateToIndex(Mu));

			return Interpolator->Interpolate(* this, Index, fmodf(Mu, 1.f));
		}

		template <typename TPathNode>
		float CPath<TPathNode>::BuildDistanceTable(float const Increment, SharedPtr<IInterpolator<TPathNode>> Interpolator)
		{
			if (! Interpolator)
				Interpolator = DefaultInterpolator;

			if (! Interpolator)
				return 0.f;

			DistanceTable.clear();
			// Store the increment at which this table was built
			MuIncrement = Increment;

			float const Max = static_cast<float>(Looping ? Nodes.size() : Nodes.size() - 1);
			TPathNode Last = Interpolator->Interpolate(* this, 0, 0.f);
			DistanceTable.push_back(0.f);

			for (float Mu = Increment;; Mu += Increment)
			{
				TPathNode Current = Interpolator->Interpolate(* this, SanitizeIndex(IntermediateToIndex(Mu)), fmodf(Mu, 1.f));
				DistanceTable.push_back((Current - Last).GetLength() + (DistanceTable.size() ? DistanceTable.back() : 0.f));
				Last = Current;

				if (Mu > Max)
				{
					TPathNode Current = Interpolator->Interpolate(* this, SanitizeIndex(IntermediateToIndex(Max)), fmodf(Max, 1.f));
					DistanceTable.push_back((Current - Last).GetLength() + (DistanceTable.size() ? DistanceTable.back() : 0.f));
					break;
				}
			}

			return DistanceTable.back();
		}


		template <typename TPathNode>
		float CPath<TPathNode>::GetIntermediateFromDistance(float const Distance, SharedPtr<IInterpolator<TPathNode>> Interpolator)
		{
			if (DistanceTable.size() == 0)
				BuildDistanceTable(0.1f, Interpolator);

			if (DistanceTable.size() == 0)
				return 0.f;

			uint Max = (uint) DistanceTable.size() - 1, Min = 0;

			float const ModulatedDistance = fmodf(Distance, DistanceTable.back());

			while (Max > Min + 1)
			{
				uint const Midpoint = (Max + Min) / 2;
				float const Value = DistanceTable[Midpoint];
				if (ModulatedDistance > Value)
					Min = Midpoint;
				else
					Max = Midpoint;
			}

			if (Max != Min + 1)
				std::cerr << "Unexpected values of Max and Min in getNodeArcLengthParametized(...): " << Min << " " << Max << std::endl;<commit_msg>Actually fix (or make a better attempt at fixing) behaviour of path arc-length parameterization need end of loop<commit_after>
#pragma once

#include <ionCore.h>
#include <vector>

#include "IInterpolator.h"


namespace ion
{
	namespace Animation
	{

		int IntermediateToIndex(float const Mu);

		template <typename TPathNode>
		class CPath : public IPath<TPathNode>
		{

		protected:

			std::vector<TPathNode> Nodes;
			std::vector<f32> DistanceTable;
			f32 MuIncrement = 0;
			bool Looping = true;

			mutable SharedPtr<IInterpolator<TPathNode>> DefaultInterpolator = SharedFromNew(new CLinearInterpolator<TPathNode>());

		public:

			std::vector<TPathNode> const & GetNodes() const;
			std::vector<TPathNode> & GetNodes();

			void AddNode(TPathNode const & Node);

			void SetLooping(bool const looping);
			bool IsLooping() const;

			void SetDefaultInterpolator(SharedPtr<IInterpolator<TPathNode>> defaultInterpolator);
			SharedPtr<IInterpolator<TPathNode>> GetDefaultInterpolator() const;

			int SanitizeIndex(int Index) const;

			TPathNode const & GetNode(int const Index) const;
			TPathNode GetNodeInterpolated(float const Intermediate, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0) const;

			float BuildDistanceTable(float const Increment = 0.1f, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0);
			float GetIntermediateFromDistance(float const Distance, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0);
			TPathNode GetNodeFromDistance(float const Distance, SharedPtr<IInterpolator<TPathNode>> Interpolator = 0);
			float GetTotalPathLength();

		};

		template <typename TPathNode>
		std::vector<TPathNode> const & CPath<TPathNode>::GetNodes() const
		{
			return Nodes;
		}

		template <typename TPathNode>
		std::vector<TPathNode> & CPath<TPathNode>::GetNodes()
		{
			return Nodes;
		}

		template <typename TPathNode>
		void CPath<TPathNode>::AddNode(TPathNode const & Node)
		{
			Nodes.push_back(Node);
			DistanceTable.clear();
		}

		template <typename TPathNode>
		void CPath<TPathNode>::SetLooping(bool const looping)
		{
			DistanceTable.clear();
			Looping = looping;
		}

		template <typename TPathNode>
		bool CPath<TPathNode>::IsLooping() const
		{
			return Looping;
		}

		template <typename TPathNode>
		void CPath<TPathNode>::SetDefaultInterpolator(SharedPtr<IInterpolator<TPathNode>> defaultInterpolator)
		{
			DefaultInterpolator = defaultInterpolator;
		}

		template <typename TPathNode>
		SharedPtr<IInterpolator<TPathNode>> CPath<TPathNode>::GetDefaultInterpolator() const
		{
			return DefaultInterpolator;
		}

		template <typename TPathNode>
		int CPath<TPathNode>::SanitizeIndex(int Index) const
		{
			if (Looping)
			{
				while (Index < 0)
					Index += (int) Nodes.size();

				if (Index >= static_cast<int>(Nodes.size()))
					Index %= (int) Nodes.size();
			}
			else
			{
				Index = Clamp<int>(Index, 0, (int) Nodes.size() - 1);
			}

			return Index;
		}

		template <typename TPathNode>
		TPathNode const & CPath<TPathNode>::GetNode(int const Index) const
		{
			return Nodes[SanitizeIndex(Index)];
		}

		template <typename TPathNode>
		TPathNode CPath<TPathNode>::GetNodeInterpolated(float const Mu, SharedPtr<IInterpolator<TPathNode>> Interpolator) const
		{
			if (! Interpolator)
				Interpolator = DefaultInterpolator;

			if (! Interpolator)
				return TPathNode();

			int const Index = SanitizeIndex(IntermediateToIndex(Mu));

			return Interpolator->Interpolate(* this, Index, fmodf(Mu, 1.f));
		}

		template <typename TPathNode>
		float CPath<TPathNode>::BuildDistanceTable(float const Increment, SharedPtr<IInterpolator<TPathNode>> Interpolator)
		{
			if (! Interpolator)
				Interpolator = DefaultInterpolator;

			if (! Interpolator)
				return 0.f;

			DistanceTable.clear();
			// Store the increment at which this table was built
			MuIncrement = Increment;

			float const Max = static_cast<float>(Looping ? Nodes.size() : Nodes.size() - 1);
			TPathNode Last = Interpolator->Interpolate(* this, 0, 0.f);
			DistanceTable.push_back(0.f);

			for (float Mu = Increment;; Mu += Increment)
			{
				if (Mu > Max)
				{
					TPathNode Current = Interpolator->Interpolate(* this, SanitizeIndex(IntermediateToIndex(Max)), fmodf(Max, 1.f));
					DistanceTable.push_back((Current - Last).GetLength() + (DistanceTable.size() ? DistanceTable.back() : 0.f));
					break;
				}

				TPathNode Current = Interpolator->Interpolate(* this, SanitizeIndex(IntermediateToIndex(Mu)), fmodf(Mu, 1.f));
				DistanceTable.push_back((Current - Last).GetLength() + (DistanceTable.size() ? DistanceTable.back() : 0.f));
				Last = Current;
			}

			return DistanceTable.back();
		}


		template <typename TPathNode>
		float CPath<TPathNode>::GetIntermediateFromDistance(float const Distance, SharedPtr<IInterpolator<TPathNode>> Interpolator)
		{
			if (DistanceTable.size() == 0)
				BuildDistanceTable(0.1f, Interpolator);

			if (DistanceTable.size() == 0)
				return 0.f;

			uint Max = (uint) DistanceTable.size() - 1, Min = 0;

			float const ModulatedDistance = fmodf(Distance, DistanceTable.back());

			while (Max > Min + 1)
			{
				uint const Midpoint = (Max + Min) / 2;
				float const Value = DistanceTable[Midpoint];
				if (ModulatedDistance > Value)
					Min = Midpoint;
				else
					Max = Midpoint;
			}

			if (Max != Min + 1)
				std::cerr << "Unexpected values of Max and Min in getNodeArcLengthParametized(...): " << Min << " " << Max << std::endl;
{'fraction_non_alphanumeric': 0.11928816387060895, 'fraction_numerical': 0.006302715728983224, 'mean_word_length': 4.333662876915472, 'pattern_counts': {'":': 0, '<': 149, '<?xml version=': 0, '>': 143, 'https://': 0, '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': '768606', 'n_tokens_mistral': 4206, 'n_tokens_neox': 3507, 'n_words': 784}