Ponca  7abd0fd82719106ad460aa0f2070dffbe375d727
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
FIT_RESULT finalize()
Finalize the procedure.
Definition cnc.hpp:445
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.
Stores the three points and normals of the triangles and provides access to Corrected Normal Current ...
Definition cnc.h:35