// Vector típus a szokásos műveletekkel
#include "vector.hh"
using Point = Vector;
using PointVector = std::vector<Point>;
using VectorVector = std::vector<Vector>;
using DoubleVector = std::vector<double>;
using DoubleMatrix = std::vector<DoubleVector>;
using PointMatrix = std::vector<PointVector>;
struct BezierCurve
{
size_t n; // fokszám => n + 1 kontrollpont
PointVector cp; // kontrollpontok
// ... // member függvények
};
double BezierCurve::bernstein(size_t i, size_t n, double u)
{
DoubleVector tmp(n + 1, 0.0);
tmp[n-i] = 1.0;
double u1 = 1.0 - u;
for (size_t k = 1; k <= n; ++k)
for (size_t j = n; j >= k; --j)
tmp[j] = tmp[j-1] * u + tmp[j] * u1;
return tmp[n];
}
Point BezierCurve::evaluateOneByOne(double u) const
{
Point p(0.0, 0.0, 0.0);
for (size_t k = 0; k <= n; ++k)
p += cp[k] * bernstein(k, n, u);
return p;
}
void BezierCurve::bernsteinAll(size_t n, double u, DoubleVector &coeff)
{
coeff.clear(); coeff.reserve(n + 1);
coeff.push_back(1.0);
double u1 = 1.0 - u;
for (size_t j = 1; j <= n; ++j) {
double saved = 0.0;
for (size_t k = 0; k < j; ++k) {
double tmp = coeff[k];
coeff[k] = saved + tmp * u1;
saved = tmp * u;
}
coeff.push_back(saved);
}
}
Point BezierCurve::evaluate(double u) const
{
DoubleVector coeff; bernsteinAll(n, u, coeff);
Point p(0.0, 0.0, 0.0);
for (size_t k = 0; k <= n; ++k)
p += cp[k] * coeff[k];
return p;
}
Point BezierCurve::
evaluateWithCachedCofficients(const DoubleVector &coeff) const
{
Point p(0.0, 0.0, 0.0);
for (size_t k = 0; k <= n; ++k)
p += cp[k] * coeff[k];
return p;
}
Point BezierCurve::evaluateByDeCasteljau(double u) const
{
PointVector tmp = cp;
double u1 = 1.0 - u;
for (size_t k = 1; k <= n; ++k)
for (size_t i = 0; i <= n - k; ++i)
tmp[i] = tmp[i] * u1 + tmp[i+1] * u;
return tmp[0];
}
Módszer | Idő |
---|---|
evaluateOneByOne |
|
evaluate |
|
evaluateWithCachedCoefficients * |
|
evaluatebyDeCasteljau |
(*) beleértve a cache-elést is
// Feltételezi, hogy d <= n
void BezierCurve::derivativeControlPoints(size_t d, PointMatrix &dcp) const
{
dcp.clear(); dcp.resize(d + 1);
dcp[0] = cp;
for (size_t k = 1; k <= d; ++k) {
size_t tmp = n - k + 1;
dcp[k].reserve(tmp);
for (size_t i = 0; i <= n - k; ++i)
dcp[k].push_back((dcp[k-1][i+1] - dcp[k-1][i]) * tmp);
}
}
void BezierCurve::bernsteinAll(size_t n, double u, DoubleMatrix &coeff)
{
coeff.clear(); coeff.resize(n + 1);
coeff[0].push_back(1.0);
double u1 = 1.0 - u;
for (size_t j = 1; j <= n; ++j) {
coeff[j].reserve(j + 1);
double saved = 0.0;
for (size_t k = 0; k < j; ++k) {
double tmp = coeff[j-1][k]; // ... = coeff[k] helyett
coeff[j].push_back(saved + tmp * u1); // coeff[k] = ... helyett
saved = tmp * u;
}
coeff[j].push_back(saved);
}
}
Point BezierCurve::
derivativesByControlPoints(double u, size_t d, VectorVector &der) const
{
size_t du = std::min(d, n);
der.clear(); der.reserve(d + 1);
DoubleMatrix coeff; bernsteinAll(n, u, coeff);
PointMatrix dcp; derivativeControlPoints(du, dcp);
for (size_t k = 0; k <= du; ++k) {
der.emplace_back(0.0, 0.0, 0.0);
for (size_t j = 0; j <= n - k; ++j)
der[k] += dcp[k][j] * coeff[n-k][j];
}
for (size_t k = n + 1; k <= d; ++k)
der.emplace_back(0.0, 0.0, 0.0);
return der[0];
}
struct BSplineCurve
{
size_t p; // fokszám
size_t n; // n + 1 = cp.size()
DoubleVector knots; // első és utolsó p+1 érték megegyezik (*)
PointVector cp; // knots.size() = cp.size() + p + 1
// ... // member függvények
};
// (*) Biztosítja a végpont-interpolációt
size_t BSplineCurve::findSpan(double u) const
{
// Külön kell kezelni az intervallum végét
if (u >= knots[n+1])
return n;
return (std::upper_bound(knots.begin() + p + 1,
knots.end(), u)
- knots.begin()) - 1;
}
// knots = {0,..,0,1,...,1} => bernstein
void BSplineCurve::
basisFunctions(size_t i, double u, DoubleVector &coeff) const
{
coeff.clear(); coeff.reserve(p + 1);
coeff.push_back(1.0);
DoubleVector left(p + 1), right(p + 1); // 0. elem nincs kihasználva
for (size_t j = 1; j <= p; ++j) {
left[j] = u - knots[i+1-j];
right[j] = knots[i+j] - u;
double saved = 0.0;
for (size_t r = 0; r < j; ++r) {
double tmp = coeff[r] / (right[r+1] + left[j-r]);
coeff[r] = saved + tmp * right[r+1];
saved = tmp * left[j-r];
}
coeff.push_back(saved);
}
}
Point BSplineCurve::evaluate(double u) const
{
double span = findSpan(u);
DoubleVector coeff; basisFunctions(span, u, coeff);
Point point(0.0, 0.0, 0.0);
for (size_t i = 0; i <= p; ++i)
point += cp[span - p + i] * coeff[i];
return point;
}
size_t BSplineCurve::findSpanWithMultiplicity(double u, size_t &multi) const
{
auto range = std::equal_range(knots.begin(), knots.end(), u);
multi = range.second - range.first;
if (u >= knots[n+1])
return n;
return (range.second - knots.begin()) - 1;
}
Point BSplineCurve::evaluateByKnotInsertion(double u) const
{
if (u <= knots[0])
return cp[0];
if (u >= knots[n+p+1])
return cp[n];
size_t s, k = findSpanWithMultiplicity(u, s), r = p - s;
PointVector tmp; tmp.reserve(r + 1);
std::copy_n(cp.begin() + k - p, r + 1, std::back_inserter(tmp));
for (size_t j = 1; j <= r; ++j)
for (size_t i = 0; i <= r - j; ++i) {
double alpha = (u - knots[k-p+j+i]) / (knots[i+k+1] - knots[k-p+j+i]);
tmp[i] = tmp[i+1] * alpha + tmp[i] * (1.0 - alpha);
}
return tmp[0];
}
// Feltételezi, hogy d <= p
// Csak az [r1, r2] kontrollpont-intervallumra számolja ki
void BSplineCurve::derivativeControlPoints(size_t d, size_t r1, size_t r2,
PointMatrix &dcp) const
{
dcp.clear(); dcp.resize(d + 1);
size_t r = r2 - r1;
dcp[0].reserve(r + 1);
for (size_t i = 0; i <= r; ++i)
dcp[0].push_back(cp[r1+i]);
for (size_t k = 1; k <= d; ++k) {
dcp[k].reserve(r + 1 - k);
size_t tmp = p - k + 1;
for (size_t i = 0; i <= r - k; ++i)
dcp[k].push_back((dcp[k-1][i+1] - dcp[k-1][i]) * tmp /
(knots[r1+i+p+1] - knots[r1+i+k]));
}
}
void BSplineCurve::basisFunctionsAll(size_t i, double u,
DoubleMatrix &coeff) const
{
coeff.clear(); coeff.resize(p + 1);
coeff[0].push_back(1.0);
DoubleVector left(p + 1), right(p + 1);
for (size_t j = 1; j <= p; ++j) {
coeff[j].reserve(j + 1);
left[j] = u - knots[i+1-j];
right[j] = knots[i+j] - u;
double saved = 0.0;
for (size_t r = 0; r < j; ++r) {
double tmp = coeff[j-1][r] / (right[r+1] + left[j-r]);
coeff[j].push_back(saved + tmp * right[r+1]);
saved = tmp * left[j-r];
}
coeff[j].push_back(saved);
}
}
Point BSplineCurve::derivativesByControlPoints(double u, size_t d,
VectorVector &der) const
{
size_t du = std::min(d, p);
der.clear();
size_t span = findSpan(u);
DoubleMatrix coeff; basisFunctionsAll(span, u, coeff);
PointMatrix dcp; derivativeControlPoints(du, span - p, span, dcp);
for (size_t k = 0; k <= du; ++k) {
der.emplace_back(0.0, 0.0, 0.0);
for (size_t j = 0; j <= p - k; ++j)
der[k] += dcp[k][j] * coeff[p-k][j];
}
for (size_t k = p + 1; k <= d; ++k)
der.emplace_back(0.0, 0.0, 0.0);
return der[0];
}
Point BSplineCurve::evaluate2DRational(double u) const
{
Point p = evaluate(u);
return Point(p.x / p.z, p.y / p.z, 1.0);
}
Point BSplineCurve::derivatives2DRational(double u, size_t d,
VectorVector &der) const
{
der.clear(); der.reserve(d + 1);
VectorVector der3d; derivativesByControlPoints(u, d, der3d);
for (size_t k = 0; k <= d; ++k) {
Vector v = der3d[k];
for (size_t i = 1; i <= k; ++i)
v = v - der[k-i] * der3d[i].z * binomial(k, i);
der.push_back(v / der3d[0].z);
}
return der[0];
}
size_t BSplineCurve::binomial(size_t n, size_t k)
{
if (k > n)
return 0;
size_t result = 1;
for (size_t d = 1; d <= k; ++d, --n)
result = result * n / d;
return result;
}