/*
								+----------------------------------+
								|                                  |
								|  ***   Catmull-Rom spline   ***  |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|           CatmullRom.h           |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __CATMULL_ROM_SPLINE_INCLUDED
#define __CATMULL_ROM_SPLINE_INCLUDED

/**
 *	@file dev/CatmullRom.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief Catmull-Rom spline
 *
 *	@date 2009-01-21
 *
 *	CCatmullRomSpline is now template parametrized by point type, default is Vector3f
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

#include "Spline.h"

/*
 *	class CCatmullRomSpline
 *		- simple Ctamull - Rom spline template, parametrized by point type
 */
template <class CPointClass = Vector3f>
class CCatmullRomSpline : public CSpline<CPointClass> {
public:
	CCatmullRomSpline()
		:CSpline<CPointClass>()
	{}

	/*
	 *	CCatmullRomSpline::CCatmullRomSpline(const std::vector<CPointClass> &r_point_list)
	 *		- default constructor
	 *		- r_point_list is list of curve points, it's not copied, only referenced
	 *		  and must remain unchanged (arc lengths precalculated in constructor)
	 *		- note catmull-rom splines have segments from second to last but one point
	 *		  so it may be useful to duplicate first and last points
	 */
	CCatmullRomSpline(const std::vector<CPointClass> &r_point_list)
		:CSpline<CPointClass>(r_point_list)
	{
		_ASSERTE(n_Arc_Num() >= 0);
	}

	/*
	 *	virtual int CCatmullRomSpline::n_Arc_Num() const
	 *		- return number of spline arcs
	 */
	virtual int n_Arc_Num() const
	{
		return (m_point_list.size() > 3)? m_point_list.size() - 3 : 0;
	}

	/*
	 *	virtual CPointClass CCatmullRomSpline::v_Arc_Position(float f_t, int n_arc) const
	 *		- returns point at position f_t along curve arc n_arc
	 */
	virtual CPointClass v_Arc_Position(float f_t, int n_arc) const
	{
		_ASSERTE(n_arc >= 0 && n_arc < n_Arc_Num());

		CPointClass v_pt0 = m_point_list[n_arc];
		CPointClass v_pt1 = m_point_list[n_arc + 1];
		CPointClass v_pt2 = m_point_list[n_arc + 2];
		CPointClass v_pt3 = m_point_list[n_arc + 3];
		// get arc points + neighbor points

		float f_t2 = f_t * f_t;
		float f_t3 = f_t2 * f_t;
		// calculate t^2, t^3

		float f_weight0 = -f_t3 + 2 * f_t2 - f_t;
		float f_weight1 = 3 * f_t3 - 5 * f_t2 + 2;
		float f_weight2 = -3 * f_t3 + 4 * f_t2 + f_t;
		float f_weight3 = f_t3 - f_t2;
		// calculate polynoms

		return (v_pt0 * f_weight0 + v_pt1 * f_weight1 +
			v_pt2 * f_weight2 + v_pt3 * f_weight3) * .5f;
		// interpolate
	}

	/*
	 *	virtual CPointClass CCatmullRomSpline::v_Arc_Derivative(float f_t, int n_arc) const
	 *		- returns tangent at position f_t along curve arc n_arc
	 */
	virtual CPointClass v_Arc_Derivative(float f_t, int n_arc) const
	{
		_ASSERTE(n_arc >= 0 && n_arc < n_Arc_Num());

		CPointClass v_pt0 = m_point_list[n_arc];
		CPointClass v_pt1 = m_point_list[n_arc + 1];
		CPointClass v_pt2 = m_point_list[n_arc + 2];
		CPointClass v_pt3 = m_point_list[n_arc + 3];
		// get arc points + neighbor points

		float f_t2 = f_t * f_t;
		float f_t3 = f_t2 * f_t;
		// calculate t^2, t^3

		float f_weight0 = -3 * f_t2 + 2 * 2 * f_t - 1;
		float f_weight1 = 3 * 3 * f_t2 - 5 * 2 * f_t + 0;
		float f_weight2 = -3 * 3 * f_t2 + 4 * 2 * f_t + 1;
		float f_weight3 = 3 * f_t2 - 2 * f_t;
		// calculate polynom derivatives

		return (v_pt0 * f_weight0 + v_pt1 * f_weight1 +
			v_pt2 * f_weight2 + v_pt3 * f_weight3) * .5f;
		// interpolate
	}
};

#endif // __CATMULL_ROM_SPLINE_INCLUDED
