Blob Blame Raw
#include "ext/NotSimmetricBezierPotential.h"
#include "tstroke.h"

#include <tmathutil.h>
#include <tcurves.h>

#include <algorithm>

using namespace std;

using namespace std;
using namespace ToonzExt;
//-----------------------------------------------------------------------------

namespace
{
typedef unary_function<double, double> unary_functionDD;

//---------------------------------------------------------------------------

class myBlendFunc : unary_functionDD
{
	//TCubic  c;
	TQuadratic curve;

public:
	myBlendFunc(double t = 0.0)
	{
		curve.setP0(TPointD(0.0, 1.0));
		curve.setP1(TPointD(0.5 * (1.0 - t), 1.0));
		curve.setP2(TPointD(1.0, 0.0));
	}

	result_type operator()(argument_type x)
	{
		result_type out = 0.0;
		x = fabs(x);
		if (x >= 1.0)
			return 0.0;
		out = curve.getPoint(x).y;
		return out;
	}
};
}

//-----------------------------------------------------------------------------

ToonzExt::NotSimmetricBezierPotential::~NotSimmetricBezierPotential()
{
}

//-----------------------------------------------------------------------------

void ToonzExt::NotSimmetricBezierPotential::setParameters_(const TStroke *ref,
														   double w,
														   double al)
{
	assert(ref);
	ref_ = ref;
	par_ = w;
	actionLength_ = al;

	strokeLength_ = ref->getLength();
	lenghtAtParam_ = ref->getLength(par_);

	// lunghezza dal pto di click all'inizio della curva
	leftFactor_ = min(lenghtAtParam_,
					  actionLength_ * 0.5); //lenghtAtParam_ / strokeLength_;

	// lunghezza dal pto di click alla fine
	rightFactor_ = min(strokeLength_ - lenghtAtParam_,
					   actionLength_ * 0.5);
}

//-----------------------------------------------------------------------------

double
ToonzExt::NotSimmetricBezierPotential::value_(double value2test) const
{
	assert(0.0 <= value2test &&
		   value2test <= 1.0);
	return this->compute_value(value2test);
}

//-----------------------------------------------------------------------------

// normalization of parameter in range interval
double ToonzExt::NotSimmetricBezierPotential::compute_shape(double value2test) const
{
	double x = ref_->getLength(value2test);
	double shape = this->actionLength_ * 0.5;
	if (isAlmostZero(shape))
		shape = 1.0;
	x = (x - lenghtAtParam_) / shape;
	return x;
}

//-----------------------------------------------------------------------------

double
ToonzExt::NotSimmetricBezierPotential::compute_value(double value2test) const
{
	myBlendFunc me;

	// on extremes use
	//     2
	//  1-x
	//

	// when is near to extreme uses a mix notation
	double x = 0.0;
	double res = 0.0;

	// lenght  at parameter
	x = ref_->getLength(value2test);

	const double tolerance = 0.0; // need to be pixel based

	// if is an extreme
	if (max(lenghtAtParam_, 0.0) < tolerance ||
		max(strokeLength_ - lenghtAtParam_, 0.0) < tolerance) {
		double tmp_al = actionLength_ * 0.5;

		// compute correct parameter considering offset
		// try to have a square curve like shape
		//
		//       2
		//  m = x
		//
		if (leftFactor_ <= tolerance)
			x = 1.0 - x / tmp_al;
		else
			x = (x - (strokeLength_ - tmp_al)) / tmp_al;

		if (x < 0.0)
			return 0.0;
		assert(0.0 <= x &&
			   x <= 1.0 + TConsts::epsilon);
		x = std::min(x, 1.0); // just to avoid problem in production code
		res = sq(x);
	} else // when is not an extreme
	{
		double lenght_at_value2test = ref_->getLength(value2test);

		const double min_level = 0.01;
		// if check a parameter over click point
		if (lenght_at_value2test >= lenghtAtParam_) {
			// check if extreme can be moved from this parameter configuration
			double tmp_x = this->compute_shape(1.0);
			double tmp_res = me(tmp_x);
			if (tmp_res > min_level) {
				// please note that in this case
				//  lenghtAtParam_ + rightFactor_ == strokeLength_
				// (by ctor).
				if (rightFactor_ != 0.0)
					x = (lenght_at_value2test - lenghtAtParam_) / rightFactor_;
				else
					x = 0.0;

				assert(0.0 - TConsts::epsilon <= x &&
					   x <= 1.0 + TConsts::epsilon);
				if (isAlmostZero(x))
					x = 0.0;
				if (areAlmostEqual(x, 1.0))
					x = 1.0;

				double how_many_of_shape = (strokeLength_ - lenghtAtParam_) / (actionLength_ * 0.5);
				assert(0.0 <= how_many_of_shape &&
					   how_many_of_shape <= 1.0);

				myBlendFunc bf(how_many_of_shape);

				return bf(x);
			}
		} else {
			// leftFactor_
			double tmp_x = this->compute_shape(0.0);
			double tmp_res = me(tmp_x);
			if (tmp_res > min_level) {
				double x = lenght_at_value2test / leftFactor_;
				assert(0.0 <= x &&
					   x <= 1.0);

				// then movement use another shape
				double diff = x - 1.0;

				double how_many_of_shape = lenghtAtParam_ / (actionLength_ * 0.5);
				assert(0.0 <= how_many_of_shape &&
					   how_many_of_shape <= 1.0);

				myBlendFunc bf(how_many_of_shape);
				return bf(diff);
			}
		}

		// default behaviour is an exp
		x = this->compute_shape(value2test);
		res = me(x);
	}
	return res;
}

//-----------------------------------------------------------------------------

ToonzExt::Potential *
ToonzExt::NotSimmetricBezierPotential::clone()
{
	return new NotSimmetricBezierPotential;
}

//-----------------------------------------------------------------------------
//  End Of File
//-----------------------------------------------------------------------------