// Curvefit.cpp: implementation of the CBezierfit class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <MATH.H>
#include "Curvefit.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

static double DistanceError(const double *x, const double *y, const double *Rawdata, int n);
static void Bezier(double u, const double *a, const double *b, 
			double x4, double y4, double &z, double &s);

void CalcBezier(const double *Rawdata, int n, double *Control)
{
	double x[4];
	double y[4];
	double e1,e2, e3;
	int		Retry;
	double x1a,x2a,y1a,y2a;

	x[0] = Rawdata[0];
	y[0] = Rawdata[1];

	x[1] = Rawdata[2];
	y[1] = Rawdata[3];

	x[2] = Rawdata[n-4];
	y[2] = Rawdata[n-3];

	x[3] = Rawdata[n-2];
	y[3] = Rawdata[n-1];

	// seed with linear interpolation...
	x[1] += x[1] - x[0];
	y[1] += y[1] - y[0];
	x[2] += x[2] - x[3];
	y[2] += y[2] - y[3];

	e1 = DistanceError(x, y, Rawdata, n);
	for (Retry = 1; Retry <= 2; Retry++)
	{
//		TRACE("Retry %d\n", Retry);
//		TRACE("      x1       y2       x2       y2    error\n");
		e3 = 0.5;
		x1a = x[1];
		while (fabs(e3) >= 0.01)
		{
			x[1] += (x[1] - x[0])*e3;
			e2 = DistanceError(x, y, Rawdata, n);
			if (e2 == e1)
				break;
			if (e2 > e1)
			{
				x[1] = x1a;
				e3 /=-3;
			}
			else
			{
				e1 = e2;
				x1a = x[1];
			}
		}

		e3 = 0.5;
		y1a = y[1];
		while (fabs(e3) >= 0.01)
		{
			y[1] += (y[1] - y[0])*e3;
			e2 = DistanceError(x, y, Rawdata, n);
			if (e2 == e1)
				break;
			if (e2 > e1)
			{
				y[1] = y1a;
				e3 /=-3;
			}
			else
			{
				e1 = e2;
				y1a = y[1];
			}
		}

		e3 = 0.5;
		x2a = x[2];
		while (fabs(e3) >= 0.01)
		{
			x[2] += (x[2] - x[3])*e3;
			e2 = DistanceError(x, y, Rawdata, n);
			if (e2 == e1)
				break;
			if (e2 > e1)
			{
				x[2] = x2a;
				e3 /=-3;
			}
			else
			{
				e1 = e2;
				x2a = x[2];
			}
		}

		e3 = 0.5;
		y2a = y[2];
		while (fabs(e3) >= 0.01)
		{
			y[2] += (y[2] - y[3])*e3;
			e2 = DistanceError(x, y, Rawdata, n);
			if (e2 == e1)
				break;
			if (e2 > e1)
			{
				y[2] = y2a;
				e3 /=-3;
			}
			else
			{
				e1 = e2;
				y2a = y[2];
			}
		}
	} // for

	Control[0] = x[1];
	Control[1] = y[1];
	Control[2] = x[2];
	Control[3] = y[2];
}

double DistanceError(const double *x, const double *y, 
					 const double *Rawdata, int n)
{
	int i;
	double a[4];
	double b[4];
	double u, u1, u2;
	double z, z1, z2,s,s1;
	double temp;
	double totalerror;
	double stepsize;
	double x4, y4;

	totalerror = 0;
	a[3] = (x[3]-x[0]+3*(x[1]-x[2]))/8;
	b[3] = (y[3]-y[0]+3*(y[1]-y[2]))/8;
	a[2] = (x[3]+x[0]-x[1]-x[2])*3/8;
	b[2] = (y[3]+y[0]-y[1]-y[2])*3/8;
	a[1] = (x[3]-x[0])/2 -a[3];
	b[1] = (y[3]-y[0])/2 -b[3];
	a[0] = (x[3]+x[0])/2 -a[2];
	b[0] = (y[3]+y[0])/2 -b[2];

	stepsize = 2.0/(n);
	for (i = 2; i <= n-3; i+=2)
	{
		x4 = Rawdata[i];
		y4 = Rawdata[i+1];
		for (u = -1; u <= 1.01; u += stepsize)
		{
			Bezier(u, a, b, x4, y4, z, s);
			if (s == 0)
			{
				u1 = u;
				z1 = z;
				s1 = s;
				break;
			}
			if (u == -1)
			{
				u1 = u;
				z1 = z;
				s1 = s;
			} 
			if (s < s1)
			{
				u1 = u;
				z1 = z;
				s1 = s;
			}
		}
		if (s1 != 0)
		{
			u = u1 + stepsize;
			if (u > 1)
				u = 1 - stepsize;
			Bezier(u, a, b, x4, y4, z, s);
			while (s != 0 && z != 0)
			{
				u2 = u;
				z2 = z;
				temp = z2-z1;
				if (temp != 0)
					u = (z2 * u1-z1*u2)/temp;
				else
					u = (u1 + u2)/2;

				if (u > 1)
					u = 1;
				else if (u < -1)
					u = -1;
				if (fabs(u-u2) < 0.001)
					break;
				u1 = u2;
				z1 = z2;
				Bezier(u, a, b, x4, y4, z, s);
			}
		}
		totalerror += s;
	}
//	TRACE("%8.3lf %8.3lf %8.3lf %8.3lf %8.3lf\n", x[1], y[1], x[2], y[2], totalerror);
	return totalerror;
}

void Bezier(double u, const double *a, const double *b, 
			double x4, double y4, double &z, double &s)
{
	double x, y;
	double dx4,dy4;
	double dx,dy;

	x = a[0] + u*(a[1] + u*(a[2]+u*a[3]));
	y = b[0] + u*(b[1] + u*(b[2]+u*b[3]));
	dx4 = x-x4; dy4 = y-y4;
	dx = a[1] + u * (a[2] + a[2] + u*3*a[3]);
	dy = b[1] + u * (b[2] + b[2] + u*3*b[3]);
	z = dx * dx4 + dy*dy4;
	s = dx4*dx4 + dy4*dy4;
}