/***************************************************************************
Interpolation.cpp - Interpolation types
-------------------
begin : Sat Feb 03 2001
copyright : (C) 2001 by Thomas Eschenbacher
email : Thomas Eschenbacher <thomas.eschenbacher@gmx.de>
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "klocale.h"
#include "Curve.h"
#include "Interpolation.h"
//***************************************************************************
//***************************************************************************
interpolation_t &operator ++(interpolation_t &i)
{
return (i = (i == INTPOL_SAH) ? INTPOL_LINEAR : interpolation_t(i+1) );
}
//***************************************************************************
//***************************************************************************
void Interpolation::InterpolationMap::fill()
{
qDebug("--- InterpolationMap::fill() ---");
append(INTPOL_LINEAR, 0, "linear", "linear");
append(INTPOL_SPLINE, 1, "spline", "spline");
append(INTPOL_NPOLYNOMIAL, 2, "n-polynom", "polynom, nth degree");
append(INTPOL_POLYNOMIAL3, 3, "3-polynom", "polynom, 3rd degree");
append(INTPOL_POLYNOMIAL5, 4, "5-polynom", "polynom, 5th degree");
append(INTPOL_POLYNOMIAL7, 5, "5-polynom", "polynom, 7th degree");
append(INTPOL_SAH, 6, "sample_hold", "sample and hold");
#undef NEVER_COMPILE_THIS
#ifdef NEVER_COMPILE_THIS
#error "this could produce problems in plugins and/or libs when \
loaded before the main application is up."
i18n("linear");
i18n("spline");
i18n("n-polynom");
i18n("3-polynom");
i18n("5-polynom");
i18n("7-polynom");
i18n("polynom, nth degree");
i18n("polynom, 3rd degree");
i18n("polynom, 5th degree");
i18n("polynom, 7th degree");
i18n("sample and hold");
#endif
}
//***************************************************************************
//***************************************************************************
// static initializer
Interpolation::InterpolationMap Interpolation::m_interpolation_map;
//***************************************************************************
Interpolation::Interpolation(interpolation_t type)
:m_curve(), m_x(), m_y(), m_der(), m_type(type)
{
}
//***************************************************************************
Interpolation::~Interpolation()
{
}
//***************************************************************************
QStringList Interpolation::descriptions(bool localized)
{
QStringList list;
unsigned int count = m_interpolation_map.count();
unsigned int i;
for (i=0; i < count; i++) {
interpolation_t index = m_interpolation_map.findFromData(i);
list.append(m_interpolation_map.description(index, localized));
}
return list;
}
//***************************************************************************
QString Interpolation::name(interpolation_t type)
{
return m_interpolation_map.name(type);
}
//***************************************************************************
unsigned int Interpolation::count()
{
return (m_curve ? m_curve->count() : 0);
}
//***************************************************************************
double Interpolation::singleInterpolation(double input)
{
Q_ASSERT(count());
if (!count()) return 0.0; // no data ?
unsigned int degree = 0;
unsigned int count = this->count();
if (input < 0.0) input = 0.0;
if (input > 1.0) input = 1.0;
switch (m_type) {
case INTPOL_LINEAR:
{
unsigned int i = 1;
while ((m_x[i] < input) && (i < count))
i++;
double dif1 = m_x[i] - m_x[i-1]; //!=0 per definition
double dif2 = input - m_x[i-1];
return (m_y[i-1] + ((m_y[i] - m_y[i-1])*dif2 / dif1));
}
case INTPOL_SPLINE:
{
double a, b, diff;
unsigned int j = 1;
while ((m_x[j] < input) && (j < count))
j++;
diff = m_x[j] - m_x[j-1];
a = (m_x[j] - input) / diff; //div should not be 0
b = (input - m_x[j-1]) / diff;
return (a*m_y[j-1] + b*m_y[j] + ((a*a*a - a)*m_der[j - 1] +
(b*b*b - b)*m_der[j])*(diff*diff) / 6);
}
case INTPOL_NPOLYNOMIAL:
{
double ny = m_y[0];
for (unsigned int j = 1; j < count; j++)
ny = ny * (input - m_x[j]) + m_y[j];
return ny;
}
case INTPOL_SAH: //Sample and hold
{
unsigned int i = 1;
while ((m_x[i] < input) && (i < count))
i++;
return m_y[i-1];
}
case INTPOL_POLYNOMIAL3:
degree = 3;
break;
case INTPOL_POLYNOMIAL5:
degree = 5;
break;
case INTPOL_POLYNOMIAL7:
degree = 7;
break;
}
if (degree && (degree <= 7)) {
// use polynom
double ny;
QMemArray<double> ax(7);
QMemArray<double> ay(7);
unsigned int i = 1;
while ((m_x[i] < input) && (i < count))
i++;
createPolynom(m_curve, ax, ay, i - 1 - degree/2, degree);
ny = ay[0];
for (unsigned int j = 1; j < degree; j++)
ny = ny * (input - ax[j]) + ay[j];
return ny;
}
return 0;
}
//***************************************************************************
bool Interpolation::prepareInterpolation(Curve *points)
{
m_curve = points;
Q_ASSERT(count());
if (!count()) return false; // no data ?
m_x.resize(count()+1);
m_y.resize(count()+1);
m_der.resize(0);
int c = 0;
for (Curve::Point *p = points->first(); p; p = points->next(p)) {
m_x[c] = p->x;
m_y[c] = p->y;
c++;
}
m_x[c] = m_y[c] = 0.0;
switch (m_type) {
case INTPOL_NPOLYNOMIAL:
createFullPolynom(m_curve, m_x, m_y);
break;
case INTPOL_SPLINE:
m_der.resize(count() + 1);
get2Derivate(m_x, m_y, m_der, count());
break;
default:
;
}
return true;
}
//***************************************************************************
QMemArray<double> Interpolation::limitedInterpolation(Curve *points,
unsigned int len)
{
QMemArray<double> y = interpolation(points, len);
for (unsigned int i = 0; i < len; i++) {
if (y[i] > 1) y[i] = 1;
if (y[i] < 0) y[i] = 0;
}
return y;
}
//***************************************************************************
QMemArray<double> Interpolation::interpolation(Curve *points,
unsigned int len)
{
Q_ASSERT(points);
Q_ASSERT(len);
if (!points) return 0;
if (!len) return 0;
Curve::Point *tmp;
unsigned int degree = 0;
QMemArray<double> y_out(len);
for (unsigned int i=0; i < len; i++) y_out[i] = 0.0;
switch (m_type) {
case INTPOL_LINEAR:
{
double x, y, lx, ly;
tmp = points->first();
if (tmp) {
lx = tmp->x;
ly = tmp->y;
for (tmp = points->next(tmp); tmp; tmp = points->next(tmp)) {
x = tmp->x;
y = tmp->y;
double dify = (y - ly);
int difx = (int) ((x - lx) * len);
int min = (int)(lx * len);
double h;
for (int i = (int)(lx * len); i < (int)(x*len); i++) {
h = (double(i - min)) / difx;
y_out[i] = ly + (h * dify);
}
lx = x;
ly = y;
}
}
break;
}
case INTPOL_SPLINE:
{
int t = 1;
unsigned int count = points->count();
double ny = 0;
QMemArray<double> der(count + 1);
QMemArray<double> x(count + 1);
QMemArray<double> y(count + 1);
for (tmp = points->first(); tmp; tmp = points->next(tmp)) {
x[t] = tmp->x;
y[t] = tmp->y;
t++;
}
get2Derivate(x, y, der, count);
int ent;
int start = (int) (x[1] * len);
for (unsigned int j = 2; j <= count; j++) {
ent = (int) (x[j] * len);
for (int i = start; i < ent; i++) {
double xin = ((double) i) / len;
double h, b, a;
h = x[j] - x[j - 1];
if (h != 0) {
a = (x[j] - xin) / h;
b = (xin - x[j - 1]) / h;
ny = (a * y[j - 1] + b * y[j] +
((a * a * a - a) * der[j - 1] +
(b * b * b - b) * der[j]) * (h * h) / 6.0);
}
y_out[i] = ny;
start = ent;
}
}
break;
}
case INTPOL_POLYNOMIAL3:
if (!degree) degree = 3;
case INTPOL_POLYNOMIAL5:
if (!degree) degree = 5;
case INTPOL_POLYNOMIAL7:
{
if (!degree) degree = 7;
unsigned int count = points->count();
double ny;
QMemArray<double> x(7);
QMemArray<double> y(7);
double ent, start;
tmp = points->first();
if (tmp) {
for (unsigned int px = 0; px < count - 1; px++) {
createPolynom (points, x, y, px - degree / 2, degree);
start = points->at(px)->x;
if (px >= count - degree / 2 + 1)
ent = 1;
else
ent = points->at(px + 1)->x;
for (int i=(int)(start*len); i<(int)(ent*len); i++) {
ny = y[0];
for (unsigned int j = 1; j < degree; j++)
ny = ny * (((double)i) / len - x[j]) + y[j];
y_out[i] = ny;
}
}
}
break;
}
case INTPOL_NPOLYNOMIAL:
{
double ny;
int count = points->count();
tmp = points->first();
if (tmp != 0) {
QMemArray<double> x(count+1);
QMemArray<double> y(count+1);
double px;
createFullPolynom(points, x, y);
for (unsigned int i = 1; i < len; i++) {
px = (double)(i) / len;
ny = y[0];
for (int j = 1; j < count; j++)
ny = ny * (px - x[j]) + y[j];
y_out[i] = ny;
}
}
break;
}
case INTPOL_SAH:
{
double lx, ly, x, y;
tmp = points->first();
if (tmp) {
lx = tmp->x;
ly = tmp->y;
for (tmp = points->next(tmp); tmp; tmp=points->next(tmp)) {
x = tmp->x;
y = tmp->y;
for (int i = (int)(lx * len); i < (int)(x*len); i++)
y_out[i] = ly;
lx = x;
ly = y;
}
}
}
}
return y_out;
}
//***************************************************************************
void Interpolation::createFullPolynom(Curve *points,
const QMemArray<double> &x, const QMemArray<double> &y)
{
Q_ASSERT(points);
Q_ASSERT(m_curve);
if (!points) return;
if (!m_curve) return;
Q_ASSERT(points->count() == m_curve->count());
if (points->count() != m_curve->count()) return;
unsigned int count = 0;
Curve::Point *tmp;
for (tmp = points->first(); (tmp); tmp = points->next(tmp)) {
x[count] = tmp->x;
y[count] = tmp->y;
count++;
}
for (unsigned int k = 0; k < count; k++)
for (unsigned int j = k; j; ) {
j--;
y[j] = (y[j] - y[j+1]) / (x[j] - x[k]);
}
}
//***************************************************************************
void Interpolation::get2Derivate(const QMemArray<double> &x,
const QMemArray<double> &y, QMemArray<double> &ab, unsigned int n)
{
Q_ASSERT(n);
if (!n) return;
unsigned int i, k;
double p, qn, sig, un;
QMemArray<double> u(n);
ab[0] = ab[1] = 0;
u[0] = u[1] = 0;
for (i = 2; i < n; i++) {
sig = (x[i] - x[i-1]) / (x[i+1] - x[i-1]);
p = sig * ab[i-1] + 2;
ab[i] = (sig-1) / p;
u[i] = (y[i+1] - y[i]) / (x[i+1] - x[i])
- (y[i] - y[i-1]) / (x[i] - x[i-1]);
u[i] = (6 * u[i] / (x[i+1] - x[i-1]) - sig * u[i-1]) / p;
}
qn = 0;
un = 0;
ab[n] = (un - qn * u[n - 1]) / (qn * ab[n - 1] + 1);
for (k = n - 1; k > 0; k--)
ab[k] = ab[k] * ab[k + 1] + u[k];
}
//***************************************************************************
void Interpolation::createPolynom(Curve *points, QMemArray<double> &x,
QMemArray<double> &y, int pos, unsigned int degree)
{
unsigned int count = 0;
if (pos < 0) {
switch (pos) {
case -3:
x[count] = -1.5;
y[count++] = points->first()->y;
pos++;
case -2:
x[count] = -1;
y[count++] = points->first()->y;
pos++;
case -1:
x[count] = -.5;
y[count++] = points->first()->y;
pos++;
}
}
Curve::Point *tmp = points->first();
for (int i = 0; i < pos; i++) tmp = points->next(tmp);
for (; (count < degree) && (tmp); tmp = points->next(tmp)) {
x[count] = tmp->x;
y[count++] = tmp->y;
}
int i = 1;
while (count < degree) {
x[count] = 1.0 + 0.5 * (i++);
y[count++] = points->last()->y;
}
// create coefficients in y[i] and x[i];
for (unsigned int k = 0; k < degree; k++)
for (int j = k - 1; j >= 0; j--)
y[j] = (y[j] - y[j + 1]) / (x[j] - x[k]);
}
//***************************************************************************
//***************************************************************************