123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- #pragma once
- #include <Mathematics/GMatrix.h>
- #include <Mathematics/LinearSystem.h>
- #include <functional>
- namespace WwiseGTE
- {
- template <typename Real>
- class RiemannianGeodesic
- {
- public:
-
-
- RiemannianGeodesic(int dimension)
- :
- integralSamples(16),
- searchSamples(32),
- derivativeStep((Real)1e-04),
- subdivisions(7),
- refinements(8),
- searchRadius((Real)1),
- refineCallback([]() {}),
- mDimension(dimension >= 2 ? dimension : 2),
- mMetric(mDimension, mDimension),
- mMetricInverse(mDimension, mDimension),
- mChristoffel1(mDimension),
- mChristoffel2(mDimension),
- mMetricDerivative(mDimension),
- mMetricInverseExists(true),
- mSubdivide(0),
- mRefine(0),
- mCurrentQuantity(0),
- mIntegralStep((Real)1 / (Real)(integralSamples - 1)),
- mSearchStep((Real)1 / (Real)searchSamples),
- mDerivativeFactor((Real)0.5 / derivativeStep)
- {
- LogAssert(dimension >= 2, "Dimension must be at least 2.");
- for (int i = 0; i < mDimension; ++i)
- {
- mChristoffel1[i].SetSize(mDimension, mDimension);
- mChristoffel2[i].SetSize(mDimension, mDimension);
- mMetricDerivative[i].SetSize(mDimension, mDimension);
- }
- }
- virtual ~RiemannianGeodesic()
- {
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int integralSamples;
- int searchSamples;
- Real derivativeStep;
- int subdivisions;
- int refinements;
- Real searchRadius;
-
- inline int GetDimension() const
- {
- return mDimension;
- }
-
- Real ComputeSegmentLength(GVector<Real> const& point0, GVector<Real> const& point1)
- {
-
-
-
-
- GVector<Real> diff = point1 - point0;
- GVector<Real> temp(mDimension);
-
- ComputeMetric(point0);
- Real qForm = Dot(diff, mMetric * diff);
- LogAssert(qForm > (Real)0, "Unexpected condition.");
- Real length = std::sqrt(qForm);
-
- ComputeMetric(point1);
- qForm = Dot(diff, mMetric * diff);
- LogAssert(qForm > (Real)0, "Unexpected condition.");
- length += std::sqrt(qForm);
- length *= (Real)0.5;
- int imax = integralSamples - 2;
- for (int i = 1; i <= imax; ++i)
- {
-
- Real t = mIntegralStep * static_cast<Real>(i);
- temp = point0 + t * diff;
- ComputeMetric(temp);
- qForm = Dot(diff, mMetric * diff);
- LogAssert(qForm > (Real)0, "Unexpected condition.");
- length += std::sqrt(qForm);
- }
- length *= mIntegralStep;
- return length;
- }
-
-
- Real ComputeTotalLength(int quantity, std::vector<GVector<Real>> const& path)
- {
- LogAssert(quantity >= 2, "Path must have at least two points.");
- Real length = ComputeSegmentLength(path[0], path[1]);
- for (int i = 1; i <= quantity - 2; ++i)
- {
- length += ComputeSegmentLength(path[i], path[i + 1]);
- }
- return length;
- }
-
-
- void ComputeGeodesic(GVector<Real> const& end0, GVector<Real> const& end1,
- int& quantity, std::vector<GVector<Real>>& path)
- {
- LogAssert(subdivisions < 32, "Exceeds maximum iterations.");
- quantity = (1 << subdivisions) + 1;
- path.resize(quantity);
- for (int i = 0; i < quantity; ++i)
- {
- path[i].SetSize(mDimension);
- }
- mCurrentQuantity = 2;
- path[0] = end0;
- path[1] = end1;
- for (mSubdivide = 1; mSubdivide <= subdivisions; ++mSubdivide)
- {
-
- int newQuantity = 2 * mCurrentQuantity - 1;
- LogAssert(newQuantity <= quantity, "Unexpected condition.");
-
-
-
- for (int i = mCurrentQuantity - 1; i > 0; --i)
- {
- path[2 * i] = path[i];
- }
-
- for (int i = 0; i <= mCurrentQuantity - 2; ++i)
- {
- Subdivide(path[2 * i], path[2 * i + 1], path[2 * i + 2]);
- }
- mCurrentQuantity = newQuantity;
-
- for (mRefine = 1; mRefine <= refinements; ++mRefine)
- {
- for (int i = 1; i <= mCurrentQuantity - 2; ++i)
- {
- Refine(path[i - 1], path[i], path[i + 1]);
- }
- }
- }
- LogAssert(mCurrentQuantity == quantity, "Unexpected condition.");
- mSubdivide = 0;
- mRefine = 0;
- mCurrentQuantity = 0;
- }
-
-
-
-
-
- bool Subdivide(GVector<Real> const& end0, GVector<Real>& mid, GVector<Real> const& end1)
- {
- mid = (Real)0.5 * (end0 + end1);
- auto save = refineCallback;
- refineCallback = []() {};
- bool changed = Refine(end0, mid, end1);
- refineCallback = save;
- return changed;
- }
-
-
-
-
-
- bool Refine(GVector<Real> const& end0, GVector<Real>& mid, GVector<Real> const& end1)
- {
-
-
- GVector<Real> temp = mid;
- GVector<Real> gradient(mDimension);
- for (int i = 0; i < mDimension; ++i)
- {
- temp[i] = mid[i] + derivativeStep;
- gradient[i] = ComputeSegmentLength(end0, temp);
- gradient[i] += ComputeSegmentLength(temp, end1);
- temp[i] = mid[i] - derivativeStep;
- gradient[i] -= ComputeSegmentLength(end0, temp);
- gradient[i] -= ComputeSegmentLength(temp, end1);
- temp[i] = mid[i];
- gradient[i] *= mDerivativeFactor;
- }
-
- Real length0 = ComputeSegmentLength(end0, mid);
- Real length1 = ComputeSegmentLength(mid, end1);
- Real oldLength = length0 + length1;
- Real tRay, newLength;
- GVector<Real> pRay(mDimension);
- Real multiplier = mSearchStep * searchRadius;
- Real minLength = oldLength;
- GVector<Real> minPoint = mid;
- for (int i = -searchSamples; i <= searchSamples; ++i)
- {
- tRay = multiplier * static_cast<Real>(i);
- pRay = mid - tRay * gradient;
- length0 = ComputeSegmentLength(end0, pRay);
- length1 = ComputeSegmentLength(end1, pRay);
- newLength = length0 + length1;
- if (newLength < minLength)
- {
- minLength = newLength;
- minPoint = pRay;
- }
- }
- mid = minPoint;
- refineCallback();
- return minLength < oldLength;
- }
-
- std::function<void(void)> refineCallback;
-
- inline int GetSubdivisionStep() const
- {
- return mSubdivide;
- }
- inline int GetRefinementStep() const
- {
- return mRefine;
- }
- inline int GetCurrentQuantity() const
- {
- return mCurrentQuantity;
- }
-
-
-
-
- Real ComputeSegmentCurvature(GVector<Real> const& point0, GVector<Real> const& point1)
- {
-
-
-
-
- GVector<Real> diff = point1 - point0;
- GVector<Real> temp(mDimension);
-
- Real curvature = ComputeIntegrand(point0, diff);
-
- curvature += ComputeIntegrand(point1, diff);
- curvature *= (Real)0.5;
- int imax = integralSamples - 2;
- for (int i = 1; i <= imax; ++i)
- {
-
- Real t = mIntegralStep * static_cast<Real>(i);
- temp = point0 + t * diff;
- curvature += ComputeIntegrand(temp, diff);
- }
- curvature *= mIntegralStep;
- return curvature;
- }
-
-
- Real ComputeTotalCurvature(int quantity, std::vector<GVector<Real>> const& path)
- {
- LogAssert(quantity >= 2, "Path must have at least two points.");
- Real curvature = ComputeSegmentCurvature(path[0], path[1]);
- for (int i = 1; i <= quantity - 2; ++i)
- {
- curvature += ComputeSegmentCurvature(path[i], path[i + 1]);
- }
- return curvature;
- }
- protected:
-
- Real ComputeIntegrand(GVector<Real> const& pos, GVector<Real> const& der)
- {
- ComputeMetric(pos);
- ComputeChristoffel1(pos);
- ComputeMetricInverse();
- ComputeChristoffel2();
-
- Real qForm0 = Dot(der, mMetric * der);
- LogAssert(qForm0 > (Real)0, "Unexpected condition.");
-
- GMatrix<Real> mat(mDimension, mDimension);
- for (int k = 0; k < mDimension; ++k)
- {
- mat += der[k] * mChristoffel1[k];
- }
-
-
- Real qForm1 = Dot(der, mat * der);
- Real ratio = -qForm1 / qForm0;
-
- GVector<Real> acc = ratio * der;
- for (int k = 0; k < mDimension; ++k)
- {
- acc[k] += Dot(der, mChristoffel2[k] * der);
- }
-
- Real curvature = std::sqrt(Dot(acc, mMetric * acc));
- return curvature;
- }
-
-
- virtual void ComputeMetric(GVector<Real> const& point) = 0;
-
-
-
- virtual void ComputeChristoffel1(GVector<Real> const& point) = 0;
-
-
- bool ComputeMetricInverse()
- {
- mMetricInverse = Inverse(mMetric, &mMetricInverseExists);
- return mMetricInverseExists;
- }
-
-
-
- void ComputeMetricDerivative()
- {
- for (int derivative = 0; derivative < mDimension; ++derivative)
- {
- for (int i0 = 0; i0 < mDimension; ++i0)
- {
- for (int i1 = 0; i1 < mDimension; ++i1)
- {
- mMetricDerivative[derivative](i0, i1) =
- mChristoffel1[derivative](i0, i1) +
- mChristoffel1[derivative](i1, i0);
- }
- }
- }
- }
-
-
-
-
- bool ComputeChristoffel2()
- {
- for (int i2 = 0; i2 < mDimension; ++i2)
- {
- for (int i0 = 0; i0 < mDimension; ++i0)
- {
- for (int i1 = 0; i1 < mDimension; ++i1)
- {
- Real fValue = (Real)0;
- for (int j = 0; j < mDimension; ++j)
- {
- fValue += mMetricInverse(i2, j) * mChristoffel1[j](i0, i1);
- }
- mChristoffel2[i2](i0, i1) = fValue;
- }
- }
- }
- return mMetricInverseExists;
- }
- int mDimension;
- GMatrix<Real> mMetric;
- GMatrix<Real> mMetricInverse;
- std::vector<GMatrix<Real>> mChristoffel1;
- std::vector<GMatrix<Real>> mChristoffel2;
- std::vector<GMatrix<Real>> mMetricDerivative;
- bool mMetricInverseExists;
-
- int mSubdivide, mRefine, mCurrentQuantity;
-
- Real mIntegralStep;
- Real mSearchStep;
- Real mDerivativeFactor;
- };
- }
|