forked from mia/Aegisub
08274f0a0c
Originally committed to SVN as r1815.
289 lines
8 KiB
C++
289 lines
8 KiB
C++
//----------------------------------------------------------------------------
|
|
// Anti-Grain Geometry (AGG) - Version 2.5
|
|
// A high quality rendering engine for C++
|
|
// Copyright (C) 2002-2006 Maxim Shemanarev
|
|
// Contact: mcseem@antigrain.com
|
|
// mcseemagg@yahoo.com
|
|
// http://antigrain.com
|
|
//
|
|
// AGG 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.
|
|
//
|
|
// AGG 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with AGG; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
// MA 02110-1301, USA.
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "agg_bcspline.h"
|
|
|
|
namespace agg
|
|
{
|
|
//------------------------------------------------------------------------
|
|
bcspline::bcspline() :
|
|
m_max(0),
|
|
m_num(0),
|
|
m_x(0),
|
|
m_y(0),
|
|
m_last_idx(-1)
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bcspline::bcspline(int num) :
|
|
m_max(0),
|
|
m_num(0),
|
|
m_x(0),
|
|
m_y(0),
|
|
m_last_idx(-1)
|
|
{
|
|
init(num);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bcspline::bcspline(int num, const double* x, const double* y) :
|
|
m_max(0),
|
|
m_num(0),
|
|
m_x(0),
|
|
m_y(0),
|
|
m_last_idx(-1)
|
|
{
|
|
init(num, x, y);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
void bcspline::init(int max)
|
|
{
|
|
if(max > 2 && max > m_max)
|
|
{
|
|
m_am.resize(max * 3);
|
|
m_max = max;
|
|
m_x = &m_am[m_max];
|
|
m_y = &m_am[m_max * 2];
|
|
}
|
|
m_num = 0;
|
|
m_last_idx = -1;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
void bcspline::add_point(double x, double y)
|
|
{
|
|
if(m_num < m_max)
|
|
{
|
|
m_x[m_num] = x;
|
|
m_y[m_num] = y;
|
|
++m_num;
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
void bcspline::prepare()
|
|
{
|
|
if(m_num > 2)
|
|
{
|
|
int i, k, n1;
|
|
double* temp;
|
|
double* r;
|
|
double* s;
|
|
double h, p, d, f, e;
|
|
|
|
for(k = 0; k < m_num; k++)
|
|
{
|
|
m_am[k] = 0.0;
|
|
}
|
|
|
|
n1 = 3 * m_num;
|
|
|
|
pod_array<double> al(n1);
|
|
temp = &al[0];
|
|
|
|
for(k = 0; k < n1; k++)
|
|
{
|
|
temp[k] = 0.0;
|
|
}
|
|
|
|
r = temp + m_num;
|
|
s = temp + m_num * 2;
|
|
|
|
n1 = m_num - 1;
|
|
d = m_x[1] - m_x[0];
|
|
e = (m_y[1] - m_y[0]) / d;
|
|
|
|
for(k = 1; k < n1; k++)
|
|
{
|
|
h = d;
|
|
d = m_x[k + 1] - m_x[k];
|
|
f = e;
|
|
e = (m_y[k + 1] - m_y[k]) / d;
|
|
al[k] = d / (d + h);
|
|
r[k] = 1.0 - al[k];
|
|
s[k] = 6.0 * (e - f) / (h + d);
|
|
}
|
|
|
|
for(k = 1; k < n1; k++)
|
|
{
|
|
p = 1.0 / (r[k] * al[k - 1] + 2.0);
|
|
al[k] *= -p;
|
|
s[k] = (s[k] - r[k] * s[k - 1]) * p;
|
|
}
|
|
|
|
m_am[n1] = 0.0;
|
|
al[n1 - 1] = s[n1 - 1];
|
|
m_am[n1 - 1] = al[n1 - 1];
|
|
|
|
for(k = n1 - 2, i = 0; i < m_num - 2; i++, k--)
|
|
{
|
|
al[k] = al[k] * al[k + 1] + s[k];
|
|
m_am[k] = al[k];
|
|
}
|
|
}
|
|
m_last_idx = -1;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
void bcspline::init(int num, const double* x, const double* y)
|
|
{
|
|
if(num > 2)
|
|
{
|
|
init(num);
|
|
int i;
|
|
for(i = 0; i < num; i++)
|
|
{
|
|
add_point(*x++, *y++);
|
|
}
|
|
prepare();
|
|
}
|
|
m_last_idx = -1;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
void bcspline::bsearch(int n, const double *x, double x0, int *i)
|
|
{
|
|
int j = n - 1;
|
|
int k;
|
|
|
|
for(*i = 0; (j - *i) > 1; )
|
|
{
|
|
if(x0 < x[k = (*i + j) >> 1]) j = k;
|
|
else *i = k;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
double bcspline::interpolation(double x, int i) const
|
|
{
|
|
int j = i + 1;
|
|
double d = m_x[i] - m_x[j];
|
|
double h = x - m_x[j];
|
|
double r = m_x[i] - x;
|
|
double p = d * d / 6.0;
|
|
return (m_am[j] * r * r * r + m_am[i] * h * h * h) / 6.0 / d +
|
|
((m_y[j] - m_am[j] * p) * r + (m_y[i] - m_am[i] * p) * h) / d;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
double bcspline::extrapolation_left(double x) const
|
|
{
|
|
double d = m_x[1] - m_x[0];
|
|
return (-d * m_am[1] / 6 + (m_y[1] - m_y[0]) / d) *
|
|
(x - m_x[0]) +
|
|
m_y[0];
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
double bcspline::extrapolation_right(double x) const
|
|
{
|
|
double d = m_x[m_num - 1] - m_x[m_num - 2];
|
|
return (d * m_am[m_num - 2] / 6 + (m_y[m_num - 1] - m_y[m_num - 2]) / d) *
|
|
(x - m_x[m_num - 1]) +
|
|
m_y[m_num - 1];
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
double bcspline::get(double x) const
|
|
{
|
|
if(m_num > 2)
|
|
{
|
|
int i;
|
|
|
|
// Extrapolation on the left
|
|
if(x < m_x[0]) return extrapolation_left(x);
|
|
|
|
// Extrapolation on the right
|
|
if(x >= m_x[m_num - 1]) return extrapolation_right(x);
|
|
|
|
// Interpolation
|
|
bsearch(m_num, m_x, x, &i);
|
|
return interpolation(x, i);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
double bcspline::get_stateful(double x) const
|
|
{
|
|
if(m_num > 2)
|
|
{
|
|
// Extrapolation on the left
|
|
if(x < m_x[0]) return extrapolation_left(x);
|
|
|
|
// Extrapolation on the right
|
|
if(x >= m_x[m_num - 1]) return extrapolation_right(x);
|
|
|
|
if(m_last_idx >= 0)
|
|
{
|
|
// Check if x is not in current range
|
|
if(x < m_x[m_last_idx] || x > m_x[m_last_idx + 1])
|
|
{
|
|
// Check if x between next points (most probably)
|
|
if(m_last_idx < m_num - 2 &&
|
|
x >= m_x[m_last_idx + 1] &&
|
|
x <= m_x[m_last_idx + 2])
|
|
{
|
|
++m_last_idx;
|
|
}
|
|
else
|
|
if(m_last_idx > 0 &&
|
|
x >= m_x[m_last_idx - 1] &&
|
|
x <= m_x[m_last_idx])
|
|
{
|
|
// x is between pevious points
|
|
--m_last_idx;
|
|
}
|
|
else
|
|
{
|
|
// Else perform full search
|
|
bsearch(m_num, m_x, x, &m_last_idx);
|
|
}
|
|
}
|
|
return interpolation(x, m_last_idx);
|
|
}
|
|
else
|
|
{
|
|
// Interpolation
|
|
bsearch(m_num, m_x, x, &m_last_idx);
|
|
return interpolation(x, m_last_idx);
|
|
}
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
}
|
|
|