Ponca  7d8ac87a7de01d881c9fde3c42e397b44bffb901
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 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
34 static FIT_RESULT generate(const IndexRange& /*ids*/, const PointContainer& /*points*/,
35 const NeighborFilter& /*w*/, std::vector<Triangle<P>>& /*triangles*/
36 )
37 {
38 throw std::invalid_argument("Triangle generation method not implemented!");
39 }
40 };
41
46 template <typename P>
47 struct TriangleGenerator<UniformGeneration, P>
48 {
49 private:
50 static constexpr int maxTriangles{100};
51
52 public:
53 using VectorType = typename P::VectorType;
54 using Scalar = typename P::Scalar;
55
56 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
57 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
58 std::vector<Triangle<P>>& triangles)
59 {
60 // Makes a new array
61 std::vector<int> indices;
62 for (int index : ids)
63 {
64 // Skip the points that are outside the kernel radius
65 if (w(points[index]).first == Scalar(0.))
66 continue;
67 indices.push_back(index);
68 }
69 if (indices.empty())
70 return UNDEFINED;
71
72 const int lastIndex = int(indices.size()) - 1;
73 for (int i = 0; i < maxTriangles; ++i)
74 {
75 // Randomly select triangles
76 int i1 = indices[Eigen::internal::random<int>(0, lastIndex)];
77 int i2 = indices[Eigen::internal::random<int>(0, lastIndex)];
78 int i3 = indices[Eigen::internal::random<int>(0, lastIndex)];
79 if (i1 == i2 || i1 == i3 || i2 == i3)
80 continue;
81
82 triangles.push_back(internal::Triangle<P>(points[i1], points[i2], points[i3]));
83 }
84 return STABLE;
85 }
86 };
87
92 template <typename P>
93 struct TriangleGenerator<IndependentGeneration, P>
94 {
95 private:
96 static constexpr int maxTriangles{100};
97
98 public:
99 using VectorType = typename P::VectorType;
100 using Scalar = typename P::Scalar;
101
102 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
103 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
104 std::vector<Triangle<P>>& triangles)
105 {
106 // Makes a new array to shuffle
107 std::vector<int> indices;
108 for (int index : ids)
109 {
110 // Skip the points that are outside the kernel radius
111 if (w(points[index]).first == Scalar(0.))
112 continue;
113 indices.push_back(index);
114 }
115 if (indices.empty())
116 return UNDEFINED;
117
118 // Shuffles the neighbors
119 std::random_device rd;
120 std::mt19937 rg(rd());
121 std::shuffle(indices.begin(), indices.end(), rg);
122
123 // Compute the triangles
124 triangles.clear();
125 const int max_triangles = std::min(maxTriangles, int(ids.size() / 3));
126 for (int nb_vt = 0; nb_vt < max_triangles - 2; nb_vt++)
127 {
128 int i1 = indices[nb_vt];
129 int i2 = indices[nb_vt + 1];
130 int i3 = indices[nb_vt + 2];
131 triangles.push_back(internal::Triangle<P>(points[i1], points[i2], points[i3]));
132 }
133 return STABLE;
134 }
135 };
136
137 template <typename P>
139 {
140 using Scalar = typename P::Scalar;
141 static constexpr Scalar avg_normal_coef{Scalar(0.5)};
142 };
143
148 template <typename P>
149 struct TriangleGenerator<HexagramGeneration, P> : protected HexagramBase<P>
150 {
151 using VectorType = typename P::VectorType;
152 using Scalar = typename P::Scalar;
153
154 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
155 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
156 std::vector<Triangle<P>>& triangles)
157 {
158 PONCA_MULTIARCH_STD_MATH(abs);
159 // Compute normal and maximum distance.
160 VectorType c = w.evalPos();
161 VectorType n = w.evalNormal();
162 VectorType a{VectorType::Zero()};
163 Scalar avg_d = Scalar(0);
164
165 bool isUndefined = true;
166 int valid_points_count = 0;
167 for (int index : ids)
168 {
169 // Skip the points that are outside the kernel radius
170 if (w(points[index]).first == Scalar(0.))
171 continue;
172 auto p = points[index];
173 avg_d += (p.pos() - c).norm();
174 a += p.normal();
175 isUndefined = false;
177 }
178 if (isUndefined)
179 return UNDEFINED;
180
181 a /= a.norm();
183 n /= n.norm();
185
186 // Define basis for sector analysis.
187 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);
188
189 const VectorType e = (m == 0) ? VectorType(0, 1, 0) : (m == 1) ? VectorType(0, 0, 1) : 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 {
203 distance2[i] = avg_d * avg_d;
204 targets[i] = avg_d * (u * cos(i * M_PI / 3.0) + v * sin(i * M_PI / 3.0));
205 positions[i] = w.evalPos();
206 normals[i] = w.evalNormal();
207 }
208
209 // Compute closest points.
210 for (int index : ids)
211 {
212 // Skip the points that are outside the kernel radius
213 if (w(points[index]).first == Scalar(0.))
214 continue;
215
216 VectorType p = points[index].pos();
217 if (p == c)
218 continue; // Skip the eval point
219 const VectorType d = p - c;
220
221 for (int j = 0; j < 6; j++)
222 {
223 const Scalar d2 = (d - targets[j]).squaredNorm();
224 if (d2 < distance2[j])
225 {
226 distance2[j] = d2;
227 positions[j] = p;
228 normals[j] = points[index].normal();
229 }
230 }
231 }
233 {normals[0], normals[2], normals[4]}));
235 {normals[1], normals[3], normals[5]}));
236
237 return STABLE;
238 }
239 };
240
245 template <typename P>
246 struct TriangleGenerator<AvgHexagramGeneration, P> : protected HexagramBase<P>
247 {
248 using VectorType = typename P::VectorType;
249 using Scalar = typename P::Scalar;
250
251 template <typename IndexRange, typename PointContainer, typename NeighborFilter>
252 static FIT_RESULT generate(const IndexRange& ids, const PointContainer& points, const NeighborFilter& w,
253 std::vector<Triangle<P>>& triangles)
254 {
255 // Compute normal and maximum distance.
256 VectorType c = w.evalPos();
257 VectorType n = w.evalNormal();
258 VectorType a = VectorType::Zero();
259 Scalar avg_d = Scalar(0);
260
261 std::array<VectorType, 6> targets;
262 Scalar avg_normal = Scalar(0.5);
263
264 bool isUndefined = true;
265 int valid_points_count = 0;
266 for (int index : ids)
267 {
268 if (w(points[index]).first == Scalar(0.))
269 continue; // Skip the points that are outside the kernel radius
270 a += points[index].normal();
271 avg_d += (points[index].pos() - c).norm();
272 isUndefined = false;
274 }
275 if (isUndefined)
276 return UNDEFINED;
277
278 a /= a.norm();
280 n /= n.norm();
282
283 // Define basis for sector analysis.
284 const int m = (std::abs(n[0]) > std::abs(n[1])) ? ((std::abs(n[0])) > std::abs(n[2]) ? 0 : 2)
285 : ((std::abs(n[1])) > std::abs(n[2]) ? 1 : 2);
286
287 const VectorType e = (m == 0) ? VectorType(0, 1, 0) : (m == 1) ? VectorType(0, 0, 1) : VectorType(1, 0, 0);
288 VectorType u = n.cross(e);
289 VectorType v = n.cross(u);
290 u /= u.norm();
291 v /= v.norm();
292
293 // Initialize the average values
294 std::array<VectorType, 6> array_avg_normals;
295 std::array<VectorType, 6> array_avg_pos;
296 std::array<int, 6> array_nb{};
297 for (int i = 0; i < 6; i++)
298 {
299 targets[i] = avg_d * (u * cos(i * M_PI / 3.0) + v * sin(i * M_PI / 3.0));
300 array_avg_normals[i] = VectorType::Zero();
301 array_avg_pos[i] = VectorType::Zero();
302 }
303
304 // Compute closest points.
305 for (int index : ids)
306 {
307 if (w(points[index]).first == Scalar(0.))
308 continue; // Skip the points that are outside the kernel radius
309 VectorType p = points[index].pos() - c;
310 int best_k = 0;
311 Scalar best_d2 = (p - targets[0]).squaredNorm();
312 for (int k = 1; k < 6; k++)
313 {
314 const Scalar d2 = (p - targets[k]).squaredNorm();
315 if (d2 < best_d2)
316 {
317 best_k = k;
318 best_d2 = d2;
319 }
320 }
321 array_avg_normals[best_k] += points[index].normal();
322 array_avg_pos[best_k] += points[index].pos();
323 array_nb[best_k] += 1;
324 }
325
326 for (int i = 0; i < 6; i++)
327 {
328 if (array_nb[i] == 0)
329 {
330 array_avg_normals[i] = w.evalNormal();
331 array_avg_pos[i] = w.evalPos();
332 }
333 else
334 {
336 array_avg_pos[i] /= Scalar(array_nb[i]);
337 }
338 }
339
340 triangles.push_back(
343 triangles.push_back(
346 return STABLE;
347 }
348 };
349} // namespace Ponca::internal
350
351namespace Ponca
352{
353 template <class P, TriangleGenerationMethod M>
354 template <typename PointContainer>
355 FIT_RESULT CNC<P, M>::compute(const PointContainer& points)
356 {
357 init();
358 std::vector<unsigned int> indicesSample(points.size());
359 std::iota(indicesSample.begin(), indicesSample.end(), 0);
360
361 m_eCurrentState = internal::TriangleGenerator<M, P>::generate(indicesSample, points, m_nFilter, m_triangles);
362 if (m_eCurrentState != STABLE)
363 return m_eCurrentState;
364 m_nb_vt = int(m_triangles.size());
365 return finalize();
366 }
367
368 template <class P, TriangleGenerationMethod M>
369 template <typename IndexRange, typename PointContainer>
370 FIT_RESULT CNC<P, M>::computeWithIds(const IndexRange& ids, const PointContainer& points)
371 {
372 init();
373 m_eCurrentState = internal::TriangleGenerator<M, P>::generate(ids, points, m_nFilter, m_triangles);
374 if (m_eCurrentState != STABLE)
375 return m_eCurrentState;
376 m_nb_vt = int(m_triangles.size());
377 return finalize();
378 }
379
380 template <class P, TriangleGenerationMethod M>
382 {
383 m_A = Scalar(0);
384 m_H = Scalar(0);
385 m_G = Scalar(0);
386
387 MatrixType localT = MatrixType::Zero();
388
389 for (int t = 0; t < m_nb_vt; ++t)
390 {
391 // Simple estimation.
392 Scalar tA = m_triangles[t].mu0InterpolatedU();
394 {
395 m_A -= tA;
396 m_H += m_triangles[t].template mu1InterpolatedU<true>();
397 m_G += m_triangles[t].template mu2InterpolatedU<true>();
398 localT += m_triangles[t].template muXYInterpolatedU<true>();
399 }
401 {
402 m_A += tA;
403 m_H += m_triangles[t].mu1InterpolatedU();
404 m_G += m_triangles[t].mu2InterpolatedU();
405 localT += m_triangles[t].muXYInterpolatedU();
406 }
407 } // end for t
408
409 m_T11 = localT(0, 0);
410 m_T12 = Scalar(0.5) * (localT(0, 1) + localT(1, 0));
411 m_T13 = Scalar(0.5) * (localT(0, 2) + localT(2, 0));
412 m_T22 = localT(1, 1);
413 m_T23 = Scalar(0.5) * (localT(1, 2) + localT(2, 1));
414 m_T33 = localT(2, 2);
415
416 MatrixType T;
417 if (m_A != Scalar(0))
418 {
419 T << m_T11, m_T12, m_T13, m_T12, m_T22, m_T23, m_T13, m_T23, m_T33;
420 T /= m_A;
421 m_H /= m_A;
422 m_G /= m_A;
423 }
424 else
425 {
426 m_H = Scalar(0);
427 m_G = Scalar(0);
428 }
429
430 std::tie(m_k2, m_k1, m_v2, m_v1) = internal::CNCEigen<P>::curvaturesFromTensor(T, 1.0, m_nFilter.evalNormal());
431
432 return STABLE;
433 }
434} // namespace Ponca
Aggregator class used to declare specialized structures using CRTP.
Definition basket.h:318
typename P::Scalar Scalar
Scalar type used for computation, as defined from template parameter P
Definition basket.h:326
FIT_RESULT finalize()
Finalize the procedure.
Definition cnc.hpp:381
FIT_RESULT compute(const PointContainer &points)
Compute function for STL-like containers.
Definition cnc.hpp:355
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:370
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:31