Ponca  97e1aa5c56f0b8ca113a6b95d36cc3b5cde35a4e
Point Cloud Analysis library
Loading...
Searching...
No Matches
cnc.hpp
1
10#pragma once
11
12#include <random>
13#include <numeric>
14
15namespace Ponca::internal
16{
29 template <TriangleGenerationMethod Method, typename P>
31 {
32 using VectorType = typename P::VectorType;
33
34 template <typename IndexRange, typename IteratorBegin, typename IteratorEnd, typename NeighborFilter>
35 static FIT_RESULT generate(const IndexRange& /*ids*/, const IteratorBegin& /*begin*/,
36 const IteratorEnd& /*end*/, const NeighborFilter& /*w*/,
37 std::vector<Triangle<P>>& /*triangles*/
38 )
39 {
40 throw std::invalid_argument("Triangle generation method not implemented!");
41 }
42
43 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
44 static FIT_RESULT generate(const IndexRange& /*ids*/, const PointContainer& /*points*/,
45 const NeighborFilter& /*w*/, std::vector<Triangle<P>>& /*triangles*/
46 )
47 {
48 throw std::invalid_argument("Triangle generation method not implemented!");
49 }
50 };
51
56 template <typename P>
57 struct TriangleGenerator<UniformGeneration, P>
58 {
59 private:
60 static constexpr int maxTriangles{100};
61
62 public:
63 using VectorType = typename P::VectorType;
64 using Scalar = typename P::Scalar;
65
66 template <typename IndexRange, typename IteratorBegin, typename IteratorEnd, typename NeighborFilter>
67 static FIT_RESULT generate(const IndexRange& ids, const IteratorBegin& begin, const IteratorEnd& end,
68 const NeighborFilter& w, std::vector<Triangle<P>>& triangles)
69 {
70 // Makes a new array
71 std::vector<int> indices;
72 for (int index : ids)
73 {
74 // Skip the points that are outside the kernel radius
75 if (w(*std::next(begin, index)).first == Scalar(0.))
76 continue;
77 indices.push_back(index);
78 }
79 if (indices.empty())
80 return UNDEFINED;
81
82 const int lastIndex = int(indices.size()) - 1;
83 for (int i = 0; i < maxTriangles; ++i)
84 {
85 // Randomly select triangles
86 int i1 = indices[Eigen::internal::random<int>(0, lastIndex)];
87 int i2 = indices[Eigen::internal::random<int>(0, lastIndex)];
88 int i3 = indices[Eigen::internal::random<int>(0, lastIndex)];
89 if (i1 == i2 || i1 == i3 || i2 == i3)
90 continue;
91
92 triangles.push_back(
93 internal::Triangle<P>(*std::next(begin, i1), *std::next(begin, i2), *std::next(begin, i3)));
94 }
95 return STABLE;
96 }
97
98 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
99 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
100 std::vector<Triangle<P>>& triangles)
101 {
102 return generate(ids, std::begin(points), std::end(points), w, triangles);
103 }
104 };
105
110 template <typename P>
111 struct TriangleGenerator<IndependentGeneration, P>
112 {
113 private:
114 static constexpr int maxTriangles{100};
115
116 public:
117 using VectorType = typename P::VectorType;
118 using Scalar = typename P::Scalar;
119
120 template <typename IndexRange, typename IteratorBegin, typename IteratorEnd, typename NeighborFilter>
121 static FIT_RESULT generate(const IndexRange& ids, const IteratorBegin& begin, const IteratorEnd& end,
122 const NeighborFilter& w, std::vector<Triangle<P>>& triangles)
123 {
124 // Makes a new array to shuffle
125 std::vector<int> indices;
126 for (int index : ids)
127 {
128 // Skip the points that are outside the kernel radius
129 if (w(*std::next(begin, index)).first == Scalar(0.))
130 continue;
131 indices.push_back(index);
132 }
133 if (indices.empty())
134 return UNDEFINED;
135
136 // Shuffles the neighbors
137 std::random_device rd;
138 std::mt19937 rg(rd());
139 std::shuffle(indices.begin(), indices.end(), rg);
140
141 // Compute the triangles
142 triangles.clear();
143 const int max_triangles = std::min(maxTriangles, int(indices.size() / 3));
144 for (int nb_vt = 0; nb_vt < max_triangles - 2; nb_vt++)
145 {
146 int i1 = indices[nb_vt];
147 int i2 = indices[nb_vt + 1];
148 int i3 = indices[nb_vt + 2];
149 triangles.push_back(
150 internal::Triangle<P>(*std::next(begin, i1), *std::next(begin, i2), *std::next(begin, i3)));
151 }
152 return STABLE;
153 }
154
155 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
156 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
157 std::vector<Triangle<P>>& triangles)
158 {
159 return generate(ids, std::begin(points), std::end(points), w, triangles);
160 }
161 };
162
163 template <typename P>
165 {
166 using Scalar = typename P::Scalar;
167 static constexpr Scalar avg_normal_coef{Scalar(0.5)};
168 };
169
174 template <typename P>
175 struct TriangleGenerator<HexagramGeneration, P> : protected HexagramBase<P>
176 {
177 using VectorType = typename P::VectorType;
178 using Scalar = typename P::Scalar;
179
180 template <typename IndexRange, typename IteratorBegin, typename IteratorEnd, typename NeighborFilter>
181 static FIT_RESULT generate(const IndexRange& ids, const IteratorBegin& begin, const IteratorEnd& end,
182 const NeighborFilter& w, std::vector<Triangle<P>>& triangles)
183 {
184 PONCA_MULTIARCH_STD_MATH(abs);
185 // Compute normal and maximum distance.
186 VectorType c = w.center();
187 VectorType n = w.data();
188 VectorType a{VectorType::Zero()};
189 Scalar avg_d = Scalar(0);
190
191 bool isUndefined = true;
192 int valid_points_count = 0;
193 for (int index : ids)
194 {
195 auto p = *std::next(begin, index);
196
197 // Skip the points that are outside the kernel radius
198 if (w(p).first == Scalar(0.))
199 continue;
200
201 avg_d += (p.pos() - c).norm();
202 a += p.normal();
203 isUndefined = false;
204 valid_points_count++;
205 }
206 if (isUndefined)
207 return UNDEFINED;
208
209 a /= a.norm();
211 n /= n.norm();
212 avg_d /= valid_points_count;
213
214 // Define basis for sector analysis.
215 const int m = abs(n[0]) > abs(n[1]) ? (abs(n[0]) > abs(n[2]) ? 0 : 2) : (abs(n[1]) > abs(n[2]) ? 1 : 2);
216
217 const VectorType e = (m == 0) ? VectorType(0, 1, 0) : (m == 1) ? VectorType(0, 0, 1) : VectorType(1, 0, 0);
218
219 VectorType u = n.cross(e);
220 VectorType v = n.cross(u);
221 u /= u.norm();
222 v /= v.norm();
223
224 std::array<VectorType, 6> positions;
225 std::array<VectorType, 6> normals;
226 std::array<Scalar, 6> distance2;
227 std::array<VectorType, 6> targets;
228
229 for (int i = 0; i < 6; i++)
230 {
231 distance2[i] = avg_d * avg_d;
232 targets[i] = avg_d * (u * cos(i * M_PI / 3.0) + v * sin(i * M_PI / 3.0));
233 positions[i] = w.center();
234 normals[i] = w.data();
235 }
236
237 // Compute closest points.
238 for (int index : ids)
239 {
240 auto pt = *std::next(begin, index);
241
242 // Skip the points that are outside the kernel radius
243 if (w(pt).first == Scalar(0.))
244 continue;
245
246 VectorType p = pt.pos();
247 if (p == c)
248 continue; // Skip the eval point
249 const VectorType d = p - c;
250
251 for (int j = 0; j < 6; j++)
252 {
253 const Scalar d2 = (d - targets[j]).squaredNorm();
254 if (d2 < distance2[j])
255 {
256 distance2[j] = d2;
257 positions[j] = p;
258 normals[j] = pt.normal();
259 }
260 }
261 }
262 triangles.push_back(internal::Triangle<P>({positions[0], positions[2], positions[4]},
263 {normals[0], normals[2], normals[4]}));
264 triangles.push_back(internal::Triangle<P>({positions[1], positions[3], positions[5]},
265 {normals[1], normals[3], normals[5]}));
266 return STABLE;
267 }
268
269 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
270 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
271 std::vector<Triangle<P>>& triangles)
272 {
273 return generate(ids, std::begin(points), std::end(points), w, triangles);
274 }
275 };
276
281 template <typename P>
282 struct TriangleGenerator<AvgHexagramGeneration, P> : protected HexagramBase<P>
283 {
284 using VectorType = typename P::VectorType;
285 using Scalar = typename P::Scalar;
286 template <typename IndexRange, typename PointIterator, typename NeighborFilter>
287 static FIT_RESULT generate(const IndexRange& ids, const PointIterator& begin, const PointIterator& end,
288 const NeighborFilter& w, std::vector<Triangle<P>>& triangles)
289 {
290 // Compute normal and maximum distance.
291 VectorType c = w.center();
292 VectorType n = w.data();
293 VectorType a = VectorType::Zero();
294 Scalar avg_d = Scalar(0);
295
296 std::array<VectorType, 6> targets;
297 Scalar avg_normal = Scalar(0.5);
298
299 bool isUndefined = true;
300 int valid_points_count = 0;
301 for (int index : ids)
302 {
303 auto p = *std::next(begin, index);
304 if (w(p).first == Scalar(0.))
305 continue; // Skip the points that are outside the kernel radius
306 a += p.normal();
307 avg_d += (p.pos() - c).norm();
308 isUndefined = false;
309 valid_points_count++;
310 }
311 if (isUndefined)
312 return UNDEFINED;
313
314 a /= a.norm();
316 n /= n.norm();
317 avg_d /= valid_points_count;
318
319 // Define basis for sector analysis.
320 const int m = (std::abs(n[0]) > std::abs(n[1])) ? ((std::abs(n[0])) > std::abs(n[2]) ? 0 : 2)
321 : ((std::abs(n[1])) > std::abs(n[2]) ? 1 : 2);
322
323 const VectorType e = (m == 0) ? VectorType(0, 1, 0) : (m == 1) ? VectorType(0, 0, 1) : VectorType(1, 0, 0);
324 VectorType u = n.cross(e);
325 VectorType v = n.cross(u);
326 u /= u.norm();
327 v /= v.norm();
328
329 // Initialize the average values
330 std::array<VectorType, 6> array_avg_normals;
331 std::array<VectorType, 6> array_avg_pos;
332 std::array<int, 6> array_nb{};
333 for (int i = 0; i < 6; i++)
334 {
335 targets[i] = avg_d * (u * cos(i * M_PI / 3.0) + v * sin(i * M_PI / 3.0));
336 array_avg_normals[i] = VectorType::Zero();
337 array_avg_pos[i] = VectorType::Zero();
338 }
339
340 // Compute closest points.
341 for (int index : ids)
342 {
343 auto pt = *std::next(begin, index);
344 if (w(pt).first == Scalar(0.))
345 continue; // Skip the points that are outside the kernel radius
346 VectorType p = pt.pos() - c;
347 int best_k = 0;
348 Scalar best_d2 = (p - targets[0]).squaredNorm();
349 for (int k = 1; k < 6; k++)
350 {
351 const Scalar d2 = (p - targets[k]).squaredNorm();
352 if (d2 < best_d2)
353 {
354 best_k = k;
355 best_d2 = d2;
356 }
357 }
358 array_avg_normals[best_k] += pt.normal();
359 array_avg_pos[best_k] += pt.pos();
360 array_nb[best_k] += 1;
361 }
362
363 for (int i = 0; i < 6; i++)
364 {
365 if (array_nb[i] == 0)
366 {
367 array_avg_normals[i] = w.data();
368 array_avg_pos[i] = w.center();
369 }
370 else
371 {
372 array_avg_normals[i] /= array_avg_normals[i].norm();
373 array_avg_pos[i] /= Scalar(array_nb[i]);
374 }
375 }
376
377 triangles.push_back(
378 internal::Triangle<P>({array_avg_pos[0], array_avg_pos[2], array_avg_pos[4]},
379 {array_avg_normals[0], array_avg_normals[2], array_avg_normals[4]}));
380 triangles.push_back(
381 internal::Triangle<P>({array_avg_pos[1], array_avg_pos[3], array_avg_pos[5]},
382 {array_avg_normals[1], array_avg_normals[3], array_avg_normals[5]}));
383 return STABLE;
384 }
385
386 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
387 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
388 std::vector<Triangle<P>>& triangles)
389 {
390 return generate(ids, std::begin(points), std::end(points), w, triangles);
391 }
392 };
393} // namespace Ponca::internal
394
395namespace Ponca
396{
397 template <class P, TriangleGenerationMethod M>
398 requires CNC_REQUIREMENTS
399 template <typename IteratorBegin, typename IteratorEnd>
400 FIT_RESULT CNC<P, M>::compute(const IteratorBegin& begin, const IteratorEnd& end)
401 {
402 init();
403 std::vector<unsigned int> indicesSample(std::distance(begin, end));
404 std::iota(indicesSample.begin(), indicesSample.end(), 0);
405
406 m_eCurrentState =
407 internal::TriangleGenerator<M, P>::generate(indicesSample, begin, end, m_nFilter, m_triangles);
408 if (m_eCurrentState != STABLE)
409 return m_eCurrentState;
410 m_nb_vt = int(m_triangles.size());
411 return finalize();
412 }
413
414 template <class P, TriangleGenerationMethod M>
415 requires CNC_REQUIREMENTS
416 template <typename PointContainer>
417 FIT_RESULT CNC<P, M>::compute(const PointContainer& points)
418 {
419 init();
420 std::vector<unsigned int> indicesSample(points.size());
421 std::iota(indicesSample.begin(), indicesSample.end(), 0);
422
423 m_eCurrentState = internal::TriangleGenerator<M, P>::generate(indicesSample, points, m_nFilter, m_triangles);
424 if (m_eCurrentState != STABLE)
425 return m_eCurrentState;
426 m_nb_vt = int(m_triangles.size());
427 return finalize();
428 }
429
430 template <class P, TriangleGenerationMethod M>
431 requires CNC_REQUIREMENTS
432 template <typename IndexRange, typename PointContainer>
433 FIT_RESULT CNC<P, M>::computeWithIds(const IndexRange& ids, const PointContainer& points)
434 {
435 init();
436 m_eCurrentState = internal::TriangleGenerator<M, P>::generate(ids, points, m_nFilter, m_triangles);
437 if (m_eCurrentState != STABLE)
438 return m_eCurrentState;
439 m_nb_vt = int(m_triangles.size());
440 return finalize();
441 }
442
443 template <class P, TriangleGenerationMethod M>
444 requires CNC_REQUIREMENTS
446 {
447 m_A = Scalar(0);
448 m_H = Scalar(0);
449 m_G = Scalar(0);
450
451 MatrixType localT = MatrixType::Zero();
452
453 for (int t = 0; t < m_nb_vt; ++t)
454 {
455 // Simple estimation.
456 Scalar tA = m_triangles[t].mu0InterpolatedU();
458 {
459 m_A -= tA;
460 m_H += m_triangles[t].template mu1InterpolatedU<true>();
461 m_G += m_triangles[t].template mu2InterpolatedU<true>();
462 localT += m_triangles[t].template muXYInterpolatedU<true>();
463 }
464 else if (tA > internal::CNCEigen<P>::epsilon)
465 {
466 m_A += tA;
467 m_H += m_triangles[t].mu1InterpolatedU();
468 m_G += m_triangles[t].mu2InterpolatedU();
469 localT += m_triangles[t].muXYInterpolatedU();
470 }
471 } // end for t
472
473 m_T11 = localT(0, 0);
474 m_T12 = Scalar(0.5) * (localT(0, 1) + localT(1, 0));
475 m_T13 = Scalar(0.5) * (localT(0, 2) + localT(2, 0));
476 m_T22 = localT(1, 1);
477 m_T23 = Scalar(0.5) * (localT(1, 2) + localT(2, 1));
478 m_T33 = localT(2, 2);
479
480 MatrixType T;
481 if (m_A != Scalar(0))
482 {
483 T << m_T11, m_T12, m_T13, m_T12, m_T22, m_T23, m_T13, m_T23, m_T33;
484 T /= m_A;
485 m_H /= m_A;
486 m_G /= m_A;
487 }
488 else
489 {
490 m_H = Scalar(0);
491 m_G = Scalar(0);
492 }
493
494 std::tie(m_k2, m_k1, m_v2, m_v1) = internal::CNCEigen<P>::curvaturesFromTensor(T, 1.0, m_nFilter.data());
495
496 return STABLE;
497 }
498} // namespace Ponca
Corrected Normal Current Fit type.
Definition cnc.h:112
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:433
FIT_RESULT compute(const IteratorBegin &begin, const IteratorEnd &end)
Convenience function for STL-like iterators Add neighbors stored in a container using STL-like iterat...
Definition cnc.hpp:400
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.
Definition concepts.h:11
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.
static constexpr Scalar epsilon
Small constant used to approximate zero.
Stores the three points and normals of the triangles and provides access to Corrected Normal Current ...
Definition cnc.h:35