You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
7.4 KiB
209 lines
7.4 KiB
// David Eberly, Geometric Tools, Redmond WA 98052
|
|
// Copyright (c) 1998-2021
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// https://www.boost.org/LICENSE_1_0.txt
|
|
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
|
|
// Version: 4.0.2019.08.13
|
|
|
|
#pragma once
|
|
|
|
#include <Mathematics/Logger.h>
|
|
#include <Mathematics/ParametricCurve.h>
|
|
|
|
namespace gte
|
|
{
|
|
template <int N, typename Real>
|
|
class TCBSplineCurve : public ParametricCurve<N, Real>
|
|
{
|
|
public:
|
|
// Construction and destruction. The object copies the input arrays.
|
|
// The number of points must be at least 2. To validate construction,
|
|
// create an object as shown:
|
|
// TCBSplineCurve<N, Real> curve(parameters);
|
|
// if (!curve) { <constructor failed, handle accordingly>; }
|
|
TCBSplineCurve(int numPoints, Vector<N, Real> const* points,
|
|
Real const* times, Real const* tension, Real const* continuity,
|
|
Real const* bias)
|
|
:
|
|
ParametricCurve<N, Real>(numPoints - 1, times)
|
|
{
|
|
LogAssert(numPoints >= 2 && points != nullptr, "Invalid input.");
|
|
|
|
mPoints.resize(numPoints);
|
|
mTension.resize(numPoints);
|
|
mContinuity.resize(numPoints);
|
|
mBias.resize(numPoints);
|
|
std::copy(points, points + numPoints, mPoints.begin());
|
|
std::copy(tension, tension + numPoints, mTension.begin());
|
|
std::copy(continuity, continuity + numPoints, mContinuity.begin());
|
|
std::copy(bias, bias + numPoints, mBias.begin());
|
|
|
|
int numSegments = numPoints - 1;
|
|
mA.resize(numSegments);
|
|
mB.resize(numSegments);
|
|
mC.resize(numSegments);
|
|
mD.resize(numSegments);
|
|
|
|
// For now, treat the first point as if it occurred twice.
|
|
ComputePoly(0, 0, 1, 2);
|
|
|
|
for (int i = 1; i < numSegments - 1; ++i)
|
|
{
|
|
ComputePoly(i - 1, i, i + 1, i + 2);
|
|
}
|
|
|
|
// For now, treat the last point as if it occurred twice.
|
|
ComputePoly(numSegments - 2, numSegments - 1, numSegments, numSegments);
|
|
|
|
this->mConstructed = true;
|
|
}
|
|
|
|
virtual ~TCBSplineCurve()
|
|
{
|
|
}
|
|
|
|
// Member access.
|
|
inline int GetNumPoints() const
|
|
{
|
|
return static_cast<int>(mPoints.size());
|
|
}
|
|
|
|
inline Vector<N, Real> const* GetPoints() const
|
|
{
|
|
return &mPoints[0];
|
|
}
|
|
|
|
inline Real const* GetTensions() const
|
|
{
|
|
return &mTension[0];
|
|
}
|
|
|
|
inline Real const* GetContinuities() const
|
|
{
|
|
return &mContinuity[0];
|
|
}
|
|
|
|
inline Real const* GetBiases() const
|
|
{
|
|
return &mBias[0];
|
|
}
|
|
|
|
// Evaluation of the curve. The function supports derivative
|
|
// calculation through order 3; that is, order <= 3 is required. If
|
|
// you want/ only the position, pass in order of 0. If you want the
|
|
// position and first derivative, pass in order of 1, and so on. The
|
|
// output array 'jet' must have enough storage to support the maximum
|
|
// order. The values are ordered as: position, first derivative,
|
|
// second derivative, third derivative.
|
|
virtual void Evaluate(Real t, unsigned int order, Vector<N, Real>* jet) const override
|
|
{
|
|
unsigned int const supOrder = ParametricCurve<N, Real>::SUP_ORDER;
|
|
if (!this->mConstructed || order >= supOrder)
|
|
{
|
|
// Return a zero-valued jet for invalid state.
|
|
for (unsigned int i = 0; i < supOrder; ++i)
|
|
{
|
|
jet[i].MakeZero();
|
|
}
|
|
return;
|
|
}
|
|
|
|
int key;
|
|
Real dt;
|
|
GetKeyInfo(t, key, dt);
|
|
dt /= (this->mTime[key + 1] - this->mTime[key]);
|
|
|
|
// Compute position.
|
|
jet[0] = mA[key] + dt * (mB[key] + dt * (mC[key] + dt * mD[key]));
|
|
if (order >= 1)
|
|
{
|
|
// Compute first derivative.
|
|
jet[1] = mB[key] + dt * ((Real)2 * mC[key] + ((Real)3 * dt) * mD[key]);
|
|
if (order >= 2)
|
|
{
|
|
// Compute second derivative.
|
|
jet[2] = (Real)2 * mC[key] + ((Real)6 * dt) * mD[key];
|
|
if (order == 3)
|
|
{
|
|
jet[3] = (Real)6 * mD[key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
// Support for construction.
|
|
void ComputePoly(int i0, int i1, int i2, int i3)
|
|
{
|
|
Vector<N, Real> diff = mPoints[i2] - mPoints[i1];
|
|
Real dt = this->mTime[i2] - this->mTime[i1];
|
|
|
|
// Build multipliers at P1.
|
|
Real oneMinusT0 = (Real)1 - mTension[i1];
|
|
Real oneMinusC0 = (Real)1 - mContinuity[i1];
|
|
Real onePlusC0 = (Real)1 + mContinuity[i1];
|
|
Real oneMinusB0 = (Real)1 - mBias[i1];
|
|
Real onePlusB0 = (Real)1 + mBias[i1];
|
|
Real adj0 = (Real)2 * dt / (this->mTime[i2] - this->mTime[i0]);
|
|
Real out0 = (Real)0.5 * adj0 * oneMinusT0 * onePlusC0 * onePlusB0;
|
|
Real out1 = (Real)0.5 * adj0 * oneMinusT0 * oneMinusC0 * oneMinusB0;
|
|
|
|
// Build outgoing tangent at P1.
|
|
Vector<N, Real> tOut = out1 * diff + out0 * (mPoints[i1] - mPoints[i0]);
|
|
|
|
// Build multipliers at point P2.
|
|
Real oneMinusT1 = (Real)1 - mTension[i2];
|
|
Real oneMinusC1 = (Real)1 - mContinuity[i2];
|
|
Real onePlusC1 = (Real)1 + mContinuity[i2];
|
|
Real oneMinusB1 = (Real)1 - mBias[i2];
|
|
Real onePlusB1 = (Real)1 + mBias[i2];
|
|
Real adj1 = (Real)2 * dt / (this->mTime[i3] - this->mTime[i1]);
|
|
Real in0 = (Real)0.5 * adj1 * oneMinusT1 * oneMinusC1 * onePlusB1;
|
|
Real in1 = (Real)0.5 * adj1 * oneMinusT1 * onePlusC1 * oneMinusB1;
|
|
|
|
// Build incoming tangent at P2.
|
|
Vector<N, Real> tIn = in1 * (mPoints[i3] - mPoints[i2]) + in0 * diff;
|
|
|
|
mA[i1] = mPoints[i1];
|
|
mB[i1] = tOut;
|
|
mC[i1] = (Real)3 * diff - (Real)2 * tOut - tIn;
|
|
mD[i1] = (Real)-2 * diff + tOut + tIn;
|
|
}
|
|
|
|
// Determine the index i for which times[i] <= t < times[i+1].
|
|
void GetKeyInfo(Real t, int& key, Real& dt) const
|
|
{
|
|
int numSegments = static_cast<int>(mA.size());
|
|
if (t <= this->mTime[0])
|
|
{
|
|
key = 0;
|
|
dt = (Real)0;
|
|
return;
|
|
}
|
|
|
|
if (t < this->mTime[numSegments])
|
|
{
|
|
for (int i = 0; i < numSegments; ++i)
|
|
{
|
|
if (t < this->mTime[i + 1])
|
|
{
|
|
key = i;
|
|
dt = t - this->mTime[i];
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
key = numSegments - 1;
|
|
dt = this->mTime[numSegments] - this->mTime[numSegments - 1];
|
|
}
|
|
|
|
std::vector<Vector<N, Real>> mPoints;
|
|
std::vector<Real> mTension, mContinuity, mBias;
|
|
|
|
// Polynomial coefficients. mA are the degree 0 coefficients, mB are
|
|
// the degree 1 coefficients, mC are the degree 2 coefficients, and mD
|
|
// are the degree 3 coefficients.
|
|
std::vector<Vector<N, Real>> mA, mB, mC, mD;
|
|
};
|
|
}
|
|
|