// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdmisc.h"
#include "nel/misc/mouse_smoother.h"
namespace NLMISC
{
//*******************************************************************************************
/// build some hermite spline value, with the given points and tangents
static inline void BuildHermiteVector(const NLMISC::CVector2f &P0,
const NLMISC::CVector2f &P1,
const NLMISC::CVector2f &T0,
const NLMISC::CVector2f &T1,
NLMISC::CVector2f &dest,
float lambda
)
{
const float lambda2 = lambda * lambda;
const float lambda3 = lambda2 * lambda;
const float h1 = 2 * lambda3 - 3 * lambda2 + 1;
const float h2 = - 2 * lambda3 + 3 * lambda2;
const float h3 = lambda3 - 2 * lambda2 + lambda;
const float h4 = lambda3 - lambda2;
/// just avoid some ctor calls here...
dest.set(h1 * P0.x + h2 * P1.x + h3 * T0.x + h4 * T1.x,
h1 * P0.y + h2 * P1.y + h3 * T0.y + h4 * T1.y);
}
//*******************************************************************************************
CMouseSmoother::CMouseSmoother(double samplingPeriod /*=0.2f*/)
{
nlassert(samplingPeriod > 0);
_SamplingPeriod = samplingPeriod;
_Init = false;
}
//*******************************************************************************************
void CMouseSmoother::setSamplingPeriod(double period)
{
if (period == _SamplingPeriod) return;
reset();
nlassert(_SamplingPeriod > 0);
_SamplingPeriod = period;
}
//*******************************************************************************************
NLMISC::CVector2f CMouseSmoother::samplePos(const CVector2f &wantedPos, double date)
{
if (!_Init)
{
_Sample[0] = _Sample[1] = _Sample[2] = _Sample[3] = CSample(date, wantedPos);
_Init = true;
}
else
{
// see if enough time has elapsed since last sample
if (date - _Sample[3].Date >= _SamplingPeriod)
{
uint numSamples = (uint) floor((date - _Sample[3].Date) / _SamplingPeriod);
numSamples = std::min(numSamples, (uint) 4);
for(uint k = 0; k < numSamples; ++k)
{
// add a new sample
_Sample[0] = _Sample[1];
_Sample[1] = _Sample[2];
_Sample[2] = _Sample[3];
_Sample[3] = CSample(date, wantedPos);
}
}
else if (date == _Sample[3].Date)
{
// update cur pos
_Sample[3] = CSample(date, wantedPos);
}
}
if (_Sample[1].Pos.x == _Sample[2].Pos.x &&
_Sample[1].Pos.y == _Sample[2].Pos.y
)
{
// special case : if pointer hasn't moved, allow a discontinuity of speed
return _Sample[2].Pos;
}
double evalDate = date - 2 * _SamplingPeriod;
clamp(evalDate, _Sample[1].Date, _Sample[2].Date);
CVector2f t0;
double dt = _Sample[2].Date - _Sample[1].Date;
if (_Sample[2].Date != _Sample[0].Date)
{
t0 = (float) dt * (_Sample[2].Pos - _Sample[0].Pos) / (float) (_Sample[2].Date - _Sample[0].Date);
}
else
{
t0= NLMISC::CVector::Null;
}
CVector2f t1;
if (_Sample[3].Date != _Sample[1].Date)
{
t1 = (float) dt * (_Sample[3].Pos - _Sample[1].Pos) / (float) (_Sample[3].Date - _Sample[1].Date);
}
else
{
t1= NLMISC::CVector::Null;
}
NLMISC::CVector2f result;
if (dt == 0) return _Sample[2].Pos;
BuildHermiteVector(_Sample[1].Pos, _Sample[2].Pos, t0, t1, result, (float) ((evalDate - _Sample[1].Date) / dt));
return result;
}
//*******************************************************************************************
void CMouseSmoother::reset()
{
_Init = false;
}
} // NLMISC