Ponca  f7f7d7cc113b4e53be6bee25005fbdbe7e016293
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
10template < class DataPoint, class _WFunctor, typename T>
11void
13{
14 Base::init();
15 m_matA.setZero();
16 m_matQ.setZero();
17 m_sumDotPP = Scalar(0.0);
18}
19
20template<class DataPoint, class _WFunctor, typename T>
21bool
23 const VectorType &localQ,
24 const DataPoint &attributes) {
25 if( Base::addLocalNeighbor(w, localQ, attributes) )
26 {
27 VectorB basis;
28 basis << attributes.normal(), attributes.normal().dot(localQ);
29
30 m_matA += w * basis * basis.transpose();
31 m_sumDotPP += w * localQ.squaredNorm();
32
33 return true;
34 }
35
36 return false;
37}
38
39template < class DataPoint, class _WFunctor, typename T>
42{
43 PONCA_MULTIARCH_STD_MATH(sqrt);
44 constexpr int Dim = DataPoint::Dim;
45
46 // Compute status
47 if(Base::finalize() != STABLE)
48 return Base::m_eCurrentState;
49 if(Base::getNumNeighbors() < DataPoint::Dim)
50 return Base::m_eCurrentState = UNDEFINED;
51 if (Base::algebraicSphere().isValid())
52 Base::m_eCurrentState = CONFLICT_ERROR_FOUND;
53 else
54 Base::m_eCurrentState = Base::getNumNeighbors() < 2*DataPoint::Dim ? UNSTABLE : STABLE;
55
56 // 1. finalize sphere fitting
57 Scalar invSumW = Scalar(1.) / Base::getWeightSum();
58
59 m_matQ.template topLeftCorner<Dim,Dim>().setIdentity();
60 m_matQ.col(Dim).template head<Dim>() = Base::m_sumP * invSumW;
61 m_matQ.row(Dim).template head<Dim>() = Base::m_sumP * invSumW;
62 m_matQ(Dim,Dim) = m_sumDotPP * invSumW;
63
64 MatrixBB M = m_matQ.inverse() * m_matA;
65 m_solver.compute(M);
66 VectorB eivals = m_solver.eigenvalues().real();
67 int maxId = 0;
68 eivals.maxCoeff(&maxId);
69
70 VectorB eivec = m_solver.eigenvectors().col(maxId).real();
71
72 // integrate
73 Base::m_ul = eivec.template head<Dim>();
74 Base::m_uq = Scalar(0.5) * eivec(Dim);
75 Base::m_uc = -invSumW * (Base::m_ul.dot(Base::m_sumP) + m_sumDotPP * Base::m_uq);
76
77 Base::m_isNormalized = false;
78
79 return Base::m_eCurrentState;
80}
81
82
83
84template < class DataPoint, class _WFunctor, int DiffType, typename T>
85void
87{
88 Base::init();
89 for(int dim = 0; dim < Base::NbDerivatives; ++dim)
90 m_dmatA[dim] = MatrixBB::Zero();
91 m_dSumDotPP = ScalarArray::Zero();
92 m_dUc = ScalarArray::Zero();
93 m_dUl = VectorArray::Zero();
94 m_dUq = ScalarArray::Zero();
95}
96
97template < class DataPoint, class _WFunctor, int DiffType, typename T>
98bool
100 Scalar w,
101 const VectorType &localQ,
102 const DataPoint &attributes,
103 ScalarArray& dw)
104{
105 if( Base::addLocalNeighbor(w, localQ, attributes, dw) )
106 {
107 VectorB basis;
108 basis << attributes.normal(), attributes.normal().dot(localQ);
109 const MatrixBB prod = basis * basis.transpose();
110
111 m_dSumDotPP += dw * localQ.squaredNorm();
112
113 for(int dim = 0; dim < Base::NbDerivatives; ++dim)
114 {
115 m_dmatA[dim] += dw[dim] * prod;
116 }
117
118 return true;
119 }
120 return false;
121}
122
123template < class DataPoint, class _WFunctor, int DiffType, typename T>
126{
127 constexpr int Dim = DataPoint::Dim;
128 constexpr Scalar epsilon = Eigen::NumTraits<Scalar>::dummy_precision();
129
130 Base::finalize();
131 if(this->isReady())
132 {
133 int i = 0;
134 Base::m_solver.eigenvalues().real().maxCoeff(&i);
135 const Scalar eigenval_i = Base::m_solver.eigenvalues().real()[i];
136 const VectorB eigenvec_i = Base::m_solver.eigenvectors().real().col(i);
137
138 const Scalar invSumW = Scalar(1) / Base::getWeightSum();
139
140 // the derivative of (A vi = li Q vi), where A and Q are symmetric real, is
141 //
142 // dA vi + A dvi = dli Q vi + li dQ vi + li Q dvi
143 //
144 // left-multiply by vj (i!=j, associated to eigenvalue lj)
145 //
146 // vj . dA vi + vj . A dvi = dli vj . Q vi + li vj . dQ vi + li vj . Q dvi
147 // ^^^^^^ ^^^^^^^^^
148 // = lj vj . Q = 0
149 //
150 // which simplified to
151 //
152 // (Q vj) . dvi = 1/(li - lj) vj . dQ vi
153 //
154 // therefore
155 //
156 // dvi = sum_j (1/(li - lj) vj . dQ vi) Q vj
157 // = Q sum_j (1/(li - lj) vj . ((dA - li dQ) vi)) vj
158 //
159 for(int dim = 0; dim < Base::NbDerivatives; ++dim)
160 {
161 MatrixBB dQ;
162 dQ.template topLeftCorner<Dim,Dim>().setZero();
163 dQ.col(Dim).template head<Dim>() = Base::m_dSumP.col(dim);
164 dQ.row(Dim).template head<Dim>() = Base::m_dSumP.col(dim);
165 dQ(Dim,Dim) = m_dSumDotPP[dim];
166 dQ -= Base::m_dSumW[dim] * Base::m_matQ;
167 dQ *= invSumW;
168
169 const VectorB vec = (m_dmatA[dim] - eigenval_i * dQ) * eigenvec_i;
170
171 VectorB deigvec = VectorB::Zero();
172 for(int j = 0; j < Dim+1; ++j)
173 {
174 if(j != i)
175 {
176 const Scalar eigenval_j = Base::m_solver.eigenvalues().real()[j];
177 const Scalar eigengap = eigenval_i - eigenval_j; // positive since eigenval_i is the maximal eigenvalue
178
179 if(eigengap > epsilon)
180 {
181 const VectorB eigenvec_j = Base::m_solver.eigenvectors().real().col(j);
182 deigvec += Scalar(1)/eigengap * eigenvec_j.dot(vec) * eigenvec_j;
183 }
184 }
185 }
186
187 deigvec = Base::m_matQ * deigvec;
188
189 m_dUq[dim] = Scalar(.5) * deigvec[dim];
190 m_dUl.col(dim) = deigvec.template head<Dim>();
191 }
192
193 // same as in OrientedSphereDerImpl::finalize()
194 m_dUc = -invSumW*( Base::m_sumP.transpose() * m_dUl
195 + Base::m_sumDotPP * m_dUq
196 + Base::m_ul.transpose() * Base::m_dSumP
197 + Base::m_uq * m_dSumDotPP
198 + Base::m_dSumW * Base::m_uc);
199
200 return FIT_RESULT::STABLE;
201 }
202 return Base::m_eCurrentState;
203}
204
205template < class DataPoint, class _WFunctor, int DiffType, typename T>
208{
209 // same as OrientedSphereDerImpl::dPotential()
210 ScalarArray dfield = m_dUc;
211 if(Base::isSpaceDer())
212 dfield.template tail<DataPoint::Dim>() += Base::m_ul;
213 return dfield;
214}
215
216template < class DataPoint, class _WFunctor, int DiffType, typename T>
218UnorientedSphereDerImpl<DataPoint, _WFunctor, DiffType, T>::dNormal() const
219{
220 // same as OrientedSphereDerImpl::dNormal()
221 VectorArray dgrad = m_dUl;
222 if(Base::isSpaceDer())
223 dgrad.template rightCols<DataPoint::Dim>().diagonal().array() += Scalar(2)*Base::m_uq;
224 Scalar norm = Base::m_ul.norm();
225 Scalar norm3 = norm*norm*norm;
226 return dgrad / norm - Base::m_ul * (Base::m_ul.transpose() * dgrad) / norm3;
227}
228
229} //namespace Ponca
typename Base::VectorType VectorType
Alias to vector type.
bool addLocalNeighbor(Scalar w, const VectorType &localQ, const DataPoint &attributes, ScalarArray &dw)
Add a neighbor to perform the fit.
typename DataPoint::Scalar Scalar
Alias to scalar type.
typename Base::ScalarArray ScalarArray
Alias to scalar derivatives array.
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.
bool addLocalNeighbor(Scalar w, const VectorType &localQ, const DataPoint &attributes)
Add a neighbor to perform the fit.
FIT_RESULT finalize()
Finalize the procedure.
typename DataPoint::Scalar Scalar
Alias to scalar type.
void init()
Set the evaluation position and reset the internal states.
This Source Code Form is subject to the terms of the Mozilla Public License, v.
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