Короче, вот код, разбирайтесь сами :)
Простой фильтр - интерполятор экспонентой
double SimpleFilter::doGetCurveValue(double d) const
{
// d должно быть в диапазоне [0, 1]
if(getDeadzoneX() >= d)
return 0;
if(getSaturationX() <= d)
return getSaturationY();
d = (d - getDeadzoneX()) / (getSaturationX() - getDeadzoneX());
const double curvature = getCurvature().front();
if(0.0 != curvature)
d = (exp(10.0 * curvature * d) - 1.0) / (exp(10.0 * curvature) - 1.0);
return d * (getSaturationY() - getDeadzoneY()) + getDeadzoneY();
}
Вот интерполятор кубическим сплайном:
// полноценное вычисление кубического сплайна довольно дорого,
// поэтому диапазон [0, range] разбиваем на steps интервалов и
// рассчитываем значения кривой в этих точках.
// если требуется значение между двумя предрассчитанными точками, то
// выбирается минимальное значение
// каждое значение curvature должно быть в диапазоне [0, 1]
// по умолчанию весь диапазон разбивается на 1000 участков
// после изменения saturation, deadzone, curvature, range
// необходимо вызвать функцию build для нового расчета фильтра
// функция build разбивает интервал range на steps точек и
// рассчитывает значение функци в каждой точке
void CubicFilter::build(int steps)
{
values_.clear();
const vector<double>& curvature = getCurvature();
if(curvature.size() < 4)
{
return;
}
const int n = (int)curvature.size();
const double xCurveStep = 1.0 / (n - 1);
step_ = 1.0 / steps;
vector<double> y2(n);
vector<double> u(n);
y2[0] = 0.0;
u[0] = 0.0;
for(int i = 1; i <= n - 2; ++i)
{
const double sig = 0.5;
const double p = sig * y2[ i - 1] + 2.0;
y2 = (sig - 1.0) / p;
u = (curvature[i + 1] - curvature) / xCurveStep -
(curvature - curvature[i - 1]) / xCurveStep;
u = (6.0 * u / (2.0 * xCurveStep) - sig * u[i - 1]) / p;
}
y2[n - 1] = 0.0;
for(int k = n - 2; k >= 0; --k)
{
y2[k] = y2[k] * y2[k + 1] + u[k];
}
const double cy = (getSaturationY() - getDeadzoneY());
for(double x = 0; 1.0 > x; x += step_)
values_.push_back(calcValue(y2, x) * cy + getDeadzoneY());
values_.push_back(calcValue(y2, 1.0) * cy + getDeadzoneY());
}
double CubicFilter::doGetCurveValue(double d) const
{
// d должно быть в диапазоне [0, 1]
if(getDeadzoneX() >= d)
return 0;
if(getSaturationX() <= d)
return getSaturationY();
if(values_.empty())
return 0;
const double value = values_[int(d / step_)];
if(value > getSaturationY())
return getSaturationY();
if(value < getDeadzoneY())
return getDeadzoneY();
return value;
}