/*
								+----------------------------------+
								|                                  |
								|     ***   Bezier cubic   ***     |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|          BezierCubic.h           |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __BEZIER_CUBIC_SPLINE_INCLUDED
#define __BEZIER_CUBIC_SPLINE_INCLUDED

/**
 *	@file dev/BezierCubic.h
 *	@date 2008
 *	@author -tHE SWINe-
 *	@brief Bezier cubic
 *
 *	@date 2008-08-21
 *
 *	CBezierCubicSpline 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 CBezierCubicSpline
 *		- simple Bezier cubic spline template, parametrized by point type
 */
template <class CPointClass = Vector3f>
class CBezierCubicSpline : public CSpline<CPointClass> {
public:
	CBezierCubicSpline()
		:CSpline<CPointClass>()
	{}

	/*
	 *	CBezierCubicSpline::CBezierCubicSpline(const std::vector<CPointClass> &r_point_list)
	 *		- default constructor
	 *		- r_point_list is list of curve points, it's contents are duplicated
	 *		- note bezier cubic spline arc is constructed between every fourth point,
	 *		  while two points between serve as tangents
	 */
	CBezierCubicSpline(const std::vector<CPointClass> &r_point_list)
		:CSpline<CPointClass>(r_point_list)
	{}

	/*
	 *	virtual int CBezierCubicSpline::n_Arc_Num() const
	 *		- return number of spline arcs
	 */
	virtual int n_Arc_Num() const
	{
		return (m_point_list.size() - 1) / 3;
	}

	/*
	 *	virtual CPointClass CBezierCubicSpline::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());

		n_arc *= 3;
		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 polygon points

		float f_omt = 1 - f_t;
		float f_omt2 = f_omt * f_omt;
		float f_omt3 = f_omt2 * f_omt;
		float f_t2 = f_t * f_t;
		float f_t3 = f_t2 * f_t;
		// calculate 1-t, (1-t)^2, (1-t)^3, t^2, t^3

		float f_weight0 = f_omt3;
		float f_weight1 = 3 * f_t * f_omt2;
		float f_weight2 = 3 * f_t2 * f_omt;
		float f_weight3 = f_t3;
		// calculate Bernstein polynoms

		return v_pt0 * f_weight0 + v_pt1 * f_weight1 +
			v_pt2 * f_weight2 + v_pt3 * f_weight3;
		// interpolate
	}

	/*
	 *	virtual CPointClass CBezierCubicSpline::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());

		n_arc *= 3;
		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 polygon points

		float f_omt = 1 - f_t;
		float f_omt2 = f_omt * f_omt;
		float f_omt3 = f_omt2 * f_omt;
		float f_t2 = f_t * f_t;
		float f_t3 = f_t2 * f_t;
		// calculate 1-t, (1-t)^2, (1-t)^3, t^2, t^3

		float f_t_ = 1;
		float f_omt_ = -f_t_;
		float f_omt2_ = f_omt * f_omt_ + f_omt_ * f_omt;
		float f_omt3_ = f_omt2 * f_omt_ + f_omt2_ * f_omt;
		float f_t2_ = f_t * f_t_ + f_t_ * f_t;
		float f_t3_ = f_t2 * f_t_ + f_t2_ * f_t;
		// calculate derivatives of 1-t, (1-t)^2, (1-t)^3, t^2, t^3

		float f_weight0 = f_omt3_;
		float f_weight1 = 3 * (f_t_ * f_omt2 + f_t * f_omt2_);
		float f_weight2 = 3 * (f_t2_ * f_omt + f_t2 * f_omt_);
		float f_weight3 = f_t3_;
		// calculate derivatives of Bernstein polynoms

		return v_pt0 * f_weight0 + v_pt1 * f_weight1 +
			v_pt2 * f_weight2 + v_pt3 * f_weight3;
		// interpolate
	}
};

#endif // __BEZIER_CUBIC_SPLINE_INCLUDED
