123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440 |
- // 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 <Mathematics/Logger.h>
- #include <Mathematics/Math.h>
- #include <Mathematics/Polynomial1.h>
- #include <array>
- // IntpBSplineUniform is the class for B-spline interpolation of uniformly
- // spaced N-dimensional data. The algorithm is described in
- // https://www.geometrictools.com/Documentation/BSplineInterpolation.pdf
- // A sample application for this topic is
- // GeometricTools/GTEngine/Samples/Imagics/BSplineInterpolation
- //
- // The Controls adapter allows access to your control points without regard
- // to how you organize your data. You can even defer the computation of a
- // control point until it is needed via the operator()(...) calls that
- // Controls must provide, and you can cache the points according to your own
- // needs. The minimal interface for a Controls adapter is
- //
- // struct Controls
- // {
- // // The control_point_type is of your choosing. It must support
- // // assignment, scalar multiplication and addition. Specifically, if
- // // C0, C1 and C2 are control points and s is a scalar, the interpolator
- // // needs to perform operations
- // // C1 = C0;
- // // C1 = C0 * s;
- // // C2 = C0 + C1;
- // typedef control_point_type Type;
- //
- // // The number of elements in the specified dimension.
- // int GetSize(int dimension) const;
- //
- // // Get a control point based on an n-tuple lookup. The interpolator
- // // does not need to know your organization; all it needs is the
- // // desired control point. The 'tuple' input must have n elements.
- // Type operator() (int const* tuple) const;
- //
- // // If you choose to use the specialized interpolators for dimensions
- // // 1, 2 or 3, you must also provide the following accessor, where
- // // the input is an n-tuple listed component-by-component (1, 2 or 3
- // // components).
- // Type operator() (int i0, int i1, ..., int inm1) const;
- // }
- namespace WwiseGTE
- {
- template <typename Real, typename Controls>
- class IntpBSplineUniformShared
- {
- protected:
- // Abstract base class construction. A virtual destructor is not
- // provided because there are no required side effects in the base
- // class when destroying objects from the derived classes.
- IntpBSplineUniformShared(int numDimensions, int const* degrees,
- Controls const& controls, typename Controls::Type ctZero, int cacheMode)
- :
- mNumDimensions(numDimensions),
- mDegree(numDimensions),
- mControls(&controls),
- mCTZero(ctZero),
- mCacheMode(cacheMode),
- mNumLocalControls(0),
- mDegreeP1(numDimensions),
- mNumControls(numDimensions),
- mTMin(numDimensions),
- mTMax(numDimensions),
- mBlender(numDimensions),
- mDCoefficient(numDimensions),
- mLMax(numDimensions),
- mPowerDSDT(numDimensions),
- mITuple(numDimensions),
- mJTuple(numDimensions),
- mKTuple(numDimensions),
- mLTuple(numDimensions),
- mSumIJTuple(numDimensions),
- mUTuple(numDimensions),
- mPTuple(numDimensions)
- {
- // The condition c+1 > d+1 is required so that when s = c+1-d, its
- // maximum value, we have at least two s-knots (d and d + 1).
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- if (mControls->GetSize(dim) <= degrees[dim] + 1)
- {
- LogError("Incompatible degree and number of controls.");
- }
- }
- mNumLocalControls = 1;
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- mDegree[dim] = degrees[dim];
- mDegreeP1[dim] = degrees[dim] + 1;
- mNumLocalControls *= mDegreeP1[dim];
- mNumControls[dim] = controls.GetSize(dim);
- mTMin[dim] = (Real)-0.5;
- mTMax[dim] = static_cast<Real>(mNumControls[dim]) - (Real)0.5;
- ComputeBlendingMatrix(mDegree[dim], mBlender[dim]);
- ComputeDCoefficients(mDegree[dim], mDCoefficient[dim], mLMax[dim]);
- ComputePowers(mDegree[dim], mNumControls[dim], mTMin[dim], mTMax[dim], mPowerDSDT[dim]);
- }
- if (mCacheMode == NO_CACHING)
- {
- mPhi.resize(mNumDimensions);
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- mPhi[dim].resize(mDegreeP1[dim]);
- }
- }
- else
- {
- InitializeTensors();
- }
- }
- #if !defined(GTE_INTP_BSPLINE_UNIFORM_NO_SPECIALIZATION)
- IntpBSplineUniformShared()
- :
- mNumDimensions(0),
- mControls(nullptr),
- mCTZero(),
- mCacheMode(0),
- mNumLocalControls(0)
- {
- }
- #endif
- public:
- // Support for caching the intermediate tensor product of control
- // points with the blending matrices. A precached container has all
- // elements precomputed before any Evaluate(...) calls. The 'bool'
- // flags are all set to 'true'. A cached container fills the elements
- // on demand. The 'bool' flags are initially 'false', indicating the
- // EvalType component has not yet been computed. After it is computed
- // and stored, the flag is set to 'true'.
- enum
- {
- NO_CACHING,
- PRE_CACHING,
- ON_DEMAND_CACHING
- };
- // Member access.
- inline int GetDegree(int dim) const
- {
- return mDegree[dim];
- }
- inline int GetNumControls(int dim) const
- {
- return mNumControls[dim];
- }
- inline Real GetTMin(int dim) const
- {
- return mTMin[dim];
- }
- inline Real GetTMax(int dim) const
- {
- return mTMax[dim];
- }
- inline int GetCacheMode() const
- {
- return mCacheMode;
- }
- protected:
- // Disallow copying and moving.
- IntpBSplineUniformShared(IntpBSplineUniformShared const&) = delete;
- IntpBSplineUniformShared& operator=(IntpBSplineUniformShared const&) = delete;
- IntpBSplineUniformShared(IntpBSplineUniformShared&&) = delete;
- IntpBSplineUniformShared& operator=(IntpBSplineUniformShared&&) = delete;
- // ComputeBlendingMatrix, ComputeDCoefficients, ComputePowers and
- // GetKey are used by the general-dimension derived classes and by
- // the specializations for dimensions 1, 2 and 3.
- // Compute the blending matrix that combines the control points and
- // the polynomial vector. The matrix A is stored in row-major order.
- static void ComputeBlendingMatrix(int degree, std::vector<Real>& A)
- {
- int const degreeP1 = degree + 1;
- A.resize(degreeP1 * degreeP1);
- if (degree == 0)
- {
- A[0] = (Real)1;
- return;
- }
- // P_{0,0}(s)
- std::vector<Polynomial1<Real>> P(degreeP1);
- P[0][0] = (Real)1;
- // L0 = s/j
- Polynomial1<Real> L0(1);
- L0[0] = (Real)0;
- // L1(s) = (j + 1 - s)/j
- Polynomial1<Real> L1(1);
- // s-1 is used in computing translated P_{j-1,k-1}(s-1)
- Polynomial1<Real> sm1 = { (Real)-1, (Real)1 };
- // Compute
- // P_{j,k}(s) = L0(s)*P_{j-1,k}(s) + L1(s)*P_{j-1,k-1}(s-1)
- // for 0 <= k <= j where 1 <= j <= degree. When k = 0,
- // P_{j-1,-1}(s) = 0, so P_{j,0}(s) = L0(s)*P_{j-1,0}(s). When
- // k = j, P_{j-1,j}(s) = 0, so P_{j,j}(s) = L1(s)*P_{j-1,j-1}(s).
- // The polynomials at level j-1 are currently stored in P[0]
- // through P[j-1]. The polynomials at level j are computed and
- // stored in P[0] through P[j]; that is, they are computed in
- // place to reduce memory usage and copying. This requires
- // computing P[k] (level j) from P[k] (level j-1) and P[k-1]
- // (level j-1), which means we have to process k = j down to
- // k = 0.
- for (int j = 1; j <= degree; ++j)
- {
- Real invJ = (Real)1 / (Real)j;
- L0[1] = invJ;
- L1[0] = (Real)1 + invJ;
- L1[1] = -invJ;
- for (int k = j; k >= 0; --k)
- {
- Polynomial1<Real> result = { (Real)0 };
- if (k > 0)
- {
- result += L1 * P[k - 1].GetTranslation((Real)1);
- }
- if (k < j)
- {
- result += L0 * P[k];
- }
- P[k] = result;
- }
- }
- // Compute Q_{d,k}(s) = P_{d,k}(s + k).
- std::vector<Polynomial1<Real>> Q(degreeP1);
- for (int k = 0; k <= degree; ++k)
- {
- Q[k] = P[k].GetTranslation(static_cast<Real>(-k));
- }
- // Extract the matrix A from the Q-polynomials. Row r of A
- // contains the coefficients of Q_{d,d-r}(s).
- for (int k = 0, row = degree; k <= degree; ++k, --row)
- {
- for (int col = 0; col <= degree; ++col)
- {
- A[col + degreeP1 * row] = Q[k][col];
- }
- }
- }
- // Compute the coefficients for the derivative polynomial terms.
- static void ComputeDCoefficients(int degree, std::vector<Real>& dCoefficients,
- std::vector<int>& ellMax)
- {
- int numDCoefficients = (degree + 1) * (degree + 2) / 2;
- dCoefficients.resize(numDCoefficients);
- for (int i = 0; i < numDCoefficients; ++i)
- {
- dCoefficients[i] = 1;
- }
- for (int order = 1, col0 = 0, col1 = degree + 1; order <= degree; ++order)
- {
- ++col0;
- for (int c = order, m = 1; c <= degree; ++c, ++m, ++col0, ++col1)
- {
- dCoefficients[col1] = dCoefficients[col0] * m;
- }
- }
- ellMax.resize(degree + 1);
- ellMax[0] = degree;
- for (int i0 = 0, i1 = 1; i1 <= degree; i0 = i1++)
- {
- ellMax[i1] = ellMax[i0] + degree - i0;
- }
- }
- // Compute powers of ds/dt.
- void ComputePowers(int degree, int numControls, Real tmin, Real tmax,
- std::vector<Real>& powerDSDT)
- {
- Real dsdt = static_cast<Real>(numControls - degree) / (tmax - tmin);
- powerDSDT.resize(degree + 1);
- powerDSDT[0] = (Real)1;
- powerDSDT[1] = dsdt;
- for (int i = 2; i <= degree; ++i)
- {
- powerDSDT[i] = powerDSDT[i - 1] * dsdt;
- }
- }
- // Determine the interval [index,index+1) corresponding to the
- // specified value of t and compute u in that interval.
- static void GetKey(Real t, Real tmin, Real tmax, Real dsdt, int numControls,
- int degree, int& index, Real& u)
- {
- // Compute s - d = ((c + 1 - d)/(c + 1))(t + 1/2), the index for
- // which d + index <= s < d + index + 1. Let u = s - d - index so
- // that 0 <= u < 1.
- if (t > tmin)
- {
- if (t < tmax)
- {
- Real smd = dsdt * (t - tmin);
- index = static_cast<uint32_t>(std::floor(smd));
- u = smd - static_cast<float>(index);
- }
- else
- {
- // In the evaluation, s = c + 1 - d and i = c - d. This
- // causes s-d-i to be 1 in G_c(c+1-d). Effectively, the
- // selection of i extends the s-domain [d,c+1) to its
- // support [d,c+1].
- index = numControls - 1 - degree;
- u = (Real)1;
- }
- }
- else
- {
- index = 0;
- u = (Real)0;
- }
- }
- // The remaining functions are used only by the general-dimension
- // derived classes when caching is enabled.
- // For the multidimensional tensor Phi{iTuple, kTuple), compute the
- // portion of the 1-dimensional index that corresponds to iTuple.
- int GetRowIndex(std::vector<int> const& i) const
- {
- int rowIndex = i[mNumDimensions - 1];
- int j1 = 2 * mNumDimensions - 2;
- for (int j0 = mNumDimensions - 2; j0 >= 0; --j0, --j1)
- {
- rowIndex = mTBound[j1] * rowIndex + i[j0];
- }
- rowIndex = mTBound[j1] * rowIndex;
- return rowIndex;
- }
- // For the multidimensional tensor Phi{iTuple, kTuple), combine the
- // GetRowIndex(...) output with kTuple to produce the full
- // 1-dimensional index.
- int GetIndex(int rowIndex, std::vector<int> const& k) const
- {
- int index = rowIndex + k[mNumDimensions - 1];
- for (int j = mNumDimensions - 2; j >= 0; --j)
- {
- index = mTBound[j] * index + k[j];
- }
- return index;
- }
- // Compute Phi(iTuple, kTuple). The 'index' value is an already
- // computed 1-dimensional index for the tensor.
- void ComputeTensor(int const* i, int const* k, int index)
- {
- auto element = mCTZero;
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- mComputeJTuple[dim] = 0;
- }
- for (int iterate = 0; iterate < mNumLocalControls; ++iterate)
- {
- Real blend(1);
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- blend *= mBlender[dim][k[dim] + mDegreeP1[dim] * mComputeJTuple[dim]];
- mComputeSumIJTuple[dim] = i[dim] + mComputeJTuple[dim];
- }
- element = element + (*mControls)(mComputeSumIJTuple.data()) * blend;
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- if (++mComputeJTuple[dim] < mDegreeP1[dim])
- {
- break;
- }
- mComputeJTuple[dim] = 0;
- }
- }
- mTensor[index] = element;
- }
- // Allocate the containers used for caching and fill in the tensor
- // for precaching when that mode is selected.
- void InitializeTensors()
- {
- mTBound.resize(2 * mNumDimensions);
- mComputeJTuple.resize(mNumDimensions);
- mComputeSumIJTuple.resize(mNumDimensions);
- mDegreeMinusOrder.resize(mNumDimensions);
- mTerm.resize(mNumDimensions);
- int current = 0;
- int numCached = 1;
- for (int dim = 0; dim < mNumDimensions; ++dim, ++current)
- {
- mTBound[current] = mDegreeP1[dim];
- numCached *= mTBound[current];
- }
- for (int dim = 0; dim < mNumDimensions; ++dim, ++current)
- {
- mTBound[current] = mNumControls[dim] - mDegree[dim];
- numCached *= mTBound[current];
- }
- mTensor.resize(numCached);
- mCached.resize(numCached);
- if (mCacheMode == PRE_CACHING)
- {
- std::vector<int> tuple(2 * mNumDimensions, 0);
- for (int index = 0; index < numCached; ++index)
- {
- ComputeTensor(&tuple[mNumDimensions], &tuple[0], index);
- for (int i = 0; i < 2 * mNumDimensions; ++i)
- {
- if (++tuple[i] < mTBound[i])
- {
- break;
- }
- tuple[i] = 0;
- }
- }
- std::fill(mCached.begin(), mCached.end(), true);
- }
- else
- {
- std::fill(mCached.begin(), mCached.end(), false);
- }
- }
- // Evaluate the interpolator. Each element of 'order' indicates the
- // order of the derivative you want to compute. For the function
- // value itself, pass in 'order' that has all 0 elements.
- typename Controls::Type EvaluateNoCaching(int const* order, Real const* t)
- {
- auto result = mCTZero;
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- if (order[dim] < 0 || order[dim] > mDegree[dim])
- {
- return result;
- }
- }
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- GetKey(t[dim], mTMin[dim], mTMax[dim], mPowerDSDT[dim][1],
- mNumControls[dim], mDegree[dim], mITuple[dim], mUTuple[dim]);
- }
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- int jIndex = 0;
- for (int j = 0; j <= mDegree[dim]; ++j)
- {
- int kjIndex = mDegree[dim] + jIndex;
- int ell = mLMax[dim][order[dim]];
- mPhi[dim][j] = (Real)0;
- for (int k = mDegree[dim]; k >= order[dim]; --k)
- {
- mPhi[dim][j] = mPhi[dim][j] * mUTuple[dim] +
- mBlender[dim][kjIndex--] * mDCoefficient[dim][ell--];
- }
- jIndex += mDegreeP1[dim];
- }
- }
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- mJTuple[dim] = 0;
- mSumIJTuple[dim] = mITuple[dim];
- mPTuple[dim] = mPhi[dim][0];
- }
- for (int iterate = 0; iterate < mNumLocalControls; ++iterate)
- {
- Real product(1);
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- product *= mPTuple[dim];
- }
- result = result + (*mControls)(mSumIJTuple.data()) * product;
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- if (++mJTuple[dim] <= mDegree[dim])
- {
- mSumIJTuple[dim] = mITuple[dim] + mJTuple[dim];
- mPTuple[dim] = mPhi[dim][mJTuple[dim]];
- break;
- }
- mJTuple[dim] = 0;
- mSumIJTuple[dim] = mITuple[dim];
- mPTuple[dim] = mPhi[dim][0];
- }
- }
- Real adjust(1);
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- adjust *= mPowerDSDT[dim][order[dim]];
- }
- result = result * adjust;
- return result;
- }
- typename Controls::Type EvaluateCaching(int const* order, Real const* t)
- {
- int numIterates = 1;
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- mDegreeMinusOrder[dim] = mDegree[dim] - order[dim];
- if (mDegreeMinusOrder[dim] < 0 || mDegreeMinusOrder[dim] > mDegree[dim])
- {
- return mCTZero;
- }
- numIterates *= mDegreeMinusOrder[dim] + 1;
- }
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- GetKey(t[dim], mTMin[dim], mTMax[dim], mPowerDSDT[dim][1],
- mNumControls[dim], mDegree[dim], mITuple[dim], mUTuple[dim]);
- }
- int rowIndex = GetRowIndex(mITuple);
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- mJTuple[dim] = 0;
- mKTuple[dim] = mDegree[dim];
- mLTuple[dim] = mLMax[dim][order[dim]];
- mTerm[dim] = mCTZero;
- }
- for (int iterate = 0; iterate < numIterates; ++iterate)
- {
- int index = GetIndex(rowIndex, mKTuple);
- if (mCacheMode == ON_DEMAND_CACHING && !mCached[index])
- {
- ComputeTensor(mITuple.data(), mKTuple.data(), index);
- mCached[index] = true;
- }
- mTerm[0] = mTerm[0] * mUTuple[0] + mTensor[index] * mDCoefficient[0][mLTuple[0]];
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- if (++mJTuple[dim] <= mDegreeMinusOrder[dim])
- {
- --mKTuple[dim];
- --mLTuple[dim];
- break;
- }
- int dimp1 = dim + 1;
- if (dimp1 < mNumDimensions)
- {
- mTerm[dimp1] = mTerm[dimp1] * mUTuple[dimp1] + mTerm[dim] * mDCoefficient[dimp1][mLTuple[dimp1]];
- mTerm[dim] = mCTZero;
- mJTuple[dim] = 0;
- mKTuple[dim] = mDegree[dim];
- mLTuple[dim] = mLMax[dim][order[dim]];
- }
- }
- }
- auto result = mTerm[mNumDimensions - 1];
- Real adjust(1);
- for (int dim = 0; dim < mNumDimensions; ++dim)
- {
- adjust *= mPowerDSDT[dim][order[dim]];
- }
- result = result * adjust;
- return result;
- }
- // Constructor inputs.
- int const mNumDimensions; // N
- std::vector<int> mDegree; // degree[N]
- Controls const* mControls;
- typename Controls::Type const mCTZero;
- int const mCacheMode;
- // Parameters for B-spline evaluation. All std::vector containers
- // have N elements.
- int mNumLocalControls; // product of (degree[]+1)
- std::vector<int> mDegreeP1;
- std::vector<int> mNumControls;
- std::vector<Real> mTMin;
- std::vector<Real> mTMax;
- std::vector<std::vector<Real>> mBlender;
- std::vector<std::vector<Real>> mDCoefficient;
- std::vector<std::vector<int>> mLMax;
- std::vector<std::vector<Real>> mPowerDSDT;
- std::vector<int> mITuple;
- std::vector<int> mJTuple;
- std::vector<int> mKTuple;
- std::vector<int> mLTuple;
- std::vector<int> mSumIJTuple;
- std::vector<Real> mUTuple;
- std::vector<Real> mPTuple;
- // Support for no-cached B-spline evaluation. The std::vector
- // container has N elements.
- std::vector<std::vector<Real>> mPhi;
- // Support for cached B-spline evaluation.
- std::vector<int> mTBound; // tbound[2*N]
- std::vector<int> mComputeJTuple; // computejtuple[N]
- std::vector<int> mComputeSumIJTuple; // computesumijtuple[N]
- std::vector<int> mDegreeMinusOrder; // degreeminusorder[N]
- std::vector<typename Controls::Type> mTerm; // mTerm[N]
- std::vector<typename Controls::Type> mTensor; // depends on numcontrols
- std::vector<bool> mCached; // same size as mTensor
- };
- }
- // Implementation for B-spline interpolation whose dimension is known at
- // compile time.
- namespace WwiseGTE
- {
- template <typename Real, typename Controls, int N = 0>
- class IntpBSplineUniform : public IntpBSplineUniformShared<Real, Controls>
- {
- public:
- // The caller is responsible for ensuring that the IntpBSplineUniform
- // object persists as long as the input 'controls' exists.
- IntpBSplineUniform(std::array<int, N> const& degrees, Controls const& controls,
- typename Controls::Type ctZero, int cacheMode)
- :
- IntpBSplineUniformShared<Real, Controls>(N, degrees.data(), controls,
- ctZero, cacheMode)
- {
- }
- // Disallow copying and moving.
- IntpBSplineUniform(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform(IntpBSplineUniform&&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform&&) = delete;
- // Evaluate the interpolator. Each element of 'order' indicates the
- // order of the derivative you want to compute. For the function
- // value itself, pass in 'order' that has all 0 elements.
- typename Controls::Type Evaluate(std::array<int, N> const& order,
- std::array<Real, N> const& t)
- {
- if (this->mCacheMode == this->NO_CACHING)
- {
- return this->EvaluateNoCaching(order.data(), t.data());
- }
- else
- {
- return this->EvaluateCaching(order.data(), t.data());
- }
- }
- };
- }
- // Implementation for B-spline interpolation whose dimension is known only
- // at run time.
- namespace WwiseGTE
- {
- template <typename Real, typename Controls>
- class IntpBSplineUniform<Real, Controls, 0> : public IntpBSplineUniformShared<Real, Controls>
- {
- public:
- // The caller is responsible for ensuring that the IntpBSplineUniform
- // object persists as long as the input 'controls' exists.
- IntpBSplineUniform(std::vector<int> const& degrees, Controls const& controls,
- typename Controls::Type ctZero, int cacheMode)
- :
- IntpBSplineUniformShared<Real, Controls>(static_cast<int>(degrees.size()),
- degrees.data(), controls, ctZero, cacheMode)
- {
- }
- // Disallow copying and moving.
- IntpBSplineUniform(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform(IntpBSplineUniform&&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform&&) = delete;
- // Evaluate the interpolator. Each element of 'order' indicates the
- // order of the derivative you want to compute. For the function
- // value itself, pass in 'order' that has all 0 elements.
- typename Controls::Type Evaluate(std::vector<int> const& order,
- std::vector<Real> const& t)
- {
- if (static_cast<int>(order.size()) >= this->mNumDimensions
- && static_cast<int>(t.size()) >= this->mNumDimensions)
- {
- if (this->mCacheMode == this->NO_CACHING)
- {
- return this->EvaluateNoCaching(order.data(), t.data());
- }
- else
- {
- return this->EvaluateCaching(order.data(), t.data());
- }
- }
- else
- {
- return this->mCTZero;
- }
- }
- };
- }
- // To use only the N-dimensional template code above, define the symbol
- // GTE_INTP_BSPLINE_UNIFORM_NO_SPECIALIZATION to disable the specializations
- // that occur below. Notice that the interfaces are different between the
- // specializations and the general code.
- #if !defined(GTE_INTP_BSPLINE_UNIFORM_NO_SPECIALIZATION)
- // Specialization for 1-dimensional data.
- namespace WwiseGTE
- {
- template <typename Real, typename Controls>
- class IntpBSplineUniform<Real, Controls, 1> : public IntpBSplineUniformShared<Real, Controls>
- {
- public:
- // The caller is responsible for ensuring that the IntpBSplineUniform
- // object persists as long as the input 'controls' exists.
- IntpBSplineUniform(int degree, Controls const& controls,
- typename Controls::Type ctZero, int cacheMode)
- :
- IntpBSplineUniformShared<Real, Controls>(),
- mDegree(degree),
- mControls(&controls),
- mCTZero(ctZero),
- mCacheMode(cacheMode)
- {
- // The condition c+1 > d+1 is required so that when s = c+1-d, its
- // maximum value, we have at least two s-knots (d and d + 1).
- if (mControls->GetSize(0) <= mDegree + 1)
- {
- LogError("Incompatible degree and number of controls.");
- }
- mDegreeP1 = mDegree + 1;
- mNumControls = mControls->GetSize(0);
- mTMin = (Real)-0.5;
- mTMax = static_cast<Real>(mNumControls) - (Real)0.5;
- mNumTRows = 0;
- mNumTCols = 0;
- this->ComputeBlendingMatrix(mDegree, mBlender);
- this->ComputeDCoefficients(mDegree, mDCoefficient, mLMax);
- this->ComputePowers(mDegree, mNumControls, mTMin, mTMax, mPowerDSDT);
- if (mCacheMode == this->NO_CACHING)
- {
- mPhi.resize(mDegreeP1);
- }
- else
- {
- InitializeTensors();
- }
- }
- // Disallow copying and moving.
- IntpBSplineUniform(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform(IntpBSplineUniform&&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform&&) = delete;
- // Member access.
- inline int GetDegree(int) const
- {
- return mDegree;
- }
- inline int GetNumControls(int) const
- {
- return mNumControls;
- }
- inline Real GetTMin(int) const
- {
- return mTMin;
- }
- inline Real GetTMax(int) const
- {
- return mTMax;
- }
- inline int GetCacheMode() const
- {
- return mCacheMode;
- }
- // Evaluate the interpolator. The order is 0 when you want the B-spline
- // function value itself. The order is 1 for the first derivative of the
- // function, and so on.
- typename Controls::Type Evaluate(std::array<int, 1> const& order,
- std::array<Real,1> const& t)
- {
- auto result = mCTZero;
- if (0 <= order[0] && order[0] <= mDegree)
- {
- int i;
- Real u;
- this->GetKey(t[0], mTMin, mTMax, mPowerDSDT[1], mNumControls, mDegree, i, u);
- if (mCacheMode == this->NO_CACHING)
- {
- int jIndex = 0;
- for (int j = 0; j <= mDegree; ++j)
- {
- int kjIndex = mDegree + jIndex;
- int ell = mLMax[order[0]];
- mPhi[j] = (Real)0;
- for (int k = mDegree; k >= order[0]; --k)
- {
- mPhi[j] = mPhi[j] * u + mBlender[kjIndex--] * mDCoefficient[ell--];
- }
- jIndex += mDegreeP1;
- }
- for (int j = 0; j <= mDegree; ++j)
- {
- result = result + (*mControls)(i + j) * mPhi[j];
- }
- }
- else
- {
- int iIndex = mNumTCols * i;
- int kiIndex = mDegree + iIndex;
- int ell = mLMax[order[0]];
- for (int k = mDegree; k >= order[0]; --k)
- {
- if (mCacheMode == this->ON_DEMAND_CACHING && !mCached[kiIndex])
- {
- ComputeTensor(i, k, kiIndex);
- mCached[kiIndex] = true;
- }
- result = result * u + mTensor[kiIndex--] * mDCoefficient[ell--];
- }
- }
- result = result * mPowerDSDT[order[0]];
- }
- return result;
- }
- protected:
- void ComputeTensor(int r, int c, int index)
- {
- auto element = mCTZero;
- for (int j = 0; j <= mDegree; ++j)
- {
- element = element + (*mControls)(r + j) * mBlender[c + mDegreeP1 * j];
- }
- mTensor[index] = element;
- }
- void InitializeTensors()
- {
- mNumTRows = mNumControls - mDegree;
- mNumTCols = mDegreeP1;
- int numCached = mNumTRows * mNumTCols;
- mTensor.resize(numCached);
- mCached.resize(numCached);
- if (mCacheMode == this->PRE_CACHING)
- {
- for (int r = 0, index = 0; r < mNumTRows; ++r)
- {
- for (int c = 0; c < mNumTCols; ++c, ++index)
- {
- ComputeTensor(r, c, index);
- }
- }
- std::fill(mCached.begin(), mCached.end(), true);
- }
- else
- {
- std::fill(mCached.begin(), mCached.end(), false);
- }
- }
- // Constructor inputs.
- int mDegree;
- Controls const* mControls;
- typename Controls::Type mCTZero;
- int mCacheMode;
- // Parameters for B-spline evaluation.
- int mDegreeP1;
- int mNumControls;
- Real mTMin, mTMax;
- std::vector<Real> mBlender;
- std::vector<Real> mDCoefficient;
- std::vector<int> mLMax;
- std::vector<Real> mPowerDSDT;
- // Support for no-cached B-spline evaluation.
- std::vector<Real> mPhi;
- // Support for cached B-spline evaluation.
- int mNumTRows, mNumTCols;
- std::vector<typename Controls::Type> mTensor;
- std::vector<bool> mCached;
- };
- }
- // Specialization for 2-dimensional data.
- namespace WwiseGTE
- {
- template <typename Real, typename Controls>
- class IntpBSplineUniform<Real, Controls, 2> : public IntpBSplineUniformShared<Real, Controls>
- {
- public:
- // The caller is responsible for ensuring that the IntpBSplineUniform2
- // object persists as long as the input 'controls' exists.
- IntpBSplineUniform(std::array<int, 2> const& degrees, Controls const& controls,
- typename Controls::Type ctZero, int cacheMode)
- :
- IntpBSplineUniformShared<Real, Controls>(),
- mDegree(degrees),
- mControls(&controls),
- mCTZero(ctZero),
- mCacheMode(cacheMode)
- {
- // The condition c+1 > d+1 is required so that when s = c+1-d, its
- // maximum value, we have at least two s-knots (d and d + 1).
- for (int dim = 0; dim < 2; ++dim)
- {
- if (mControls->GetSize(dim) <= mDegree[dim] + 1)
- {
- LogError("Incompatible degree and number of controls.");
- }
- }
- for (int dim = 0; dim < 2; ++dim)
- {
- mDegreeP1[dim] = mDegree[dim] + 1;
- mNumControls[dim] = mControls->GetSize(dim);
- mTMin[dim] = (Real)-0.5;
- mTMax[dim] = static_cast<Real>(mNumControls[dim]) - (Real)0.5;
- mNumTRows[dim] = 0;
- mNumTCols[dim] = 0;
- this->ComputeBlendingMatrix(mDegree[dim], mBlender[dim]);
- this->ComputeDCoefficients(mDegree[dim], mDCoefficient[dim], mLMax[dim]);
- this->ComputePowers(mDegree[dim], mNumControls[dim], mTMin[dim], mTMax[dim], mPowerDSDT[dim]);
- }
- if (mCacheMode == this->NO_CACHING)
- {
- for (int dim = 0; dim < 2; ++dim)
- {
- mPhi[dim].resize(mDegreeP1[dim]);
- }
- }
- else
- {
- InitializeTensors();
- }
- }
- // Disallow copying and moving.
- IntpBSplineUniform(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform(IntpBSplineUniform&&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform&&) = delete;
- // Member access.
- inline int GetDegree(int dim) const
- {
- return mDegree[dim];
- }
- inline int GetNumControls(int dim) const
- {
- return mNumControls[dim];
- }
- inline Real GetTMin(int dim) const
- {
- return mTMin[dim];
- }
- inline Real GetTMax(int dim) const
- {
- return mTMax[dim];
- }
- inline int GetCacheMode() const
- {
- return mCacheMode;
- }
- // Evaluate the interpolator. The order is (0,0) when you want the
- // B-spline function value itself. The order0 is 1 for the first
- // derivative with respect to t0 and the order1 is 1 for the first
- // derivative with respect to t1. Higher-order derivatives in other
- // t-inputs are computed similarly.
- typename Controls::Type Evaluate(std::array<int, 2> const& order,
- std::array<Real, 2> const& t)
- {
- auto result = mCTZero;
- if (0 <= order[0] && order[0] <= mDegree[0]
- && 0 <= order[1] && order[1] <= mDegree[1])
- {
- std::array<int, 2> i;
- std::array<Real, 2> u;
- for (int dim = 0; dim < 2; ++dim)
- {
- this->GetKey(t[dim], mTMin[dim], mTMax[dim], mPowerDSDT[dim][1],
- mNumControls[dim], mDegree[dim], i[dim], u[dim]);
- }
- if (mCacheMode == this->NO_CACHING)
- {
- for (int dim = 0; dim < 2; ++dim)
- {
- int jIndex = 0;
- for (int j = 0; j <= mDegree[dim]; ++j)
- {
- int kjIndex = mDegree[dim] + jIndex;
- int ell = mLMax[dim][order[dim]];
- mPhi[dim][j] = (Real)0;
- for (int k = mDegree[dim]; k >= order[dim]; --k)
- {
- mPhi[dim][j] = mPhi[dim][j] * u[dim] +
- mBlender[dim][kjIndex--] * mDCoefficient[dim][ell--];
- }
- jIndex += mDegreeP1[dim];
- }
- }
- for (int j1 = 0; j1 <= mDegree[1]; ++j1)
- {
- Real phi1 = mPhi[1][j1];
- for (int j0 = 0; j0 <= mDegree[0]; ++j0)
- {
- Real phi0 = mPhi[0][j0];
- Real phi01 = phi0 * phi1;
- result = result + (*mControls)(i[0] + j0, i[1] + j1) * phi01;
- }
- }
- }
- else
- {
- int i0i1Index = mNumTCols[1] * (i[0] + mNumTRows[0] * i[1]);
- int k1i0i1Index = mDegree[1] + i0i1Index;
- int ell1 = mLMax[1][order[1]];
- for (int k1 = mDegree[1]; k1 >= order[1]; --k1)
- {
- int k0k1i0i1Index = mDegree[0] + mNumTCols[0] * k1i0i1Index;
- int ell0 = mLMax[0][order[0]];
- auto term = mCTZero;
- for (int k0 = mDegree[0]; k0 >= order[0]; --k0)
- {
- if (mCacheMode == this->ON_DEMAND_CACHING && !mCached[k0k1i0i1Index])
- {
- ComputeTensor(i[0], i[1], k0, k1, k0k1i0i1Index);
- mCached[k0k1i0i1Index] = true;
- }
- term = term * u[0] + mTensor[k0k1i0i1Index--] * mDCoefficient[0][ell0--];
- }
- result = result * u[1] + term * mDCoefficient[1][ell1--];
- --k1i0i1Index;
- }
- }
- Real adjust(1);
- for (int dim = 0; dim < 2; ++dim)
- {
- adjust *= mPowerDSDT[dim][order[dim]];
- }
- result = result * adjust;
- }
- return result;
- }
- void ComputeTensor(int r0, int r1, int c0, int c1, int index)
- {
- auto element = mCTZero;
- for (int j1 = 0; j1 <= mDegree[1]; ++j1)
- {
- Real blend1 = mBlender[1][c1 + mDegreeP1[1] * j1];
- for (int j0 = 0; j0 <= mDegree[0]; ++j0)
- {
- Real blend0 = mBlender[0][c0 + mDegreeP1[0] * j0];
- Real blend01 = blend0 * blend1;
- element = element + (*mControls)(r0 + j0, r1 + j1) * blend01;
- }
- }
- mTensor[index] = element;
- }
- void InitializeTensors()
- {
- int numCached = 1;
- for (int dim = 0; dim < 2; ++dim)
- {
- mNumTRows[dim] = mNumControls[dim] - mDegree[dim];
- mNumTCols[dim] = mDegreeP1[dim];
- numCached *= mNumTRows[dim] * mNumTCols[dim];
- }
- mTensor.resize(numCached);
- mCached.resize(numCached);
- if (mCacheMode == this->PRE_CACHING)
- {
- for (int r1 = 0, index = 0; r1 < mNumTRows[1]; ++r1)
- {
- for (int r0 = 0; r0 < mNumTRows[0]; ++r0)
- {
- for (int c1 = 0; c1 < mNumTCols[1]; ++c1)
- {
- for (int c0 = 0; c0 < mNumTCols[0]; ++c0, ++index)
- {
- ComputeTensor(r0, r1, c0, c1, index);
- }
- }
- }
- }
- std::fill(mCached.begin(), mCached.end(), true);
- }
- else
- {
- std::fill(mCached.begin(), mCached.end(), false);
- }
- }
- // Constructor inputs.
- std::array<int, 2> mDegree;
- Controls const* mControls;
- typename Controls::Type mCTZero;
- int mCacheMode;
- // Parameters for B-spline evaluation.
- std::array<int, 2> mDegreeP1;
- std::array<int, 2> mNumControls;
- std::array<Real, 2> mTMin, mTMax;
- std::array<std::vector<Real>, 2> mBlender;
- std::array<std::vector<Real>, 2> mDCoefficient;
- std::array<std::vector<int>, 2> mLMax;
- std::array<std::vector<Real>, 2> mPowerDSDT;
- // Support for no-cached B-spline evaluation.
- std::array<std::vector<Real>, 2> mPhi;
- // Support for cached B-spline evaluation.
- std::array<int, 2> mNumTRows, mNumTCols;
- std::vector<typename Controls::Type> mTensor;
- std::vector<bool> mCached;
- };
- }
- // Specialization for 3-dimensional data.
- namespace WwiseGTE
- {
- template <typename Real, typename Controls>
- class IntpBSplineUniform<Real, Controls, 3> : public IntpBSplineUniformShared<Real, Controls>
- {
- public:
- // The caller is responsible for ensuring that the IntpBSplineUniform3
- // object persists as long as the input 'controls' exists.
- IntpBSplineUniform(std::array<int, 3> const& degrees, Controls const& controls,
- typename Controls::Type ctZero, int cacheMode)
- :
- IntpBSplineUniformShared<Real, Controls>(),
- mDegree(degrees),
- mControls(&controls),
- mCTZero(ctZero),
- mCacheMode(cacheMode)
- {
- // The condition c+1 > d+1 is required so that when s = c+1-d, its
- // maximum value, we have at least two s-knots (d and d + 1).
- for (int dim = 0; dim < 3; ++dim)
- {
- if (mControls->GetSize(dim) <= mDegree[dim] + 1)
- {
- LogError("Incompatible degree and number of controls.");
- }
- }
- for (int dim = 0; dim < 3; ++dim)
- {
- mDegreeP1[dim] = mDegree[dim] + 1;
- mNumControls[dim] = mControls->GetSize(dim);
- mTMin[dim] = (Real)-0.5;
- mTMax[dim] = static_cast<Real>(mNumControls[dim]) - (Real)0.5;
- mNumTRows[dim] = 0;
- mNumTCols[dim] = 0;
- this->ComputeBlendingMatrix(mDegree[dim], mBlender[dim]);
- this->ComputeDCoefficients(mDegree[dim], mDCoefficient[dim], mLMax[dim]);
- this->ComputePowers(mDegree[dim], mNumControls[dim], mTMin[dim], mTMax[dim], mPowerDSDT[dim]);
- }
- if (mCacheMode == this->NO_CACHING)
- {
- for (int dim = 0; dim < 3; ++dim)
- {
- mPhi[dim].resize(mDegreeP1[dim]);
- }
- }
- else
- {
- InitializeTensors();
- }
- }
- // Disallow copying and moving.
- IntpBSplineUniform(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform const&) = delete;
- IntpBSplineUniform(IntpBSplineUniform&&) = delete;
- IntpBSplineUniform& operator=(IntpBSplineUniform&&) = delete;
- // Member access. The input i specifies the dimension (0, 1, 2).
- inline int GetDegree(int dim) const
- {
- return mDegree[dim];
- }
- inline int GetNumControls(int dim) const
- {
- return mNumControls[dim];
- }
- inline Real GetTMin(int dim) const
- {
- return mTMin[dim];
- }
- inline Real GetTMax(int dim) const
- {
- return mTMax[dim];
- }
- // Evaluate the interpolator. The order is (0,0,0) when you want the
- // B-spline function value itself. The order0 is 1 for the first
- // derivative with respect to t0, the order1 is 1 for the first
- // derivative with respect to t1 or the order2 is 1 for the first
- // derivative with respect to t2. Higher-order derivatives in other
- // t-inputs are computed similarly.
- typename Controls::Type Evaluate(std::array<int, 3> const& order,
- std::array<Real, 3> const& t)
- {
- auto result = mCTZero;
- if (0 <= order[0] && order[0] <= mDegree[0]
- && 0 <= order[1] && order[1] <= mDegree[1]
- && 0 <= order[2] && order[2] <= mDegree[2])
- {
- std::array<int, 3> i;
- std::array<Real, 3> u;
- for (int dim = 0; dim < 3; ++dim)
- {
- this->GetKey(t[dim], mTMin[dim], mTMax[dim], mPowerDSDT[dim][1],
- mNumControls[dim], mDegree[dim], i[dim], u[dim]);
- }
- if (mCacheMode == this->NO_CACHING)
- {
- for (int dim = 0; dim < 3; ++dim)
- {
- int jIndex = 0;
- for (int j = 0; j <= mDegree[dim]; ++j)
- {
- int kjIndex = mDegree[dim] + jIndex;
- int ell = mLMax[dim][order[dim]];
- mPhi[dim][j] = (Real)0;
- for (int k = mDegree[dim]; k >= order[dim]; --k)
- {
- mPhi[dim][j] = mPhi[dim][j] * u[dim] +
- mBlender[dim][kjIndex--] * mDCoefficient[dim][ell--];
- }
- jIndex += mDegreeP1[dim];
- }
- }
- for (int j2 = 0; j2 <= mDegree[2]; ++j2)
- {
- Real phi2 = mPhi[2][j2];
- for (int j1 = 0; j1 <= mDegree[1]; ++j1)
- {
- Real phi1 = mPhi[1][j1];
- Real phi12 = phi1 * phi2;
- for (int j0 = 0; j0 <= mDegree[0]; ++j0)
- {
- Real phi0 = mPhi[0][j0];
- Real phi012 = phi0 * phi12;
- result = result + (*mControls)(i[0] + j0, i[1] + j1, i[2] + j2) * phi012;
- }
- }
- }
- }
- else
- {
- int i0i1i2Index = mNumTCols[2] * (i[0] + mNumTRows[0] * (i[1] + mNumTRows[1] * i[2]));
- int k2i0i1i2Index = mDegree[2] + i0i1i2Index;
- int ell2 = mLMax[2][order[2]];
- for (int k2 = mDegree[2]; k2 >= order[2]; --k2)
- {
- int k1k2i0i1i2Index = mDegree[1] + mNumTCols[1] * k2i0i1i2Index;
- int ell1 = mLMax[1][order[1]];
- auto term1 = mCTZero;
- for (int k1 = mDegree[1]; k1 >= order[1]; --k1)
- {
- int k0k1k2i0i1i2Index = mDegree[0] + mNumTCols[0] * k1k2i0i1i2Index;
- int ell0 = mLMax[0][order[0]];
- auto term0 = mCTZero;
- for (int k0 = mDegree[0]; k0 >= order[0]; --k0)
- {
- if (mCacheMode == this->ON_DEMAND_CACHING && !mCached[k0k1k2i0i1i2Index])
- {
- ComputeTensor(i[0], i[1], i[2], k0, k1, k2, k0k1k2i0i1i2Index);
- mCached[k0k1k2i0i1i2Index] = true;
- }
- term0 = term0 * u[0] + mTensor[k0k1k2i0i1i2Index--] * mDCoefficient[0][ell0--];
- }
- term1 = term1 * u[1] + term0 * mDCoefficient[1][ell1--];
- --k1k2i0i1i2Index;
- }
- result = result * u[2] + term1 * mDCoefficient[2][ell2--];
- --k2i0i1i2Index;
- }
- }
- Real adjust(1);
- for (int dim = 0; dim < 3; ++dim)
- {
- adjust *= mPowerDSDT[dim][order[dim]];
- }
- result = result * adjust;
- }
- return result;
- }
- protected:
- void ComputeTensor(int r0, int r1, int r2, int c0, int c1, int c2, int index)
- {
- auto element = mCTZero;
- for (int j2 = 0; j2 <= mDegree[2]; ++j2)
- {
- Real blend2 = mBlender[2][c2 + mDegreeP1[2] * j2];
- for (int j1 = 0; j1 <= mDegree[1]; ++j1)
- {
- Real blend1 = mBlender[1][c1 + mDegreeP1[1] * j1];
- Real blend12 = blend1 * blend2;
- for (int j0 = 0; j0 <= mDegree[0]; ++j0)
- {
- Real blend0 = mBlender[0][c0 + mDegreeP1[0] * j0];
- Real blend012 = blend0 * blend12;
- element = element + (*mControls)(r0 + j0, r1 + j1, r2 + j2) * blend012;
- }
- }
- }
- mTensor[index] = element;
- }
- void InitializeTensors()
- {
- int numCached = 1;
- for (int dim = 0; dim < 3; ++dim)
- {
- mNumTRows[dim] = mNumControls[dim] - mDegree[dim];
- mNumTCols[dim] = mDegreeP1[dim];
- numCached *= mNumTRows[dim] * mNumTCols[dim];
- }
- mTensor.resize(numCached);
- mCached.resize(numCached);
- if (mCacheMode == this->PRE_CACHING)
- {
- for (int r2 = 0, index = 0; r2 < mNumTRows[2]; ++r2)
- {
- for (int r1 = 0; r1 < mNumTRows[1]; ++r1)
- {
- for (int r0 = 0; r0 < mNumTRows[0]; ++r0)
- {
- for (int c2 = 0; c2 < mNumTCols[2]; ++c2)
- {
- for (int c1 = 0; c1 < mNumTCols[1]; ++c1)
- {
- for (int c0 = 0; c0 < mNumTCols[0]; ++c0, ++index)
- {
- ComputeTensor(r0, r1, r2, c0, c1, c2, index);
- }
- }
- }
- }
- }
- }
- std::fill(mCached.begin(), mCached.end(), true);
- }
- else
- {
- std::fill(mCached.begin(), mCached.end(), false);
- }
- }
- // Constructor inputs.
- std::array<int, 3> mDegree;
- Controls const* mControls;
- typename Controls::Type mCTZero;
- int mCacheMode;
- // Parameters for B-spline evaluation.
- std::array<int, 3> mDegreeP1;
- std::array<int, 3> mNumControls;
- std::array<Real, 3> mTMin, mTMax;
- std::array<std::vector<Real>, 3> mBlender;
- std::array<std::vector<Real>, 3> mDCoefficient;
- std::array<std::vector<int>, 3> mLMax;
- std::array<std::vector<Real>, 3> mPowerDSDT;
- // Support for no-cached B-spline evaluation.
- std::array<std::vector<Real>, 3> mPhi;
- // Support for cached B-spline evaluation.
- std::array<int, 3> mNumTRows, mNumTCols;
- std::vector<typename Controls::Type> mTensor;
- std::vector<bool> mCached;
- };
- }
- #endif
|