// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // 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 #include #include namespace WwiseGTE { template class BezierCurve : public ParametricCurve { public: // Construction and destruction. The number of control points must be // degree + 1. This object copies the input array. The domain is t // in [0,1]. To validate construction, create an object as shown: // BezierCurve curve(parameters); // if (!curve) { ; } BezierCurve(int degree, Vector const* controls) : ParametricCurve((Real)0, (Real)1), mDegree(degree), mNumControls(degree + 1), mChoose(mNumControls, mNumControls) { LogAssert(degree >= 2 && controls != nullptr, "Invalid input."); // Copy the controls. mControls[0].resize(mNumControls); std::copy(controls, controls + mNumControls, mControls[0].begin()); // Compute first-order differences. mControls[1].resize(mNumControls - 1); for (int i = 0; i < mNumControls - 1; ++i) { mControls[1][i] = mControls[0][i + 1] - mControls[0][i]; } // Compute second-order differences. mControls[2].resize(mNumControls - 2); for (int i = 0; i < mNumControls - 2; ++i) { mControls[2][i] = mControls[1][i + 1] - mControls[1][i]; } // Compute third-order differences. if (degree >= 3) { mControls[3].resize(mNumControls - 3); for (int i = 0; i < mNumControls - 3; ++i) { mControls[3][i] = mControls[2][i + 1] - mControls[2][i]; } } // Compute combinatorial values Choose(n,k) and store in mChoose[n][k]. // The values mChoose[r][c] are invalid for r < c; that is, we use only // the entries for r >= c. mChoose[0][0] = (Real)1; mChoose[1][0] = (Real)1; mChoose[1][1] = (Real)1; for (int i = 2; i <= mDegree; ++i) { mChoose[i][0] = (Real)1; mChoose[i][i] = (Real)1; for (int j = 1; j < i; ++j) { mChoose[i][j] = mChoose[i - 1][j - 1] + mChoose[i - 1][j]; } } this->mConstructed = true; } virtual ~BezierCurve() { } // Member access. inline int GetDegree() const { return mDegree; } inline int GetNumControls() const { return mNumControls; } inline Vector const* GetControls() const { return &mControls[0][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* jet) const override { unsigned int const supOrder = ParametricCurve::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; } // Compute position. Real omt = (Real)1 - t; jet[0] = Compute(t, omt, 0); if (order >= 1) { // Compute first derivative. jet[1] = Compute(t, omt, 1); if (order >= 2) { // Compute second derivative. jet[2] = Compute(t, omt, 2); if (order >= 3) { // Compute third derivative. if (mDegree >= 3) { jet[3] = Compute(t, omt, 3); } else { jet[3].MakeZero(); } } } } } protected: // Support for Evaluate(...). Vector Compute(Real t, Real omt, int order) const { Vector result = omt * mControls[order][0]; Real tpow = t; int isup = mDegree - order; for (int i = 1; i < isup; ++i) { Real c = mChoose[isup][i] * tpow; result = (result + c * mControls[order][i]) * omt; tpow *= t; } result = (result + tpow * mControls[order][isup]); int multiplier = 1; for (int i = 0; i < order; ++i) { multiplier *= mDegree - i; } result *= (Real)multiplier; return result; } int mDegree, mNumControls; std::array>, ParametricCurve::SUP_ORDER> mControls; Array2 mChoose; }; }