/* Copyright (c) <2003-2019> <Newton Game Dynamics>
* 
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* 
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely
*/

#include "dContainersStdAfx.h"
#include "dBezierSpline.h"

dBezierSpline::dBezierSpline ()
	:dContainersAlloc()
	,m_knotVector(16)
	,m_controlPoints(16)
	,m_degree(0)
	,m_knotsCount(0)
	,m_controlPointsCount(0)
{
}

dBezierSpline::dBezierSpline (const dBezierSpline& src)
	:m_knotVector(src.m_knotsCount)
	,m_controlPoints(src.m_controlPointsCount)
	,m_degree(0)
	,m_knotsCount(0)
	,m_controlPointsCount(0)
{
	if (src.m_knotsCount) {
		CreateFromKnotVectorAndControlPoints (src.m_degree, src.m_knotsCount - 2 * src.m_degree, &src.m_knotVector[src.m_degree], &src.m_controlPoints[0]);
	}
}

dBezierSpline::~dBezierSpline ()
{
	Clear();
}

void dBezierSpline::Clear()
{
	m_degree = 0;
	m_knotsCount = 0;
	m_controlPointsCount = 0;
}

dBezierSpline& dBezierSpline::operator = (const dBezierSpline &copy)
{
	Clear();
	if (copy.m_knotsCount) {
		CreateFromKnotVectorAndControlPoints (copy.m_degree, copy.m_knotsCount - 2 * copy.m_degree, &copy.m_knotVector[copy.m_degree], &copy.m_controlPoints[0]);
	}
	return *this;
}

int dBezierSpline::GetDegree () const
{
	return m_degree;
}

void dBezierSpline::CreateFromKnotVectorAndControlPoints (int degree, int knotCount, const dFloat64* const knotVector, const dBigVector* const controlPoints)
{
	Clear();
	dAssert (knotCount);
	dAssert (knotVector[0] == 0.0f);
	dAssert (knotVector[knotCount - 1] == 1.0f);

	m_degree = degree;
	m_knotsCount = knotCount + 2 * degree;
	m_controlPointsCount = knotCount + m_degree - 1;

	for (int i = 0; i < m_controlPointsCount; i++) {
		m_controlPoints[i] = controlPoints[i];
	}

	for (int i = 0; i < m_degree; i ++) {
		m_knotVector[i] = dFloat64(0.0f);
		m_knotVector[i + m_knotsCount - m_degree] = dFloat64(1.0f);
	}

	for (int i = 0; i < knotCount; i ++) {
		m_knotVector[i + m_degree] = knotVector[i];
		dAssert (m_knotVector[i + m_degree] >= m_knotVector[i + m_degree - 1]);
	}
}

int dBezierSpline::GetKnotCount() const
{
	return m_knotsCount;
}

dArray<dFloat64>& dBezierSpline::GetKnotArray()
{
	return m_knotVector;
}

const dArray<dFloat64>& dBezierSpline::GetKnotArray() const
{
	return m_knotVector;
}

dFloat64 dBezierSpline::GetKnot(int i) const
{
	return m_knotVector[i];
}

int dBezierSpline::GetControlPointCount() const
{
	return m_controlPointsCount;
}

dBigVector dBezierSpline::GetControlPoint(int i) const
{
	return m_controlPoints[i];
}

void dBezierSpline::SetControlPoint(int i, const dBigVector& point)
{
	m_controlPoints[i] = point;
}

dArray<dBigVector>& dBezierSpline::GetControlPointArray()
{
	return m_controlPoints;
}

const dArray<dBigVector>& dBezierSpline::GetControlPointArray() const
{
	return m_controlPoints;
}

int dBezierSpline::GetSpan(dFloat64 u) const
{
	int low = m_degree;
	int high = m_knotsCount - m_degree - 1;

	dAssert (u >= 0.0f);
	dAssert (u <= 1.0f);
	while ((high - low) >= 4) {
		int mid = (low + high) >> 1;
		if (u > m_knotVector[mid]) {
			low = mid;
		} else {
			high = mid;
		}
	}

	dAssert (m_knotVector[low] <= u);
	for (int i = low; i < m_degree + m_knotsCount + 1; i ++) {
		if (m_knotVector[i + 1] >= u) {
			return i;
		}
	}
	dAssert (0);
	return 0;
}

void dBezierSpline::BasicsFunctions (dFloat64 u, int span, dFloat64* const BasicFunctionsOut) const
{
	BasicFunctionsOut[0] = 1.0f;

	dFloat64* const left = dAlloca(dFloat64, m_knotsCount + 32);
	dFloat64* const right = dAlloca(dFloat64, m_knotsCount + 32);

	for (int j = 1; j <= m_degree; j ++) {
		left[j] = u - m_knotVector[span + 1 - j]; 
		right[j] = m_knotVector[span + j] - u;

		dFloat64 saved = 0.0f;
		for (int r = 0; r < j; r ++) {
			dFloat64 temp = BasicFunctionsOut[r] / (right[r + 1] + left[j - r]);
			BasicFunctionsOut[r] = saved + temp * right[r + 1];
			saved = temp * left[j - r];
		}
		BasicFunctionsOut[j] = saved;
	}
}

void dBezierSpline::BasicsFunctionsDerivatives (dFloat64 u, int span, dFloat64* const derivativesOut) const
{
	dFloat64* const a = dAlloca(dFloat64, m_knotsCount + 32);
	dFloat64* const ndu = dAlloca(dFloat64, m_knotsCount + 32);
	dFloat64* const left = dAlloca(dFloat64, m_knotsCount + 32);
	dFloat64* const right = dAlloca(dFloat64, m_knotsCount + 32);

	const int width = m_degree + 1;
	ndu[0] = 1.0f;
	for (int j = 1; j <= m_degree; j ++) {
		left[j] = u - m_knotVector[span + 1 - j];
		right[j] = m_knotVector[span + j] - u;
		dFloat64 saved = 0.0f;
		for (int r = 0; r < j; r ++) {
			ndu[j * width + r] = right[r + 1] + left[j - r];

			dFloat64 temp = ndu[r * width + j - 1] / ndu[j * width + r];
			ndu[r * width + j] = saved + temp * right[r + 1];
			saved = temp * left[j - r];
		}
		ndu[j * width + j] = saved;
	}


	for (int j = 0; j <= m_degree; j ++) {
		derivativesOut[width * 0 + j] = ndu [width * j + m_degree];
	}

	for (int r = 0; r <= m_degree; r ++) {
		int s1 = 0;
		int s2 = 1;
		a[0] = 1.0f;
		for (int k = 1; k <= m_degree; k ++) {
			dFloat64 d = 0.0f;
			int rk = r - k;
			int pk = m_degree - k;
			if (r >= k)  {
				a[width * s2 + 0] = a[width * s1 + 0] / ndu[width * (pk + 1) + rk];
				d = a[width * s2 + 0] * ndu[width * rk + pk];
			}
			int j1 = 0;
			int j2 = 0;
			if (rk >= -1) {
				j1 = 1;
			} else {
				j1 = -rk;
			}

			if ((r - 1) <= pk) {
				j2 = k-1;
			} else {
				j2 = m_degree-r;
			}
			for (int j = j1; j <= j2; j ++) {
				a[width * s2 + j] = (a[width * s1 + j] - a[width * s1 + j - 1]) / ndu[width * (pk + 1) + rk + j];
				d += a[width * s2 + j] * ndu[width * (rk + j) + pk];
			}
			if (r <= pk) {
				a[width * s2 + k] = -a[width * s1 + k - 1] / ndu[width * (pk + 1) + r];
				d += a[width * s2 + k] * ndu[width * r + pk];
			}
			derivativesOut[width * k + r] = d;
			dSwap(s1, s2);
		}
	}

	int s = m_degree;
	for (int k = 1; k <= m_degree; k ++) {
		for (int j = 0; j <= m_degree; j ++) {
			derivativesOut[width * k + j] *= s;
		}
		s *= (m_degree - k);
	}
}

dBigVector dBezierSpline::CurvePoint (dFloat64 u, int span) const
{
	dBigVector point (0.0f);
	dFloat64* const basicFunctions = dAlloca(dFloat64, m_knotsCount + 32);
	BasicsFunctions (u, span, basicFunctions);
	for (int i = 0; i <= m_degree; i ++) {
		point += m_controlPoints[span - m_degree + i].Scale (basicFunctions[i]);
	}
	return point;
}

dBigVector dBezierSpline::CurvePoint (dFloat64 u) const
{
	u = dClamp (u, dFloat64 (0.0f), dFloat64 (1.0f));
	int span = GetSpan(u);
	return CurvePoint (u, span);
}

dBigVector dBezierSpline::CurveDerivative (dFloat64 u, int index) const
{
	u = dClamp (u, dFloat64 (0.0f), dFloat64 (1.0f));
	dAssert (index <= m_degree);
	
	dFloat64* const basicsFuncDerivatives = dAlloca(dFloat64, m_knotsCount + 32);
	int span = GetSpan(u);
	BasicsFunctionsDerivatives (u, span, basicsFuncDerivatives);

	const int with = m_degree + 1;
	dBigVector point (0.0f);
	for (int i = 0; i <= m_degree; i ++) {
		point += m_controlPoints[span - m_degree + i].Scale (basicsFuncDerivatives[with * index + i]);
	}
	return point;
}

int dBezierSpline::CurveAllDerivatives (dFloat64 u, dBigVector* const derivatives) const
{
	u = dMod (u, dFloat64(1.0f));
	dFloat64* const basicsFuncDerivatives = dAlloca(dFloat64, m_knotsCount + 32);
	int span = GetSpan(u);
	BasicsFunctionsDerivatives (u, span, basicsFuncDerivatives);

	const int with = m_degree + 1;
	dBigVector point (0.0f);
	for (int j = 0; j <= m_degree; j ++) {
		dBigVector ck (0.0f);
		for (int i = 0; i <= m_degree; i ++) {
			ck += m_controlPoints[span - m_degree + i].Scale (basicsFuncDerivatives[with * j + i]);
		}
		derivatives[j] = ck;
	}

	return m_degree + 1;
}

void dBezierSpline::GlobalCubicInterpolation (int count, const dBigVector* const points, const dBigVector& firstTangent, const dBigVector& lastTangent)
{
	CreateCubicKnotVector (count, points);
	CreateCubicControlPoints (count, points, firstTangent, lastTangent);
}

void dBezierSpline::CreateCubicKnotVector(int count, const dBigVector* const points)
{
	dFloat64 d = dFloat64(0.0f);
	dAssert (count >= 2);

	dFloat64* const u = dAlloca(dFloat64, m_knotsCount + 32);
	u[0] = dFloat64(0.0f);
	for (int i = 1; i < count; i ++) {
		dBigVector step (points[i] - points[i - 1]);
		dFloat64 len = dSqrt (step.DotProduct3(step));
		u[i] = dSqrt (len); 
		d += u[i];
	}

	for (int i = 1; i < count; i ++) {
		u[i] = u[i-1] + u[i] / d;
	}
	u[0] = dFloat64 (0.0f);
	u[count - 1] = dFloat64(1.0f);

	m_degree = 3;
	m_knotsCount = count + 2 * m_degree;

	for (int i = 0; i < (m_degree + 1); i ++) {
		m_knotVector[i] = dFloat64(0.0f);
		m_knotVector[i + m_knotsCount - m_degree - 1] = dFloat64(1.0f);
	}

	for (int i = 1; i < (count - 1); i ++) {
		dFloat64 acc = dFloat64 (0.0f);
		for (int j = 0; j < m_degree; j ++) {
			acc += u[j + i - 1];
		}
		m_knotVector[m_degree + i] = acc / dFloat64 (3.0f);
	}
}

void dBezierSpline::CreateCubicControlPoints(int count, const dBigVector* const points, const dBigVector& firstTangent, const dBigVector& lastTangent)
{
	dFloat64 abc[4];
	if ((m_knotsCount - 2 * (m_degree - 1)) != m_controlPointsCount) {
		m_controlPointsCount = m_knotsCount - 2 * (m_degree - 1);
	}

	m_controlPoints[0] = points[0];
	m_controlPoints[m_controlPointsCount - 1] = points[count - 1];

	m_controlPoints[1] = m_controlPoints[0] + firstTangent.Scale (m_knotVector[m_degree + 1] / 3.0f);
	m_controlPoints[m_controlPointsCount - 2] = m_controlPoints[m_controlPointsCount - 1] - lastTangent.Scale ((1.0f - m_knotVector[m_knotsCount - m_degree - 2]) / 3.0f);
	if (count == 3) {
		BasicsFunctions (m_knotVector[m_degree + 1], m_degree + 1, abc);
		m_controlPoints[2]  = points[1] - m_controlPoints[1].Scale (abc[0]) - m_controlPoints[3].Scale (abc[2]);
		m_controlPoints[2] = m_controlPoints[2].Scale (1.0f / abc[1]);
	} else {
		dFloat64* const dd = dAlloca(dFloat64, m_knotsCount + 32);
		BasicsFunctions (m_knotVector[m_degree + 1], m_degree + 1, abc);
		dFloat64 den = abc[1];
		m_controlPoints[2]  = (points[1] - m_controlPoints[1].Scale (abc[0])).Scale (1.0f / den);
		for (int i = 3; i < (count - 1); i ++) {
			dd[i + 1] = abc[2] / den;
			BasicsFunctions (m_knotVector[i + 2], i + 2, abc);
			den = abc[1] - abc[0] * dd[i + 1];
			m_controlPoints[i]  = (points[i - 1] - m_controlPoints[i - 1].Scale (abc[0])).Scale (1.0f / den);
		}

		dd[count] = abc[2] / den;
		BasicsFunctions (m_knotVector[count + 1], count + 1, abc);
		den = abc[1] - abc[0] * dd[count];
		m_controlPoints[count - 1] = (points[count - 2] - m_controlPoints[count].Scale (abc[2]) - m_controlPoints[count - 2].Scale (abc[0])).Scale (1.0f / den);

		for (int i = count - 2; i >= 2; i --) {
			m_controlPoints[i] -= m_controlPoints[i + 1].Scale (dd[i + 2]);
		}
	}
}

dFloat64 dBezierSpline::CalculateLength (dFloat64 tol) const
{
	dBigVector stackPool[32][3];
	int stack = 0;

	dFloat64 length = 0.0f;
	dFloat64 tol2 = tol * tol;
	dFloat64 u0 = m_knotVector[m_degree];
	dBigVector p0 (CurvePoint (u0));

	for (int i = m_degree; i < (m_knotsCount - m_degree - 1); i ++) {
		dFloat64 u1 = m_knotVector[i + 1];
		dBigVector p1 (CurvePoint (u1));
		stackPool[stack][0] = p0;
		stackPool[stack][1] = p1;
		stackPool[stack][2] = dBigVector (u0, u1, dFloat64(0.0f), dFloat64(0.0f));
		stack ++;
		while (stack) {
			stack --;
			dBigVector q0 (stackPool[stack][0]);
			dBigVector q1 (stackPool[stack][1]);
			dFloat64 t0 = stackPool[stack][2][0];
			dFloat64 t1 = stackPool[stack][2][1];
			dFloat64 t01 = (t1 + t0) * 0.5f;

			dBigVector p01 ((q1 + q0).Scale (0.5f));
			dBigVector q01 (CurvePoint (t01));
			dBigVector err (q01 - p01);

			dFloat64 err2 = err.DotProduct3(err);
			if (err2 < tol2) {
				dBigVector step (q1 - q0);
				length += dSqrt (step.DotProduct3(step));
			} else {
				stackPool[stack][0] = q01;
				stackPool[stack][1] = q1;
				stackPool[stack][2] = dBigVector (t01, t1, dFloat64(0.0f), dFloat64(0.0f));
				stack ++;

				stackPool[stack][0] = q0;
				stackPool[stack][1] = q01;
				stackPool[stack][2] = dBigVector (t0, t01, dFloat64(0.0f), dFloat64(0.0f));
				stack ++;
			}
		}
		u0 = u1;
		p0 = p1;
	}

	return length;
}

void dBezierSpline::InsertKnot (dFloat64 u)
{
	const int k = GetSpan(u);
	int multiplicity = 0;
	for (int i = 0; i < m_degree; i ++) {
		multiplicity += (dAbs (m_knotVector[k + i + 1] - u) < dFloat64 (1.0e-5f)) ? 1 : 0;
	}
	if (multiplicity == m_degree) {
		return;
	}

	for (int i = m_knotsCount; i > (k + 1); i --) {
		m_knotVector[i] = m_knotVector[i - 1];
	}
	m_knotVector[k + 1] = u;

	dBigVector Rw[16];
	for (int i = 0; i <= m_degree; i ++) {
		Rw[i] = m_controlPoints[k - m_degree + i];
	}

	const int m = k - m_degree + 1;
	dAssert(m >= 0);
	dAssert((k + 1 - 1 - 0) >= 0);
	dAssert((m_degree - 1 - 0) >= 0);

	for (int i = 0; i <= (m_degree - 1); i ++) {
		dFloat64 alpha = (u  - m_knotVector[m + i]) / (m_knotVector[i + k + 1] - m_knotVector[m + i]);
		Rw[i] = Rw[i + 1].Scale (alpha) + Rw[i].Scale (dFloat64 (1.0f) - alpha);
	}

	for (int i = m_controlPointsCount; i > k; i--) {
		m_controlPoints[i] = m_controlPoints[i - 1];
	}
	m_controlPoints[m] = Rw[0];
	m_controlPoints[k + 1 - 1 - 0] = Rw[m_degree - 1 - 0];
	for (int i = m + 1; i < k; i++) {
		dAssert((i - m) >= 0);
		m_controlPoints[i] = Rw[i - m];
	}

	m_knotsCount ++;
	m_controlPointsCount ++;
}

bool dBezierSpline::RemoveKnot (dFloat64 u, dFloat64 tol)
{
	int r = GetSpan(u) + 1;
	dAssert (m_knotVector[r - 1] < u);
	if (dAbs (m_knotVector[r] - u) > 1.0e-5f) {
		return false;
	}

	int s = 1;
	int last = r - s;
	int first = r - m_degree;
	int ord = m_degree + 1;
	dBigVector temp[16];

	bool removableFlag = false;
	int t = 0;
	for ( ; t < m_degree; t ++) {
		int off = first - 1;
		temp[0] = m_controlPoints[off];
		temp[last + 1 - off] = m_controlPoints[last + 1];
		int i = first;
		int j = last;
		int ii = 1;
		int jj = last - off;

		while ((j - i) > t) {
			dFloat64 alpha_i = (u - m_knotVector[i]) / (m_knotVector[i + ord + t] - m_knotVector[i]);
			dFloat64 alpha_j = (u - m_knotVector[j - t]) / (m_knotVector[j + ord] - m_knotVector[j - t]);
			temp[ii] = (m_controlPoints[i] - temp[ii - 1].Scale (dFloat64 (1.0f) - alpha_i)).Scale (dFloat64 (1.0f) / alpha_i);
			temp[jj] = (m_controlPoints[j] - temp[jj + 1].Scale (alpha_j)).Scale (dFloat64 (1.0f) / (dFloat64 (1.0f) - alpha_j));
			i ++;
			j --;
			ii ++;
			jj --;
		}
		if ((j - i) < t) {
			dBigVector diff (temp[ii - 1] - temp[jj + 1]);
			removableFlag = diff.DotProduct3(diff) < (tol * tol);
		} else {
			dFloat64 alpha_i = (u - m_knotVector[i]) / (m_knotVector[i + ord + t] - m_knotVector[i]);
			dBigVector p (temp[ii + t + 1].Scale (alpha_i) + temp[ii - 1].Scale (dFloat64 (1.0f) - alpha_i));
			dBigVector diff (m_controlPoints[i] - p);
			removableFlag = diff.DotProduct3(diff) < (tol * tol);
		}
		if (!removableFlag) {
			break;
		}

		i = first;
		j = last;
		while ((j - 1) > t) {
			m_controlPoints[i] = temp[i - off];
			m_controlPoints[j] = temp[j - off];
			i ++;
			j --;
		}
		first --;
		last ++;
	}

	if (t) {
		for (int k = r + t; k < m_knotsCount; k ++) {
			m_knotVector[k - t] = m_knotVector[k];
		}

		int fOut = (2 * r - s - m_degree) / 2;
		int j = fOut;
		int i = j;
		for (int k = 1; k < t; k ++) {
			if ((k % 2) == 1) {
				i ++;
			} else {
				j = j - 1;
			}
		}
		for (int k = i + 1; k < m_controlPointsCount; k ++) {
			m_controlPoints[j] = m_controlPoints[k];
			j ++;
		}

		m_knotsCount -= t;
		m_controlPointsCount -= t;
	}


	return removableFlag;
}

dFloat64 dBezierSpline::FindClosestKnot(dBigVector& closestPoint, const dBigVector& point, int subdivitionSteps) const
{
	int startSpan = m_degree;
	dFloat64 bestU = 0.0f;
	dFloat64 distance2 = 1.0e10f;
	dBigVector closestControlPoint(m_controlPoints[0]);
	subdivitionSteps = dMax(subdivitionSteps, 1);
	dFloat64 scale = 1.0f / subdivitionSteps;
	for (int span = m_degree; span < (m_knotsCount - m_degree - 1); span++) {
		dFloat64 param = 0.0f;
		for (int i = 0; i < subdivitionSteps; i++) {
			dFloat64 u = m_knotVector[span] + (m_knotVector[span + 1] - m_knotVector[span]) * param;
			param += scale;
			dBigVector p(CurvePoint(u, span));
			dBigVector dp(p - point);
			dFloat64 dist2 = dp.DotProduct3(dp);
			if (dist2 < distance2) {
				bestU = u;
				startSpan = span;
				distance2 = dist2;
				closestControlPoint = p;
			}
		}
	}

	dBigVector p(CurvePoint(0.999f));
	dBigVector dp(p - point);
	dFloat64 dist2 = dp.DotProduct3(dp);
	if (dist2 < distance2) {
		bestU = dFloat64(0.999f);
		startSpan = m_knotsCount - m_degree - 2;
		closestControlPoint = p;
	}

	dBigVector derivatives[32];
	dFloat64 u0 = bestU;

	bool stop = false;
	for (int i = 0; (i < 20) && !stop; i++) {
		CurveAllDerivatives(u0, derivatives);

		dBigVector dist(closestControlPoint - point);
		dFloat64 num = derivatives[1].DotProduct3(dist);
		dFloat64 den = derivatives[2].DotProduct3(dist) + derivatives[1].DotProduct3(derivatives[1]);
		dFloat64 u1 = dClamp(u0 - num / den, dFloat64(0.0), dFloat64(1.0));
		if (u1 < m_knotVector[startSpan]) {
			startSpan--;
			dAssert(startSpan >= 0);
		} else if (u1 >= m_knotVector[startSpan + 1]) {
			startSpan++;
			dAssert(startSpan < (m_knotsCount - m_degree));
		}

		dAssert(startSpan >= m_degree);
		dAssert(startSpan <= (m_knotsCount - m_degree - 1));
		closestControlPoint = CurvePoint(u1, startSpan);

		stop |= (dAbs(u1 - u0) < 1.0e-10) || (num * num < ((dist.DotProduct3(dist)) * (derivatives[1].DotProduct3(derivatives[1])) * 1.0e-10));
		u0 = u1;
	}

	closestPoint = closestControlPoint;
	return u0;
}
