Ponca  bab7704293a2c36e5bed9dea40def7ba839bfe08
Point Cloud Analysis library
Loading...
Searching...
No Matches
unorientedSphereFit.hpp
1/*
2 This Source Code Form is subject to the terms of the Mozilla Public
3 License, v. 2.0. If a copy of the MPL was not distributed with this
4 file, You can obtain one at http://mozilla.org/MPL/2.0/.
5*/
6
7namespace Ponca
8{
9
10 template <class DataPoint, class _NFilter, typename T>
12 {
13 Base::init();
14 m_matA.setZero();
15 m_matQ.setZero();
16 m_sumDotPP = Scalar(0.0);
17 }
18
19 template <class DataPoint, class _NFilter, typename T>
21 const DataPoint& attributes)
22 {
23 Base::addLocalNeighbor(w, localQ, attributes);
24 VectorB basis;
25 basis << attributes.normal(), attributes.normal().dot(localQ);
26
27 m_matA += w * basis * basis.transpose();
28 m_sumDotPP += w * localQ.squaredNorm();
29 }
30
31 template <class DataPoint, class _NFilter, typename T>
33 {
34 PONCA_MULTIARCH_STD_MATH(sqrt);
35 constexpr int Dim = DataPoint::Dim;
36
37 // Compute status
38 if (Base::finalize() != STABLE)
39 return Base::m_eCurrentState;
40 if (Base::getNumNeighbors() < DataPoint::Dim)
41 return Base::m_eCurrentState = UNDEFINED;
42 if (Base::algebraicSphere().isValid())
43 Base::m_eCurrentState = CONFLICT_ERROR_FOUND;
44 else
45 Base::m_eCurrentState = Base::getNumNeighbors() < 2 * DataPoint::Dim ? UNSTABLE : STABLE;
46
47 // 1. finalize sphere fitting
48 Scalar invSumW = Scalar(1.) / Base::getWeightSum();
49
50 m_matQ.template topLeftCorner<Dim, Dim>().setIdentity();
51 m_matQ.col(Dim).template head<Dim>() = Base::m_sumP * invSumW;
52 m_matQ.row(Dim).template head<Dim>() = Base::m_sumP * invSumW;
53 m_matQ(Dim, Dim) = m_sumDotPP * invSumW;
54
55 MatrixBB M = m_matQ.inverse() * m_matA;
56 m_solver.compute(M);
57 VectorB eivals = m_solver.eigenvalues().real();
58 int maxId = 0;
59 eivals.maxCoeff(&maxId);
60
61 VectorB eivec = m_solver.eigenvectors().col(maxId).real();
62
63 // integrate
64 Base::m_ul = eivec.template head<Dim>();
65 Base::m_uq = Scalar(0.5) * eivec(Dim);
66 Base::m_uc = -invSumW * (Base::m_ul.dot(Base::m_sumP) + m_sumDotPP * Base::m_uq);
67
68 Base::m_isNormalized = false;
69
70 return Base::m_eCurrentState;
71 }
72
73 template <class DataPoint, class _NFilter, int DiffType, typename T>
75 {
76 Base::init();
77 for (int dim = 0; dim < Base::NbDerivatives; ++dim)
78 m_dmatA[dim] = MatrixBB::Zero();
79 m_dSumDotPP = ScalarArray::Zero();
80 m_dUc = ScalarArray::Zero();
81 m_dUl = VectorArray::Zero();
82 m_dUq = ScalarArray::Zero();
83 }
84
85 template <class DataPoint, class _NFilter, int DiffType, typename T>
87 const DataPoint& attributes,
88 ScalarArray& dw)
89 {
90 Base::addLocalNeighbor(w, localQ, attributes, dw);
91 VectorB basis;
92 basis << attributes.normal(), attributes.normal().dot(localQ);
93 const MatrixBB prod = basis * basis.transpose();
94
95 m_dSumDotPP += dw * localQ.squaredNorm();
96
97 for (int dim = 0; dim < Base::NbDerivatives; ++dim)
98 {
99 m_dmatA[dim] += dw[dim] * prod;
100 }
101 }
102
103 template <class DataPoint, class _NFilter, int DiffType, typename T>
105 {
106 constexpr int Dim = DataPoint::Dim;
107 constexpr Scalar epsilon = Eigen::NumTraits<Scalar>::dummy_precision();
108
109 Base::finalize();
110 if (this->isReady())
111 {
112 int i = 0;
113 Base::m_solver.eigenvalues().real().maxCoeff(&i);
114 const Scalar eigenval_i = Base::m_solver.eigenvalues().real()[i];
115 const VectorB eigenvec_i = Base::m_solver.eigenvectors().real().col(i);
116
117 const Scalar invSumW = Scalar(1) / Base::getWeightSum();
118
119 // the derivative of (A vi = li Q vi), where A and Q are symmetric real, is
120 //
121 // dA vi + A dvi = dli Q vi + li dQ vi + li Q dvi
122 //
123 // left-multiply by vj (i!=j, associated to eigenvalue lj)
124 //
125 // vj . dA vi + vj . A dvi = dli vj . Q vi + li vj . dQ vi + li vj . Q dvi
126 // ^^^^^^ ^^^^^^^^^
127 // = lj vj . Q = 0
128 //
129 // which simplified to
130 //
131 // (Q vj) . dvi = 1/(li - lj) vj . dQ vi
132 //
133 // therefore
134 //
135 // dvi = sum_j (1/(li - lj) vj . dQ vi) Q vj
136 // = Q sum_j (1/(li - lj) vj . ((dA - li dQ) vi)) vj
137 //
138 for (int dim = 0; dim < Base::NbDerivatives; ++dim)
139 {
140 MatrixBB dQ;
141 dQ.template topLeftCorner<Dim, Dim>().setZero();
142 dQ.col(Dim).template head<Dim>() = Base::m_dSumP.col(dim);
143 dQ.row(Dim).template head<Dim>() = Base::m_dSumP.col(dim);
144 dQ(Dim, Dim) = m_dSumDotPP[dim];
145 dQ -= Base::m_dSumW[dim] * Base::m_matQ;
146 dQ *= invSumW;
147
148 const VectorB vec = (m_dmatA[dim] - eigenval_i * dQ) * eigenvec_i;
149
150 VectorB deigvec = VectorB::Zero();
151 for (int j = 0; j < Dim + 1; ++j)
152 {
153 if (j != i)
154 {
155 const Scalar eigenval_j = Base::m_solver.eigenvalues().real()[j];
156 const Scalar eigengap =
157 eigenval_i - eigenval_j; // positive since eigenval_i is the maximal eigenvalue
158
159 if (eigengap > epsilon)
160 {
161 const VectorB eigenvec_j = Base::m_solver.eigenvectors().real().col(j);
162 deigvec += Scalar(1) / eigengap * eigenvec_j.dot(vec) * eigenvec_j;
163 }
164 }
165 }
166
167 deigvec = Base::m_matQ * deigvec;
168
169 m_dUq[dim] = Scalar(.5) * deigvec[dim];
170 m_dUl.col(dim) = deigvec.template head<Dim>();
171 }
172
173 // same as in OrientedSphereDerImpl::finalize()
174 m_dUc = -invSumW *
175 (Base::m_sumP.transpose() * m_dUl + Base::m_sumDotPP * m_dUq +
176 Base::m_ul.transpose() * Base::m_dSumP + Base::m_uq * m_dSumDotPP + Base::m_dSumW * Base::m_uc);
177
178 return FIT_RESULT::STABLE;
179 }
180 return Base::m_eCurrentState;
181 }
182
183 template <class DataPoint, class _NFilter, int DiffType, typename T>
185 DataPoint, _NFilter, DiffType, T>::dPotential() const
186 {
187 // same as OrientedSphereDerImpl::dPotential()
188 ScalarArray dfield = m_dUc;
189 if (Base::isSpaceDer())
190 dfield.template tail<DataPoint::Dim>() += Base::m_ul;
191 return dfield;
192 }
193
194 template <class DataPoint, class _NFilter, int DiffType, typename T>
196 DataPoint, _NFilter, DiffType, T>::dNormal() const
197 {
198 // same as OrientedSphereDerImpl::dNormal()
199 VectorArray dgrad = m_dUl;
200 if (Base::isSpaceDer())
201 dgrad.template rightCols<DataPoint::Dim>().diagonal().array() += Scalar(2) * Base::m_uq;
202 Scalar norm = Base::m_ul.norm();
203 Scalar norm3 = norm * norm * norm;
204 return dgrad / norm - Base::m_ul * (Base::m_ul.transpose() * dgrad) / norm3;
205 }
206
207} // namespace Ponca
FIT_RESULT finalize()
Finalize the procedure.
void addLocalNeighbor(Scalar w, const VectorType &localQ, const DataPoint &attributes, ScalarArray &dw)
Add a neighbor to perform the fit.
typename Base::ScalarArray ScalarArray
Alias to scalar derivatives array.
typename Base::VectorType VectorType
Alias to vector type.
typename DataPoint::Scalar Scalar
Alias to scalar type.
void init()
Set the evaluation position and reset the internal states.
typename Base::VectorArray VectorArray
Alias to vector derivatives array.
void init()
Set the evaluation position and reset the internal states.
FIT_RESULT finalize()
Finalize the procedure.
typename Base::VectorType VectorType
Alias to vector type.
typename DataPoint::Scalar Scalar
Alias to scalar type.
void addLocalNeighbor(Scalar w, const VectorType &localQ, const DataPoint &attributes)
Add a neighbor to perform the fit.
This Source Code Form is subject to the terms of the Mozilla Public License, v.
DiffType
Flags defining which derivatives need to be computed.
Definition enums.h:34
FIT_RESULT
Enum corresponding to the state of a fitting method (and what the finalize function returns)
Definition enums.h:15
@ UNDEFINED
The fitting is undefined, you can't use it for valid results.
Definition enums.h:22
@ CONFLICT_ERROR_FOUND
Multiple classes of the fitting procedure initialize the primitive.
Definition enums.h:27
@ STABLE
The fitting is stable and ready to use.
Definition enums.h:17
@ UNSTABLE
The fitting is ready to use but it is considered as unstable (if the number of neighbors is low for e...
Definition enums.h:20