1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-11-25 17:53:47 +00:00
Aquaria/BBGE/Interpolators.cpp

243 lines
6.5 KiB
C++

#include "Interpolators.h"
#include <math.h>
// usually one would expect that a bspline goes from t=0 to t=1.
// here, splines eval between these -0.5 .. +0.5.
// this way 0 is perfectly in the center (which is a nice property to have)
// but more importantly the Quad::drawGrid in its default state
// has values in -0.5..+0.5 in its initial state.
// So we follow the same here, that the spline produces values
// in -0.5..+0.5 in its initial state.
static const float TMIN = -0.5f;
static const float TMAX = 0.5f;
CosineInterpolator::CosineInterpolator()
{
}
void CosineInterpolator::clear()
{
pxy.clear();
}
void CosineInterpolator::setPoints(const Vector* p, size_t n)
{
pxy.resize(n);
for(size_t i = 0; i < n; ++i)
{
pxy[i].first = p[i].x;
pxy[i].second = p[i].y;
}
std::sort(pxy.begin(), pxy.end());
}
float CosineInterpolator::operator()(float x) const
{
if (x < pxy[0].first)
return pxy[0].second;
size_t N = pxy.size() - 1;
for (size_t i = 0; i < N; ++i)
{
// TODO: binary search
if (pxy[i + 1].first > x)
{
float xfactor = (x - pxy[i].first) / (pxy[i + 1].first - pxy[i].first);
float yfactor = (1.0f - cos(xfactor * 3.141596f)) * 0.5f;
return yfactor * pxy[i + 1].second + (1 - yfactor) * pxy[i].second;
}
}
return pxy[N].second;
}
void CosineInterpolator::interpolateRange(float *ys, const float* xs, size_t n)
{
size_t i = 0, k = 0;
for( ; k < n; ++k)
{
const float x = xs[k];
while(x < pxy[i].first)
{
++i;
if(i >= pxy.size())
goto tail;
}
ys[k] = pxy[i].second;
}
return;
tail:
const float v = pxy.back().second;
for( ; k < n; ++k)
ys[k] = v;
}
BSpline2D::BSpline2D()
: _cpx(0), _cpy(0), _degx(0), _degy(0), _tmin(0), _tmax(0)
{
}
void BSpline2D::resize(size_t cx, size_t cy, unsigned degx, unsigned degy)
{
const float tmin = TMIN;
const float tmax = TMAX;
knotsX.resize(tbsp__getNumKnots(cx, degx));
knotsY.resize(tbsp__getNumKnots(cy, degy));
tbsp::fillKnotVector<float>(&knotsX[0], cx, degx, tmin, tmax);
tbsp::fillKnotVector<float>(&knotsY[0], cy, degy, tmin, tmax);
_cpx = cx;
_cpy = cy;
_degx = degx;
_degy = degy;
_tmin = tmin;
_tmax = tmax;
}
void BSpline2D::recalc(Vector* dst, size_t xres, size_t yres, const Vector *controlpoints)
{
const unsigned maxDeg = std::max(_degx, _degy);
std::vector<Vector> tmpv;
size_t tmpn = (yres * _cpx) + maxDeg;
size_t tmpsz = tmpn * sizeof(Vector);
Vector *tmp;
if(tmpsz < 17*1024)
tmp = (Vector*)alloca(tmpsz);
else
{
tmpv.resize(tmpn);
tmp = &tmpv[0];
}
// tmp[] layout: leftmost part: entries to hold the matrix as it's being built;
// rightmost part: maxDeg entries as workmem for the deBoor eval
Vector *work = tmp + (tmpn - maxDeg);
// Each column -> Y-axis interpolation
for(size_t x = 0; x < _cpx; ++x)
{
const Vector *srccol = &controlpoints[x];
Vector *dstcol = &tmp[x];
tbsp::evalRange(dstcol, yres, &work[0], &knotsY[0], srccol, _cpy, _degy, _tmin, _tmax, _cpx, _cpx);
}
// Each row -> X-axis interpolation
for(size_t y = 0; y < yres; ++y)
{
const Vector *srcrow = &tmp[y * _cpx];
tbsp::evalRange(dst, xres, &work[0], &knotsX[0], srcrow, _cpx, _degx, _tmin, _tmax);
dst += xres;
}
}
// TODO: add minx/y, maxx/y params?
// this should NOT be tmin?! probably?
void BSpline2D::reset(Vector* controlpoints)
{
const float dx = (_tmax - _tmin) / float(_cpx - 1);
const float dy = (_tmax - _tmin) / float(_cpy - 1);
float yy = _tmin;
for(size_t y = 0; y < _cpy; ++y, yy += dy)
{
float xx = _tmin;
for(size_t x = 0; x < _cpx; ++x, xx += dx)
*controlpoints++ = Vector(xx, yy);
}
}
void BSpline2DWithPoints::resize(size_t cx, size_t cy, unsigned degx, unsigned degy)
{
controlpoints.resize(cx * cy);
BSpline2D::resize(cx, cy, degx, degy);
}
void BSpline2DWithPoints::recalc(Vector* dst, size_t xres, size_t yres)
{
BSpline2D::recalc(dst, xres, yres, &controlpoints[0]);
}
void BSpline2DWithPoints::reset()
{
BSpline2D::reset(&controlpoints[0]);
}
bool BSpline2DControlPointGenerator::resize(size_t cx, size_t cy)
{
const size_t interpStorageSizeX = tbsp__getInterpolatorStorageSize(cx, cx);
const size_t interpStorageSizeY = tbsp__getInterpolatorStorageSize(cy, cy);
const size_t interpStorageNeeded = interpStorageSizeX + interpStorageSizeY;
floats.resize(interpStorageNeeded);
cp2d.init(cx, cy);
const size_t maxcp = std::max(cx, cy);
vectmp.resize(maxcp);
return interp.x.init(&floats[0], cx, cx)
&& interp.y.init(&floats[interpStorageSizeX], cy, cy);
}
bool BSpline2DControlPointGenerator::refresh(const float* knotsx, const float* knotsy, unsigned degx, unsigned degy)
{
const size_t maxcp = vectmp.size();
const size_t tmpn = tbsp__getInterpolatorRefreshTempSize(maxcp, maxcp);
const size_t tmpsz = tmpn * sizeof(float);
float *tmp;
std::vector<float> tmpv;
if(tmpsz < 17*1024)
tmp = (float*)alloca(tmpsz);
else
{
tmpv.resize(tmpn);
tmp = &tmpv[0];
}
return interp.x.refresh(tmp, knotsx, degx)
&& interp.y.refresh(tmp, knotsy, degy);
}
Vector* BSpline2DControlPointGenerator::generateControlPoints(const Vector *points2d)
{
const size_t cpx = interp.x.getNumInputPoints();
const size_t cpy = interp.y.getNumInputPoints();
// y direction first
for(size_t x = 0; x < cpx; ++x)
{
const Vector *src = &points2d[x];
for(size_t y = 0; y < cpy; ++y, src += cpx)
vectmp[y] = *src;
// solve in-place
interp.y.generateControlPoints<Vector>(&vectmp[0], NULL, &vectmp[0]);
for(size_t y = 0; y < cpy; ++y)
cp2d(x, y) = vectmp[y];
}
// x direction
for(size_t y = 0; y < cpy; ++y)
{
Vector *row = cp2d.row(y);
// solve in-place
interp.x.generateControlPoints<Vector>(row, NULL, row);
}
return cp2d.data();
}
bool BSpline2DControlPointGeneratorWithPoints::resize(size_t cx, size_t cy)
{
designpoints.resize(cx * cy);
return BSpline2DControlPointGenerator::resize(cx, cy);
}
Vector* BSpline2DControlPointGeneratorWithPoints::generateControlPoints()
{
return BSpline2DControlPointGenerator::generateControlPoints(&designpoints[0]);
}