123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- #pragma once
- #include <Mathematics/GaussNewtonMinimizer.h>
- #include <Mathematics/LevenbergMarquardtMinimizer.h>
- #include <Mathematics/RootsPolynomial.h>
- namespace WwiseGTE
- {
- template <typename Real>
- class ApprCone3
- {
- public:
- ApprCone3()
- :
- mNumPoints(0),
- mPoints(nullptr)
- {
-
- mFFunction = [this](GVector<Real> const& P, GVector<Real>& F)
- {
- Vector<3, Real> V = { P[0], P[1], P[2] };
- Vector<3, Real> W = { P[3], P[4], P[5] };
- for (int i = 0; i < mNumPoints; ++i)
- {
- Vector<3, Real> delta = V - mPoints[i];
- Real deltaDotW = Dot(delta, W);
- F[i] = Dot(delta, delta) - deltaDotW * deltaDotW;
- }
- };
-
-
- mJFunction = [this](GVector<Real> const& P, GMatrix<Real>& J)
- {
- Vector<3, Real> V = { P[0], P[1], P[2] };
- Vector<3, Real> W = { P[3], P[4], P[5] };
- for (int row = 0; row < mNumPoints; ++row)
- {
- Vector<3, Real> delta = V - mPoints[row];
- Real deltaDotW = Dot(delta, W);
- Vector<3, Real> temp0 = delta - deltaDotW * W;
- Vector<3, Real> temp1 = deltaDotW * delta;
- for (int col = 0; col < 3; ++col)
- {
- J(row, col) = (Real)2 * temp0[col];
- J(row, col + 3) = (Real)-2 * temp1[col];
- }
- }
- };
- }
-
-
-
-
-
-
-
-
-
- typename GaussNewtonMinimizer<Real>::Result
- operator()(int numPoints, Vector<3, Real> const* points,
- size_t maxIterations, Real updateLengthTolerance, Real errorDifferenceTolerance,
- bool useConeInputAsInitialGuess,
- Vector<3, Real>& coneVertex, Vector<3, Real>& coneAxis, Real& coneAngle)
- {
- mNumPoints = numPoints;
- mPoints = points;
- GaussNewtonMinimizer<Real> minimizer(6, mNumPoints, mFFunction, mJFunction);
- Real coneCosAngle;
- if (useConeInputAsInitialGuess)
- {
- Normalize(coneAxis);
- coneCosAngle = std::cos(coneAngle);
- }
- else
- {
- ComputeInitialCone(coneVertex, coneAxis, coneCosAngle);
- }
-
- GVector<Real> initial(6);
- initial[0] = coneVertex[0];
- initial[1] = coneVertex[1];
- initial[2] = coneVertex[2];
-
- initial[3] = coneAxis[0] / coneCosAngle;
- initial[4] = coneAxis[1] / coneCosAngle;
- initial[5] = coneAxis[2] / coneCosAngle;
- auto result = minimizer(initial, maxIterations, updateLengthTolerance,
- errorDifferenceTolerance);
-
-
-
- for (int i = 0; i < 3; ++i)
- {
- coneVertex[i] = result.minLocation[i];
- coneAxis[i] = result.minLocation[i + 3];
- }
-
-
-
-
- coneCosAngle = std::min((Real)1 / Normalize(coneAxis), (Real)1);
- coneAngle = std::acos(coneCosAngle);
- mNumPoints = 0;
- mPoints = nullptr;
- return result;
- }
-
-
-
-
-
- typename LevenbergMarquardtMinimizer<Real>::Result
- operator()(int numPoints, Vector<3, Real> const* points,
- size_t maxIterations, Real updateLengthTolerance, Real errorDifferenceTolerance,
- Real lambdaFactor, Real lambdaAdjust, size_t maxAdjustments,
- bool useConeInputAsInitialGuess,
- Vector<3, Real>& coneVertex, Vector<3, Real>& coneAxis, Real& coneAngle)
- {
- mNumPoints = numPoints;
- mPoints = points;
- LevenbergMarquardtMinimizer<Real> minimizer(6, mNumPoints, mFFunction, mJFunction);
- Real coneCosAngle;
- if (useConeInputAsInitialGuess)
- {
- Normalize(coneAxis);
- coneCosAngle = std::cos(coneAngle);
- }
- else
- {
- ComputeInitialCone(coneVertex, coneAxis, coneCosAngle);
- }
-
- GVector<Real> initial(6);
- initial[0] = coneVertex[0];
- initial[1] = coneVertex[1];
- initial[2] = coneVertex[2];
-
- initial[3] = coneAxis[0] / coneCosAngle;
- initial[4] = coneAxis[1] / coneCosAngle;
- initial[5] = coneAxis[2] / coneCosAngle;
- auto result = minimizer(initial, maxIterations, updateLengthTolerance,
- errorDifferenceTolerance, lambdaFactor, lambdaAdjust, maxAdjustments);
-
-
-
- for (int i = 0; i < 3; ++i)
- {
- coneVertex[i] = result.minLocation[i];
- coneAxis[i] = result.minLocation[i + 3];
- }
-
-
-
-
- coneCosAngle = std::min((Real)1 / Normalize(coneAxis), (Real)1);
- coneAngle = std::acos(coneCosAngle);
- mNumPoints = 0;
- mPoints = nullptr;
- return result;
- }
- private:
- void ComputeInitialCone(Vector<3, Real>& coneVertex, Vector<3, Real>& coneAxis, Real& coneCosAngle)
- {
-
- Vector<3, Real> center{ (Real)0, (Real)0, (Real)0 };
- Real const invNumPoints = (Real)1 / (Real)mNumPoints;
- for (int i = 0; i < mNumPoints; ++i)
- {
- center += mPoints[i];
- }
- center *= invNumPoints;
-
- coneAxis = { (Real)0, (Real)0, (Real)0 };
- for (int i = 0; i < mNumPoints; ++i)
- {
- Vector<3, Real> diff = mPoints[i] - center;
- coneAxis += Dot(diff, diff) * diff;
- }
- coneAxis *= invNumPoints;
- Normalize(coneAxis);
-
-
- Real c10 = (Real)0, c20 = (Real)0, c30 = (Real)0, c01 = (Real)0;
- Real c02 = (Real)0, c11 = (Real)0, c21 = (Real)0;
- for (int i = 0; i < mNumPoints; ++i)
- {
- Vector<3, Real> diff = mPoints[i] - center;
- Real ai = Dot(coneAxis, diff);
- Real aisqr = ai * ai;
- Real bi = Dot(diff, diff);
- c10 += ai;
- c20 += aisqr;
- c30 += aisqr * ai;
- c01 += bi;
- c02 += bi * bi;
- c11 += ai * bi;
- c21 += aisqr * bi;
- }
- c10 *= invNumPoints;
- c20 *= invNumPoints;
- c30 *= invNumPoints;
- c01 *= invNumPoints;
- c02 *= invNumPoints;
- c11 *= invNumPoints;
- c21 *= invNumPoints;
-
- Real e0 = (Real)3 * c10;
- Real e1 = (Real)2 * c20 + c01;
- Real e2 = c11;
- Real e3 = (Real)3 * c20;
- Real e4 = c30;
-
- Real g0 = c11 * c21 - c02 * c30;
- Real g1 = c01 * c21 - (Real)3 * c02 * c20 + (Real)2 * (c20 * c21 - c11 * (c30 - c11));
- Real g2 = (Real)3 * (c11 * (c01 - c20) + c10 * (c21 - c02));
- Real g3 = c21 - c02 + c01 * (c01 + c20) + (Real)2 * (c10 * (c30 - c11) - c20 * c20);
- Real g4 = c30 - c11 + c10 * (c01 - c20);
-
- std::map<Real, int> rmMap;
- RootsPolynomial<Real>::SolveQuartic(g0, g1, g2, g3, g4, rmMap);
-
-
-
-
-
-
- std::vector<std::array<Real, 3>> info;
- Real s, t;
- for (auto const& element : rmMap)
- {
- t = element.first;
- if (t > (Real)0)
- {
- Real p3 = e2 + t * (e1 + t * (e0 + t));
- if (p3 != (Real)0)
- {
- Real q3 = e4 + t * (e3 + t * (e0 + t));
- s = q3 / p3;
- if ((Real)0 < s && s < (Real)1)
- {
- Real error(0);
- for (int i = 0; i < mNumPoints; ++i)
- {
- Vector<3, Real> diff = mPoints[i] - center;
- Real ai = Dot(coneAxis, diff);
- Real bi = Dot(diff, diff);
- Real tpai = t + ai;
- Real Fi = s * (bi + t * ((Real)2 * ai + t)) - tpai * tpai;
- error += Fi * Fi;
- }
- error *= invNumPoints;
- std::array<Real, 3> item = { s, t, error };
- info.push_back(item);
- }
- }
- }
- }
- Real minError = std::numeric_limits<Real>::max();
- std::array<Real, 3> minItem = { (Real)0, (Real)0, minError };
- for (auto const& item : info)
- {
- if (item[2] < minError)
- {
- minItem = item;
- }
- }
- if (minItem[2] < std::numeric_limits<Real>::max())
- {
-
- coneVertex = center - minItem[1] * coneAxis;
- coneCosAngle = std::sqrt(minItem[0]);
- }
- else
- {
- coneVertex = center;
- coneCosAngle = (Real)GTE_C_INV_SQRT_2;
- }
- }
- int mNumPoints;
- Vector<3, Real> const* mPoints;
- std::function<void(GVector<Real> const&, GVector<Real>&)> mFFunction;
- std::function<void(GVector<Real> const&, GMatrix<Real>&)> mJFunction;
- };
- }
|