FreeCAD
788 строк · 25.5 Кб
1/***************************************************************************
2* Copyright (c) 2012 Werner Mayer <wmayer[at]users.sourceforge.net> *
3* *
4* This file is part of the FreeCAD CAx development system. *
5* *
6* This library is free software; you can redistribute it and/or *
7* modify it under the terms of the GNU Library General Public *
8* License as published by the Free Software Foundation; either *
9* version 2 of the License, or (at your option) any later version. *
10* *
11* This library is distributed in the hope that it will be useful, *
12* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14* GNU Library General Public License for more details. *
15* *
16* You should have received a copy of the GNU Library General Public *
17* License along with this library; see the file COPYING.LIB. If not, *
18* write to the Free Software Foundation, Inc., 59 Temple Place, *
19* Suite 330, Boston, MA 02111-1307, USA *
20* *
21***************************************************************************/
22
23#include "PreCompiled.h"
24
25#include <Base/Exception.h>
26#include <Mod/Mesh/App/Core/Algorithm.h>
27#include <Mod/Mesh/App/Core/Elements.h>
28#include <Mod/Mesh/App/Core/MeshKernel.h>
29#include <Mod/Mesh/App/Mesh.h>
30#include <Mod/Points/App/Points.h>
31
32#include "SurfaceTriangulation.h"
33
34
35// http://svn.pointclouds.org/pcl/tags/pcl-1.5.1/test/
36#if defined(HAVE_PCL_SURFACE)
37#include <boost/math/special_functions/fpclassify.hpp>
38#include <boost/random.hpp>
39#include <pcl/common/common.h>
40#include <pcl/common/io.h>
41#include <pcl/features/normal_3d.h>
42#include <pcl/pcl_config.h>
43#include <pcl/point_traits.h>
44#include <pcl/point_types.h>
45#include <pcl/surface/ear_clipping.h>
46#include <pcl/surface/gp3.h>
47#include <pcl/surface/grid_projection.h>
48#include <pcl/surface/marching_cubes_hoppe.h>
49#include <pcl/surface/marching_cubes_rbf.h>
50#include <pcl/surface/mls.h>
51#include <pcl/surface/organized_fast_mesh.h>
52#include <pcl/surface/poisson.h>
53
54#ifndef PCL_REVISION_VERSION
55#define PCL_REVISION_VERSION 0
56#endif
57
58using namespace pcl;
59using namespace pcl::io;
60using namespace std;
61using namespace Reen;
62
63// See
64// http://www.ics.uci.edu/~gopi/PAPERS/Euro00.pdf
65// http://www.ics.uci.edu/~gopi/PAPERS/CGMV.pdf
66SurfaceTriangulation::SurfaceTriangulation(const Points::PointKernel& pts, Mesh::MeshObject& mesh)
67: myPoints(pts)
68, myMesh(mesh)
69, mu(0)
70, searchRadius(0)
71{}
72
73void SurfaceTriangulation::perform(int ksearch)
74{
75PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>);
76PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
77search::KdTree<PointXYZ>::Ptr tree;
78search::KdTree<PointNormal>::Ptr tree2;
79
80cloud->reserve(myPoints.size());
81for (Points::PointKernel::const_iterator it = myPoints.begin(); it != myPoints.end(); ++it) {
82if (!boost::math::isnan(it->x) && !boost::math::isnan(it->y)
83&& !boost::math::isnan(it->z)) {
84cloud->push_back(PointXYZ(it->x, it->y, it->z));
85}
86}
87
88// Create search tree
89tree.reset(new search::KdTree<PointXYZ>(false));
90tree->setInputCloud(cloud);
91
92// Normal estimation
93NormalEstimation<PointXYZ, Normal> n;
94PointCloud<Normal>::Ptr normals(new PointCloud<Normal>());
95n.setInputCloud(cloud);
96// n.setIndices (indices[B);
97n.setSearchMethod(tree);
98n.setKSearch(ksearch);
99n.compute(*normals);
100
101// Concatenate XYZ and normal information
102pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
103
104// Create search tree
105tree2.reset(new search::KdTree<PointNormal>);
106tree2->setInputCloud(cloud_with_normals);
107
108// Init objects
109GreedyProjectionTriangulation<PointNormal> gp3;
110
111// Set parameters
112gp3.setInputCloud(cloud_with_normals);
113gp3.setSearchMethod(tree2);
114gp3.setSearchRadius(searchRadius);
115gp3.setMu(mu);
116gp3.setMaximumNearestNeighbors(100);
117gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees
118gp3.setMinimumAngle(M_PI / 18); // 10 degrees
119gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees
120gp3.setNormalConsistency(false);
121gp3.setConsistentVertexOrdering(true);
122
123// Reconstruct
124PolygonMesh mesh;
125gp3.reconstruct(mesh);
126
127MeshConversion::convert(mesh, myMesh);
128
129// Additional vertex information
130// std::vector<int> parts = gp3.getPartIDs();
131// std::vector<int> states = gp3.getPointStates();
132}
133
134void SurfaceTriangulation::perform(const std::vector<Base::Vector3f>& normals)
135{
136if (myPoints.size() != normals.size()) {
137throw Base::RuntimeError("Number of points doesn't match with number of normals");
138}
139
140PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
141search::KdTree<PointNormal>::Ptr tree;
142
143cloud_with_normals->reserve(myPoints.size());
144std::size_t num_points = myPoints.size();
145const std::vector<Base::Vector3f>& points = myPoints.getBasicPoints();
146for (std::size_t index = 0; index < num_points; index++) {
147const Base::Vector3f& p = points[index];
148const Base::Vector3f& n = normals[index];
149if (!boost::math::isnan(p.x) && !boost::math::isnan(p.y) && !boost::math::isnan(p.z)) {
150PointNormal pn;
151pn.x = p.x;
152pn.y = p.y;
153pn.z = p.z;
154pn.normal_x = n.x;
155pn.normal_y = n.y;
156pn.normal_z = n.z;
157cloud_with_normals->push_back(pn);
158}
159}
160
161// Create search tree
162tree.reset(new search::KdTree<PointNormal>);
163tree->setInputCloud(cloud_with_normals);
164
165// Init objects
166GreedyProjectionTriangulation<PointNormal> gp3;
167
168// Set parameters
169gp3.setInputCloud(cloud_with_normals);
170gp3.setSearchMethod(tree);
171gp3.setSearchRadius(searchRadius);
172gp3.setMu(mu);
173gp3.setMaximumNearestNeighbors(100);
174gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees
175gp3.setMinimumAngle(M_PI / 18); // 10 degrees
176gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees
177gp3.setNormalConsistency(true);
178gp3.setConsistentVertexOrdering(true);
179
180// Reconstruct
181PolygonMesh mesh;
182gp3.reconstruct(mesh);
183
184MeshConversion::convert(mesh, myMesh);
185
186// Additional vertex information
187// std::vector<int> parts = gp3.getPartIDs();
188// std::vector<int> states = gp3.getPointStates();
189}
190
191// ----------------------------------------------------------------------------
192
193// See
194// http://www.cs.jhu.edu/~misha/Code/PoissonRecon/Version8.0/
195PoissonReconstruction::PoissonReconstruction(const Points::PointKernel& pts, Mesh::MeshObject& mesh)
196: myPoints(pts)
197, myMesh(mesh)
198, depth(-1)
199, solverDivide(-1)
200, samplesPerNode(-1.0f)
201{}
202
203void PoissonReconstruction::perform(int ksearch)
204{
205PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>);
206PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
207search::KdTree<PointXYZ>::Ptr tree;
208search::KdTree<PointNormal>::Ptr tree2;
209
210cloud->reserve(myPoints.size());
211for (Points::PointKernel::const_iterator it = myPoints.begin(); it != myPoints.end(); ++it) {
212if (!boost::math::isnan(it->x) && !boost::math::isnan(it->y)
213&& !boost::math::isnan(it->z)) {
214cloud->push_back(PointXYZ(it->x, it->y, it->z));
215}
216}
217
218// Create search tree
219tree.reset(new search::KdTree<PointXYZ>(false));
220tree->setInputCloud(cloud);
221
222// Normal estimation
223NormalEstimation<PointXYZ, Normal> n;
224PointCloud<Normal>::Ptr normals(new PointCloud<Normal>());
225n.setInputCloud(cloud);
226// n.setIndices (indices[B);
227n.setSearchMethod(tree);
228n.setKSearch(ksearch);
229n.compute(*normals);
230
231// Concatenate XYZ and normal information
232pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
233
234// Create search tree
235tree2.reset(new search::KdTree<PointNormal>);
236tree2->setInputCloud(cloud_with_normals);
237
238// Init objects
239Poisson<PointNormal> poisson;
240
241// Set parameters
242poisson.setInputCloud(cloud_with_normals);
243poisson.setSearchMethod(tree2);
244if (depth >= 1) {
245poisson.setDepth(depth);
246}
247if (solverDivide >= 1) {
248poisson.setSolverDivide(solverDivide);
249}
250if (samplesPerNode >= 1.0f) {
251poisson.setSamplesPerNode(samplesPerNode);
252}
253
254// Reconstruct
255PolygonMesh mesh;
256poisson.reconstruct(mesh);
257
258MeshConversion::convert(mesh, myMesh);
259}
260
261void PoissonReconstruction::perform(const std::vector<Base::Vector3f>& normals)
262{
263if (myPoints.size() != normals.size()) {
264throw Base::RuntimeError("Number of points doesn't match with number of normals");
265}
266
267PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
268search::KdTree<PointNormal>::Ptr tree;
269
270cloud_with_normals->reserve(myPoints.size());
271std::size_t num_points = myPoints.size();
272const std::vector<Base::Vector3f>& points = myPoints.getBasicPoints();
273for (std::size_t index = 0; index < num_points; index++) {
274const Base::Vector3f& p = points[index];
275const Base::Vector3f& n = normals[index];
276if (!boost::math::isnan(p.x) && !boost::math::isnan(p.y) && !boost::math::isnan(p.z)) {
277PointNormal pn;
278pn.x = p.x;
279pn.y = p.y;
280pn.z = p.z;
281pn.normal_x = n.x;
282pn.normal_y = n.y;
283pn.normal_z = n.z;
284cloud_with_normals->push_back(pn);
285}
286}
287
288// Create search tree
289tree.reset(new search::KdTree<PointNormal>);
290tree->setInputCloud(cloud_with_normals);
291
292// Init objects
293Poisson<PointNormal> poisson;
294
295// Set parameters
296poisson.setInputCloud(cloud_with_normals);
297poisson.setSearchMethod(tree);
298if (depth >= 1) {
299poisson.setDepth(depth);
300}
301if (solverDivide >= 1) {
302poisson.setSolverDivide(solverDivide);
303}
304if (samplesPerNode >= 1.0f) {
305poisson.setSamplesPerNode(samplesPerNode);
306}
307
308// Reconstruct
309PolygonMesh mesh;
310poisson.reconstruct(mesh);
311
312MeshConversion::convert(mesh, myMesh);
313}
314
315// ----------------------------------------------------------------------------
316
317GridReconstruction::GridReconstruction(const Points::PointKernel& pts, Mesh::MeshObject& mesh)
318: myPoints(pts)
319, myMesh(mesh)
320{}
321
322void GridReconstruction::perform(int ksearch)
323{
324PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>);
325PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
326search::KdTree<PointXYZ>::Ptr tree;
327search::KdTree<PointNormal>::Ptr tree2;
328
329cloud->reserve(myPoints.size());
330for (Points::PointKernel::const_iterator it = myPoints.begin(); it != myPoints.end(); ++it) {
331if (!boost::math::isnan(it->x) && !boost::math::isnan(it->y)
332&& !boost::math::isnan(it->z)) {
333cloud->push_back(PointXYZ(it->x, it->y, it->z));
334}
335}
336
337// Create search tree
338tree.reset(new search::KdTree<PointXYZ>(false));
339tree->setInputCloud(cloud);
340
341// Normal estimation
342NormalEstimation<PointXYZ, Normal> n;
343PointCloud<Normal>::Ptr normals(new PointCloud<Normal>());
344n.setInputCloud(cloud);
345// n.setIndices (indices[B);
346n.setSearchMethod(tree);
347n.setKSearch(ksearch);
348n.compute(*normals);
349
350// Concatenate XYZ and normal information
351pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
352
353// Create search tree
354tree2.reset(new search::KdTree<PointNormal>);
355tree2->setInputCloud(cloud_with_normals);
356
357// Init objects
358GridProjection<PointNormal> grid;
359
360// Set parameters
361grid.setResolution(0.005);
362grid.setPaddingSize(3);
363grid.setNearestNeighborNum(100);
364grid.setMaxBinarySearchLevel(10);
365grid.setInputCloud(cloud_with_normals);
366grid.setSearchMethod(tree2);
367
368// Reconstruct
369PolygonMesh mesh;
370grid.reconstruct(mesh);
371
372MeshConversion::convert(mesh, myMesh);
373}
374
375void GridReconstruction::perform(const std::vector<Base::Vector3f>& normals)
376{
377if (myPoints.size() != normals.size()) {
378throw Base::RuntimeError("Number of points doesn't match with number of normals");
379}
380
381PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
382search::KdTree<PointNormal>::Ptr tree;
383
384cloud_with_normals->reserve(myPoints.size());
385std::size_t num_points = myPoints.size();
386const std::vector<Base::Vector3f>& points = myPoints.getBasicPoints();
387for (std::size_t index = 0; index < num_points; index++) {
388const Base::Vector3f& p = points[index];
389const Base::Vector3f& n = normals[index];
390if (!boost::math::isnan(p.x) && !boost::math::isnan(p.y) && !boost::math::isnan(p.z)) {
391PointNormal pn;
392pn.x = p.x;
393pn.y = p.y;
394pn.z = p.z;
395pn.normal_x = n.x;
396pn.normal_y = n.y;
397pn.normal_z = n.z;
398cloud_with_normals->push_back(pn);
399}
400}
401
402// Create search tree
403tree.reset(new search::KdTree<PointNormal>);
404tree->setInputCloud(cloud_with_normals);
405
406// Init objects
407GridProjection<PointNormal> grid;
408
409// Set parameters
410grid.setResolution(0.005);
411grid.setPaddingSize(3);
412grid.setNearestNeighborNum(100);
413grid.setMaxBinarySearchLevel(10);
414grid.setInputCloud(cloud_with_normals);
415grid.setSearchMethod(tree);
416
417// Reconstruct
418PolygonMesh mesh;
419grid.reconstruct(mesh);
420
421MeshConversion::convert(mesh, myMesh);
422}
423
424// ----------------------------------------------------------------------------
425
426ImageTriangulation::ImageTriangulation(int width,
427int height,
428const Points::PointKernel& pts,
429Mesh::MeshObject& mesh)
430: width(width)
431, height(height)
432, myPoints(pts)
433, myMesh(mesh)
434{}
435
436void ImageTriangulation::perform()
437{
438if (myPoints.size() != static_cast<std::size_t>(width * height)) {
439throw Base::RuntimeError("Number of points doesn't match with given width and height");
440}
441
442// construct dataset
443pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_organized(new pcl::PointCloud<pcl::PointXYZ>());
444cloud_organized->width = width;
445cloud_organized->height = height;
446cloud_organized->points.resize(cloud_organized->width * cloud_organized->height);
447
448const std::vector<Base::Vector3f>& points = myPoints.getBasicPoints();
449
450int npoints = 0;
451for (size_t i = 0; i < cloud_organized->height; i++) {
452for (size_t j = 0; j < cloud_organized->width; j++) {
453const Base::Vector3f& p = points[npoints];
454cloud_organized->points[npoints].x = p.x;
455cloud_organized->points[npoints].y = p.y;
456cloud_organized->points[npoints].z = p.z;
457npoints++;
458}
459}
460
461OrganizedFastMesh<PointXYZ> ofm;
462
463// Set parameters
464ofm.setInputCloud(cloud_organized);
465// This parameter is not yet implemented (pcl 1.7)
466ofm.setMaxEdgeLength(1.5);
467ofm.setTrianglePixelSize(1);
468ofm.setTriangulationType(OrganizedFastMesh<PointXYZ>::TRIANGLE_ADAPTIVE_CUT);
469ofm.storeShadowedFaces(true);
470
471// Reconstruct
472PolygonMesh mesh;
473ofm.reconstruct(mesh);
474
475MeshConversion::convert(mesh, myMesh);
476
477// remove invalid points
478//
479MeshCore::MeshKernel& kernel = myMesh.getKernel();
480const MeshCore::MeshFacetArray& face = kernel.GetFacets();
481MeshCore::MeshAlgorithm meshAlg(kernel);
482meshAlg.SetPointFlag(MeshCore::MeshPoint::INVALID);
483std::vector<MeshCore::PointIndex> validPoints;
484validPoints.reserve(face.size() * 3);
485for (MeshCore::MeshFacetArray::_TConstIterator it = face.begin(); it != face.end(); ++it) {
486validPoints.push_back(it->_aulPoints[0]);
487validPoints.push_back(it->_aulPoints[1]);
488validPoints.push_back(it->_aulPoints[2]);
489}
490
491// remove duplicates
492std::sort(validPoints.begin(), validPoints.end());
493validPoints.erase(std::unique(validPoints.begin(), validPoints.end()), validPoints.end());
494meshAlg.ResetPointsFlag(validPoints, MeshCore::MeshPoint::INVALID);
495
496unsigned long countInvalid = meshAlg.CountPointFlag(MeshCore::MeshPoint::INVALID);
497if (countInvalid > 0) {
498std::vector<MeshCore::PointIndex> invalidPoints;
499invalidPoints.reserve(countInvalid);
500meshAlg.GetPointsFlag(invalidPoints, MeshCore::MeshPoint::INVALID);
501
502kernel.DeletePoints(invalidPoints);
503}
504}
505
506// ----------------------------------------------------------------------------
507
508Reen::MarchingCubesRBF::MarchingCubesRBF(const Points::PointKernel& pts, Mesh::MeshObject& mesh)
509: myPoints(pts)
510, myMesh(mesh)
511{}
512
513void Reen::MarchingCubesRBF::perform(int ksearch)
514{
515PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>);
516PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
517search::KdTree<PointXYZ>::Ptr tree;
518search::KdTree<PointNormal>::Ptr tree2;
519
520cloud->reserve(myPoints.size());
521for (Points::PointKernel::const_iterator it = myPoints.begin(); it != myPoints.end(); ++it) {
522if (!boost::math::isnan(it->x) && !boost::math::isnan(it->y)
523&& !boost::math::isnan(it->z)) {
524cloud->push_back(PointXYZ(it->x, it->y, it->z));
525}
526}
527
528// Create search tree
529tree.reset(new search::KdTree<PointXYZ>(false));
530tree->setInputCloud(cloud);
531
532// Normal estimation
533NormalEstimation<PointXYZ, Normal> n;
534PointCloud<Normal>::Ptr normals(new PointCloud<Normal>());
535n.setInputCloud(cloud);
536// n.setIndices (indices[B);
537n.setSearchMethod(tree);
538n.setKSearch(ksearch);
539n.compute(*normals);
540
541// Concatenate XYZ and normal information
542pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
543
544// Create search tree
545tree2.reset(new search::KdTree<PointNormal>);
546tree2->setInputCloud(cloud_with_normals);
547
548// Init objects
549pcl::MarchingCubesRBF<PointNormal> rbf;
550
551// Set parameters
552rbf.setIsoLevel(0);
553rbf.setGridResolution(60, 60, 60);
554rbf.setPercentageExtendGrid(0.1f);
555rbf.setOffSurfaceDisplacement(0.02f);
556
557rbf.setInputCloud(cloud_with_normals);
558rbf.setSearchMethod(tree2);
559
560// Reconstruct
561PolygonMesh mesh;
562rbf.reconstruct(mesh);
563
564MeshConversion::convert(mesh, myMesh);
565}
566
567void Reen::MarchingCubesRBF::perform(const std::vector<Base::Vector3f>& normals)
568{
569if (myPoints.size() != normals.size()) {
570throw Base::RuntimeError("Number of points doesn't match with number of normals");
571}
572
573PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
574search::KdTree<PointNormal>::Ptr tree;
575
576cloud_with_normals->reserve(myPoints.size());
577std::size_t num_points = myPoints.size();
578const std::vector<Base::Vector3f>& points = myPoints.getBasicPoints();
579for (std::size_t index = 0; index < num_points; index++) {
580const Base::Vector3f& p = points[index];
581const Base::Vector3f& n = normals[index];
582if (!boost::math::isnan(p.x) && !boost::math::isnan(p.y) && !boost::math::isnan(p.z)) {
583PointNormal pn;
584pn.x = p.x;
585pn.y = p.y;
586pn.z = p.z;
587pn.normal_x = n.x;
588pn.normal_y = n.y;
589pn.normal_z = n.z;
590cloud_with_normals->push_back(pn);
591}
592}
593
594// Create search tree
595tree.reset(new search::KdTree<PointNormal>);
596tree->setInputCloud(cloud_with_normals);
597
598
599// Init objects
600pcl::MarchingCubesRBF<PointNormal> rbf;
601
602// Set parameters
603rbf.setIsoLevel(0);
604rbf.setGridResolution(60, 60, 60);
605rbf.setPercentageExtendGrid(0.1f);
606rbf.setOffSurfaceDisplacement(0.02f);
607
608rbf.setInputCloud(cloud_with_normals);
609rbf.setSearchMethod(tree);
610
611// Reconstruct
612PolygonMesh mesh;
613rbf.reconstruct(mesh);
614
615MeshConversion::convert(mesh, myMesh);
616}
617
618// ----------------------------------------------------------------------------
619
620Reen::MarchingCubesHoppe::MarchingCubesHoppe(const Points::PointKernel& pts, Mesh::MeshObject& mesh)
621: myPoints(pts)
622, myMesh(mesh)
623{}
624
625void Reen::MarchingCubesHoppe::perform(int ksearch)
626{
627PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>);
628PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
629search::KdTree<PointXYZ>::Ptr tree;
630search::KdTree<PointNormal>::Ptr tree2;
631
632cloud->reserve(myPoints.size());
633for (Points::PointKernel::const_iterator it = myPoints.begin(); it != myPoints.end(); ++it) {
634if (!boost::math::isnan(it->x) && !boost::math::isnan(it->y)
635&& !boost::math::isnan(it->z)) {
636cloud->push_back(PointXYZ(it->x, it->y, it->z));
637}
638}
639
640// Create search tree
641tree.reset(new search::KdTree<PointXYZ>(false));
642tree->setInputCloud(cloud);
643
644// Normal estimation
645NormalEstimation<PointXYZ, Normal> n;
646PointCloud<Normal>::Ptr normals(new PointCloud<Normal>());
647n.setInputCloud(cloud);
648// n.setIndices (indices[B);
649n.setSearchMethod(tree);
650n.setKSearch(ksearch);
651n.compute(*normals);
652
653// Concatenate XYZ and normal information
654pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
655
656// Create search tree
657tree2.reset(new search::KdTree<PointNormal>);
658tree2->setInputCloud(cloud_with_normals);
659
660// Init objects
661pcl::MarchingCubesHoppe<PointNormal> hoppe;
662
663// Set parameters
664hoppe.setIsoLevel(0);
665hoppe.setGridResolution(60, 60, 60);
666hoppe.setPercentageExtendGrid(0.1f);
667
668hoppe.setInputCloud(cloud_with_normals);
669hoppe.setSearchMethod(tree2);
670
671// Reconstruct
672PolygonMesh mesh;
673hoppe.reconstruct(mesh);
674
675MeshConversion::convert(mesh, myMesh);
676}
677
678void Reen::MarchingCubesHoppe::perform(const std::vector<Base::Vector3f>& normals)
679{
680if (myPoints.size() != normals.size()) {
681throw Base::RuntimeError("Number of points doesn't match with number of normals");
682}
683
684PointCloud<PointNormal>::Ptr cloud_with_normals(new PointCloud<PointNormal>);
685search::KdTree<PointNormal>::Ptr tree;
686
687cloud_with_normals->reserve(myPoints.size());
688std::size_t num_points = myPoints.size();
689const std::vector<Base::Vector3f>& points = myPoints.getBasicPoints();
690for (std::size_t index = 0; index < num_points; index++) {
691const Base::Vector3f& p = points[index];
692const Base::Vector3f& n = normals[index];
693if (!boost::math::isnan(p.x) && !boost::math::isnan(p.y) && !boost::math::isnan(p.z)) {
694PointNormal pn;
695pn.x = p.x;
696pn.y = p.y;
697pn.z = p.z;
698pn.normal_x = n.x;
699pn.normal_y = n.y;
700pn.normal_z = n.z;
701cloud_with_normals->push_back(pn);
702}
703}
704
705// Create search tree
706tree.reset(new search::KdTree<PointNormal>);
707tree->setInputCloud(cloud_with_normals);
708
709
710// Init objects
711pcl::MarchingCubesHoppe<PointNormal> hoppe;
712
713// Set parameters
714hoppe.setIsoLevel(0);
715hoppe.setGridResolution(60, 60, 60);
716hoppe.setPercentageExtendGrid(0.1f);
717
718hoppe.setInputCloud(cloud_with_normals);
719hoppe.setSearchMethod(tree);
720
721// Reconstruct
722PolygonMesh mesh;
723hoppe.reconstruct(mesh);
724
725MeshConversion::convert(mesh, myMesh);
726}
727
728// ----------------------------------------------------------------------------
729
730void MeshConversion::convert(const pcl::PolygonMesh& pclMesh, Mesh::MeshObject& meshObject)
731{
732// number of points
733size_t nr_points = pclMesh.cloud.width * pclMesh.cloud.height;
734size_t point_size = pclMesh.cloud.data.size() / nr_points;
735// number of faces for header
736size_t nr_faces = pclMesh.polygons.size();
737
738MeshCore::MeshPointArray points;
739points.reserve(nr_points);
740MeshCore::MeshFacetArray facets;
741facets.reserve(nr_faces);
742
743// get vertices
744MeshCore::MeshPoint vertex;
745for (size_t i = 0; i < nr_points; ++i) {
746int xyz = 0;
747for (size_t d = 0; d < pclMesh.cloud.fields.size(); ++d) {
748int c = 0;
749// adding vertex
750if ((pclMesh.cloud.fields[d].datatype ==
751#if PCL_VERSION_COMPARE(>, 1, 6, 0)
752pcl::PCLPointField::FLOAT32)
753&&
754#else
755sensor_msgs::PointField::FLOAT32)
756&&
757#endif
758(pclMesh.cloud.fields[d].name == "x" || pclMesh.cloud.fields[d].name == "y"
759|| pclMesh.cloud.fields[d].name == "z")) {
760float value;
761memcpy(&value,
762&pclMesh.cloud.data[i * point_size + pclMesh.cloud.fields[d].offset
763+ c * sizeof(float)],
764sizeof(float));
765vertex[xyz] = value;
766if (++xyz == 3) {
767points.push_back(vertex);
768break;
769}
770}
771}
772}
773// get faces
774MeshCore::MeshFacet face;
775for (size_t i = 0; i < nr_faces; i++) {
776face._aulPoints[0] = pclMesh.polygons[i].vertices[0];
777face._aulPoints[1] = pclMesh.polygons[i].vertices[1];
778face._aulPoints[2] = pclMesh.polygons[i].vertices[2];
779facets.push_back(face);
780}
781
782MeshCore::MeshKernel kernel;
783kernel.Adopt(points, facets, true);
784meshObject.swap(kernel);
785meshObject.harmonizeNormals();
786}
787
788#endif // HAVE_PCL_SURFACE
789