ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
TriangleMeshFactory.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <IntersectionTest.h>
9 #include <Logging.h>
10 #include <Parallel.h>
11 
12 #include <Eigen/Dense>
13 #include <algorithm>
14 #include <numeric>
15 #include <queue>
16 #include <random>
17 #include <tuple>
18 
19 #include "ecvHObjectCaster.h"
20 #include "ecvKDTreeFlann.h"
21 #include "ecvMesh.h"
22 #include "ecvPointCloud.h"
23 #include "ecvQhull.h"
24 
25 #ifdef _OPENMP
26 #include <omp.h>
27 #endif
28 
29 using namespace cloudViewer;
30 
32  typedef std::tuple<double, double, double> Coordinate3;
33  std::unordered_map<Coordinate3, size_t, utility::hash_tuple<Coordinate3>>
34  point_to_old_index;
35  std::vector<int> index_old_to_new(getVerticeSize());
36  bool has_vert_normal = hasNormals();
37  bool has_vert_color = hasColors();
38  size_t old_vertex_num = getVerticeSize();
39  size_t k = 0; // new index
40  for (size_t i = 0; i < old_vertex_num; i++) { // old index
41  Coordinate3 coord = std::make_tuple(getVertice(i)(0), getVertice(i)(1),
42  getVertice(i)(2));
43  if (point_to_old_index.find(coord) == point_to_old_index.end()) {
44  point_to_old_index[coord] = i;
45  setVertice(k, getVertice(i));
46  if (has_vert_normal) setVertexNormal(k, getVertexNormal(i));
47  if (has_vert_color) setVertexColor(k, getVertexColor(i));
48  index_old_to_new[i] = (int)k;
49  k++;
50  } else {
51  index_old_to_new[i] = index_old_to_new[point_to_old_index[coord]];
52  }
53  }
54 
55  // do some cleaning
56  {
57  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
58  if (cloud) {
59  cloud->resize(static_cast<unsigned>(k));
60  } else {
62  "[RemoveUnreferencedVertices] ccMesh has not associated "
63  "cloud.");
64  }
65  }
66 
67  if (k < old_vertex_num) {
68  for (auto &triangle : *getTrianglesPtr()) {
69  triangle.i1 = static_cast<unsigned>(index_old_to_new[triangle.i1]);
70  triangle.i2 = static_cast<unsigned>(index_old_to_new[triangle.i2]);
71  triangle.i3 = static_cast<unsigned>(index_old_to_new[triangle.i3]);
72  }
73  if (hasAdjacencyList()) {
74  ComputeAdjacencyList();
75  }
76  }
78  "[RemoveDuplicatedVertices] {:d} vertices have been removed.",
79  (int)(old_vertex_num - k));
80 
81  return *this;
82 }
83 
85  if (hasTriangleUvs()) {
87  "[RemoveDuplicatedTriangles] This mesh contains triangle uvs "
88  "that are not handled in this function");
89  }
90  typedef std::tuple<int, int, int> Index3;
91  std::unordered_map<Index3, size_t, utility::hash_tuple<Index3>>
92  triangle_to_old_index;
93  bool has_tri_normal = hasTriNormals();
94  size_t old_triangle_num = size();
95  size_t k = 0;
96  for (size_t i = 0; i < old_triangle_num; i++) {
97  Index3 index;
98  // We first need to find the minimum index. Because triangle (0-1-2)
99  // and triangle (2-0-1) are the same.
100  Eigen::Vector3i triangleInd = getTriangle(i);
101  if (triangleInd(0) <= triangleInd(1)) {
102  if (triangleInd(0) <= triangleInd(2)) {
103  index = std::make_tuple(triangleInd(0), triangleInd(1),
104  triangleInd(2));
105  } else {
106  index = std::make_tuple(triangleInd(2), triangleInd(0),
107  triangleInd(1));
108  }
109  } else {
110  if (triangleInd(1) <= triangleInd(2)) {
111  index = std::make_tuple(triangleInd(1), triangleInd(2),
112  triangleInd(0));
113  } else {
114  index = std::make_tuple(triangleInd(2), triangleInd(0),
115  triangleInd(1));
116  }
117  }
118  if (triangle_to_old_index.find(index) == triangle_to_old_index.end()) {
119  triangle_to_old_index[index] = i;
120  setTriangle(k, triangleInd);
121  if (has_tri_normal) setTriangleNorm(k, getTriangleNorm(i));
122  k++;
123  }
124  }
125 
126  // do some cleaning
127  resize(k);
128  if (has_tri_normal) m_triNormals->resize(k);
129  if (k < old_triangle_num && hasAdjacencyList()) {
130  ComputeAdjacencyList();
131  }
133  "[RemoveDuplicatedTriangles] {:d} triangles have been removed.",
134  (int)(old_triangle_num - k));
135 
136  return *this;
137 }
138 
140  std::vector<bool> vertex_has_reference(getVerticeSize(), false);
141  for (const auto &triangle : *getTrianglesPtr()) {
142  vertex_has_reference[triangle.i1] = true;
143  vertex_has_reference[triangle.i2] = true;
144  vertex_has_reference[triangle.i3] = true;
145  }
146 
147  std::vector<int> index_old_to_new(getVerticeSize());
148  bool has_vert_normal = hasNormals();
149  bool has_vert_color = hasColors();
150  size_t old_vertex_num = getVerticeSize();
151  size_t k = 0; // new index
152  for (size_t i = 0; i < old_vertex_num; i++) { // old index
153  if (vertex_has_reference[i]) {
154  setVertice(k, getVertice(i));
155  if (has_vert_normal) setVertexNormal(k, getVertexNormal(i));
156  if (has_vert_color) setVertexColor(k, getVertexColor(i));
157  index_old_to_new[i] = (int)k;
158  k++;
159  } else {
160  index_old_to_new[i] = -1;
161  }
162  }
163 
164  // do some cleaning
165  {
166  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
167  if (cloud) {
168  cloud->resize(static_cast<unsigned>(k));
169  } else {
171  "[RemoveUnreferencedVertices] ccMesh has not associated "
172  "cloud.");
173  }
174  }
175 
176  if (k < old_vertex_num) {
177  for (auto &triangle : *getTrianglesPtr()) {
178  triangle.i1 = static_cast<unsigned>(index_old_to_new[triangle.i1]);
179  triangle.i2 = static_cast<unsigned>(index_old_to_new[triangle.i2]);
180  triangle.i3 = static_cast<unsigned>(index_old_to_new[triangle.i3]);
181  }
182  if (hasAdjacencyList()) {
183  ComputeAdjacencyList();
184  }
185  }
187  "[RemoveUnreferencedVertices] {:d} vertices have been removed.",
188  (int)(old_vertex_num - k));
189 
190  return *this;
191 }
192 
194  if (hasTriangleUvs()) {
196  "[RemoveDegenerateTriangles] This mesh contains triangle uvs "
197  "that are not handled in this function");
198  }
199  bool has_tri_normal = hasTriNormals();
200  size_t old_triangle_num = size();
201  size_t k = 0;
202  for (size_t i = 0; i < old_triangle_num; i++) {
203  auto triangle = getTriangle(i);
204  if (triangle(0) != triangle(1) && triangle(1) != triangle(2) &&
205  triangle(2) != triangle(0)) {
206  setTriangle(k, getTriangle(i));
207  if (has_tri_normal) setTriangleNorm(k, getTriangleNorm(i));
208  k++;
209  }
210  }
211 
212  // do some cleaning
213  resize(k);
214  if (has_tri_normal) m_triNormals->resize(k);
215  if (k < old_triangle_num && hasAdjacencyList()) {
216  ComputeAdjacencyList();
217  }
219  "[RemoveDegenerateTriangles] {:d} triangles have been "
220  "removed.",
221  (int)(old_triangle_num - k));
222  return *this;
223 }
224 
226  if (hasTriangleUvs()) {
228  "[RemoveNonManifoldEdges] This mesh contains triangle uvs that "
229  "are not handled in this function");
230  }
231  std::vector<double> triangle_areas;
232  GetSurfaceArea(triangle_areas);
233 
234  bool mesh_is_edge_manifold = false;
235  while (!mesh_is_edge_manifold) {
236  mesh_is_edge_manifold = true;
237  auto edges_to_triangles = GetEdgeToTrianglesMap();
238 
239  for (auto &kv : edges_to_triangles) {
240  size_t n_edge_triangle_refs = kv.second.size();
241  // check if the given edge is manifold
242  // (has exactly 1, or 2 adjacent triangles)
243  if (n_edge_triangle_refs == 1u || n_edge_triangle_refs == 2u) {
244  continue;
245  }
246 
247  // There is at least one edge that is non-manifold
248  mesh_is_edge_manifold = false;
249 
250  // if the edge is non-manifold, then check if a referenced
251  // triangle has already been removed
252  // (triangle area has been set to < 0), otherwise remove triangle
253  // with smallest surface area until number of adjacent triangles
254  // is <= 2.
255  // 1) count triangles that are not marked deleted
256  int n_triangles = 0;
257  for (int tidx : kv.second) {
258  if (triangle_areas[tidx] > 0) {
259  n_triangles++;
260  }
261  }
262  // 2) mark smallest triangles as deleted by setting
263  // surface area to -1
264  int n_triangles_to_delete = n_triangles - 2;
265  while (n_triangles_to_delete > 0) {
266  // find triangle with smallest area
267  int min_tidx = -1;
268  double min_area = std::numeric_limits<double>::max();
269  for (int tidx : kv.second) {
270  double area = triangle_areas[tidx];
271  if (area > 0 && area < min_area) {
272  min_tidx = tidx;
273  min_area = area;
274  }
275  }
276 
277  // mark triangle as deleted by setting area to -1
278  triangle_areas[min_tidx] = -1;
279  n_triangles_to_delete--;
280  }
281  }
282 
283  // delete marked triangles
284  bool has_tri_normal = hasTriNormals();
285  size_t to_tidx = 0;
286  for (size_t from_tidx = 0; from_tidx < size(); ++from_tidx) {
287  if (triangle_areas[from_tidx] > 0) {
288  setTriangle(to_tidx, getTriangle(from_tidx));
289  triangle_areas[to_tidx] = triangle_areas[from_tidx];
290  if (has_tri_normal) {
291  setTriangleNorm(to_tidx, getTriangleNorm(from_tidx));
292  }
293  to_tidx++;
294  }
295  }
296 
297  // do some cleaning
298  {
299  resize(to_tidx);
300  triangle_areas.resize(to_tidx);
301  if (has_tri_normal) {
302  m_triNormals->resize(to_tidx);
303  }
304  }
305  }
306  return *this;
307 }
308 
311  // precompute all neighbours
312  utility::LogDebug("Precompute Neighbours");
313  std::vector<std::vector<int>> nbs(getVerticeSize());
314 #ifdef _OPENMP
315 #pragma omp parallel for schedule(static) \
316  num_threads(utility::EstimateMaxThreads())
317 #endif
318  for (int idx = 0; idx < int(getVerticeSize()); ++idx) {
319  std::vector<double> dists2;
320  kdtree.SearchRadius(getVertice(static_cast<size_t>(idx)), eps, nbs[idx],
321  dists2);
322  }
323  utility::LogDebug("Done Precompute Neighbours");
324 
325  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
326  assert(cloud);
327 
328  bool has_vertex_normals = hasNormals();
329  bool has_vertex_colors = hasColors();
330  std::vector<CCVector3> new_vertices;
331  std::vector<CCVector3> new_vertex_normals;
332  ColorsTableType new_vertex_colors = *cloud->rgbColors();
333  std::unordered_map<int, int> new_vert_mapping;
334  for (int vidx = 0; vidx < int(getVerticeSize()); ++vidx) {
335  if (new_vert_mapping.count(vidx) > 0) {
336  continue;
337  }
338 
339  int new_vidx = int(new_vertices.size());
340  new_vert_mapping[vidx] = new_vidx;
341 
342  CCVector3 vertex = getVertice(static_cast<size_t>(vidx));
344  if (has_vertex_normals) {
345  normal = getVertexNormal(static_cast<size_t>(vidx));
346  }
347  Eigen::Vector3d color;
348  if (has_vertex_colors) {
349  color = getVertexColor(static_cast<size_t>(vidx));
350  }
351  int n = 1;
352  for (int nb : nbs[vidx]) {
353  if (vidx == nb || new_vert_mapping.count(nb) > 0) {
354  continue;
355  }
356  vertex += getVertice(static_cast<size_t>(nb));
357  if (has_vertex_normals) {
358  normal += getVertexNormal(static_cast<size_t>(nb));
359  }
360  if (has_vertex_colors) {
361  color += getVertexColor(static_cast<size_t>(nb));
362  }
363  new_vert_mapping[nb] = new_vidx;
364  n += 1;
365  }
366  new_vertices.push_back(vertex / n);
367  if (has_vertex_normals) {
368  new_vertex_normals.push_back(normal / n);
369  }
370  if (has_vertex_colors) {
371  new_vertex_colors.push_back(ecvColor::Rgb::FromEigen(color / n));
372  }
373  }
374  utility::LogDebug("Merged {} vertices",
375  getVerticeSize() - new_vertices.size());
376 
377  std::swap(cloud->getPoints(), new_vertices);
378  if (has_vertex_normals) {
379  cloud->setPointNormals(new_vertex_normals);
380  }
381  if (has_vertex_colors) {
382  std::swap(*cloud->rgbColors(), new_vertex_colors);
383  }
384 
385  for (auto &triangle : *getTrianglesPtr()) {
386  triangle.i1 = static_cast<unsigned>(new_vert_mapping[triangle.i1]);
387  triangle.i2 = static_cast<unsigned>(new_vert_mapping[triangle.i2]);
388  triangle.i3 = static_cast<unsigned>(new_vert_mapping[triangle.i3]);
389  }
390 
391  if (hasTriNormals()) {
392  computePerTriangleNormals();
393  }
394 
395  return *this;
396 }
397 
398 ccMesh &ccMesh::PaintUniformColor(const Eigen::Vector3d &color) {
399  if (getAssociatedCloud() &&
400  getAssociatedCloud()->isKindOf(CV_TYPES::POINT_CLOUD)) {
401  ccPointCloud *cloud =
402  ccHObjectCaster::ToPointCloud(getAssociatedCloud());
403  cloud->PaintUniformColor(color);
404  }
405  return *this;
406 }
407 
408 std::tuple<std::shared_ptr<ccMesh>, std::vector<size_t>>
411 }
412 
413 std::shared_ptr<ccMesh> ccMesh::FilterSharpen(int number_of_iterations,
414  double strength,
415  FilterScope scope) const {
416  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
417  assert(cloud);
418 
419  bool filter_vertex =
420  scope == FilterScope::All || scope == FilterScope::Vertex;
421  bool filter_normal =
422  (scope == FilterScope::All || scope == FilterScope::Normal) &&
423  cloud->hasNormals();
424  bool filter_color =
425  (scope == FilterScope::All || scope == FilterScope::Color) &&
426  cloud->hasColors();
427 
428  std::vector<CCVector3> prev_vertices = cloud->getPoints();
429 
430  std::vector<CCVector3> prev_vertex_normals;
431  if (filter_normal) {
432  prev_vertex_normals = cloud->getPointNormals();
433  }
434  ColorsTableType prev_vertex_colors;
435  if (filter_color) {
436  prev_vertex_colors = *cloud->rgbColors();
437  }
438 
439  ccPointCloud *baseVertices = new ccPointCloud("vertices");
440  assert(baseVertices);
441  baseVertices->setEnabled(false);
442  // DGM: no need to lock it as it is only used by one mesh!
443  baseVertices->setLocked(false);
444  std::shared_ptr<ccMesh> mesh = std::make_shared<ccMesh>(baseVertices);
445  mesh->addChild(baseVertices);
446 
447  baseVertices->resize(cloud->size());
448  if (cloud->hasNormals()) {
449  baseVertices->resizeTheNormsTable();
450  }
451  if (cloud->hasColors()) {
452  baseVertices->resizeTheRGBTable();
453  }
454 
455  mesh->setTriangles(getTriangles());
456  mesh->adjacency_list_ = adjacency_list_;
457  if (!mesh->hasAdjacencyList()) {
458  mesh->ComputeAdjacencyList();
459  }
460 
461  for (int iter = 0; iter < number_of_iterations; ++iter) {
462  for (size_t vidx = 0; vidx < baseVertices->size(); ++vidx) {
463  CCVector3 vertex_sum(0, 0, 0);
464  CCVector3 normal_sum(0, 0, 0);
465  Eigen::Vector3d color_sum(0, 0, 0);
466  for (int nbidx : mesh->adjacency_list_[vidx]) {
467  if (filter_vertex) {
468  vertex_sum += prev_vertices[nbidx];
469  }
470  if (filter_normal) {
471  normal_sum += prev_vertex_normals[nbidx];
472  }
473  if (filter_color) {
474  color_sum +=
475  ecvColor::Rgb::ToEigen(prev_vertex_colors.getValue(
476  static_cast<size_t>(nbidx)));
477  }
478  }
479 
480  size_t nb_size = mesh->adjacency_list_[vidx].size();
481  if (filter_vertex) {
482  CCVector3 p = prev_vertices[vidx] +
483  static_cast<float>(strength) *
484  (prev_vertices[vidx] *
485  static_cast<float>(nb_size) -
486  vertex_sum);
487  baseVertices->setPoint(vidx, p);
488  }
489 
490  if (filter_normal) {
491  CCVector3 p = prev_vertex_normals[vidx] +
492  static_cast<float>(strength) *
493  (prev_vertex_normals[vidx] *
494  static_cast<float>(nb_size) -
495  normal_sum);
496  baseVertices->setPointNormal(vidx, p);
497  }
498  if (filter_color) {
499  baseVertices->setPointColor(
500  vidx,
501  ecvColor::Rgb::ToEigen(prev_vertex_colors[vidx]) +
502  strength * (ecvColor::Rgb::ToEigen(
503  prev_vertex_colors[vidx]) *
504  nb_size -
505  color_sum));
506  }
507  }
508  if (iter < number_of_iterations - 1) {
509  std::swap(baseVertices->getPoints(), prev_vertices);
510  if (filter_normal) {
511  prev_vertex_normals = baseVertices->getPointNormals();
512  }
513  if (filter_color) {
514  std::swap(*baseVertices->rgbColors(), prev_vertex_colors);
515  }
516  }
517  }
518 
519  if (hasTriNormals()) {
520  mesh->ComputeTriangleNormals();
521  }
522 
523  // do some cleaning
524  {
525  baseVertices->shrinkToFit();
526  mesh->shrinkToFit();
527  NormsIndexesTableType *normals = mesh->getTriNormsTable();
528  if (normals) {
529  normals->shrink_to_fit();
530  }
531  }
532  return mesh;
533 }
534 
535 std::shared_ptr<ccMesh> ccMesh::FilterSmoothSimple(int number_of_iterations,
536  FilterScope scope) const {
537  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
538  assert(cloud);
539 
540  bool filter_vertex =
541  scope == FilterScope::All || scope == FilterScope::Vertex;
542  bool filter_normal =
543  (scope == FilterScope::All || scope == FilterScope::Normal) &&
544  cloud->hasNormals();
545  bool filter_color =
546  (scope == FilterScope::All || scope == FilterScope::Color) &&
547  cloud->hasColors();
548 
549  std::vector<CCVector3> prev_vertices = cloud->getPoints();
550  std::vector<CCVector3> prev_vertex_normals;
551  if (filter_normal) {
552  prev_vertex_normals = cloud->getPointNormals();
553  }
554  ColorsTableType prev_vertex_colors;
555  if (filter_color) {
556  prev_vertex_colors = *cloud->rgbColors();
557  }
558 
559  ccPointCloud *baseVertices = new ccPointCloud("vertices");
560  assert(baseVertices);
561  baseVertices->setEnabled(false);
562  // DGM: no need to lock it as it is only used by one mesh!
563  baseVertices->setLocked(false);
564  std::shared_ptr<ccMesh> mesh = std::make_shared<ccMesh>(baseVertices);
565  mesh->addChild(baseVertices);
566 
567  baseVertices->resize(cloud->size());
568  if (cloud->hasNormals()) {
569  baseVertices->resizeTheNormsTable();
570  }
571  if (cloud->hasColors()) {
572  baseVertices->resizeTheRGBTable();
573  }
574 
575  mesh->setTriangles(getTriangles());
576  mesh->adjacency_list_ = adjacency_list_;
577  if (!mesh->hasAdjacencyList()) {
578  mesh->ComputeAdjacencyList();
579  }
580 
581  for (int iter = 0; iter < number_of_iterations; ++iter) {
582  for (size_t vidx = 0; vidx < baseVertices->size(); ++vidx) {
583  CCVector3 vertex_sum(0, 0, 0);
584  CCVector3 normal_sum(0, 0, 0);
585  Eigen::Vector3d color_sum(0, 0, 0);
586  for (int nbidx : mesh->adjacency_list_[vidx]) {
587  if (filter_vertex) {
588  vertex_sum += prev_vertices[nbidx];
589  }
590  if (filter_normal) {
591  normal_sum += prev_vertex_normals[nbidx];
592  }
593  if (filter_color) {
594  color_sum += ecvColor::Rgb::ToEigen(
595  prev_vertex_colors.at(static_cast<size_t>(nbidx)));
596  }
597  }
598 
599  size_t nb_size = mesh->adjacency_list_[vidx].size();
600  if (filter_vertex) {
601  baseVertices->setPoint(
602  vidx,
603  (prev_vertices[vidx] + vertex_sum) / (1 + nb_size));
604  }
605  if (filter_normal) {
606  baseVertices->setPointNormal(
607  vidx, (prev_vertex_normals[vidx] + normal_sum) /
608  (1 + nb_size));
609  }
610  if (filter_color) {
611  baseVertices->setPointColor(
612  vidx,
613  (ecvColor::Rgb::ToEigen(prev_vertex_colors[vidx]) +
614  color_sum) /
615  (1 + nb_size));
616  }
617  }
618  if (iter < number_of_iterations - 1) {
619  std::swap(baseVertices->getPoints(), prev_vertices);
620  if (filter_normal) {
621  prev_vertex_normals = baseVertices->getPointNormals();
622  }
623  if (filter_color) {
624  std::swap(*baseVertices->rgbColors(), prev_vertex_colors);
625  }
626  }
627  }
628 
629  if (hasTriNormals()) {
630  mesh->ComputeTriangleNormals();
631  }
632 
633  // do some cleaning
634  {
635  baseVertices->shrinkToFit();
636  mesh->shrinkToFit();
637  NormsIndexesTableType *normals = mesh->getTriNormsTable();
638  if (normals) {
639  normals->shrink_to_fit();
640  }
641  }
642  return mesh;
643 }
644 
646  std::shared_ptr<ccMesh> &mesh,
647  const std::vector<CCVector3> &prev_vertices,
648  const std::vector<CCVector3> &prev_vertex_normals,
649  const ColorsTableType &prev_vertex_colors,
650  const std::vector<std::unordered_set<int>> &adjacency_list,
651  double lambda,
652  bool filter_vertex,
653  bool filter_normal,
654  bool filter_color) const {
655  ccPointCloud *cloud =
656  ccHObjectCaster::ToPointCloud(mesh->getAssociatedCloud());
657  assert(cloud);
658 
659  for (size_t vidx = 0; vidx < cloud->size(); ++vidx) {
660  CCVector3 vertex_sum(0, 0, 0);
661  CCVector3 normal_sum(0, 0, 0);
662  Eigen::Vector3d color_sum(0, 0, 0);
663  double total_weight = 0;
664  for (int nbidx : mesh->adjacency_list_[vidx]) {
665  auto diff = prev_vertices[vidx] - prev_vertices[nbidx];
666  double dist = diff.norm();
667  double weight = 1. / (dist + 1e-12);
668  total_weight += weight;
669 
670  if (filter_vertex) {
671  vertex_sum += static_cast<float>(weight) * prev_vertices[nbidx];
672  }
673  if (filter_normal) {
674  normal_sum +=
675  static_cast<float>(weight) * prev_vertex_normals[nbidx];
676  }
677  if (filter_color) {
678  color_sum += weight *
679  ecvColor::Rgb::ToEigen(prev_vertex_colors[nbidx]);
680  }
681  }
682 
683  if (filter_vertex) {
684  cloud->setPoint(vidx, prev_vertices[vidx] +
685  static_cast<float>(lambda) *
686  (vertex_sum / total_weight -
687  prev_vertices[vidx]));
688  }
689  if (filter_normal) {
690  cloud->setPointNormal(vidx,
691  prev_vertex_normals[vidx] +
692  static_cast<float>(lambda) *
693  (normal_sum / total_weight -
694  prev_vertex_normals[vidx]));
695  }
696  if (filter_color) {
697  Eigen::Vector3d C =
698  ecvColor::Rgb::ToEigen(prev_vertex_colors[vidx]) +
699  lambda * (color_sum / total_weight -
700  ecvColor::Rgb::ToEigen(prev_vertex_colors[vidx]));
701  cloud->setPointColor(vidx, C);
702  }
703  }
704 }
705 
706 std::shared_ptr<ccMesh> ccMesh::FilterSmoothLaplacian(int number_of_iterations,
707  double lambda,
708  FilterScope scope) const {
709  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
710  assert(cloud);
711 
712  bool filter_vertex =
713  scope == FilterScope::All || scope == FilterScope::Vertex;
714  bool filter_normal =
715  (scope == FilterScope::All || scope == FilterScope::Normal) &&
716  cloud->hasNormals();
717  bool filter_color =
718  (scope == FilterScope::All || scope == FilterScope::Color) &&
719  cloud->hasColors();
720 
721  std::vector<CCVector3> prev_vertices = cloud->getPoints();
722  std::vector<CCVector3> prev_vertex_normals;
723  if (filter_normal) {
724  prev_vertex_normals = cloud->getPointNormals();
725  }
726  ColorsTableType prev_vertex_colors;
727  if (filter_color) {
728  prev_vertex_colors = *cloud->rgbColors();
729  }
730 
731  ccPointCloud *baseVertices = new ccPointCloud("vertices");
732  assert(baseVertices);
733  baseVertices->setEnabled(false);
734  // DGM: no need to lock it as it is only used by one mesh!
735  baseVertices->setLocked(false);
736  std::shared_ptr<ccMesh> mesh = std::make_shared<ccMesh>(baseVertices);
737  mesh->addChild(baseVertices);
738 
739  baseVertices->resize(cloud->size());
740  if (cloud->hasNormals()) {
741  baseVertices->resizeTheNormsTable();
742  }
743  if (cloud->hasColors()) {
744  baseVertices->resizeTheRGBTable();
745  }
746 
747  mesh->setTriangles(getTriangles());
748  mesh->adjacency_list_ = adjacency_list_;
749  if (!mesh->hasAdjacencyList()) {
750  mesh->ComputeAdjacencyList();
751  }
752 
753  for (int iter = 0; iter < number_of_iterations; ++iter) {
754  FilterSmoothLaplacianHelper(mesh, prev_vertices, prev_vertex_normals,
755  prev_vertex_colors, mesh->adjacency_list_,
756  lambda, filter_vertex, filter_normal,
757  filter_color);
758  if (iter < number_of_iterations - 1) {
759  std::swap(baseVertices->getPoints(), prev_vertices);
760  if (filter_normal) {
761  prev_vertex_normals = baseVertices->getPointNormals();
762  }
763  if (filter_color) {
764  std::swap(*baseVertices->rgbColors(), prev_vertex_colors);
765  }
766  }
767  }
768 
769  if (hasTriNormals()) {
770  mesh->ComputeTriangleNormals();
771  }
772 
773  // do some cleaning
774  {
775  baseVertices->shrinkToFit();
776  mesh->shrinkToFit();
777  NormsIndexesTableType *normals = mesh->getTriNormsTable();
778  if (normals) {
779  normals->shrink_to_fit();
780  }
781  }
782  return mesh;
783 }
784 
785 std::shared_ptr<ccMesh> ccMesh::FilterSmoothTaubin(int number_of_iterations,
786  double lambda,
787  double mu,
788  FilterScope scope) const {
789  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
790  assert(cloud);
791 
792  bool filter_vertex =
793  scope == FilterScope::All || scope == FilterScope::Vertex;
794  bool filter_normal =
795  (scope == FilterScope::All || scope == FilterScope::Normal) &&
796  cloud->hasNormals();
797  bool filter_color =
798  (scope == FilterScope::All || scope == FilterScope::Color) &&
799  cloud->hasColors();
800 
801  std::vector<CCVector3> prev_vertices = cloud->getPoints();
802  std::vector<CCVector3> prev_vertex_normals;
803  if (filter_normal) {
804  prev_vertex_normals = cloud->getPointNormals();
805  }
806  ColorsTableType prev_vertex_colors;
807  if (filter_color) {
808  prev_vertex_colors = *cloud->rgbColors();
809  }
810 
811  ccPointCloud *baseVertices = new ccPointCloud("vertices");
812  assert(baseVertices);
813  baseVertices->setEnabled(false);
814  // DGM: no need to lock it as it is only used by one mesh!
815  baseVertices->setLocked(false);
816  std::shared_ptr<ccMesh> mesh = std::make_shared<ccMesh>(baseVertices);
817  mesh->addChild(baseVertices);
818 
819  baseVertices->resize(cloud->size());
820  if (cloud->hasNormals()) {
821  baseVertices->resizeTheNormsTable();
822  }
823  if (cloud->hasColors()) {
824  baseVertices->resizeTheRGBTable();
825  }
826 
827  mesh->setTriangles(getTriangles());
828  mesh->adjacency_list_ = adjacency_list_;
829  if (!mesh->hasAdjacencyList()) {
830  mesh->ComputeAdjacencyList();
831  }
832 
833  for (int iter = 0; iter < number_of_iterations; ++iter) {
834  FilterSmoothLaplacianHelper(mesh, prev_vertices, prev_vertex_normals,
835  prev_vertex_colors, mesh->adjacency_list_,
836  lambda, filter_vertex, filter_normal,
837  filter_color);
838 
839  std::swap(baseVertices->getPoints(), prev_vertices);
840  if (filter_normal) {
841  prev_vertex_normals = baseVertices->getPointNormals();
842  }
843  if (filter_color) {
844  std::swap(*baseVertices->rgbColors(), prev_vertex_colors);
845  }
846 
847  FilterSmoothLaplacianHelper(mesh, prev_vertices, prev_vertex_normals,
848  prev_vertex_colors, mesh->adjacency_list_,
849  mu, filter_vertex, filter_normal,
850  filter_color);
851 
852  if (iter < number_of_iterations - 1) {
853  std::swap(baseVertices->getPoints(), prev_vertices);
854  if (filter_normal) {
855  prev_vertex_normals = baseVertices->getPointNormals();
856  }
857  if (filter_color) {
858  std::swap(*baseVertices->rgbColors(), prev_vertex_colors);
859  }
860  }
861  }
862 
863  if (hasTriNormals()) {
864  mesh->ComputeTriangleNormals();
865  }
866 
867  // do some cleaning
868  {
869  baseVertices->shrinkToFit();
870  mesh->shrinkToFit();
871  NormsIndexesTableType *normals = mesh->getTriNormsTable();
872  if (normals) {
873  normals->shrink_to_fit();
874  }
875  }
876  return mesh;
877 }
878 
880  std::unordered_set<Eigen::Vector2i, utility::hash_eigen<Eigen::Vector2i>>
881  edges;
882 
883  for (auto triangle : getTriangles()) {
884  edges.emplace(GetOrderedEdge(triangle(0), triangle(1)));
885  edges.emplace(GetOrderedEdge(triangle(0), triangle(2)));
886  edges.emplace(GetOrderedEdge(triangle(1), triangle(2)));
887  }
888 
889  int E = int(edges.size());
890  int V = int(getVerticeSize());
891  int F = int(size());
892  return V + F - E;
893 }
894 
895 std::vector<Eigen::Vector2i> ccMesh::GetNonManifoldEdges(
896  bool allow_boundary_edges /* = true */) const {
897  auto edges = GetEdgeToTrianglesMap();
898  std::vector<Eigen::Vector2i> non_manifold_edges;
899  for (auto &kv : edges) {
900  if ((allow_boundary_edges &&
901  (kv.second.size() < 1 || kv.second.size() > 2)) ||
902  (!allow_boundary_edges && kv.second.size() != 2)) {
903  non_manifold_edges.push_back(kv.first);
904  }
905  }
906  return non_manifold_edges;
907 }
908 
909 bool ccMesh::IsEdgeManifold(bool allow_boundary_edges /* = true */) const {
910  auto edges = GetEdgeToTrianglesMap();
911  for (auto &kv : edges) {
912  if ((allow_boundary_edges &&
913  (kv.second.size() < 1 || kv.second.size() > 2)) ||
914  (!allow_boundary_edges && kv.second.size() != 2)) {
915  return false;
916  }
917  }
918  return true;
919 }
920 
921 std::vector<int> ccMesh::GetNonManifoldVertices() const {
922  std::vector<std::unordered_set<int>> vert_to_triangles(getVerticeSize());
923  for (size_t tidx = 0; tidx < size(); ++tidx) {
924  const auto &tria = getTriangle(tidx);
925  vert_to_triangles[tria(0)].emplace(int(tidx));
926  vert_to_triangles[tria(1)].emplace(int(tidx));
927  vert_to_triangles[tria(2)].emplace(int(tidx));
928  }
929 
930  std::vector<int> non_manifold_verts;
931  for (int vidx = 0; vidx < int(getVerticeSize()); ++vidx) {
932  const auto &triangles = vert_to_triangles[vidx];
933  if (triangles.size() == 0) {
934  continue;
935  }
936 
937  // collect edges and vertices
938  std::unordered_map<int, std::unordered_set<int>> edges;
939  for (int tidx : triangles) {
940  const auto &triangle = getTriangle(static_cast<size_t>(tidx));
941  if (triangle(0) != vidx && triangle(1) != vidx) {
942  edges[triangle(0)].emplace(triangle(1));
943  edges[triangle(1)].emplace(triangle(0));
944  } else if (triangle(0) != vidx && triangle(2) != vidx) {
945  edges[triangle(0)].emplace(triangle(2));
946  edges[triangle(2)].emplace(triangle(0));
947  } else if (triangle(1) != vidx && triangle(2) != vidx) {
948  edges[triangle(1)].emplace(triangle(2));
949  edges[triangle(2)].emplace(triangle(1));
950  }
951  }
952 
953  // test if vertices are connected
954  std::queue<int> next;
955  std::unordered_set<int> visited;
956  next.push(edges.begin()->first);
957  visited.emplace(edges.begin()->first);
958  while (!next.empty()) {
959  int vert = next.front();
960  next.pop();
961 
962  for (auto nb : edges[vert]) {
963  if (visited.count(nb) == 0) {
964  visited.emplace(nb);
965  next.emplace(nb);
966  }
967  }
968  }
969  if (visited.size() != edges.size()) {
970  non_manifold_verts.push_back(vidx);
971  }
972  }
973 
974  return non_manifold_verts;
975 }
976 
978  return GetNonManifoldVertices().empty();
979 }
980 
981 std::vector<Eigen::Vector2i> ccMesh::GetSelfIntersectingTriangles() const {
982  std::vector<Eigen::Vector2i> self_intersecting_triangles;
983  for (size_t tidx0 = 0; tidx0 < size() - 1; ++tidx0) {
984  const Eigen::Vector3i &tria_p = getTriangle(tidx0);
985  const Eigen::Vector3d &p0 = getVertice(static_cast<size_t>(tria_p(0)));
986  const Eigen::Vector3d &p1 = getVertice(static_cast<size_t>(tria_p(1)));
987  const Eigen::Vector3d &p2 = getVertice(static_cast<size_t>(tria_p(2)));
988  for (size_t tidx1 = tidx0 + 1; tidx1 < size(); ++tidx1) {
989  const Eigen::Vector3i &tria_q = getTriangle(tidx1);
990  // check if neighbour triangle
991  if (tria_p(0) == tria_q(0) || tria_p(0) == tria_q(1) ||
992  tria_p(0) == tria_q(2) || tria_p(1) == tria_q(0) ||
993  tria_p(1) == tria_q(1) || tria_p(1) == tria_q(2) ||
994  tria_p(2) == tria_q(0) || tria_p(2) == tria_q(1) ||
995  tria_p(2) == tria_q(2)) {
996  continue;
997  }
998 
999  // check for intersection
1000  const Eigen::Vector3d &q0 =
1001  getVertice(static_cast<size_t>(tria_q(0)));
1002  const Eigen::Vector3d &q1 =
1003  getVertice(static_cast<size_t>(tria_q(1)));
1004  const Eigen::Vector3d &q2 =
1005  getVertice(static_cast<size_t>(tria_q(2)));
1007  q1, q2)) {
1008  self_intersecting_triangles.push_back(
1009  Eigen::Vector2i(tidx0, tidx1));
1010  }
1011  }
1012  }
1013  return self_intersecting_triangles;
1014 }
1015 
1017  return !GetSelfIntersectingTriangles().empty();
1018 }
1019 
1020 bool ccMesh::IsBoundingBoxIntersecting(const ccMesh &other) const {
1021  return utility::IntersectionTest::AABBAABB(GetMinBound(), GetMaxBound(),
1022  other.GetMinBound(),
1023  other.GetMaxBound());
1024 }
1025 
1026 bool ccMesh::IsIntersecting(const ccMesh &other) const {
1027  if (!IsBoundingBoxIntersecting(other)) {
1028  return false;
1029  }
1030  for (size_t tidx0 = 0; tidx0 < size(); ++tidx0) {
1031  const Eigen::Vector3i &tria_p = getTriangle(tidx0);
1032  const Eigen::Vector3d &p0 = getVertice(static_cast<size_t>(tria_p(0)));
1033  const Eigen::Vector3d &p1 = getVertice(static_cast<size_t>(tria_p(1)));
1034  const Eigen::Vector3d &p2 = getVertice(static_cast<size_t>(tria_p(2)));
1035  for (size_t tidx1 = 0; tidx1 < other.size(); ++tidx1) {
1036  const Eigen::Vector3i &tria_q = other.getTriangle(tidx1);
1037  const Eigen::Vector3d &q0 =
1038  other.getVertice(static_cast<size_t>(tria_q(0)));
1039  const Eigen::Vector3d &q1 =
1040  other.getVertice(static_cast<size_t>(tria_q(1)));
1041  const Eigen::Vector3d &q2 =
1042  other.getVertice(static_cast<size_t>(tria_q(2)));
1044  q1, q2)) {
1045  return true;
1046  }
1047  }
1048  }
1049  return false;
1050 }
1051 
1052 template <typename F>
1053 bool OrientTriangleHelper(const std::vector<Eigen::Vector3i> &triangles,
1054  F &swap) {
1055  std::unordered_map<Eigen::Vector2i, Eigen::Vector2i,
1057  edge_to_orientation;
1058  std::unordered_set<int> unvisited_triangles;
1059  std::unordered_map<Eigen::Vector2i, std::unordered_set<int>,
1061  adjacent_triangles;
1062  std::queue<int> triangle_queue;
1063 
1064  auto VerifyAndAdd = [&](int vidx0, int vidx1) {
1065  Eigen::Vector2i key = ccMesh::GetOrderedEdge(vidx0, vidx1);
1066  if (edge_to_orientation.count(key) > 0) {
1067  if (edge_to_orientation.at(key)(0) == vidx0) {
1068  return false;
1069  }
1070  } else {
1071  edge_to_orientation[key] = Eigen::Vector2i(vidx0, vidx1);
1072  }
1073  return true;
1074  };
1075  auto AddTriangleNbsToQueue = [&](const Eigen::Vector2i &edge) {
1076  for (int nb_tidx : adjacent_triangles[edge]) {
1077  triangle_queue.push(nb_tidx);
1078  }
1079  };
1080 
1081  for (size_t tidx = 0; tidx < triangles.size(); ++tidx) {
1082  unvisited_triangles.insert(int(tidx));
1083  const auto &triangle = triangles[tidx];
1084  int vidx0 = triangle(0);
1085  int vidx1 = triangle(1);
1086  int vidx2 = triangle(2);
1087  adjacent_triangles[ccMesh::GetOrderedEdge(vidx0, vidx1)].insert(
1088  int(tidx));
1089  adjacent_triangles[ccMesh::GetOrderedEdge(vidx1, vidx2)].insert(
1090  int(tidx));
1091  adjacent_triangles[ccMesh::GetOrderedEdge(vidx2, vidx0)].insert(
1092  int(tidx));
1093  }
1094 
1095  while (!unvisited_triangles.empty()) {
1096  int tidx;
1097  if (triangle_queue.empty()) {
1098  tidx = *unvisited_triangles.begin();
1099  } else {
1100  tidx = triangle_queue.front();
1101  triangle_queue.pop();
1102  }
1103  if (unvisited_triangles.count(tidx) > 0) {
1104  unvisited_triangles.erase(tidx);
1105  } else {
1106  continue;
1107  }
1108 
1109  const auto &triangle = triangles[tidx];
1110  int vidx0 = triangle(0);
1111  int vidx1 = triangle(1);
1112  int vidx2 = triangle(2);
1113  Eigen::Vector2i key01 = ccMesh::GetOrderedEdge(vidx0, vidx1);
1114  Eigen::Vector2i key12 = ccMesh::GetOrderedEdge(vidx1, vidx2);
1115  Eigen::Vector2i key20 = ccMesh::GetOrderedEdge(vidx2, vidx0);
1116  bool exist01 = edge_to_orientation.count(key01) > 0;
1117  bool exist12 = edge_to_orientation.count(key12) > 0;
1118  bool exist20 = edge_to_orientation.count(key20) > 0;
1119 
1120  if (!(exist01 || exist12 || exist20)) {
1121  edge_to_orientation[key01] = Eigen::Vector2i(vidx0, vidx1);
1122  edge_to_orientation[key12] = Eigen::Vector2i(vidx1, vidx2);
1123  edge_to_orientation[key20] = Eigen::Vector2i(vidx2, vidx0);
1124  } else {
1125  // one flip is allowed
1126  if (exist01 && edge_to_orientation.at(key01)(0) == vidx0) {
1127  std::swap(vidx0, vidx1);
1128  swap(tidx, 0, 1);
1129  } else if (exist12 && edge_to_orientation.at(key12)(0) == vidx1) {
1130  std::swap(vidx1, vidx2);
1131  swap(tidx, 1, 2);
1132  } else if (exist20 && edge_to_orientation.at(key20)(0) == vidx2) {
1133  std::swap(vidx2, vidx0);
1134  swap(tidx, 2, 0);
1135  }
1136 
1137  // check if each edge looks in different direction compared to
1138  // existing ones if not existend, add the edge to map
1139  if (!VerifyAndAdd(vidx0, vidx1)) {
1140  return false;
1141  }
1142  if (!VerifyAndAdd(vidx1, vidx2)) {
1143  return false;
1144  }
1145  if (!VerifyAndAdd(vidx2, vidx0)) {
1146  return false;
1147  }
1148  }
1149 
1150  AddTriangleNbsToQueue(key01);
1151  AddTriangleNbsToQueue(key12);
1152  AddTriangleNbsToQueue(key20);
1153  }
1154  return true;
1155 }
1156 
1157 bool ccMesh::IsOrientable() const {
1158  auto NoOp = [](int, int, int) {};
1159  return OrientTriangleHelper(getTriangles(), NoOp);
1160 }
1161 
1162 bool ccMesh::IsWatertight() const {
1163  return IsEdgeManifold(false) && IsVertexManifold() && !IsSelfIntersecting();
1164 }
1165 
1167  auto SwapTriangleOrder = [&](int tidx, int idx0, int idx1) {
1168  std::swap(getTriangleVertIndexes(static_cast<unsigned>(tidx))->i[idx0],
1169  getTriangleVertIndexes(static_cast<unsigned>(tidx))->i[idx1]);
1170  };
1171  return OrientTriangleHelper(getTriangles(), SwapTriangleOrder);
1172 }
1173 
1174 std::tuple<std::vector<int>, std::vector<size_t>, std::vector<double>>
1176  std::vector<int> triangle_clusters(this->size(), -1);
1177  std::vector<size_t> num_triangles;
1178  std::vector<double> areas;
1179 
1180  utility::LogDebug("[ClusterConnectedTriangles] Compute triangle adjacency");
1181  auto edges_to_triangles = GetEdgeToTrianglesMap();
1182  std::vector<std::unordered_set<int>> adjacency_list(this->size());
1183 #ifdef _OPENMP
1184 #pragma omp parallel for schedule(static) \
1185  num_threads(utility::EstimateMaxThreads())
1186 #endif
1187  for (int tidx = 0; tidx < int(size()); ++tidx) {
1188  const auto &triangle = getTriangle(static_cast<size_t>(tidx));
1189  for (auto tnb :
1190  edges_to_triangles[GetOrderedEdge(triangle(0), triangle(1))]) {
1191  adjacency_list[tidx].insert(tnb);
1192  }
1193  for (auto tnb :
1194  edges_to_triangles[GetOrderedEdge(triangle(0), triangle(2))]) {
1195  adjacency_list[tidx].insert(tnb);
1196  }
1197  for (auto tnb :
1198  edges_to_triangles[GetOrderedEdge(triangle(1), triangle(2))]) {
1199  adjacency_list[tidx].insert(tnb);
1200  }
1201  }
1203  "[ClusterConnectedTriangles] Done computing triangle adjacency");
1204 
1205  int cluster_idx = 0;
1206  for (int tidx = 0; tidx < int(this->size()); ++tidx) {
1207  if (triangle_clusters[tidx] != -1) {
1208  continue;
1209  }
1210 
1211  std::queue<int> triangle_queue;
1212  int cluster_n_triangles = 0;
1213  double cluster_area = 0;
1214 
1215  triangle_queue.push(tidx);
1216  triangle_clusters[tidx] = cluster_idx;
1217  while (!triangle_queue.empty()) {
1218  int cluster_tidx = triangle_queue.front();
1219  triangle_queue.pop();
1220 
1221  cluster_n_triangles++;
1222  cluster_area += GetTriangleArea(cluster_tidx);
1223 
1224  for (auto tnb : adjacency_list[cluster_tidx]) {
1225  if (triangle_clusters[tnb] == -1) {
1226  triangle_queue.push(tnb);
1227  triangle_clusters[tnb] = cluster_idx;
1228  }
1229  }
1230  }
1231 
1232  num_triangles.push_back(cluster_n_triangles);
1233  areas.push_back(cluster_area);
1234  cluster_idx++;
1235  }
1236 
1238  "[ClusterConnectedTriangles] Done clustering, #clusters={}",
1239  cluster_idx);
1240  return std::make_tuple(triangle_clusters, num_triangles, areas);
1241 }
1242 
1244  const std::vector<size_t> &triangle_indices) {
1245  std::vector<bool> triangle_mask(size(), false);
1246  for (auto tidx : triangle_indices) {
1247  if (tidx >= 0 && tidx < size()) {
1248  triangle_mask[tidx] = true;
1249  } else {
1251  "[RemoveTriangles] contains triangle index {} that is not "
1252  "within the bounds",
1253  tidx);
1254  }
1255  }
1256 
1257  RemoveTrianglesByMask(triangle_mask);
1258 }
1259 
1260 void ccMesh::RemoveTrianglesByMask(const std::vector<bool> &triangle_mask) {
1261  if (triangle_mask.size() != this->size()) {
1262  utility::LogError("triangle_mask has a different size than triangles_");
1263  }
1264 
1265  bool has_tri_normal = hasTriNormals();
1266  int to_tidx = 0;
1267  for (size_t from_tidx = 0; from_tidx < this->size(); ++from_tidx) {
1268  if (!triangle_mask[from_tidx]) {
1269  setTriangle(to_tidx, getTriangle(from_tidx));
1270  if (has_tri_normal) {
1271  setTriangleNorm(to_tidx, getTriangleNorm(from_tidx));
1272  }
1273  to_tidx++;
1274  }
1275  }
1276 
1277  this->resize(to_tidx);
1278  if (has_tri_normal) {
1279  getTriNormsTable()->resize(to_tidx);
1280  }
1281 }
1282 
1283 void ccMesh::RemoveVerticesByIndex(const std::vector<size_t> &vertex_indices) {
1284  std::vector<bool> vertex_mask(getVerticeSize(), false);
1285  for (auto vidx : vertex_indices) {
1286  if (vidx >= 0 && vidx < getVerticeSize()) {
1287  vertex_mask[vidx] = true;
1288  } else {
1290  "[RemoveVerticessByIndex] contains vertex index {} that is "
1291  "not within the bounds",
1292  vidx);
1293  }
1294  }
1295 
1296  RemoveVerticesByMask(vertex_mask);
1297 }
1298 
1299 void ccMesh::RemoveVerticesByMask(const std::vector<bool> &vertex_mask) {
1300  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
1301  assert(cloud);
1302 
1303  if (vertex_mask.size() != cloud->size()) {
1304  utility::LogError("vertex_mask has a different size than vertices_");
1305  }
1306 
1307  bool has_normal = cloud->hasNormals();
1308  bool has_color = cloud->hasColors();
1309  int to_vidx = 0;
1310  std::unordered_map<int, int> vertex_map;
1311  for (unsigned from_vidx = 0; from_vidx < cloud->size(); ++from_vidx) {
1312  if (!vertex_mask[from_vidx]) {
1313  vertex_map[static_cast<int>(from_vidx)] = static_cast<int>(to_vidx);
1314  cloud->setPoint(to_vidx, *cloud->getPoint(from_vidx));
1315  if (has_normal) {
1316  cloud->setPointNormal(to_vidx,
1317  cloud->getPointNormal(from_vidx));
1318  }
1319  if (has_color) {
1320  cloud->setPointColor(to_vidx, cloud->getPointColor(from_vidx));
1321  }
1322  to_vidx++;
1323  }
1324  }
1325 
1326  cloud->resize(to_vidx);
1327 
1328  std::vector<bool> triangle_mask(this->size());
1329  for (unsigned tidx = 0; tidx < this->size(); ++tidx) {
1330  cloudViewer::VerticesIndexes *tria = getTriangleVertIndexes(tidx);
1331  triangle_mask[tidx] = vertex_mask[tria->i[0]] ||
1332  vertex_mask[tria->i[1]] ||
1333  vertex_mask[tria->i[2]];
1334  if (!triangle_mask[tidx]) {
1335  tria->i[0] = vertex_map[tria->i[0]];
1336  tria->i[1] = vertex_map[tria->i[1]];
1337  tria->i[2] = vertex_map[tria->i[2]];
1338  }
1339  }
1340  RemoveTrianglesByMask(triangle_mask);
1341 }
1342 
1343 std::unordered_map<Eigen::Vector2i,
1344  double,
1347  const std::unordered_map<Eigen::Vector2i,
1348  std::vector<int>,
1350  &edges_to_vertices,
1351  double min_weight) const {
1352  std::unordered_map<Eigen::Vector2i, double,
1354  weights;
1355 
1356  for (const auto &edge_v2s : edges_to_vertices) {
1357  Eigen::Vector2i edge = edge_v2s.first;
1358  double weight_sum = 0;
1359  int N = 0;
1360  for (int v2 : edge_v2s.second) {
1361  Eigen::Vector3d a = getVertice(static_cast<size_t>(edge(0))) -
1362  getVertice(static_cast<size_t>(v2));
1363  Eigen::Vector3d b = getVertice(static_cast<size_t>(edge(1))) -
1364  getVertice(static_cast<size_t>(v2));
1365 
1366  double weight = a.dot(b) / (a.cross(b)).norm();
1367  weight_sum += weight;
1368  N++;
1369  }
1370  double weight = N > 0 ? weight_sum / N : 0;
1371  if (weight < min_weight) {
1372  weights[edge] = min_weight;
1373  } else {
1374  weights[edge] = weight;
1375  }
1376  }
1377  return weights;
1378 }
1379 
1380 ccMesh &ccMesh::ComputeTriangleNormals(bool normalized /* = true*/) {
1381  computePerTriangleNormals();
1382  if (normalized) {
1383  NormalizeNormals();
1384  }
1385  return *this;
1386 }
1387 
1388 ccMesh &ccMesh::ComputeVertexNormals(bool normalized /* = true*/) {
1389  if (!hasTriNormals()) {
1390  ComputeTriangleNormals(false);
1391  }
1392  computePerVertexNormals();
1393 
1394  if (normalized) {
1395  NormalizeNormals();
1396  }
1397  return *this;
1398 }
1399 
1401  ccPointCloud *cloud = ccHObjectCaster::ToPointCloud(m_associatedCloud);
1402  if (cloud && cloud->hasNormals()) {
1403  cloud->NormalizeNormals();
1404  }
1405 
1406  if (hasTriNormals()) {
1407  for (size_t i = 0; i < m_triNormals->size(); ++i) {
1408  ccNormalVectors::GetNormalPtr(m_triNormals->getValue(i))
1409  .normalize();
1410  }
1411  }
1412 
1413  return *this;
1414 }
1415 
1417  adjacency_list_.clear();
1418  adjacency_list_.resize(getVerticeSize());
1419  for (size_t i = 0; i < size(); ++i) {
1420  Eigen::Vector3i triangle = getTriangle(i);
1421  adjacency_list_[triangle(0)].insert(triangle(1));
1422  adjacency_list_[triangle(0)].insert(triangle(2));
1423  adjacency_list_[triangle(1)].insert(triangle(0));
1424  adjacency_list_[triangle(1)].insert(triangle(2));
1425  adjacency_list_[triangle(2)].insert(triangle(0));
1426  adjacency_list_[triangle(2)].insert(triangle(1));
1427  }
1428  return *this;
1429 }
1430 
1431 std::shared_ptr<ccPointCloud> ccMesh::SamplePointsPoissonDisk(
1432  size_t number_of_points,
1433  double init_factor /* = 5 */,
1434  const std::shared_ptr<ccPointCloud> pcl_init /* = nullptr */,
1435  bool use_triangle_normal /* = false */,
1436  int seed /* = -1 */) {
1437  if (number_of_points <= 0) {
1438  utility::LogError("[SamplePointsPoissonDisk] number_of_points <= 0");
1439  }
1440  if (size() == 0) {
1442  "[SamplePointsPoissonDisk] input mesh has no triangles");
1443  }
1444  if (pcl_init == nullptr && init_factor < 1) {
1446  "[SamplePointsPoissonDisk] either pass pcl_init with #points "
1447  "> number_of_points or init_factor > 1");
1448  }
1449  if (pcl_init != nullptr && pcl_init->size() < number_of_points) {
1451  "[SamplePointsPoissonDisk] either pass pcl_init with #points "
1452  "> number_of_points, or init_factor > 1");
1453  }
1454 
1455  // Compute area of each triangle and sum surface area
1456  std::vector<double> triangle_areas;
1457  double surface_area = GetSurfaceArea(triangle_areas);
1458 
1459  // Compute init points using uniform sampling
1460  std::shared_ptr<ccPointCloud> pcl;
1461  if (pcl_init == nullptr) {
1462  pcl = SamplePointsUniformlyImpl(size_t(init_factor * number_of_points),
1463  triangle_areas, surface_area,
1464  use_triangle_normal, seed);
1465  } else {
1466  pcl = std::make_shared<ccPointCloud>();
1467  *pcl = *pcl_init;
1468  }
1469 
1470  // Set-up sample elimination
1471  double alpha = 8; // constant defined in paper
1472  double beta = 0.5; // constant defined in paper
1473  double gamma = 1.5; // constant defined in paper
1474  double ratio = double(number_of_points) / double(pcl->size());
1475  double r_max = 2 * std::sqrt((surface_area / number_of_points) /
1476  (2 * std::sqrt(3.)));
1477  double r_min = r_max * beta * (1 - std::pow(ratio, gamma));
1478 
1479  std::vector<double> weights(pcl->size());
1480  std::vector<bool> deleted(pcl->size(), false);
1482 
1483  auto WeightFcn = [&](double d2) {
1484  double d = std::sqrt(d2);
1485  if (d < r_min) {
1486  d = r_min;
1487  }
1488  return std::pow(1 - d / r_max, alpha);
1489  };
1490 
1491  auto ComputePointWeight = [&](int pidx0) {
1492  std::vector<int> nbs;
1493  std::vector<double> dists2;
1494  kdtree.SearchRadius(pcl->getEigenPoint(static_cast<size_t>(pidx0)),
1495  r_max, nbs, dists2);
1496  double weight = 0;
1497  for (size_t nbidx = 0; nbidx < nbs.size(); ++nbidx) {
1498  int pidx1 = nbs[nbidx];
1499  // only count weights if not the same point if not deleted
1500  if (pidx0 == pidx1 || deleted[pidx1]) {
1501  continue;
1502  }
1503  weight += WeightFcn(dists2[nbidx]);
1504  }
1505 
1506  weights[pidx0] = weight;
1507  };
1508 
1509  // init weights and priority queue
1510  typedef std::tuple<int, double> QueueEntry;
1511  auto WeightCmp = [](const QueueEntry &a, const QueueEntry &b) {
1512  return std::get<1>(a) < std::get<1>(b);
1513  };
1514  std::priority_queue<QueueEntry, std::vector<QueueEntry>,
1515  decltype(WeightCmp)>
1516  queue(WeightCmp);
1517  for (size_t pidx0 = 0; pidx0 < pcl->size(); ++pidx0) {
1518  ComputePointWeight(int(pidx0));
1519  queue.push(QueueEntry(int(pidx0), weights[pidx0]));
1520  };
1521 
1522  // sample elimination
1523  size_t current_number_of_points = pcl->size();
1524  while (current_number_of_points > number_of_points) {
1525  int pidx;
1526  double weight;
1527  std::tie(pidx, weight) = queue.top();
1528  queue.pop();
1529 
1530  // test if the entry is up to date (because of reinsert)
1531  if (deleted[pidx] || weight != weights[pidx]) {
1532  continue;
1533  }
1534 
1535  // delete current sample
1536  deleted[pidx] = true;
1537  current_number_of_points--;
1538 
1539  // update weights
1540  std::vector<int> nbs;
1541  std::vector<double> dists2;
1542  kdtree.SearchRadius(pcl->getEigenPoint(static_cast<size_t>(pidx)),
1543  r_max, nbs, dists2);
1544  for (int nb : nbs) {
1545  ComputePointWeight(nb);
1546  queue.push(QueueEntry(nb, weights[nb]));
1547  }
1548  }
1549 
1550  // update pcl
1551  bool has_vert_normal = pcl->hasNormals();
1552  bool has_vert_color = pcl->hasColors();
1553  bool has_textures_ = hasTextures();
1554  bool has_triangle_uvs_ = hasTriangleUvs();
1555  size_t next_free = 0;
1556  for (size_t idx = 0; idx < pcl->size(); ++idx) {
1557  if (!deleted[idx]) {
1558  pcl->setPoint(next_free,
1559  *pcl->getPoint(static_cast<unsigned>(idx)));
1560  if (has_vert_normal) {
1561  pcl->setPointNormal(
1562  next_free,
1563  pcl->getPointNormal(static_cast<unsigned>(idx)));
1564  }
1565  if (has_vert_color || (has_textures_ && has_triangle_uvs_)) {
1566  pcl->setPointColor(
1567  static_cast<unsigned>(next_free),
1568  pcl->getPointColor(static_cast<unsigned>(idx)));
1569  }
1570  next_free++;
1571  }
1572  }
1573 
1574  // pcl->shrinkToFit();
1575  pcl->resize(static_cast<unsigned>(next_free));
1576  if (has_vert_normal) {
1577  pcl->resizeTheNormsTable();
1578  }
1579  if (has_vert_color) {
1580  pcl->resizeTheRGBTable();
1581  }
1582 
1583  return pcl;
1584 }
1585 
1586 std::unordered_map<Eigen::Vector2i,
1587  std::vector<int>,
1590  std::unordered_map<Eigen::Vector2i, std::vector<int>,
1592  trias_per_edge;
1593  auto AddEdge = [&](int vidx0, int vidx1, int tidx) {
1594  trias_per_edge[GetOrderedEdge(vidx0, vidx1)].push_back(tidx);
1595  };
1596 
1597  Eigen::Vector3i triangle;
1598  for (size_t tidx = 0; tidx < size(); ++tidx) {
1599  getTriangleVertIndexes(tidx, triangle);
1600  AddEdge(triangle(0), triangle(1), int(tidx));
1601  AddEdge(triangle(1), triangle(2), int(tidx));
1602  AddEdge(triangle(2), triangle(0), int(tidx));
1603  }
1604  return trias_per_edge;
1605 }
1606 
1607 std::unordered_map<Eigen::Vector2i,
1608  std::vector<int>,
1611  std::unordered_map<Eigen::Vector2i, std::vector<int>,
1613  trias_per_edge;
1614  auto AddEdge = [&](int vidx0, int vidx1, int vidx2) {
1615  trias_per_edge[GetOrderedEdge(vidx0, vidx1)].push_back(vidx2);
1616  };
1617 
1618  Eigen::Vector3i triangle;
1619  for (size_t tidx = 0; tidx < size(); ++tidx) {
1620  getTriangleVertIndexes(tidx, triangle);
1621  AddEdge(triangle(0), triangle(1), triangle(2));
1622  AddEdge(triangle(1), triangle(2), triangle(0));
1623  AddEdge(triangle(2), triangle(0), triangle(1));
1624  }
1625  return trias_per_edge;
1626 }
1627 
1628 double ccMesh::ComputeTriangleArea(const Eigen::Vector3d &p0,
1629  const Eigen::Vector3d &p1,
1630  const Eigen::Vector3d &p2) {
1631  const Eigen::Vector3d x = p0 - p1;
1632  const Eigen::Vector3d y = p0 - p2;
1633  double area = 0.5 * x.cross(y).norm();
1634  return area;
1635 }
1636 
1637 double ccMesh::GetTriangleArea(size_t triangle_idx) const {
1638  Eigen::Vector3d vertex0, vertex1, vertex2;
1639  getTriangleVertices(static_cast<unsigned int>(triangle_idx), vertex0.data(),
1640  vertex1.data(), vertex2.data());
1641  return ComputeTriangleArea(vertex0, vertex1, vertex2);
1642 }
1643 
1644 double ccMesh::GetSurfaceArea() const {
1645  double surface_area = 0;
1646  for (size_t tidx = 0; tidx < size(); ++tidx) {
1647  double triangle_area = GetTriangleArea(tidx);
1648  surface_area += triangle_area;
1649  }
1650  return surface_area;
1651 }
1652 
1653 double ccMesh::GetSurfaceArea(std::vector<double> &triangle_areas) const {
1654  double surface_area = 0;
1655  triangle_areas.resize(size());
1656  for (size_t tidx = 0; tidx < size(); ++tidx) {
1657  double triangle_area = GetTriangleArea(tidx);
1658  triangle_areas[tidx] = triangle_area;
1659  surface_area += triangle_area;
1660  }
1661  return surface_area;
1662 }
1663 
1664 double ccMesh::GetVolume() const {
1665  // Computes the signed volume of the tetrahedron defined by
1666  // the three triangle vertices and the origin. The sign is determined by
1667  // checking if the origin is at the same side as the normal with respect to
1668  // the triangle.
1669  auto GetSignedVolumeOfTriangle = [&](size_t tidx) {
1670  const Eigen::Vector3i &triangle = getTriangle(tidx);
1671  const Eigen::Vector3d &vertex0 = getVertice(triangle(0));
1672  const Eigen::Vector3d &vertex1 = getVertice(triangle(1));
1673  const Eigen::Vector3d &vertex2 = getVertice(triangle(2));
1674  return vertex0.dot(vertex1.cross(vertex2)) / 6.0;
1675  };
1676 
1677  if (!IsWatertight()) {
1679  "The mesh is not watertight, and the volume cannot be "
1680  "computed.");
1681  }
1682  if (!IsOrientable()) {
1684  "The mesh is not orientable, and the volume cannot be "
1685  "computed.");
1686  }
1687 
1688  double volume = 0;
1689  std::int64_t num_triangles = static_cast<std::int64_t>(this->size());
1690 #pragma omp parallel for reduction(+ : volume)
1691  for (std::int64_t tidx = 0; tidx < num_triangles; ++tidx) {
1692  volume += GetSignedVolumeOfTriangle(tidx);
1693  }
1694  return std::abs(volume);
1695 }
1696 
1697 Eigen::Vector4d ccMesh::ComputeTrianglePlane(const Eigen::Vector3d &p0,
1698  const Eigen::Vector3d &p1,
1699  const Eigen::Vector3d &p2) {
1700  const Eigen::Vector3d e0 = p1 - p0;
1701  const Eigen::Vector3d e1 = p2 - p0;
1702  Eigen::Vector3d abc = e0.cross(e1);
1703  double norm = abc.norm();
1704  // if the three points are co-linear, return invalid plane
1705  if (norm == 0) {
1706  return Eigen::Vector4d(0, 0, 0, 0);
1707  }
1708  abc /= norm;
1709  double d = -abc.dot(p0);
1710  return Eigen::Vector4d(abc(0), abc(1), abc(2), d);
1711 }
1712 
1713 Eigen::Vector4d ccMesh::GetTrianglePlane(size_t triangle_idx) const {
1714  Eigen::Vector3d vertex0, vertex1, vertex2;
1715  getTriangleVertices(static_cast<unsigned int>(triangle_idx), vertex0.data(),
1716  vertex1.data(), vertex2.data());
1717  return ComputeTrianglePlane(vertex0, vertex1, vertex2);
1718 }
1719 
1720 std::shared_ptr<ccPointCloud> ccMesh::SamplePointsUniformlyImpl(
1721  size_t number_of_points,
1722  std::vector<double> &triangle_areas,
1723  double surface_area,
1724  bool use_triangle_normal,
1725  int seed) {
1726  // triangle areas to cdf
1727  triangle_areas[0] /= surface_area;
1728  for (size_t tidx = 1; tidx < size(); ++tidx) {
1729  triangle_areas[tidx] =
1730  triangle_areas[tidx] / surface_area + triangle_areas[tidx - 1];
1731  }
1732 
1733  // sample point cloud
1734  bool has_vert_normal = m_associatedCloud->hasNormals();
1735  bool has_vert_color = m_associatedCloud->hasColors();
1736  bool has_textures_ = hasTextures();
1737  bool has_triangle_uvs_ = hasTriangleUvs();
1738  bool has_triangle_material_ids_ = hasTriangleMaterialIds();
1739  if (seed == -1) {
1740  std::random_device rd;
1741  seed = rd();
1742  }
1743  std::mt19937 mt(seed);
1744  std::uniform_real_distribution<double> dist(0.0, 1.0);
1745  auto pcd = std::make_shared<ccPointCloud>();
1746  pcd->resize(static_cast<unsigned>(number_of_points));
1747  if (has_vert_normal || use_triangle_normal) {
1748  pcd->resizeTheNormsTable();
1749  }
1750  if (use_triangle_normal && !hasTriNormals()) {
1751  computeNormals(true);
1752  }
1753  if (has_vert_color || (has_textures_ && has_triangle_uvs_)) {
1754  pcd->resizeTheRGBTable();
1755  }
1756  size_t point_idx = 0;
1757  for (size_t tidx = 0; tidx < size(); ++tidx) {
1758  size_t n = size_t(std::round(triangle_areas[tidx] * number_of_points));
1759  while (point_idx < n) {
1760  double r1 = dist(mt);
1761  double r2 = dist(mt);
1762  double a = (1 - std::sqrt(r1));
1763  double b = std::sqrt(r1) * (1 - r2);
1764  double c = std::sqrt(r1) * r2;
1765 
1766  Eigen::Vector3d vert0, vert1, vert2;
1767  getTriangleVertices(static_cast<unsigned int>(tidx), vert0.data(),
1768  vert1.data(), vert2.data());
1769  Eigen::Vector3d temp = a * vert0 + b * vert1 + c * vert2;
1770  pcd->setPoint(point_idx, temp);
1771 
1772  assert(m_associatedCloud);
1773  ccPointCloud *cloud = (ccPointCloud *)m_associatedCloud;
1774  const cloudViewer::VerticesIndexes *tri =
1775  getTriangleVertIndexes(static_cast<unsigned int>(tidx));
1776 
1777  if (has_vert_normal && !use_triangle_normal) {
1778  Eigen::Vector3d N = a * cloud->getEigenNormal(tri->i1) +
1779  b * cloud->getEigenNormal(tri->i2) +
1780  c * cloud->getEigenNormal(tri->i3);
1781  pcd->setPointNormal(point_idx, N);
1782  }
1783  if (use_triangle_normal) {
1784  pcd->setPointNormal(point_idx, getTriangleNorm(tidx));
1785  }
1786  // if there is no texture, sample from vertex color
1787  if (has_vert_color && !has_textures_ && !has_triangle_uvs_) {
1788  Eigen::Vector3d C = a * cloud->getEigenColor(tri->i1) +
1789  b * cloud->getEigenColor(tri->i2) +
1790  c * cloud->getEigenColor(tri->i3);
1791  pcd->setPointColor(point_idx, C);
1792  }
1793 
1794  // if there is a texture, sample from texture instead
1795  if (has_textures_ && has_triangle_uvs_ &&
1796  has_triangle_material_ids_) {
1797  Eigen::Vector2d uv = a * triangle_uvs_[3 * tidx] +
1798  b * triangle_uvs_[3 * tidx + 1] +
1799  c * triangle_uvs_[3 * tidx + 2];
1800  int material_id = triangle_material_ids_[tidx];
1801  int w = textures_[material_id].width_;
1802  int h = textures_[material_id].height_;
1803 
1804  pcd->setPointColor(
1805  point_idx,
1806  Eigen::Vector3d(
1807  (double)*(textures_[material_id]
1808  .PointerAt<uint8_t>(uv(0) * w,
1809  uv(1) * h,
1810  0)) /
1811  255,
1812  (double)*(textures_[material_id]
1813  .PointerAt<uint8_t>(uv(0) * w,
1814  uv(1) * h,
1815  1)) /
1816  255,
1817  (double)*(textures_[material_id]
1818  .PointerAt<uint8_t>(uv(0) * w,
1819  uv(1) * h,
1820  2)) /
1821  255));
1822  }
1823 
1824  point_idx++;
1825  }
1826  }
1827 
1828  return pcd;
1829 }
1830 
1831 std::shared_ptr<ccPointCloud> ccMesh::SamplePointsUniformly(
1832  size_t number_of_points,
1833  bool use_triangle_normal /* = false */,
1834  int seed /* = -1 */) {
1835  if (number_of_points <= 0) {
1836  utility::LogError("[SamplePointsUniformly] number_of_points <= 0");
1837  }
1838  if (size() == 0) {
1840  "[SamplePointsUniformly] input mesh has no triangles");
1841  }
1842 
1843  // Compute area of each triangle and sum surface area
1844  std::vector<double> triangle_areas;
1845  double surface_area = GetSurfaceArea(triangle_areas);
1846 
1847  return SamplePointsUniformlyImpl(number_of_points, triangle_areas,
1848  surface_area, use_triangle_normal, seed);
1849 }
1850 
1851 std::shared_ptr<ccMesh> ccMesh::CreateTetrahedron(
1852  double radius /* = 1.0*/, bool create_uv_map /* = false*/) {
1853  ccPointCloud *baseVertices = new ccPointCloud("vertices");
1854  assert(baseVertices);
1855  auto mesh = std::make_shared<ccMesh>(baseVertices);
1856  if (radius <= 0) {
1857  utility::LogError("[CreateTetrahedron] radius <= 0");
1858  }
1859 
1860  // Vertices.
1861  baseVertices->addEigenPoint(
1862  radius * Eigen::Vector3d(std::sqrt(8. / 9.), 0, -1. / 3.));
1863  baseVertices->addEigenPoint(radius * Eigen::Vector3d(-std::sqrt(2. / 9.),
1864  std::sqrt(2. / 3.),
1865  -1. / 3.));
1866  baseVertices->addEigenPoint(radius * Eigen::Vector3d(-std::sqrt(2. / 9.),
1867  -std::sqrt(2. / 3.),
1868  -1. / 3.));
1869  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0., 0., 1.));
1870 
1871  // Triangles.
1872  mesh->addTriangle(Eigen::Vector3i(0, 2, 1));
1873  mesh->addTriangle(Eigen::Vector3i(0, 3, 2));
1874  mesh->addTriangle(Eigen::Vector3i(0, 1, 3));
1875  mesh->addTriangle(Eigen::Vector3i(1, 2, 3));
1876 
1877  // UV Map.
1878  if (create_uv_map) {
1879  mesh->triangle_uvs_ = {{0.866, 0.5}, {0.433, 0.75}, {0.433, 0.25},
1880  {0.866, 0.5}, {0.866, 1.0}, {0.433, 0.75},
1881  {0.866, 0.5}, {0.433, 0.25}, {0.866, 0.0},
1882  {0.433, 0.25}, {0.433, 0.75}, {0.0, 0.5}};
1883  }
1884 
1885  // do some cleaning
1886  {
1887  baseVertices->shrinkToFit();
1888  mesh->shrinkToFit();
1889  NormsIndexesTableType *normals = mesh->getTriNormsTable();
1890  if (normals) {
1891  normals->shrink_to_fit();
1892  }
1893  }
1894 
1895  baseVertices->setEnabled(false);
1896  // DGM: no need to lock it as it is only used by one mesh!
1897  baseVertices->setLocked(false);
1898  mesh->addChild(baseVertices);
1899  return mesh;
1900 }
1901 
1902 std::shared_ptr<ccMesh> ccMesh::CreateOctahedron(
1903  double radius /* = 1.0*/, bool create_uv_map /* = false*/) {
1904  ccPointCloud *baseVertices = new ccPointCloud("vertices");
1905  assert(baseVertices);
1906  auto mesh = std::make_shared<ccMesh>(baseVertices);
1907 
1908  if (radius <= 0) {
1909  utility::LogError("[CreateOctahedron] radius <= 0");
1910  }
1911 
1912  // Vertices.
1913  baseVertices->addEigenPoint(radius * Eigen::Vector3d(1, 0, 0));
1914  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, 1, 0));
1915  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, 0, 1));
1916  baseVertices->addEigenPoint(radius * Eigen::Vector3d(-1, 0, 0));
1917  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, -1, 0));
1918  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, 0, -1));
1919 
1920  // Triangles.
1921  mesh->addTriangle(Eigen::Vector3i(0, 1, 2));
1922  mesh->addTriangle(Eigen::Vector3i(1, 3, 2));
1923  mesh->addTriangle(Eigen::Vector3i(3, 4, 2));
1924  mesh->addTriangle(Eigen::Vector3i(4, 0, 2));
1925  mesh->addTriangle(Eigen::Vector3i(0, 5, 1));
1926  mesh->addTriangle(Eigen::Vector3i(1, 5, 3));
1927  mesh->addTriangle(Eigen::Vector3i(3, 5, 4));
1928  mesh->addTriangle(Eigen::Vector3i(4, 5, 0));
1929 
1930  // UV Map.
1931  if (create_uv_map) {
1932  mesh->triangle_uvs_ = {
1933  {0.0, 0.75}, {0.1444, 0.5}, {0.2887, 0.75}, {0.1444, 0.5},
1934  {0.433, 0.5}, {0.2887, 0.75}, {0.433, 0.5}, {0.5773, 0.75},
1935  {0.2887, 0.75}, {0.5773, 0.75}, {0.433, 1.0}, {0.2887, 0.75},
1936  {0.0, 0.25}, {0.2887, 0.25}, {0.1444, 0.5}, {0.1444, 0.5},
1937  {0.2887, 0.25}, {0.433, 0.5}, {0.433, 0.5}, {0.2887, 0.25},
1938  {0.5773, 0.25}, {0.5773, 0.25}, {0.2887, 0.25}, {0.433, 0.0}};
1939  }
1940 
1941  // do some cleaning
1942  {
1943  baseVertices->shrinkToFit();
1944  mesh->shrinkToFit();
1945  NormsIndexesTableType *normals = mesh->getTriNormsTable();
1946  if (normals) {
1947  normals->shrink_to_fit();
1948  }
1949  }
1950 
1951  baseVertices->setEnabled(false);
1952  // DGM: no need to lock it as it is only used by one mesh!
1953  baseVertices->setLocked(false);
1954  mesh->addChild(baseVertices);
1955 
1956  return mesh;
1957 }
1958 
1959 std::shared_ptr<ccMesh> ccMesh::CreateIcosahedron(
1960  double radius /* = 1.0*/, bool create_uv_map /* = false*/) {
1961  ccPointCloud *baseVertices = new ccPointCloud("vertices");
1962  assert(baseVertices);
1963  auto mesh = std::make_shared<ccMesh>(baseVertices);
1964  if (radius <= 0) {
1965  utility::LogError("[CreateIcosahedron] radius <= 0");
1966  }
1967  const double p = (1. + std::sqrt(5.)) / 2.;
1968 
1969  // Vertices.
1970  baseVertices->addEigenPoint(radius * Eigen::Vector3d(-1, 0, p));
1971  baseVertices->addEigenPoint(radius * Eigen::Vector3d(1, 0, p));
1972  baseVertices->addEigenPoint(radius * Eigen::Vector3d(1, 0, -p));
1973  baseVertices->addEigenPoint(radius * Eigen::Vector3d(-1, 0, -p));
1974  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, -p, 1));
1975  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, p, 1));
1976  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, p, -1));
1977  baseVertices->addEigenPoint(radius * Eigen::Vector3d(0, -p, -1));
1978  baseVertices->addEigenPoint(radius * Eigen::Vector3d(-p, -1, 0));
1979  baseVertices->addEigenPoint(radius * Eigen::Vector3d(p, -1, 0));
1980  baseVertices->addEigenPoint(radius * Eigen::Vector3d(p, 1, 0));
1981  baseVertices->addEigenPoint(radius * Eigen::Vector3d(-p, 1, 0));
1982 
1983  // Triangles.
1984  mesh->addTriangle(Eigen::Vector3i(0, 4, 1));
1985  mesh->addTriangle(Eigen::Vector3i(0, 1, 5));
1986  mesh->addTriangle(Eigen::Vector3i(1, 4, 9));
1987  mesh->addTriangle(Eigen::Vector3i(1, 9, 10));
1988  mesh->addTriangle(Eigen::Vector3i(1, 10, 5));
1989  mesh->addTriangle(Eigen::Vector3i(0, 8, 4));
1990  mesh->addTriangle(Eigen::Vector3i(0, 11, 8));
1991  mesh->addTriangle(Eigen::Vector3i(0, 5, 11));
1992  mesh->addTriangle(Eigen::Vector3i(5, 6, 11));
1993  mesh->addTriangle(Eigen::Vector3i(5, 10, 6));
1994  mesh->addTriangle(Eigen::Vector3i(4, 8, 7));
1995  mesh->addTriangle(Eigen::Vector3i(4, 7, 9));
1996  mesh->addTriangle(Eigen::Vector3i(3, 6, 2));
1997  mesh->addTriangle(Eigen::Vector3i(3, 2, 7));
1998  mesh->addTriangle(Eigen::Vector3i(2, 6, 10));
1999  mesh->addTriangle(Eigen::Vector3i(2, 10, 9));
2000  mesh->addTriangle(Eigen::Vector3i(2, 9, 7));
2001  mesh->addTriangle(Eigen::Vector3i(3, 11, 6));
2002  mesh->addTriangle(Eigen::Vector3i(3, 8, 11));
2003  mesh->addTriangle(Eigen::Vector3i(3, 7, 8));
2004 
2005  // UV Map.
2006  if (create_uv_map) {
2007  mesh->triangle_uvs_ = {
2008  {0.0001, 0.1819}, {0.1575, 0.091}, {0.1575, 0.2728},
2009  {0.0001, 0.3637}, {0.1575, 0.2728}, {0.1575, 0.4546},
2010  {0.1575, 0.2728}, {0.1575, 0.091}, {0.3149, 0.1819},
2011  {0.1575, 0.2728}, {0.3149, 0.1819}, {0.3149, 0.3637},
2012  {0.1575, 0.2728}, {0.3149, 0.3637}, {0.1575, 0.4546},
2013  {0.0001, 0.909}, {0.1575, 0.8181}, {0.1575, 0.9999},
2014  {0.0001, 0.7272}, {0.1575, 0.6363}, {0.1575, 0.8181},
2015  {0.0001, 0.5454}, {0.1575, 0.4546}, {0.1575, 0.6363},
2016  {0.1575, 0.4546}, {0.3149, 0.5454}, {0.1575, 0.6363},
2017  {0.1575, 0.4546}, {0.3149, 0.3637}, {0.3149, 0.5454},
2018  {0.1575, 0.9999}, {0.1575, 0.8181}, {0.3149, 0.909},
2019  {0.1575, 0.091}, {0.3149, 0.0001}, {0.3149, 0.1819},
2020  {0.3149, 0.7272}, {0.3149, 0.5454}, {0.4724, 0.6363},
2021  {0.3149, 0.7272}, {0.4724, 0.8181}, {0.3149, 0.909},
2022  {0.4724, 0.4546}, {0.3149, 0.5454}, {0.3149, 0.3637},
2023  {0.4724, 0.2728}, {0.3149, 0.3637}, {0.3149, 0.1819},
2024  {0.4724, 0.091}, {0.3149, 0.1819}, {0.3149, 0.0001},
2025  {0.3149, 0.7272}, {0.1575, 0.6363}, {0.3149, 0.5454},
2026  {0.3149, 0.7272}, {0.1575, 0.8181}, {0.1575, 0.6363},
2027  {0.3149, 0.7272}, {0.3149, 0.909}, {0.1575, 0.8181}};
2028  }
2029 
2030  // do some cleaning
2031  {
2032  baseVertices->shrinkToFit();
2033  mesh->shrinkToFit();
2034  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2035  if (normals) {
2036  normals->shrink_to_fit();
2037  }
2038  }
2039 
2040  baseVertices->setEnabled(false);
2041  // DGM: no need to lock it as it is only used by one mesh!
2042  baseVertices->setLocked(false);
2043  mesh->addChild(baseVertices);
2044  return mesh;
2045 }
2046 
2047 std::shared_ptr<ccMesh> ccMesh::CreatePlane(double width /* = 1.0*/,
2048  double height /* = 1.0*/,
2049  bool create_uv_map /* = false*/) {
2050  ccPointCloud *baseVertices = new ccPointCloud("vertices");
2051  assert(baseVertices);
2052  auto mesh = std::make_shared<ccMesh>(baseVertices);
2053  if (width <= 0) {
2054  utility::LogError("[CreatePlane] width <= 0");
2055  }
2056  if (height <= 0) {
2057  utility::LogError("[CreatePlane] height <= 0");
2058  }
2059 
2060  // B ------ C
2061  // | |
2062  // A ------ D
2063  if (!baseVertices->resize(4)) {
2064  utility::LogError("not enough memory!");
2065  }
2066  *baseVertices->getPointPtr(0) = Eigen::Vector3d(-width / 2, -height / 2, 0);
2067  *baseVertices->getPointPtr(1) = Eigen::Vector3d(-width / 2, height / 2, 0);
2068  *baseVertices->getPointPtr(2) = Eigen::Vector3d(width / 2, height / 2, 0);
2069  *baseVertices->getPointPtr(3) = Eigen::Vector3d(width / 2, -height / 2, 0);
2070  // Triangles.
2071  mesh->addTriangle(Eigen::Vector3i(0, 2, 1)); // A C B
2072  mesh->addTriangle(Eigen::Vector3i(0, 3, 2)); // A D C
2073 
2074  // do some cleaning
2075  {
2076  baseVertices->shrinkToFit();
2077  mesh->shrinkToFit();
2078  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2079  if (normals) {
2080  normals->shrink_to_fit();
2081  }
2082  }
2083 
2084  baseVertices->setEnabled(false);
2085  // DGM: no need to lock it as it is only used by one mesh!
2086  baseVertices->setLocked(false);
2087  mesh->addChild(baseVertices);
2088  return mesh;
2089 }
2090 
2091 std::shared_ptr<ccMesh> ccMesh::CreateBox(
2092  double width /* = 1.0*/,
2093  double height /* = 1.0*/,
2094  double depth /* = 1.0*/,
2095  bool create_uv_map /* = false*/,
2096  bool map_texture_to_each_face /*= false*/) {
2097  ccPointCloud *baseVertices = new ccPointCloud("vertices");
2098  assert(baseVertices);
2099  auto mesh = std::make_shared<ccMesh>(baseVertices);
2100  if (width <= 0) {
2101  utility::LogError("[CreateBox] width <= 0");
2102  }
2103  if (height <= 0) {
2104  utility::LogError("[CreateBox] height <= 0");
2105  }
2106  if (depth <= 0) {
2107  utility::LogError("[CreateBox] depth <= 0");
2108  }
2109 
2110  // Vertices.
2111  if (!baseVertices->resize(8)) {
2112  utility::LogError("not enough memory!");
2113  }
2114  *baseVertices->getPointPtr(0) = Eigen::Vector3d(0.0, 0.0, 0.0);
2115  *baseVertices->getPointPtr(1) = Eigen::Vector3d(width, 0.0, 0.0);
2116  *baseVertices->getPointPtr(2) = Eigen::Vector3d(0.0, 0.0, depth);
2117  *baseVertices->getPointPtr(3) = Eigen::Vector3d(width, 0.0, depth);
2118  *baseVertices->getPointPtr(4) = Eigen::Vector3d(0.0, height, 0.0);
2119  *baseVertices->getPointPtr(5) = Eigen::Vector3d(width, height, 0.0);
2120  *baseVertices->getPointPtr(6) = Eigen::Vector3d(0.0, height, depth);
2121  *baseVertices->getPointPtr(7) = Eigen::Vector3d(width, height, depth);
2122 
2123  // Triangles.
2124  mesh->addTriangle(Eigen::Vector3i(4, 7, 5));
2125  mesh->addTriangle(Eigen::Vector3i(4, 6, 7));
2126  mesh->addTriangle(Eigen::Vector3i(0, 2, 4));
2127  mesh->addTriangle(Eigen::Vector3i(2, 6, 4));
2128  mesh->addTriangle(Eigen::Vector3i(0, 1, 2));
2129  mesh->addTriangle(Eigen::Vector3i(1, 3, 2));
2130  mesh->addTriangle(Eigen::Vector3i(1, 5, 7));
2131  mesh->addTriangle(Eigen::Vector3i(1, 7, 3));
2132  mesh->addTriangle(Eigen::Vector3i(2, 3, 7));
2133  mesh->addTriangle(Eigen::Vector3i(2, 7, 6));
2134  mesh->addTriangle(Eigen::Vector3i(0, 4, 1));
2135  mesh->addTriangle(Eigen::Vector3i(1, 4, 5));
2136 
2137  // UV Map.
2138  if (create_uv_map) {
2139  if (map_texture_to_each_face) {
2140  mesh->triangle_uvs_ = {
2141  {0.0, 0.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 1.0},
2142  {1.0, 1.0}, {0.0, 0.0}, {0.0, 1.0}, {1.0, 0.0}, {0.0, 1.0},
2143  {1.0, 1.0}, {1.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0},
2144  {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}, {0.0, 0.0}, {1.0, 0.0},
2145  {1.0, 1.0}, {0.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}, {0.0, 0.0},
2146  {1.0, 0.0}, {1.0, 1.0}, {0.0, 0.0}, {1.0, 1.0}, {0.0, 1.0},
2147  {0.0, 0.0}, {0.0, 1.0}, {1.0, 0.0}, {1.0, 0.0}, {0.0, 1.0},
2148  {1.0, 1.0}};
2149  } else {
2150  mesh->triangle_uvs_ = {
2151  {0.5, 0.5}, {0.75, 0.75}, {0.5, 0.75}, {0.5, 0.5},
2152  {0.75, 0.5}, {0.75, 0.75}, {0.25, 0.5}, {0.25, 0.25},
2153  {0.5, 0.5}, {0.25, 0.25}, {0.5, 0.25}, {0.5, 0.5},
2154  {0.25, 0.5}, {0.25, 0.75}, {0.0, 0.5}, {0.25, 0.75},
2155  {0.0, 0.75}, {0.0, 0.5}, {0.25, 0.75}, {0.5, 0.75},
2156  {0.5, 1.0}, {0.25, 0.75}, {0.5, 1.0}, {0.25, 1.0},
2157  {0.25, 0.25}, {0.25, 0.0}, {0.5, 0.0}, {0.25, 0.25},
2158  {0.5, 0.0}, {0.5, 0.25}, {0.25, 0.5}, {0.5, 0.5},
2159  {0.25, 0.75}, {0.25, 0.75}, {0.5, 0.5}, {0.5, 0.75}};
2160  }
2161  }
2162 
2163  // do some cleaning
2164  {
2165  baseVertices->shrinkToFit();
2166  mesh->shrinkToFit();
2167  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2168  if (normals) {
2169  normals->shrink_to_fit();
2170  }
2171  }
2172 
2173  baseVertices->setEnabled(false);
2174  // DGM: no need to lock it as it is only used by one mesh!
2175  baseVertices->setLocked(false);
2176  mesh->addChild(baseVertices);
2177  return mesh;
2178 }
2179 
2180 std::shared_ptr<ccMesh> ccMesh::CreateSphere(double radius /* = 1.0*/,
2181  int resolution /* = 20*/,
2182  bool create_uv_map /* = false*/) {
2183  ccPointCloud *baseVertices = new ccPointCloud("vertices");
2184  assert(baseVertices);
2185  auto mesh = std::make_shared<ccMesh>(baseVertices);
2186 
2187  if (radius <= 0) {
2188  utility::LogError("[CreateSphere] radius <= 0");
2189  }
2190  if (resolution <= 0) {
2191  utility::LogError("[CreateSphere] resolution <= 0");
2192  }
2193 
2194  if (!baseVertices->resize(2 * resolution * (resolution - 1) + 2)) {
2195  utility::LogError("not enough memory!");
2196  }
2197 
2198  std::unordered_map<int64_t, std::pair<double, double>> map_vertices_to_uv;
2199  std::unordered_map<int64_t, std::pair<double, double>>
2200  map_cut_vertices_to_uv;
2201 
2202  *baseVertices->getPointPtr(0) = Eigen::Vector3d(0.0, 0.0, radius);
2203  *baseVertices->getPointPtr(1) = Eigen::Vector3d(0.0, 0.0, -radius);
2204  double step = M_PI / (double)resolution;
2205  for (int i = 1; i < resolution; i++) {
2206  double alpha = step * i;
2207  double uv_row = (1.0 / (resolution)) * i;
2208  int base = 2 + 2 * resolution * (i - 1);
2209  for (int j = 0; j < 2 * resolution; j++) {
2210  double theta = step * j;
2211  double uv_col = (1.0 / (2.0 * resolution)) * j;
2212  Eigen::Vector3d temp =
2213  Eigen::Vector3d(sin(alpha) * cos(theta),
2214  sin(alpha) * sin(theta), cos(alpha)) *
2215  radius;
2216  baseVertices->setEigenPoint(static_cast<std::size_t>(base + j),
2217  temp);
2218  if (create_uv_map) {
2219  map_vertices_to_uv[base + j] = std::make_pair(uv_row, uv_col);
2220  }
2221  }
2222  if (create_uv_map) {
2223  map_cut_vertices_to_uv[base] = std::make_pair(uv_row, 1.0);
2224  }
2225  }
2226 
2227  // Triangles for poles.
2228  for (int j = 0; j < 2 * resolution; j++) {
2229  int j1 = (j + 1) % (2 * resolution);
2230  int base = 2;
2231  mesh->addTriangle(Eigen::Vector3i(0, base + j, base + j1));
2232  base = 2 + 2 * resolution * (resolution - 2);
2233  mesh->addTriangle(Eigen::Vector3i(1, base + j1, base + j));
2234  }
2235 
2236  // UV coordinates mapped to triangles for poles.
2237  if (create_uv_map) {
2238  for (int j = 0; j < 2 * resolution - 1; j++) {
2239  int j1 = (j + 1) % (2 * resolution);
2240  int base = 2;
2241  double width = 1.0 / (2.0 * resolution);
2242  double base_offset = width / 2.0;
2243  double uv_col = base_offset + width * j;
2244  mesh->triangle_uvs_.emplace_back(0.0, uv_col);
2245  mesh->triangle_uvs_.emplace_back(
2246  Eigen::Vector2d(map_vertices_to_uv[base + j].first,
2247  map_vertices_to_uv[base + j].second));
2248  mesh->triangle_uvs_.emplace_back(
2249  Eigen::Vector2d(map_vertices_to_uv[base + j1].first,
2250  map_vertices_to_uv[base + j1].second));
2251 
2252  base = 2 + 2 * resolution * (resolution - 2);
2253  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(1.0, uv_col));
2254  mesh->triangle_uvs_.emplace_back(
2255  Eigen::Vector2d(map_vertices_to_uv[base + j1].first,
2256  map_vertices_to_uv[base + j1].second));
2257  mesh->triangle_uvs_.emplace_back(
2258  Eigen::Vector2d(map_vertices_to_uv[base + j].first,
2259  map_vertices_to_uv[base + j].second));
2260  }
2261 
2262  // UV coordinates mapped to triangles for poles, with cut-vertices.
2263  int j = 2 * resolution - 1;
2264  int base = 2;
2265  double width = 1.0 / (2.0 * resolution);
2266  double base_offset = width / 2.0;
2267  double uv_col = base_offset + width * j;
2268  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(0.0, uv_col));
2269  mesh->triangle_uvs_.emplace_back(
2270  Eigen::Vector2d(map_vertices_to_uv[base + j].first,
2271  map_vertices_to_uv[base + j].second));
2272  mesh->triangle_uvs_.emplace_back(
2273  Eigen::Vector2d(map_cut_vertices_to_uv[base].first,
2274  map_cut_vertices_to_uv[base].second));
2275 
2276  base = 2 + 2 * resolution * (resolution - 2);
2277  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(1.0, uv_col));
2278  mesh->triangle_uvs_.emplace_back(
2279  Eigen::Vector2d(map_cut_vertices_to_uv[base].first,
2280  map_cut_vertices_to_uv[base].second));
2281  mesh->triangle_uvs_.emplace_back(
2282  Eigen::Vector2d(map_vertices_to_uv[base + j].first,
2283  map_vertices_to_uv[base + j].second));
2284  }
2285 
2286  // Triangles for non-polar region.
2287  for (int i = 1; i < resolution - 1; i++) {
2288  int base1 = 2 + 2 * resolution * (i - 1);
2289  int base2 = base1 + 2 * resolution;
2290  for (int j = 0; j < 2 * resolution; j++) {
2291  int j1 = (j + 1) % (2 * resolution);
2292  mesh->addTriangle(
2293  Eigen::Vector3i(base2 + j, base1 + j1, base1 + j));
2294  mesh->addTriangle(
2295  Eigen::Vector3i(base2 + j, base2 + j1, base1 + j1));
2296  }
2297  }
2298 
2299  // UV coordinates mapped to triangles for non-polar region.
2300  if (create_uv_map) {
2301  for (int i = 1; i < resolution - 1; i++) {
2302  int base1 = 2 + 2 * resolution * (i - 1);
2303  int base2 = base1 + 2 * resolution;
2304  for (int j = 0; j < 2 * resolution - 1; j++) {
2305  int j1 = (j + 1) % (2 * resolution);
2306  mesh->triangle_uvs_.emplace_back(
2307  map_vertices_to_uv[base2 + j].first,
2308  map_vertices_to_uv[base2 + j].second);
2309  mesh->triangle_uvs_.emplace_back(
2310  Eigen::Vector2d(map_vertices_to_uv[base1 + j1].first,
2311  map_vertices_to_uv[base1 + j1].second));
2312  mesh->triangle_uvs_.emplace_back(
2313  Eigen::Vector2d(map_vertices_to_uv[base1 + j].first,
2314  map_vertices_to_uv[base1 + j].second));
2315 
2316  mesh->triangle_uvs_.emplace_back(
2317  Eigen::Vector2d(map_vertices_to_uv[base2 + j].first,
2318  map_vertices_to_uv[base2 + j].second));
2319  mesh->triangle_uvs_.emplace_back(
2320  Eigen::Vector2d(map_vertices_to_uv[base2 + j1].first,
2321  map_vertices_to_uv[base2 + j1].second));
2322  mesh->triangle_uvs_.emplace_back(
2323  Eigen::Vector2d(map_vertices_to_uv[base1 + j1].first,
2324  map_vertices_to_uv[base1 + j1].second));
2325  }
2326 
2327  // UV coordinates mapped to triangles for non-polar region with
2328  // cut-vertices.
2329  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2330  map_vertices_to_uv[base2 + 2 * resolution - 1].first,
2331  map_vertices_to_uv[base2 + 2 * resolution - 1].second));
2332  mesh->triangle_uvs_.emplace_back(
2333  Eigen::Vector2d(map_cut_vertices_to_uv[base1].first,
2334  map_cut_vertices_to_uv[base1].second));
2335  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2336  map_vertices_to_uv[base1 + 2 * resolution - 1].first,
2337  map_vertices_to_uv[base1 + 2 * resolution - 1].second));
2338 
2339  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2340  map_vertices_to_uv[base2 + 2 * resolution - 1].first,
2341  map_vertices_to_uv[base2 + 2 * resolution - 1].second));
2342  mesh->triangle_uvs_.emplace_back(
2343  Eigen::Vector2d(map_cut_vertices_to_uv[base2].first,
2344  map_cut_vertices_to_uv[base2].second));
2345  mesh->triangle_uvs_.emplace_back(
2346  Eigen::Vector2d(map_cut_vertices_to_uv[base1].first,
2347  map_cut_vertices_to_uv[base1].second));
2348  }
2349  }
2350 
2351  // do some cleaning
2352  {
2353  baseVertices->shrinkToFit();
2354  mesh->shrinkToFit();
2355  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2356  if (normals) {
2357  normals->shrink_to_fit();
2358  }
2359  }
2360 
2361  baseVertices->setEnabled(false);
2362  // DGM: no need to lock it as it is only used by one mesh!
2363  baseVertices->setLocked(false);
2364  mesh->addChild(baseVertices);
2365  return mesh;
2366 }
2367 
2368 std::shared_ptr<ccMesh> ccMesh::CreateCylinder(
2369  double radius /* = 1.0*/,
2370  double height /* = 2.0*/,
2371  int resolution /* = 20*/,
2372  int split /* = 4*/,
2373  bool create_uv_map /* = false*/) {
2374  ccPointCloud *baseVertices = new ccPointCloud("vertices");
2375  assert(baseVertices);
2376  auto mesh = std::make_shared<ccMesh>(baseVertices);
2377  if (radius <= 0) {
2378  utility::LogError("[CreateCylinder] radius <= 0");
2379  }
2380  if (height <= 0) {
2381  utility::LogError("[CreateCylinder] height <= 0");
2382  }
2383  if (resolution <= 0) {
2384  utility::LogError("[CreateCylinder] resolution <= 0");
2385  }
2386  if (split <= 0) {
2387  utility::LogError("[CreateCylinder] split <= 0");
2388  }
2389 
2390  if (!baseVertices->resize(resolution * (split + 1) + 2)) {
2391  utility::LogError("not enough memory!");
2392  }
2393  *baseVertices->getPointPtr(0) = Eigen::Vector3d(0.0, 0.0, height * 0.5);
2394  *baseVertices->getPointPtr(1) = Eigen::Vector3d(0.0, 0.0, -height * 0.5);
2395  double step = M_PI * 2.0 / (double)resolution;
2396  double h_step = height / (double)split;
2397  for (int i = 0; i <= split; i++) {
2398  for (int j = 0; j < resolution; j++) {
2399  double theta = step * j;
2400  baseVertices->setEigenPoint(
2401  static_cast<std::size_t>(2 + resolution * i + j),
2402  Eigen::Vector3d(cos(theta) * radius, sin(theta) * radius,
2403  height * 0.5 - h_step * i));
2404  }
2405  }
2406 
2407  std::unordered_map<int64_t, std::pair<double, double>> map_vertices_to_uv;
2408  std::unordered_map<int64_t, std::pair<double, double>>
2409  map_cut_vertices_to_uv;
2410 
2411  // Mapping vertices to UV coordinates.
2412  if (create_uv_map) {
2413  for (int i = 0; i <= split; i++) {
2414  double uv_row = (1.0 / (double)split) * i;
2415  for (int j = 0; j < resolution; j++) {
2416  // double theta = step * j;
2417  double uv_col = (1.0 / (double)resolution) * j;
2418  map_vertices_to_uv[2 + resolution * i + j] =
2419  std::make_pair(uv_row, uv_col);
2420  }
2421  map_cut_vertices_to_uv[2 + resolution * i] =
2422  std::make_pair(uv_row, 1.0);
2423  }
2424  }
2425 
2426  // Triangles for top and bottom face.
2427  for (int j = 0; j < resolution; j++) {
2428  int j1 = (j + 1) % resolution;
2429  int base = 2;
2430  mesh->addTriangle(Eigen::Vector3i(0, base + j, base + j1));
2431  base = 2 + resolution * split;
2432  mesh->addTriangle(Eigen::Vector3i(1, base + j1, base + j));
2433  }
2434 
2435  // UV coordinates mapped to triangles for top and bottom face.
2436  if (create_uv_map) {
2437  for (int j = 0; j < resolution; j++) {
2438  int j1 = (j + 1) % resolution;
2439  double theta = step * j;
2440  double theta1 = step * j1;
2441  double uv_radius = 0.25;
2442 
2443  mesh->triangle_uvs_.push_back(Eigen::Vector2d(0.75, 0.25));
2444  mesh->triangle_uvs_.push_back(
2445  Eigen::Vector2d(0.75 + uv_radius * cos(theta),
2446  0.25 + uv_radius * sin(theta)));
2447  mesh->triangle_uvs_.push_back(
2448  Eigen::Vector2d(0.75 + uv_radius * cos(theta1),
2449  0.25 + uv_radius * sin(theta1)));
2450 
2451  mesh->triangle_uvs_.push_back(Eigen::Vector2d(0.75, 0.75));
2452  mesh->triangle_uvs_.push_back(
2453  Eigen::Vector2d(0.75 + uv_radius * cos(theta1),
2454  0.75 + uv_radius * sin(theta1)));
2455  mesh->triangle_uvs_.push_back(
2456  Eigen::Vector2d(0.75 + uv_radius * cos(theta),
2457  0.75 + uv_radius * sin(theta)));
2458  }
2459  }
2460 
2461  // Triangles for cylindrical surface.
2462  for (int i = 0; i < split; i++) {
2463  int base1 = 2 + resolution * i;
2464  int base2 = base1 + resolution;
2465  for (int j = 0; j < resolution; j++) {
2466  int j1 = (j + 1) % resolution;
2467  mesh->addTriangle(
2468  Eigen::Vector3i(base2 + j, base1 + j1, base1 + j));
2469  mesh->addTriangle(
2470  Eigen::Vector3i(base2 + j, base2 + j1, base1 + j1));
2471  }
2472  }
2473 
2474  // UV coordinates mapped to triangles for cylindrical surface.
2475  if (create_uv_map) {
2476  for (int i = 0; i < split; i++) {
2477  int base1 = 2 + resolution * i;
2478  int base2 = base1 + resolution;
2479  for (int j = 0; j < resolution - 1; j++) {
2480  int j1 = (j + 1) % resolution;
2481 
2482  mesh->triangle_uvs_.emplace_back(
2483  map_vertices_to_uv[base2 + j].first,
2484  map_vertices_to_uv[base2 + j].second);
2485  mesh->triangle_uvs_.emplace_back(
2486  Eigen::Vector2d(map_vertices_to_uv[base1 + j1].first,
2487  map_vertices_to_uv[base1 + j1].second));
2488  mesh->triangle_uvs_.emplace_back(
2489  Eigen::Vector2d(map_vertices_to_uv[base1 + j].first,
2490  map_vertices_to_uv[base1 + j].second));
2491 
2492  mesh->triangle_uvs_.emplace_back(
2493  Eigen::Vector2d(map_vertices_to_uv[base2 + j].first,
2494  map_vertices_to_uv[base2 + j].second));
2495  mesh->triangle_uvs_.emplace_back(
2496  Eigen::Vector2d(map_vertices_to_uv[base2 + j1].first,
2497  map_vertices_to_uv[base2 + j1].second));
2498  mesh->triangle_uvs_.emplace_back(
2499  Eigen::Vector2d(map_vertices_to_uv[base1 + j1].first,
2500  map_vertices_to_uv[base1 + j1].second));
2501  }
2502 
2503  // UV coordinates mapped to triangles for cylindrical surface with
2504  // cut-vertices.
2505  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2506  map_vertices_to_uv[base2 + resolution - 1].first,
2507  map_vertices_to_uv[base2 + resolution - 1].second));
2508  mesh->triangle_uvs_.emplace_back(
2509  Eigen::Vector2d(map_cut_vertices_to_uv[base1].first,
2510  map_cut_vertices_to_uv[base1].second));
2511  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2512  map_vertices_to_uv[base1 + resolution - 1].first,
2513  map_vertices_to_uv[base1 + resolution - 1].second));
2514 
2515  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2516  map_vertices_to_uv[base2 + resolution - 1].first,
2517  map_vertices_to_uv[base2 + resolution - 1].second));
2518  mesh->triangle_uvs_.emplace_back(
2519  Eigen::Vector2d(map_cut_vertices_to_uv[base2].first,
2520  map_cut_vertices_to_uv[base2].second));
2521  mesh->triangle_uvs_.emplace_back(
2522  Eigen::Vector2d(map_cut_vertices_to_uv[base1].first,
2523  map_cut_vertices_to_uv[base1].second));
2524  }
2525  }
2526 
2527  // do some cleaning
2528  {
2529  baseVertices->shrinkToFit();
2530  mesh->shrinkToFit();
2531  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2532  if (normals) {
2533  normals->shrink_to_fit();
2534  }
2535  }
2536 
2537  baseVertices->setEnabled(false);
2538  // DGM: no need to lock it as it is only used by one mesh!
2539  baseVertices->setLocked(false);
2540  mesh->addChild(baseVertices);
2541  return mesh;
2542 }
2543 
2544 std::shared_ptr<ccMesh> ccMesh::CreateCone(double radius /* = 1.0*/,
2545  double height /* = 2.0*/,
2546  int resolution /* = 20*/,
2547  int split /* = 4*/,
2548  bool create_uv_map /* = false*/) {
2549  ccPointCloud *baseVertices = new ccPointCloud("vertices");
2550  assert(baseVertices);
2551  auto mesh = std::make_shared<ccMesh>(baseVertices);
2552 
2553  if (radius <= 0) {
2554  utility::LogError("[CreateCone] radius <= 0");
2555  }
2556  if (height <= 0) {
2557  utility::LogError("[CreateCone] height <= 0");
2558  }
2559  if (resolution <= 0) {
2560  utility::LogError("[CreateCone] resolution <= 0");
2561  }
2562  if (split <= 0) {
2563  utility::LogError("[CreateCone] split <= 0");
2564  }
2565 
2566  if (!baseVertices->resize(resolution * split + 2)) {
2567  utility::LogError("not enough memory!");
2568  }
2569  *baseVertices->getPointPtr(0) = Eigen::Vector3d(0.0, 0.0, 0.0);
2570  *baseVertices->getPointPtr(1) = Eigen::Vector3d(0.0, 0.0, height);
2571  double step = M_PI * 2.0 / (double)resolution;
2572  double h_step = height / (double)split;
2573  double r_step = radius / (double)split;
2574  std::unordered_map<int64_t, std::pair<double, double>> map_vertices_to_uv;
2575  for (int i = 0; i < split; i++) {
2576  int base = 2 + resolution * i;
2577  double r = r_step * (split - i);
2578  for (int j = 0; j < resolution; j++) {
2579  double theta = step * j;
2580  baseVertices->setEigenPoint(
2581  static_cast<std::size_t>(base + j),
2582  Eigen::Vector3d(cos(theta) * r, sin(theta) * r,
2583  h_step * i));
2584 
2585  // Mapping vertices to UV coordinates.
2586  if (create_uv_map) {
2587  double factor = 0.25 * r / radius;
2588  map_vertices_to_uv[base + j] = std::make_pair(
2589  factor * cos(theta), factor * sin(theta));
2590  }
2591  }
2592  }
2593 
2594  for (int j = 0; j < resolution; j++) {
2595  int j1 = (j + 1) % resolution;
2596  int base = 2;
2597  mesh->addTriangle(Eigen::Vector3i(0, base + j1, base + j));
2598  base = 2 + resolution * (split - 1);
2599  mesh->addTriangle(Eigen::Vector3i(1, base + j, base + j1));
2600  }
2601 
2602  if (create_uv_map) {
2603  for (int j = 0; j < resolution; j++) {
2604  int j1 = (j + 1) % resolution;
2605  // UV coordinates mapped to triangles for bottom surface.
2606  int base = 2;
2607  mesh->triangle_uvs_.push_back(Eigen::Vector2d(0.5, 0.25));
2608  mesh->triangle_uvs_.push_back(Eigen::Vector2d(
2609  0.5 + map_vertices_to_uv[base + j1].first,
2610  0.25 + map_vertices_to_uv[base + j1].second));
2611  mesh->triangle_uvs_.push_back(Eigen::Vector2d(
2612  0.5 + map_vertices_to_uv[base + j].first,
2613  0.25 + map_vertices_to_uv[base + j].second));
2614 
2615  // UV coordinates mapped to triangles for top segment of conical
2616  // surface.
2617  base = 2 + resolution * (split - 1);
2618  mesh->triangle_uvs_.push_back(Eigen::Vector2d(0.5, 0.75));
2619  mesh->triangle_uvs_.push_back(Eigen::Vector2d(
2620  0.5 + map_vertices_to_uv[base + j].first,
2621  0.75 + map_vertices_to_uv[base + j].second));
2622  mesh->triangle_uvs_.push_back(Eigen::Vector2d(
2623  0.5 + map_vertices_to_uv[base + j1].first,
2624  0.75 + map_vertices_to_uv[base + j1].second));
2625  }
2626  }
2627 
2628  // Triangles for conical surface other than top-segment.
2629  for (int i = 0; i < split - 1; i++) {
2630  int base1 = 2 + resolution * i;
2631  int base2 = base1 + resolution;
2632  for (int j = 0; j < resolution; j++) {
2633  int j1 = (j + 1) % resolution;
2634  mesh->addTriangle(
2635  Eigen::Vector3i(base2 + j1, base1 + j, base1 + j1));
2636  mesh->addTriangle(
2637  Eigen::Vector3i(base2 + j1, base2 + j, base1 + j));
2638  }
2639  }
2640 
2641  // UV coordinates mapped to triangles for conical surface other than
2642  // top-segment.
2643  if (create_uv_map) {
2644  for (int i = 0; i < split - 1; i++) {
2645  int base1 = 2 + resolution * i;
2646  int base2 = base1 + resolution;
2647  for (int j = 0; j < resolution; j++) {
2648  int j1 = (j + 1) % resolution;
2649  mesh->triangle_uvs_.emplace_back(
2650  0.5 + map_vertices_to_uv[base2 + j1].first,
2651  0.75 + map_vertices_to_uv[base2 + j1].second);
2652  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2653  0.5 + map_vertices_to_uv[base1 + j].first,
2654  0.75 + map_vertices_to_uv[base1 + j].second));
2655  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2656  0.5 + map_vertices_to_uv[base1 + j1].first,
2657  0.75 + map_vertices_to_uv[base1 + j1].second));
2658 
2659  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2660  0.5 + map_vertices_to_uv[base2 + j1].first,
2661  0.75 + map_vertices_to_uv[base2 + j1].second));
2662  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2663  0.5 + map_vertices_to_uv[base2 + j].first,
2664  0.75 + map_vertices_to_uv[base2 + j].second));
2665  mesh->triangle_uvs_.emplace_back(Eigen::Vector2d(
2666  0.5 + map_vertices_to_uv[base1 + j].first,
2667  0.75 + map_vertices_to_uv[base1 + j].second));
2668  }
2669  }
2670  }
2671 
2672  // do some cleaning
2673  {
2674  baseVertices->shrinkToFit();
2675  mesh->shrinkToFit();
2676  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2677  if (normals) {
2678  normals->shrink_to_fit();
2679  }
2680  }
2681 
2682  baseVertices->setEnabled(false);
2683  // DGM: no need to lock it as it is only used by one mesh!
2684  baseVertices->setLocked(false);
2685  mesh->addChild(baseVertices);
2686  return mesh;
2687 }
2688 
2689 std::shared_ptr<ccMesh> ccMesh::CreateTorus(double torus_radius /* = 1.0 */,
2690  double tube_radius /* = 0.5 */,
2691  int radial_resolution /* = 20 */,
2692  int tubular_resolution /* = 20 */) {
2693  ccPointCloud *baseVertices = new ccPointCloud("vertices");
2694  assert(baseVertices);
2695  auto mesh = std::make_shared<ccMesh>(baseVertices);
2696 
2697  if (torus_radius <= 0) {
2698  utility::LogError("[CreateTorus] torus_radius <= 0");
2699  }
2700  if (tube_radius <= 0) {
2701  utility::LogError("[CreateTorus] tube_radius <= 0");
2702  }
2703  if (radial_resolution <= 0) {
2704  utility::LogError("[CreateTorus] radial_resolution <= 0");
2705  }
2706  if (tubular_resolution <= 0) {
2707  utility::LogError("[CreateTorus] tubular_resolution <= 0");
2708  }
2709 
2710  if (!baseVertices->resize(radial_resolution * tubular_resolution) ||
2711  !mesh->resize(2 * radial_resolution * tubular_resolution)) {
2712  utility::LogError("not enough memory!");
2713  }
2714 
2715  auto vert_idx = [&](int uidx, int vidx) {
2716  return uidx * tubular_resolution + vidx;
2717  };
2718  double u_step = 2 * M_PI / double(radial_resolution);
2719  double v_step = 2 * M_PI / double(tubular_resolution);
2720  Eigen::Vector3d temp;
2721  for (int uidx = 0; uidx < radial_resolution; ++uidx) {
2722  double u = uidx * u_step;
2723  Eigen::Vector3d w(cos(u), sin(u), 0);
2724  for (int vidx = 0; vidx < tubular_resolution; ++vidx) {
2725  double v = vidx * v_step;
2726  temp = torus_radius * w + tube_radius * cos(v) * w +
2727  Eigen::Vector3d(0, 0, tube_radius * sin(v));
2728  *baseVertices->getPointPtr(
2729  static_cast<unsigned int>(vert_idx(uidx, vidx))) = temp;
2730 
2731  int tri_idx = (uidx * tubular_resolution + vidx) * 2;
2732  mesh->setTriangle(
2733  static_cast<unsigned int>(tri_idx + 0),
2735  vert_idx((uidx + 1) % radial_resolution, vidx),
2736  vert_idx((uidx + 1) % radial_resolution,
2737  (vidx + 1) % tubular_resolution),
2738  vert_idx(uidx, vidx)));
2739  mesh->setTriangle(
2740  static_cast<unsigned int>(tri_idx + 1),
2742  vert_idx(uidx, vidx),
2743  vert_idx((uidx + 1) % radial_resolution,
2744  (vidx + 1) % tubular_resolution),
2745  vert_idx(uidx, (vidx + 1) % tubular_resolution)));
2746  }
2747  }
2748 
2749  // do some cleaning
2750  {
2751  baseVertices->shrinkToFit();
2752  mesh->shrinkToFit();
2753  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2754  if (normals) {
2755  normals->shrink_to_fit();
2756  }
2757  }
2758 
2759  baseVertices->setEnabled(false);
2760  // DGM: no need to lock it as it is only used by one mesh!
2761  baseVertices->setLocked(false);
2762  mesh->addChild(baseVertices);
2763  return mesh;
2764 }
2765 
2766 std::shared_ptr<ccMesh> ccMesh::CreateArrow(double cylinder_radius /* = 1.0*/,
2767  double cone_radius /* = 1.5*/,
2768  double cylinder_height /* = 5.0*/,
2769  double cone_height /* = 4.0*/,
2770  int resolution /* = 20*/,
2771  int cylinder_split /* = 4*/,
2772  int cone_split /* = 1*/) {
2773  if (cylinder_radius <= 0) {
2774  utility::LogError("[CreateArrow] cylinder_radius <= 0");
2775  }
2776  if (cone_radius <= 0) {
2777  utility::LogError("[CreateArrow] cone_radius <= 0");
2778  }
2779  if (cylinder_height <= 0) {
2780  utility::LogError("[CreateArrow] cylinder_height <= 0");
2781  }
2782  if (cone_height <= 0) {
2783  utility::LogError("[CreateArrow] cone_height <= 0");
2784  }
2785  if (resolution <= 0) {
2786  utility::LogError("[CreateArrow] resolution <= 0");
2787  }
2788  if (cylinder_split <= 0) {
2789  utility::LogError("[CreateArrow] cylinder_split <= 0");
2790  }
2791  if (cone_split <= 0) {
2792  utility::LogError("[CreateArrow] cone_split <= 0");
2793  }
2794  Eigen::Matrix4d transformation = Eigen::Matrix4d::Identity();
2795  auto mesh_cylinder = CreateCylinder(cylinder_radius, cylinder_height,
2796  resolution, cylinder_split);
2797  transformation(2, 3) = cylinder_height * 0.5;
2798  mesh_cylinder->Transform(transformation);
2799  auto mesh_cone =
2800  CreateCone(cone_radius, cone_height, resolution, cone_split);
2801  transformation(2, 3) = cylinder_height;
2802  mesh_cone->Transform(transformation);
2803  auto mesh_arrow = mesh_cylinder;
2804  *mesh_arrow += *mesh_cone;
2805  return mesh_arrow;
2806 }
2807 
2808 std::shared_ptr<ccMesh> ccMesh::CreateCoordinateFrame(
2809  double size /* = 1.0*/,
2810  const Eigen::Vector3d &origin /* = Eigen::Vector3d(0.0, 0.0, 0.0)*/) {
2811  if (size <= 0) {
2812  utility::LogError("[CreateCoordinateFrame] size <= 0");
2813  }
2814  auto mesh_frame = CreateSphere(0.06 * size);
2815  mesh_frame->ComputeVertexNormals();
2816  mesh_frame->PaintUniformColor(Eigen::Vector3d(0.5, 0.5, 0.5));
2817 
2818  std::shared_ptr<ccMesh> mesh_arrow;
2819  Eigen::Matrix4d transformation;
2820 
2821  mesh_arrow = CreateArrow(0.035 * size, 0.06 * size, 0.8 * size, 0.2 * size);
2822  mesh_arrow->ComputeVertexNormals();
2823  mesh_arrow->PaintUniformColor(Eigen::Vector3d(1.0, 0.0, 0.0));
2824  mesh_arrow->showColors(true);
2825  transformation << 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1;
2826  mesh_arrow->Transform(transformation);
2827  *mesh_frame += *mesh_arrow;
2828 
2829  mesh_arrow = CreateArrow(0.035 * size, 0.06 * size, 0.8 * size, 0.2 * size);
2830  mesh_arrow->ComputeVertexNormals();
2831  mesh_arrow->PaintUniformColor(Eigen::Vector3d(0.0, 1.0, 0.0));
2832  transformation << 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1;
2833  mesh_arrow->Transform(transformation);
2834  *mesh_frame += *mesh_arrow;
2835 
2836  mesh_arrow = CreateArrow(0.035 * size, 0.06 * size, 0.8 * size, 0.2 * size);
2837  mesh_arrow->ComputeVertexNormals();
2838  mesh_arrow->PaintUniformColor(Eigen::Vector3d(0.0, 0.0, 1.0));
2839  transformation << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1;
2840  mesh_arrow->Transform(transformation);
2841  *mesh_frame += *mesh_arrow;
2842 
2843  transformation = Eigen::Matrix4d::Identity();
2844  transformation.block<3, 1>(0, 3) = origin;
2845  mesh_frame->Transform(transformation);
2846 
2847  return mesh_frame;
2848 }
2849 
2850 std::shared_ptr<ccMesh> ccMesh::CreateMobius(int length_split /* = 70 */,
2851  int width_split /* = 15 */,
2852  int twists /* = 1 */,
2853  double radius /* = 1 */,
2854  double flatness /* = 1 */,
2855  double width /* = 1 */,
2856  double scale /* = 1 */) {
2857  ccPointCloud *baseVertices = new ccPointCloud("vertices");
2858  assert(baseVertices);
2859  auto mesh = std::make_shared<ccMesh>(baseVertices);
2860 
2861  if (length_split <= 0) {
2862  utility::LogError("[CreateMobius] length_split <= 0");
2863  }
2864  if (width_split <= 0) {
2865  utility::LogError("[CreateMobius] width_split <= 0");
2866  }
2867  if (twists < 0) {
2868  utility::LogError("[CreateMobius] twists < 0");
2869  }
2870  if (radius <= 0) {
2871  utility::LogError("[CreateMobius] radius <= 0");
2872  }
2873  if (flatness == 0) {
2874  utility::LogError("[CreateMobius] flatness == 0");
2875  }
2876  if (width <= 0) {
2877  utility::LogError("[CreateMobius] width <= 0");
2878  }
2879  if (scale <= 0) {
2880  utility::LogError("[CreateMobius] scale <= 0");
2881  }
2882 
2883  if (!baseVertices->resize(length_split * width_split)) {
2884  utility::LogError("not enough memory!");
2885  }
2886 
2887  double u_step = 2 * M_PI / length_split;
2888  double v_step = width / (width_split - 1);
2889  for (int uidx = 0; uidx < length_split; ++uidx) {
2890  double u = uidx * u_step;
2891  double cos_u = std::cos(u);
2892  double sin_u = std::sin(u);
2893  for (int vidx = 0; vidx < width_split; ++vidx) {
2894  unsigned int idx =
2895  static_cast<unsigned int>(uidx * width_split + vidx);
2896  double v = -width / 2.0 + vidx * v_step;
2897  double alpha = twists * 0.5 * u;
2898  double cos_alpha = std::cos(alpha);
2899  double sin_alpha = std::sin(alpha);
2900  baseVertices->getPointPtr(idx)->x =
2901  static_cast<PointCoordinateType>(
2902  scale * ((cos_alpha * cos_u * v) + radius * cos_u));
2903  baseVertices->getPointPtr(idx)->y =
2904  static_cast<PointCoordinateType>(
2905  scale * ((cos_alpha * sin_u * v) + radius * sin_u));
2906  baseVertices->getPointPtr(idx)->z =
2907  static_cast<PointCoordinateType>(scale * sin_alpha * v *
2908  flatness);
2909  }
2910  }
2911 
2912  for (int uidx = 0; uidx < length_split - 1; ++uidx) {
2913  for (int vidx = 0; vidx < width_split - 1; ++vidx) {
2914  if ((uidx + vidx) % 2 == 0) {
2915  mesh->addTriangle(
2916  Eigen::Vector3i(uidx * width_split + vidx,
2917  (uidx + 1) * width_split + vidx + 1,
2918  uidx * width_split + vidx + 1));
2919  mesh->addTriangle(
2920  Eigen::Vector3i(uidx * width_split + vidx,
2921  (uidx + 1) * width_split + vidx,
2922  (uidx + 1) * width_split + vidx + 1));
2923  } else {
2924  mesh->addTriangle(
2925  Eigen::Vector3i(uidx * width_split + vidx + 1,
2926  uidx * width_split + vidx,
2927  (uidx + 1) * width_split + vidx));
2928  mesh->addTriangle(
2929  Eigen::Vector3i(uidx * width_split + vidx + 1,
2930  (uidx + 1) * width_split + vidx,
2931  (uidx + 1) * width_split + vidx + 1));
2932  }
2933  }
2934  }
2935 
2936  int uidx = length_split - 1;
2937  for (int vidx = 0; vidx < width_split - 1; ++vidx) {
2938  if (twists % 2 == 1) {
2939  if ((uidx + vidx) % 2 == 0) {
2940  mesh->addTriangle(
2941  Eigen::Vector3i((width_split - 1) - (vidx + 1),
2942  uidx * width_split + vidx,
2943  uidx * width_split + vidx + 1));
2944  mesh->addTriangle(Eigen::Vector3i(
2945  (width_split - 1) - vidx, uidx * width_split + vidx,
2946  (width_split - 1) - (vidx + 1)));
2947  } else {
2948  mesh->addTriangle(Eigen::Vector3i(uidx * width_split + vidx,
2949  uidx * width_split + vidx + 1,
2950  (width_split - 1) - vidx));
2951  mesh->addTriangle(Eigen::Vector3i(
2952  (width_split - 1) - vidx, uidx * width_split + vidx + 1,
2953  (width_split - 1) - (vidx + 1)));
2954  }
2955  } else {
2956  if ((uidx + vidx) % 2 == 0) {
2957  mesh->addTriangle(
2958  Eigen::Vector3i(uidx * width_split + vidx, vidx + 1,
2959  uidx * width_split + vidx + 1));
2960  mesh->addTriangle(Eigen::Vector3i(uidx * width_split + vidx,
2961  vidx, vidx + 1));
2962  } else {
2963  mesh->addTriangle(
2964  Eigen::Vector3i(uidx * width_split + vidx, vidx,
2965  uidx * width_split + vidx + 1));
2966  mesh->addTriangle(Eigen::Vector3i(uidx * width_split + vidx + 1,
2967  vidx, vidx + 1));
2968  }
2969  }
2970  }
2971 
2972  // do some cleaning
2973  {
2974  baseVertices->shrinkToFit();
2975  mesh->shrinkToFit();
2976  NormsIndexesTableType *normals = mesh->getTriNormsTable();
2977  if (normals) {
2978  normals->shrink_to_fit();
2979  }
2980  }
2981 
2982  baseVertices->setEnabled(false);
2983  // DGM: no need to lock it as it is only used by one mesh!
2984  baseVertices->setLocked(false);
2985  mesh->addChild(baseVertices);
2986 
2987  return mesh;
2988 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
bool OrientTriangleHelper(const std::vector< Eigen::Vector3i > &triangles, F &swap)
double normal[3]
int width
int size
int height
math::float4 next
math::float4 color
math::float2 uv
Eigen::Vector3d origin
Definition: VoxelGridIO.cpp:26
Array of RGB colors for each point.
Array of compressed 3D normals (single index)
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
Type & getValue(size_t index)
Definition: ecvArray.h:100
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Triangular mesh.
Definition: ecvMesh.h:35
bool IsIntersecting(const ccMesh &other) const
std::shared_ptr< ccPointCloud > SamplePointsPoissonDisk(size_t number_of_points, double init_factor=5, const std::shared_ptr< ccPointCloud > pcl_init=nullptr, bool use_triangle_normal=false, int seed=-1)
double GetTriangleArea(size_t triangle_idx) const
std::vector< int > GetNonManifoldVertices() const
ccMesh & NormalizeNormals()
static std::shared_ptr< ccMesh > CreateBox(double width=1.0, double height=1.0, double depth=1.0, bool create_uv_map=false, bool map_texture_to_each_face=false)
static std::shared_ptr< ccMesh > CreateTorus(double torus_radius=1.0, double tube_radius=0.5, int radial_resolution=30, int tubular_resolution=20)
void RemoveTrianglesByMask(const std::vector< bool > &triangle_mask)
This function removes the triangles that are masked in triangle_mask. Call RemoveUnreferencedVertices...
ccMesh & PaintUniformColor(const Eigen::Vector3d &color)
Assigns each vertex in the ccMesh the same color.
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
Definition: ecvMesh.cpp:828
ccMesh & RemoveDegenerateTriangles()
Function that removes degenerate triangles, i.e., triangles that reference a single vertex multiple t...
ccMesh & RemoveDuplicatedTriangles()
Function that removes duplicated triangles, i.e., removes triangles that reference the same three ver...
bool IsVertexManifold() const
static std::shared_ptr< ccMesh > CreateCylinder(double radius=1.0, double height=2.0, int resolution=20, int split=4, bool create_uv_map=false)
int EulerPoincareCharacteristic() const
bool OrientTriangles()
double GetSurfaceArea() const
virtual unsigned size() const override
Returns the number of triangles.
Definition: ecvMesh.cpp:2143
std::shared_ptr< ccMesh > FilterSmoothSimple(int number_of_iterations, FilterScope scope=FilterScope::All) const
Function to smooth triangle mesh with simple neighbour average.
static std::shared_ptr< ccMesh > CreateSphere(double radius=1.0, int resolution=20, bool create_uv_map=false)
static std::shared_ptr< ccMesh > CreateCoordinateFrame(double size=1.0, const Eigen::Vector3d &origin=Eigen::Vector3d(0.0, 0.0, 0.0))
ccMesh & ComputeTriangleNormals(bool normalized=true)
Function to compute triangle normals, usually called before rendering.
ccMesh & MergeCloseVertices(double eps)
Function that will merge close by vertices to a single one. The vertex position, normal and color wil...
ccMesh & RemoveUnreferencedVertices()
This function removes vertices from the triangle mesh that are not referenced in any triangle of the ...
bool IsSelfIntersecting() const
void RemoveTrianglesByIndex(const std::vector< size_t > &triangle_indices)
This function removes the triangles with index in triangle_indices. Call RemoveUnreferencedVertices t...
std::shared_ptr< ccMesh > FilterSmoothTaubin(int number_of_iterations, double lambda=0.5, double mu=-0.53, FilterScope scope=FilterScope::All) const
Function to smooth triangle mesh using method of Taubin, "Curve and Surface Smoothing Without Shrinka...
double GetVolume() const
void FilterSmoothLaplacianHelper(std::shared_ptr< ccMesh > &mesh, const std::vector< CCVector3 > &prev_vertices, const std::vector< CCVector3 > &prev_vertex_normals, const ColorsTableType &prev_vertex_colors, const std::vector< std::unordered_set< int >> &adjacency_list, double lambda, bool filter_vertex, bool filter_normal, bool filter_color) const
static std::shared_ptr< ccMesh > CreateOctahedron(double radius=1.0, bool create_uv_map=false)
static std::shared_ptr< ccMesh > CreateTetrahedron(double radius=1.0, bool create_uv_map=false)
Eigen::Vector3d getVertice(size_t index) const
Definition: ecvMesh.cpp:2213
Eigen::Vector4d GetTrianglePlane(size_t triangle_idx) const
std::shared_ptr< ccPointCloud > SamplePointsUniformly(size_t number_of_points, bool use_triangle_normal=false, int seed=-1)
std::unordered_map< Eigen::Vector2i, std::vector< int >, cloudViewer::utility::hash_eigen< Eigen::Vector2i > > GetEdgeToVerticesMap() const
std::tuple< std::vector< int >, std::vector< size_t >, std::vector< double > > ClusterConnectedTriangles() const
Function that clusters connected triangles, i.e., triangles that are connected via edges are assigned...
std::vector< Eigen::Vector2i > GetNonManifoldEdges(bool allow_boundary_edges=true) const
void RemoveVerticesByIndex(const std::vector< size_t > &vertex_indices)
This function removes the vertices with index in vertex_indices. Note that also all triangles associa...
std::shared_ptr< ccMesh > FilterSharpen(int number_of_iterations, double strength, FilterScope scope=FilterScope::All) const
Function to sharpen triangle mesh.
std::tuple< std::shared_ptr< ccMesh >, std::vector< size_t > > ComputeConvexHull() const
static double ComputeTriangleArea(const Eigen::Vector3d &p0, const Eigen::Vector3d &p1, const Eigen::Vector3d &p2)
Function that computes the area of a mesh triangle.
std::shared_ptr< ccPointCloud > SamplePointsUniformlyImpl(size_t number_of_points, std::vector< double > &triangle_areas, double surface_area, bool use_triangle_normal, int seed)
static Eigen::Vector4d ComputeTrianglePlane(const Eigen::Vector3d &p0, const Eigen::Vector3d &p1, const Eigen::Vector3d &p2)
static std::shared_ptr< ccMesh > CreateArrow(double cylinder_radius=1.0, double cone_radius=1.5, double cylinder_height=5.0, double cone_height=4.0, int resolution=20, int cylinder_split=4, int cone_split=1)
static std::shared_ptr< ccMesh > CreateCone(double radius=1.0, double height=2.0, int resolution=20, int split=1, bool create_uv_map=false)
bool IsOrientable() const
void clear()
Definition: ecvMesh.cpp:2126
bool IsWatertight() const
void RemoveVerticesByMask(const std::vector< bool > &vertex_mask)
This function removes the vertices that are masked in vertex_mask. Note that also all triangles assoc...
bool IsBoundingBoxIntersecting(const ccMesh &other) const
static std::shared_ptr< ccMesh > CreatePlane(double width=1.0, double height=1.0, bool create_uv_map=false)
bool IsEdgeManifold(bool allow_boundary_edges=true) const
Eigen::Vector3i getTriangle(size_t index) const
Definition: ecvMesh.cpp:2448
ccMesh & RemoveNonManifoldEdges()
Function that removes all non-manifold edges, by successively deleting triangles with the smallest su...
std::unordered_map< Eigen::Vector2i, double, cloudViewer::utility::hash_eigen< Eigen::Vector2i > > ComputeEdgeWeightsCot(const std::unordered_map< Eigen::Vector2i, std::vector< int >, cloudViewer::utility::hash_eigen< Eigen::Vector2i >> &edges_to_vertices, double min_weight=std::numeric_limits< double >::lowest()) const
Function that computes for each edge in the triangle mesh and passed as parameter edges_to_vertices t...
ccMesh & RemoveDuplicatedVertices()
Function that removes duplicated verties, i.e., vertices that have identical coordinates.
static std::shared_ptr< ccMesh > CreateMobius(int length_split=70, int width_split=15, int twists=1, double radius=1, double flatness=1, double width=1, double scale=1)
virtual Eigen::Vector3d GetMinBound() const override
Returns min bounds for geometry coordinates.
Definition: ecvMesh.cpp:820
std::shared_ptr< ccMesh > FilterSmoothLaplacian(int number_of_iterations, double lambda, FilterScope scope=FilterScope::All) const
Function to smooth triangle mesh using Laplacian.
std::vector< Eigen::Vector2i > GetSelfIntersectingTriangles() const
static Eigen::Vector2i GetOrderedEdge(int vidx0, int vidx1)
Helper function to get an edge with ordered vertex indices.
Definition: ecvMesh.h:1005
ccMesh & ComputeAdjacencyList()
Function to compute adjacency list, call before adjacency list is.
ccMesh & ComputeVertexNormals(bool normalized=true)
Function to compute vertex normals, usually called before rendering.
static std::shared_ptr< ccMesh > CreateIcosahedron(double radius=1.0, bool create_uv_map=false)
std::unordered_map< Eigen::Vector2i, std::vector< int >, cloudViewer::utility::hash_eigen< Eigen::Vector2i > > GetEdgeToTrianglesMap() const
static CCVector3 & GetNormalPtr(unsigned normIndex)
virtual void setLocked(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:117
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
ColorsTableType * rgbColors() const
Returns pointer on RGB colors table.
bool resizeTheNormsTable()
Resizes the compressed normals array.
Eigen::Vector3d getEigenNormal(size_t index) const
std::vector< CCVector3 > getPointNormals() const
bool hasNormals() const override
Returns whether normals are enabled or not.
bool resizeTheRGBTable(bool fillWithWhite=false)
Resizes the RGB colors array.
bool hasColors() const override
Returns whether colors are enabled or not.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void setPointNormals(const std::vector< CCVector3 > &normals)
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
void setPointNormal(size_t pointIndex, const CCVector3 &N)
Sets a particular point normal (shortcut)
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
Eigen::Vector3d getEigenColor(size_t index) const
void shrinkToFit()
Removes unused capacity.
ccPointCloud & PaintUniformColor(const Eigen::Vector3d &color)
Assigns each vertex in the ccMesh the same color.
ccPointCloud & NormalizeNormals()
Normalize point normals to length 1.`.
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
FilterScope
Indicates the scope of filter operations.
Definition: GenericMesh.h:44
void addEigenPoint(const Eigen::Vector3d &point)
std::vector< CCVector3 > & getPoints()
CCVector3 * getPointPtr(size_t index)
unsigned size() const override
Definition: PointCloudTpl.h:38
void setEigenPoint(size_t index, const Eigen::Vector3d &point)
void setPoint(size_t index, const CCVector3 &P)
const CCVector3 * getPoint(unsigned index) const override
KDTree with FLANN for nearest neighbor search.
int SearchRadius(const T &query, double radius, std::vector< int > &indices, std::vector< double > &distance2) const
static std::tuple< std::shared_ptr< ccMesh >, std::vector< size_t > > ComputeConvexHull(const std::vector< Eigen::Vector3d > &points)
Definition: ecvQhull.cpp:34
static bool TriangleTriangle3d(const Eigen::Vector3d &p0, const Eigen::Vector3d &p1, const Eigen::Vector3d &p2, const Eigen::Vector3d &q0, const Eigen::Vector3d &q1, const Eigen::Vector3d &q2)
static bool AABBAABB(const Eigen::Vector3d &min0, const Eigen::Vector3d &max0, const Eigen::Vector3d &min1, const Eigen::Vector3d &max1)
constexpr static RgbTpl FromEigen(const Eigen::Vector3d &t)
Definition: ecvColorTypes.h:86
static Eigen::Vector3d ToEigen(const Type col[3])
Definition: ecvColorTypes.h:72
double normals[3]
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
a[190]
const double * e
normal_z y
normal_z x
@ POINT_CLOUD
Definition: CVTypes.h:104
bool computeNormals(const ccHObject::Container &selectedEntities, QWidget *parent)
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
static Edge< T > GetOrderedEdge(T vidx0, T vidx1)
brief Helper function to get an edge with ordered vertex indices.
static std::unordered_map< Edge< T >, std::vector< size_t >, utility::hash_tuple< Edge< T > > > GetEdgeToTrianglesMap(const core::Tensor &tris_cpu)
void swap(optional< T > &x, optional< T > &y) noexcept(noexcept(x.swap(y)))
Definition: Optional.h:890
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
Definition: knncpp.h:30
Eigen::Matrix< Index, 2, 1 > Vector2i
Definition: knncpp.h:29
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
Triangle described by the indexes of its 3 vertices.