Ponca  5b0151ad2869758185d699615c3cca5855cc2cee
Point Cloud Analysis library
Loading...
Searching...
No Matches
cnc.hpp
1
10#pragma once
11
12#include <random>
13
14namespace Ponca::internal {
25 template <TriangleGenerationMethod Method, typename P>
27 using VectorType = typename P::VectorType;
28 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
29 static FIT_RESULT generate(
30 const IndexRange& /*ids*/,
31 const PointContainer& /*points*/,
32 const NeighborFilter& /*w*/,
33 std::vector<Triangle<P>>& /*triangles*/
34 ) {
35 throw std::invalid_argument("Triangle generation method not implemented!");
36 }
37 };
38
43 template <typename P>
44 struct TriangleGenerator<UniformGeneration, P> {
45 private:
46 static constexpr int maxTriangles {100};
47 public:
48 using VectorType = typename P::VectorType;
49 using Scalar = typename P::Scalar;
50
51 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
52 static FIT_RESULT generate(
53 const IndexRange& ids,
54 const PointContainer& points,
55 const NeighborFilter& w,
56 std::vector<Triangle<P>>& triangles
57 ) {
58 // Makes a new array
59 std::vector<int> indices;
60 for ( int index : ids ) {
61 // Skip the points that are outside the kernel radius
62 if (w(points[ index ]).first == Scalar(0.))
63 continue;
64 indices.push_back(index);
65 }
66 if (indices.empty())
67 return UNDEFINED;
68
69 const int lastIndex = int(indices.size()) - 1;
70 for (int i = 0; i < maxTriangles; ++i) {
71 // Randomly select triangles
72 int i1 = indices[Eigen::internal::random<int>(0, lastIndex)];
73 int i2 = indices[Eigen::internal::random<int>(0, lastIndex)];
74 int i3 = indices[Eigen::internal::random<int>(0, lastIndex)];
75 if (i1 == i2 || i1 == i3 || i2 == i3) continue;
76
77 triangles.push_back(internal::Triangle<P>(points[i1], points[i2], points[i3]));
78 }
79 return STABLE;
80 }
81 };
82
87 template <typename P>
88 struct TriangleGenerator<IndependentGeneration, P> {
89 private:
90 static constexpr int maxTriangles {100};
91 public:
92 using VectorType = typename P::VectorType;
93 using Scalar = typename P::Scalar;
94
95 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
96 static FIT_RESULT generate(
97 const IndexRange& ids,
98 const PointContainer& points,
99 const NeighborFilter& w,
100 std::vector<Triangle<P>>& triangles
101 ) {
102 // Makes a new array to shuffle
103 std::vector<int> indices;
104 for ( int index : ids ) {
105 // Skip the points that are outside the kernel radius
106 if (w(points[ index ]).first == Scalar(0.))
107 continue;
108 indices.push_back(index);
109 }
110 if (indices.empty())
111 return UNDEFINED;
112
113 // Shuffles the neighbors
114 std::random_device rd;
115 std::mt19937 rg(rd());
116 std::shuffle(indices.begin(), indices.end(), rg);
117
118 // Compute the triangles
119 triangles.clear();
120 const int max_triangles = std::min(maxTriangles, int(ids.size() / 3));
121 for (int nb_vt = 0; nb_vt < max_triangles-2; nb_vt++) {
122 int i1 = indices[nb_vt];
123 int i2 = indices[nb_vt+1];
124 int i3 = indices[nb_vt+2];
125 triangles.push_back(internal::Triangle<P>(points[i1], points[i2], points[i3]));
126 }
127 return STABLE;
128 }
129 };
130
131 template<typename P>
133 using Scalar = typename P::Scalar;
134 static constexpr Scalar avg_normal_coef {Scalar(0.5)};
135 };
136
137
142 template <typename P>
143 struct TriangleGenerator<HexagramGeneration, P> : protected HexagramBase<P> {
144 using VectorType = typename P::VectorType;
145 using Scalar = typename P::Scalar;
146
147 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
148 static FIT_RESULT generate(
149 const IndexRange& ids,
150 const PointContainer& points,
151 const NeighborFilter& w,
152 std::vector<Triangle<P>>& triangles
153 ) {
154 PONCA_MULTIARCH_STD_MATH(abs);
155 // Compute normal and maximum distance.
156 VectorType c = w.evalPos();
157 VectorType n = w.evalNormal();
158 VectorType a {VectorType::Zero()};
159 Scalar avg_d = Scalar(0);
160
161 bool isUndefined = true;
162 int valid_points_count = 0;
163 for ( int index : ids ) {
164 // Skip the points that are outside the kernel radius
165 if (w(points[ index ]).first == Scalar(0.))
166 continue;
167 auto p = points[ index ];
168 avg_d += ( p.pos() - c ).norm();
169 a += p.normal();
170 isUndefined = false;
171 valid_points_count ++;
172 }
173 if (isUndefined)
174 return UNDEFINED;
175
176 a /= a.norm();
178 n /= n.norm();
179 avg_d /= valid_points_count;
180
181 // Define basis for sector analysis.
182 const int m = abs( n[0] ) > abs( n[1] )
183 ? ( abs( n[0] ) > abs( n[2] ) ? 0 : 2 )
184 : ( abs( n[1] ) > abs( n[2] ) ? 1 : 2 ) ;
185
186 const VectorType e =
187 ( m == 0 ) ? VectorType( 0, 1, 0 ) :
188 ( m == 1 ) ? VectorType( 0, 0, 1 ) :
189 VectorType( 1, 0, 0 ) ;
190
191 VectorType u = n.cross( e );
192 VectorType v = n.cross( u );
193 u /= u.norm();
194 v /= v.norm();
195
196 std::array<VectorType , 6> positions ;
197 std::array<VectorType , 6> normals ;
198 std::array< Scalar , 6 > distance2;
199 std::array< VectorType, 6 > targets;
200
201 for ( int i = 0 ; i < 6 ; i++ ) {
202 distance2[ i ] = avg_d * avg_d;
203 targets [ i ] = avg_d * ( u * cos(i * M_PI / 3.0) + v * sin(i * M_PI / 3.0) );
204 positions[ i ] = w.evalPos();
205 normals [ i ] = w.evalNormal();
206 }
207
208 // Compute closest points.
209 for ( int index : ids ) {
210 // Skip the points that are outside the kernel radius
211 if (w(points[ index ]).first == Scalar(0.))
212 continue;
213
214 VectorType p = points[ index ].pos();
215 if ( p == c ) continue; // Skip the eval point
216 const VectorType d = p - c;
217
218 for ( int j = 0 ; j < 6 ; j++ ) {
219 const Scalar d2 = ( d - targets[ j ]).squaredNorm();
220 if ( d2 < distance2[ j ] ) {
221 distance2[ j ] = d2;
222 positions[ j ] = p;
223 normals [ j ] = points[ index ].normal();
224 }
225 }
226 }
227 triangles.push_back(internal::Triangle<P>({positions[0] , positions[2] , positions[4]}, {normals[0] , normals[2], normals[4]}));
228 triangles.push_back(internal::Triangle<P>({positions[1] , positions[3] , positions[5]}, {normals[1] , normals[3], normals[5]}));
229
230 return STABLE;
231 }
232 };
233
238 template <typename P>
239 struct TriangleGenerator<AvgHexagramGeneration, P> : protected HexagramBase<P> {
240 using VectorType = typename P::VectorType;
241 using Scalar = typename P::Scalar;
242
243 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
244 static FIT_RESULT generate(
245 const IndexRange& ids,
246 const PointContainer& points,
247 const NeighborFilter& w,
248 std::vector<Triangle<P>>& triangles
249 ) {
250 // Compute normal and maximum distance.
251 VectorType c = w.evalPos();
252 VectorType n = w.evalNormal();
253 VectorType a = VectorType::Zero();
254 Scalar avg_d = Scalar(0);
255
256 std::array< VectorType, 6 > targets;
257 Scalar avg_normal = Scalar(0.5);
258
259 bool isUndefined = true;
260 int valid_points_count = 0;
261 for ( int index : ids ) {
262 if (w(points[ index ]).first == Scalar(0.))
263 continue; // Skip the points that are outside the kernel radius
264 a += points[ index ].normal();
265 avg_d += ( points[ index ].pos() - c ).norm();
266 isUndefined = false;
267 valid_points_count ++;
268 }
269 if (isUndefined)
270 return UNDEFINED;
271
272 a /= a.norm();
274 n /= n.norm();
275 avg_d /= valid_points_count;
276
277 // Define basis for sector analysis.
278 const int m = ( std::abs( n[0] ) > std::abs ( n[1] ))
279 ? ( ( std::abs( n[0] ) ) > std::abs( n[2] ) ? 0 : 2 )
280 : ( ( std::abs( n[1] ) ) > std::abs( n[2] ) ? 1 : 2 );
281
282 const VectorType e = ( m == 0 ) ? VectorType( 0, 1, 0 ) :
283 ( m == 1 ) ? VectorType( 0, 0, 1 ) :
284 VectorType( 1, 0, 0 ) ;
285 VectorType u = n.cross( e );
286 VectorType v = n.cross( u );
287 u /= u.norm();
288 v /= v.norm();
289
290 // Initialize the average values
291 std::array< VectorType, 6 > array_avg_normals;
292 std::array< VectorType, 6 > array_avg_pos;
293 std::array< int, 6 > array_nb {};
294 for (int i = 0 ; i < 6 ; i++ ) {
295 targets[ i ] = avg_d * ( u * cos(i * M_PI / 3.0) + v * sin(i * M_PI / 3.0) );
296 array_avg_normals[ i ] = VectorType::Zero();
297 array_avg_pos [ i ] = VectorType::Zero();
298 }
299
300 // Compute closest points.
301 for (int index : ids) {
302 if (w(points[ index ]).first == Scalar(0.))
303 continue; // Skip the points that are outside the kernel radius
304 VectorType p = points[ index ].pos() - c;
305 int best_k = 0;
306 Scalar best_d2 = ( p - targets[ 0 ] ).squaredNorm();
307 for (int k = 1 ; k < 6 ; k++) {
308 const Scalar d2 = ( p - targets[ k ] ).squaredNorm();
309 if ( d2 < best_d2 ) {
310 best_k = k;
311 best_d2 = d2;
312 }
313 }
314 array_avg_normals[ best_k ] += points[ index ].normal();
315 array_avg_pos [ best_k ] += points[ index ].pos();
316 array_nb[ best_k ] += 1;
317 }
318
319 for (int i = 0 ; i < 6 ; i++) {
320 if ( array_nb[ i ] == 0 ) {
321 array_avg_normals[ i ] = w.evalNormal();
322 array_avg_pos [ i ] = w.evalPos();
323 } else {
324 array_avg_normals[ i ] /= array_avg_normals[ i ].norm();
325 array_avg_pos [ i ] /= array_nb[ i ];
326 }
327 }
328
329 triangles.push_back(internal::Triangle<P>(
330 { array_avg_pos[0] , array_avg_pos[2] , array_avg_pos[4] },
331 { array_avg_normals[0], array_avg_normals[2], array_avg_normals[4] }
332 ));
333 triangles.push_back(internal::Triangle<P>(
334 { array_avg_pos[1] , array_avg_pos[3] , array_avg_pos[5] },
335 { array_avg_normals[1], array_avg_normals[3], array_avg_normals[5] }
336 ));
337 return STABLE;
338 }
339 };
340} // namespace Ponca::internal
341
342namespace Ponca {
343 template < class P, TriangleGenerationMethod M>
344 template <typename PointContainer>
345 FIT_RESULT CNC<P, M>::compute( const PointContainer& points ) {
346 init();
347 std::vector<unsigned int> indicesSample(points.size());
348 std::iota(indicesSample.begin(), indicesSample.end(), 0);
349
350 m_eCurrentState = internal::TriangleGenerator<M, P>::generate( indicesSample, points, m_nFilter, m_triangles);
351 if (m_eCurrentState != STABLE) return m_eCurrentState;
352 m_nb_vt = m_triangles.size();
353 return finalize();
354 }
355
356 template < class P, TriangleGenerationMethod M>
357 template <typename IndexRange, typename PointContainer>
358 FIT_RESULT CNC<P, M>::computeWithIds( const IndexRange& ids, const PointContainer& points ) {
359 init();
360 m_eCurrentState = internal::TriangleGenerator<M, P>::generate( ids, points, m_nFilter, m_triangles);
361 if (m_eCurrentState != STABLE) return m_eCurrentState;
362 m_nb_vt = m_triangles.size();
363 return finalize();
364 }
365
366 template < class P, TriangleGenerationMethod M>
368 m_A = Scalar(0);
369 m_H = Scalar(0);
370 m_G = Scalar(0);
371
372 MatrixType localT = MatrixType::Zero();
373
374 for (int t = 0; t < m_nb_vt; ++t) {
375 // Simple estimation.
376 Scalar tA = m_triangles[t].mu0InterpolatedU();
378 m_A -= tA;
379 m_H += m_triangles[t].template mu1InterpolatedU<true>();
380 m_G += m_triangles[t].template mu2InterpolatedU<true>();
381 localT += m_triangles[t].template muXYInterpolatedU<true>();
382 } else if (tA > internal::CNCEigen<P>::epsilon) {
383 m_A += tA;
384 m_H += m_triangles[t].mu1InterpolatedU();
385 m_G += m_triangles[t].mu2InterpolatedU();
386 localT += m_triangles[t].muXYInterpolatedU();
387 }
388 } // end for t
389
390 m_T11 = localT(0,0);
391 m_T12 = 0.5 * (localT(0,1) + localT(1,0));
392 m_T13 = 0.5 * (localT(0,2) + localT(2,0));
393 m_T22 = localT(1,1);
394 m_T23 = 0.5 * (localT(1,2) + localT(2,1));
395 m_T33 = localT(2,2);
396
397 MatrixType T;
398 if (m_A != Scalar(0)) {
399 T << m_T11, m_T12, m_T13,
400 m_T12, m_T22, m_T23,
401 m_T13, m_T23, m_T33;
402 T /= m_A;
403 m_H /= m_A;
404 m_G /= m_A;
405 } else {
406 m_H = Scalar(0);
407 m_G = Scalar(0);
408 }
409
410 std::tie (m_k2, m_k1, m_v2, m_v1) = internal::CNCEigen<P>::curvaturesFromTensor(T, 1.0, m_nFilter.evalNormal());
411
412 return STABLE;
413 }
414} // namespace Ponca
FIT_RESULT finalize()
Finalize the procedure.
Definition cnc.hpp:367
FIT_RESULT compute(const PointContainer &points)
Compute function for STL-like containers.
Definition cnc.hpp:345
FIT_RESULT computeWithIds(const IndexRange &ids, const PointContainer &points)
Compute function that iterates over a subset of sampled points from an STL-Like container.
Definition cnc.hpp:358
Copyright (c) 2022 Jacques-Olivier Lachaud (jacques-olivier.lachaud@univ-savoie.fr) Laboratory of Mat...
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
@ STABLE
The fitting is stable and ready to use.
Definition enums.h:17
This class contains some stand-alone CorrectedNormalCurrent formulas for triangles,...
static std::tuple< Scalar, Scalar, VectorType, VectorType > curvaturesFromTensor(const MatrixType &tensor, const Scalar area, const VectorType &N)
Computing principal curvatures k1 and k2 from tensor.
Stores the three points and normals of the triangles and provides access to Corrected Normal Current ...
Definition cnc.h:30