int main(int argc, char **argv) { options.parse(argc, argv); carve::input::Input inputs; std::vector<carve::mesh::MeshSet<3> *> polys; std::vector<carve::line::PolylineSet *> lines; std::vector<carve::point::PointSet *> points; if (options.file == "") { readPLY(std::cin, inputs); } else { if (endswith(options.file, ".ply")) { readPLY(options.file, inputs); } else if (endswith(options.file, ".vtk")) { readVTK(options.file, inputs); } else if (endswith(options.file, ".obj")) { readOBJ(options.file, inputs); } } for (std::list<carve::input::Data *>::const_iterator i = inputs.input.begin(); i != inputs.input.end(); ++i) { carve::mesh::MeshSet<3> *p; carve::point::PointSet *ps; carve::line::PolylineSet *l; if ((p = carve::input::Input::create<carve::mesh::MeshSet<3> >(*i)) != NULL) { if (options.canonicalize) p->canonicalize(); if (options.obj) { writeOBJ(std::cout, p); } else if (options.vtk) { writeVTK(std::cout, p); } else { writePLY(std::cout, p, options.ascii); } delete p; } else if ((l = carve::input::Input::create<carve::line::PolylineSet>(*i)) != NULL) { if (options.obj) { writeOBJ(std::cout, l); } else if (options.vtk) { writeVTK(std::cout, l); } else { writePLY(std::cout, l, options.ascii); } delete l; } else if ((ps = carve::input::Input::create<carve::point::PointSet>(*i)) != NULL) { if (options.obj) { std::cerr << "Can't write a point set in .obj format" << std::endl; } else if (options.vtk) { std::cerr << "Can't write a point set in .vtk format" << std::endl; } else { writePLY(std::cout, ps, options.ascii); } delete ps; } } return 0; }
int main(int argc, char **argv) { try { carve::input::Input inputs; readPLY(std::string(argv[1]), inputs); carve::mesh::MeshSet<3> *p; p = carve::input::Input::create<carve::mesh::MeshSet<3> >(*inputs.input.begin()); carve::mesh::MeshSimplifier simplifier; simplifier.removeFins(p); simplifier.removeLowVolumeManifolds(p, 1.0); // p->transform(carve::geom::quantize<10,3>()); simplifier.simplify(p, 1e-2, 1.0, M_PI/180.0, 2e-3); // std::cerr << "n_flips: " << simplifier.improveMesh_conservative(p) << std::endl; simplifier.removeFins(p); simplifier.removeLowVolumeManifolds(p, 1.0); writePLY(std::cout, p, true); return 0; } catch (carve::exception e) { std::cerr << "exception: " << e.str() << std::endl; } }
int main(int argc, char **argv) { carve::mesh::MeshSet<3> *a, *b; a = readPLYasMesh(argv[1]); b = readPLYasMesh(argv[2]); DetailClip detail_clip_collector(a, b); carve::mesh::MeshSet<3> *c = carve::csg::CSG().compute(a, b, detail_clip_collector, NULL, carve::csg::CSG::CLASSIFY_EDGE); writePLY(std::cout, c, false); return 0; }
IGL_INLINE bool igl::writePLY( const std::string & filename, const Eigen::PlainObjectBase<DerivedV> & V, const Eigen::PlainObjectBase<DerivedF> & F, const bool ascii) { Eigen::MatrixXd N,UV; return writePLY(filename,V,F,N,UV,ascii); }
IGL_INLINE bool igl::writePLY( const std::string & filename, const Eigen::MatrixBase<DerivedV> & V, const Eigen::MatrixBase<DerivedF> & F, const bool ascii) { Eigen::Matrix<typename DerivedV::Scalar,Eigen::Dynamic,Eigen::Dynamic> N,UV; return writePLY(filename,V,F,N,UV,ascii); }
int main(int argc, char **argv) { options.parse(argc, argv); carve::poly::Polyhedron *poly = readModel(options.file); if (!poly) exit(1); std::vector<carve::poly::Vertex<3> > out_vertices = poly->vertices; std::vector<carve::poly::Face<3> > out_faces; size_t N = 0; for (size_t i = 0; i < poly->faces.size(); ++i) { carve::poly::Face<3> &f = poly->faces[i]; N += f.nVertices() - 2; } out_faces.reserve(N); for (size_t i = 0; i < poly->faces.size(); ++i) { carve::poly::Face<3> &f = poly->faces[i]; std::vector<carve::triangulate::tri_idx> result; std::vector<const carve::poly::Polyhedron::vertex_t *> vloop; f.getVertexLoop(vloop); carve::triangulate::triangulate(carve::poly::p2_adapt_project<3>(f.project), vloop, result); if (options.improve) { carve::triangulate::improve(carve::poly::p2_adapt_project<3>(f.project), vloop, result); } for (size_t j = 0; j < result.size(); ++j) { out_faces.push_back(carve::poly::Face<3>( &out_vertices[poly->vertexToIndex_fast(vloop[result[j].a])], &out_vertices[poly->vertexToIndex_fast(vloop[result[j].b])], &out_vertices[poly->vertexToIndex_fast(vloop[result[j].c])] )); } } carve::poly::Polyhedron *result = new carve::poly::Polyhedron(out_faces, out_vertices); if (options.canonicalize) result->canonicalize(); if (options.obj) { writeOBJ(std::cout, result); } else if (options.vtk) { writeVTK(std::cout, result); } else { writePLY(std::cout, result, options.ascii); } delete result; delete poly; }
void HumanBodyLaser::compute3DPoints() { cout << "\nCompute 3D points from the disparity result." << endl; Mat mask = erodeMask(mskL, (stereoPyr[0]->GetWsize())*2); Mat disp = stereoPyr[0]->GetDisp(); vector<Point3f> points(0); vector<Vec3b> colors(0); vector<Vec3f> normals(0); float offsetD = stPointL.x - stPointR.x; int height = imSize.height; int width = imSize.width; int validnum = 0; for(int y = 0; y < height; ++y) { float * pdsp = (float *)disp.ptr<float>(y); uchar * pmsk = (uchar *)mask.ptr<uchar>(y); for(int x = 0; x < width; ++x) { if (pmsk[x] == 0) { pdsp[x] = 0; continue; } if (pdsp[x] != 0) { pdsp[x] += offsetD; validnum ++; } } } Mat colorImL; imL.convertTo(colorImL, CV_8U, 255); //RGB Disp2Point(disp, stPointL, colorImL, mask, QMatrix, points, colors, normals); //refine3DPoints(points, colors, validnum); //string savefn = outdir + "/pointcloud_" + camL + camR + "_" + frame + ".ply"; string savefn = "d_pointcloud_" + camL + camR + "_" + frame + ".ply"; writePLY(savefn, width, height, validnum, PT_HAS_COLOR|PT_HAS_NORMAL, points, colors, normals); cout << "\nsave " << savefn << " done. " << validnum << " Points." << endl; }
int main (int argc, char** argv) { sensor_msgs::PointCloud2 cloud_blob; pcl::PointCloud<PointT> cloud; if (pcl::io::loadPCDFile (argv[1], cloud_blob) == -1) { ROS_ERROR ("Couldn't read file test_pcd.pcd"); return (-1); } ROS_INFO ("Loaded %d data points from test_pcd.pcd with the following fields: %s", (int)(cloud_blob.width * cloud_blob.height), pcl::getFieldsList (cloud_blob).c_str ()); // Convert to the templated message type pcl::fromROSMsg (cloud_blob, cloud); pcl::PointCloud<PointT>::Ptr cloud_ptr (new pcl::PointCloud<PointT> (cloud)); pcl::PassThrough<PointT> pass; pass.setInputCloud (cloud_ptr); pass.filter (cloud); ROS_INFO ("Size after removing NANs : %d", (int)cloud.points.size()); ColorRGB color; vector<pcl::PointPLY> points (cloud.points.size()); for(size_t i = 0; i < cloud.points.size(); i++) { color.assignColor(cloud.points.at(i).rgb); points.at(i).x = cloud.points.at(i).x; points.at(i).y = cloud.points.at(i).y; points.at(i).z = cloud.points.at(i).z; points.at(i).r = color.getR(); points.at(i).g = color.getG(); points.at(i).b = color.getB(); } std::string fn (argv[1]); fn = fn.substr(0,fn.find('.')); fn = fn+ ".ply"; // pcl::io::savePCDFileASCII (fn, cloud); writePLY(fn,points); ROS_INFO ("Saved %d data points to ply file.", (int)cloud.points.size ()); return (0); }
int main(int argc, char **argv) { typedef std::vector<carve::geom2d::P2> loop_t; std::ifstream in(argv[1]); unsigned file_num = 0; while (in.good()) { std::string s; std::getline(in, s); if (in.eof()) break; std::vector<std::vector<carve::geom2d::P2> > paths; parsePath(s, paths); std::cerr << "paths.size()=" << paths.size() << std::endl; if (paths.size() > 1) { std::vector<std::vector<std::pair<size_t, size_t> > > result; std::vector<std::vector<carve::geom2d::P2> > merged; result = carve::triangulate::mergePolygonsAndHoles(paths); merged.resize(result.size()); for (size_t i = 0; i < result.size(); ++i) { std::vector<std::pair<size_t, size_t> > &p = result[i]; merged[i].reserve(p.size()); for (size_t j = 0; j < p.size(); ++j) { merged[i].push_back(paths[p[j].first][p[j].second]); } } paths.swap(merged); } carve::poly::Polyhedron *p = extrude(paths, carve::geom::VECTOR(0,0,100)); p->transform(carve::math::Matrix::TRANS(-p->aabb.pos)); std::ostringstream outf; outf << "file_" << file_num++ << ".ply"; std::ofstream out(outf.str().c_str()); writePLY(out, p, false); delete p; } return 0; }
void MeshDumper::deserialize(MPI_Status& stat) { std::string ovName; int nvertices, ntriangles; SimpleSerializer::deserialize(data, ovName, nvertices, ntriangles, connectivity, vertices); std::string tstr = std::to_string(timeStamp++); std::string currentFname = path + "/" + ovName + "_" + std::string(5 - tstr.length(), '0') + tstr + ".ply"; if (activated) { int nObjects = vertices.size() / nvertices; writePLY(comm, currentFname, nvertices*nObjects, nvertices, ntriangles*nObjects, ntriangles, nObjects, connectivity, vertices); } }
template<typename CloudType> void operator()(CloudType& cloud) const { writePLY(*cloud, file); }
int main(int argc, char **argv) { options.parse(argc, argv); carve::mesh::MeshSet<3> *poly; if (options.axis == Options::ERR) { std::cerr << "need to specify a closure plane." << std::endl; exit(1); } if (options.file == "-") { poly = readPLYasMesh(std::cin); } else if (endswith(options.file, ".ply")) { poly = readPLYasMesh(options.file); } else if (endswith(options.file, ".vtk")) { poly = readVTKasMesh(options.file); } else if (endswith(options.file, ".obj")) { poly = readOBJasMesh(options.file); } if (poly == NULL) { std::cerr << "failed to load polyhedron" << std::endl; exit(1); } std::cerr << "poly aabb = " << poly->getAABB() << std::endl; if (poly->getAABB().compareAxis(carve::geom::axis_pos(options.axis, options.pos)) == 0) { std::cerr << "poly aabb intersects closure plane." << std::endl; exit(1); } for (size_t i = 0; i < poly->meshes.size(); ++i) { carve::mesh::MeshSet<3>::mesh_t *mesh = poly->meshes[i]; const size_t N = mesh->open_edges.size(); if (N == 0) continue; mesh->faces.reserve(N + 1); carve::mesh::MeshSet<3>::edge_t *start = mesh->open_edges[0]; std::vector<carve::mesh::MeshSet<3>::edge_t *> edges_to_close; edges_to_close.resize(N); carve::mesh::MeshSet<3>::edge_t *edge = start; size_t j = 0; do { edges_to_close[j++] = edge; edge = edge->perimNext(); } while (edge != start); CARVE_ASSERT(j == N); std::vector<carve::mesh::MeshSet<3>::vertex_t> projected; projected.resize(N); for (j = 0; j < N; ++j) { edge = edges_to_close[j]; projected[j].v = edge->vert->v; projected[j].v.v[options.axis] = options.pos; } for (j = 0; j < N; ++j) { edge = edges_to_close[j]; carve::mesh::MeshSet<3>::face_t *quad = new carve::mesh::MeshSet<3>::face_t(edge->v2(), edge->v1(), &projected[j], &projected[(j+1)%N]); quad->mesh = mesh; edge->rev = quad->edge; quad->edge->rev = edge; mesh->faces.push_back(quad); } for (j = 0; j < N; ++j) { carve::mesh::MeshSet<3>::edge_t *e1 = edges_to_close[j]->rev->prev; carve::mesh::MeshSet<3>::edge_t *e2 = edges_to_close[(j+1)%N]->rev->next; e1->rev = e2; e2->rev = e1; } for (j = 0; j < N; ++j) { edge = edges_to_close[j]->rev; edge->validateLoop(); } carve::mesh::MeshSet<3>::face_t *loop = carve::mesh::MeshSet<3>::face_t::closeLoop(edges_to_close[0]->rev->next->next); loop->mesh = mesh; mesh->faces.push_back(loop); poly->collectVertices(); } if (options.obj) { writeOBJ(std::cout, poly); } else if (options.vtk) { writeVTK(std::cout, poly); } else { writePLY(std::cout, poly, options.ascii); } return 0; }
int process(const tendrils& i, const tendrils& o) { // //clean up the model std::vector<unsigned int> removed; surfels::finalCleanup(*model, *params, removed); //convert to cloud pcl::PointCloud<surfels::surfelPt>::Ptr surfelCloud(new pcl::PointCloud<surfels::surfelPt>()); surfels::convertModelToCloud(*model, *surfelCloud); //passthrough filter (remove anything at or below table height pcl::PointCloud<surfels::surfelPt>::Ptr tmpCloud(new pcl::PointCloud<surfels::surfelPt>()); // pcl::PassThrough<surfels::surfelPt> pass; // pass.setInputCloud(surfelCloud); // pass.setFilterFieldName("z"); // pass.setFilterLimits(0.001, 1.0); // pass.filter(*tmpCloud); // surfelCloud.swap(tmpCloud); //statistical outlier filter pcl::StatisticalOutlierRemoval<surfels::surfelPt> sor; sor.setInputCloud(surfelCloud); sor.setMeanK(50); sor.setStddevMulThresh(2.0); sor.filter(*tmpCloud); surfelCloud.swap(tmpCloud); //try to determine the dimensions of the base float minX = 0, minY = 0, maxX = 0, maxY = 0; pcl::PassThrough<surfels::surfelPt> basePass; basePass.setInputCloud(surfelCloud); basePass.setFilterFieldName("z"); basePass.setFilterLimits(0.001, 0.02); basePass.filter(*tmpCloud); for (unsigned int i = 0; i < tmpCloud->points.size(); i++) { surfels::surfelPt const& pt = tmpCloud->points[i]; if (i == 0 || pt.x < minX) minX = pt.x; if (i == 0 || pt.x > maxX) maxX = pt.x; if (i == 0 || pt.y < minY) minY = pt.y; if (i == 0 || pt.y > maxY) maxY = pt.y; } float maxRadius = fabs(minX); if (maxX < maxRadius) maxRadius = maxX; if (fabs(minY) < maxRadius) maxRadius = fabs(minY); if (maxY < maxRadius) maxRadius = maxY; //fake seeing the bottom so the reconstruction won't have a lumpy bottom surfels::surfelPt origin, tmp; origin.x = origin.y = origin.z = 0; origin.normal_x = origin.normal_y = 0; origin.normal_z = -1.0; surfelCloud->points.push_back(origin); surfelCloud->width++; float radius_inc = 0.002; for (float radius = radius_inc; radius < maxRadius; radius += radius_inc) { float angle_inc = M_PI / (400 * radius); for (float theta = 0; theta < 2 * M_PI; theta += angle_inc) { tmp = origin; tmp.x += radius * cos(theta); tmp.y += radius * sin(theta); surfelCloud->points.push_back(tmp); surfelCloud->width++; } } //save as ply //rgbd::write_ply_file(*surfelCloud,out_file); writePLY(*surfelCloud, *filename); return ecto::OK; }
int main(int argc, char **argv) { std::vector<std::string> tokens; options.parse(argc, argv); if (options.args.size() != 2) { std::cerr << options.usageStr(); exit(1); } carve::mesh::MeshSet<3> *object = load(options.args[0]); if (object == NULL) { std::cerr << "failed to load object file [" << options.args[0] << "]" << std::endl; exit(1); } carve::mesh::MeshSet<3> *planes = load(options.args[1]); if (planes == NULL) { std::cerr << "failed to load split plane file [" << options.args[1] << "]" << std::endl; exit(1); } carve::mesh::MeshSet<3> *result = NULL; carve::csg::CSG csg; if (options.triangulate) { #if !defined(DISABLE_GLU_TRIANGULATOR) if (options.glu_triangulate) { csg.hooks.registerHook(new GLUTriangulator, carve::csg::CSG::Hooks::PROCESS_OUTPUT_FACE_BIT); if (options.improve) { csg.hooks.registerHook(new carve::csg::CarveTriangulationImprover, carve::csg::CSG::Hooks::PROCESS_OUTPUT_FACE_BIT); } } else { #endif if (options.improve) { csg.hooks.registerHook(new carve::csg::CarveTriangulatorWithImprovement, carve::csg::CSG::Hooks::PROCESS_OUTPUT_FACE_BIT); } else { csg.hooks.registerHook(new carve::csg::CarveTriangulator, carve::csg::CSG::Hooks::PROCESS_OUTPUT_FACE_BIT); } #if !defined(DISABLE_GLU_TRIANGULATOR) } #endif } else if (options.no_holes) { csg.hooks.registerHook(new carve::csg::CarveHoleResolver, carve::csg::CSG::Hooks::PROCESS_OUTPUT_FACE_BIT); } Slice slice_collector(object, planes); result = csg.compute(object, planes, slice_collector, NULL, carve::csg::CSG::CLASSIFY_EDGE); if (result) { result->separateMeshes(); if (options.canonicalize) result->canonicalize(); if (options.obj) { writeOBJ(std::cout, result); } else if (options.vtk) { writeVTK(std::cout, result); } else { writePLY(std::cout, result, options.ascii); } } if (object) delete object; if (planes) delete planes; if (result) delete result; }
IGL_INLINE void igl::copyleft::cgal::propagate_winding_numbers( const Eigen::PlainObjectBase<DerivedV>& V, const Eigen::PlainObjectBase<DerivedF>& F, const Eigen::PlainObjectBase<DerivedL>& labels, Eigen::PlainObjectBase<DerivedW>& W) { #ifdef PROPAGATE_WINDING_NUMBER_TIMING const auto & tictoc = []() { static double t_start = igl::get_seconds(); double diff = igl::get_seconds()-t_start; t_start += diff; return diff; }; tictoc(); #endif const size_t num_faces = F.rows(); //typedef typename DerivedF::Scalar Index; Eigen::MatrixXi E, uE; Eigen::VectorXi EMAP; std::vector<std::vector<size_t> > uE2E; igl::unique_edge_map(F, E, uE, EMAP, uE2E); if (!propagate_winding_numbers_helper::is_orientable(F, uE, uE2E)) { std::cerr << "Input mesh is not orientable!" << std::endl; } Eigen::VectorXi P; const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "extract manifold patches: " << tictoc() << std::endl; #endif DerivedW per_patch_cells; const size_t num_cells = igl::copyleft::cgal::extract_cells( V, F, P, E, uE, uE2E, EMAP, per_patch_cells); #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "extract cells: " << tictoc() << std::endl;; #endif typedef std::tuple<size_t, bool, size_t> CellConnection; std::vector<std::set<CellConnection> > cell_adjacency(num_cells); for (size_t i=0; i<num_patches; i++) { const int positive_cell = per_patch_cells(i,0); const int negative_cell = per_patch_cells(i,1); cell_adjacency[positive_cell].emplace(negative_cell, false, i); cell_adjacency[negative_cell].emplace(positive_cell, true, i); } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "cell connection: " << tictoc() << std::endl; #endif auto save_cell = [&](const std::string& filename, size_t cell_id) { std::vector<size_t> faces; for (size_t i=0; i<num_patches; i++) { if ((per_patch_cells.row(i).array() == cell_id).any()) { for (size_t j=0; j<num_faces; j++) { if ((size_t)P[j] == i) { faces.push_back(j); } } } } Eigen::MatrixXi cell_faces(faces.size(), 3); for (size_t i=0; i<faces.size(); i++) { cell_faces.row(i) = F.row(faces[i]); } Eigen::MatrixXd vertices(V.rows(), 3); for (size_t i=0; i<(size_t)V.rows(); i++) { assign_scalar(V(i,0), vertices(i,0)); assign_scalar(V(i,1), vertices(i,1)); assign_scalar(V(i,2), vertices(i,2)); } writePLY(filename, vertices, cell_faces); }; #ifndef NDEBUG { // Check for odd cycle. Eigen::VectorXi cell_labels(num_cells); cell_labels.setZero(); Eigen::VectorXi parents(num_cells); parents.setConstant(-1); auto trace_parents = [&](size_t idx) { std::list<size_t> path; path.push_back(idx); while ((size_t)parents[path.back()] != path.back()) { path.push_back(parents[path.back()]); } return path; }; for (size_t i=0; i<num_cells; i++) { if (cell_labels[i] == 0) { cell_labels[i] = 1; std::queue<size_t> Q; Q.push(i); parents[i] = i; while (!Q.empty()) { size_t curr_idx = Q.front(); Q.pop(); int curr_label = cell_labels[curr_idx]; for (const auto& neighbor : cell_adjacency[curr_idx]) { if (cell_labels[std::get<0>(neighbor)] == 0) { cell_labels[std::get<0>(neighbor)] = curr_label * -1; Q.push(std::get<0>(neighbor)); parents[std::get<0>(neighbor)] = curr_idx; } else { if (cell_labels[std::get<0>(neighbor)] != curr_label * -1) { std::cerr << "Odd cell cycle detected!" << std::endl; auto path = trace_parents(curr_idx); path.reverse(); auto path2 = trace_parents(std::get<0>(neighbor)); path.insert(path.end(), path2.begin(), path2.end()); for (auto cell_id : path) { std::cout << cell_id << " "; std::stringstream filename; filename << "cell_" << cell_id << ".ply"; save_cell(filename.str(), cell_id); } std::cout << std::endl; } // Do not fail when odd cycle is detected because the resulting // integer winding number field, although inconsistent, may still // be used if the problem region is local and embedded within a // valid volume. //assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1); } } } } } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "check for odd cycle: " << tictoc() << std::endl; #endif } #endif size_t outer_facet; bool flipped; Eigen::VectorXi I; I.setLinSpaced(num_faces, 0, num_faces-1); igl::copyleft::cgal::outer_facet(V, F, I, outer_facet, flipped); #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "outer facet: " << tictoc() << std::endl; #endif const size_t outer_patch = P[outer_facet]; const size_t infinity_cell = per_patch_cells(outer_patch, flipped?1:0); Eigen::VectorXi patch_labels(num_patches); const int INVALID = std::numeric_limits<int>::max(); patch_labels.setConstant(INVALID); for (size_t i=0; i<num_faces; i++) { if (patch_labels[P[i]] == INVALID) { patch_labels[P[i]] = labels[i]; } else { assert(patch_labels[P[i]] == labels[i]); } } assert((patch_labels.array() != INVALID).all()); const size_t num_labels = patch_labels.maxCoeff()+1; Eigen::MatrixXi per_cell_W(num_cells, num_labels); per_cell_W.setConstant(INVALID); per_cell_W.row(infinity_cell).setZero(); std::queue<size_t> Q; Q.push(infinity_cell); while (!Q.empty()) { size_t curr_cell = Q.front(); Q.pop(); for (const auto& neighbor : cell_adjacency[curr_cell]) { size_t neighbor_cell, patch_idx; bool direction; std::tie(neighbor_cell, direction, patch_idx) = neighbor; if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) { per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell); for (size_t i=0; i<num_labels; i++) { int inc = (patch_labels[patch_idx] == (int)i) ? (direction ? -1:1) :0; per_cell_W(neighbor_cell, i) = per_cell_W(curr_cell, i) + inc; } Q.push(neighbor_cell); } else { #ifndef NDEBUG // Checking for winding number consistency. // This check would inevitably fail for meshes that contain open // boundary or non-orientable. However, the inconsistent winding number // field would still be useful in some cases such as when problem region // is local and embedded within the volume. This, unfortunately, is the // best we can do because the problem of computing integer winding // number is ill-defined for open and non-orientable surfaces. for (size_t i=0; i<num_labels; i++) { if ((int)i == patch_labels[patch_idx]) { int inc = direction ? -1:1; //assert(per_cell_W(neighbor_cell, i) == // per_cell_W(curr_cell, i) + inc); } else { //assert(per_cell_W(neighbor_cell, i) == // per_cell_W(curr_cell, i)); } } #endif } } } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "propagate winding number: " << tictoc() << std::endl; #endif W.resize(num_faces, num_labels*2); for (size_t i=0; i<num_faces; i++) { const size_t patch = P[i]; const size_t positive_cell = per_patch_cells(patch, 0); const size_t negative_cell = per_patch_cells(patch, 1); for (size_t j=0; j<num_labels; j++) { W(i,j*2 ) = per_cell_W(positive_cell, j); W(i,j*2+1) = per_cell_W(negative_cell, j); } } #ifdef PROPAGATE_WINDING_NUMBER_TIMING std::cout << "save result: " << tictoc() << std::endl; #endif }
IGL_INLINE void igl::copyleft::cgal::propagate_winding_numbers( const Eigen::PlainObjectBase<DerivedV>& V, const Eigen::PlainObjectBase<DerivedF>& F, const Eigen::PlainObjectBase<DerivedL>& labels, Eigen::PlainObjectBase<DerivedW>& W) { const size_t num_faces = F.rows(); //typedef typename DerivedF::Scalar Index; Eigen::MatrixXi E, uE; Eigen::VectorXi EMAP; std::vector<std::vector<size_t> > uE2E; igl::unique_edge_map(F, E, uE, EMAP, uE2E); if (!propagate_winding_numbers_helper::is_orientable(F, uE, uE2E)) { std::cerr << "Input mesh is not orientable!" << std::endl; } Eigen::VectorXi P; const size_t num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P); DerivedW per_patch_cells; const size_t num_cells = igl::copyleft::cgal::extract_cells( V, F, P, E, uE, uE2E, EMAP, per_patch_cells); typedef std::tuple<size_t, bool, size_t> CellConnection; std::vector<std::set<CellConnection> > cell_adjacency(num_cells); for (size_t i=0; i<num_patches; i++) { const int positive_cell = per_patch_cells(i,0); const int negative_cell = per_patch_cells(i,1); cell_adjacency[positive_cell].emplace(negative_cell, false, i); cell_adjacency[negative_cell].emplace(positive_cell, true, i); } auto save_cell = [&](const std::string& filename, size_t cell_id) { std::vector<size_t> faces; for (size_t i=0; i<num_patches; i++) { if ((per_patch_cells.row(i).array() == cell_id).any()) { for (size_t j=0; j<num_faces; j++) { if ((size_t)P[j] == i) { faces.push_back(j); } } } } Eigen::MatrixXi cell_faces(faces.size(), 3); for (size_t i=0; i<faces.size(); i++) { cell_faces.row(i) = F.row(faces[i]); } Eigen::MatrixXd vertices(V.rows(), 3); for (size_t i=0; i<(size_t)V.rows(); i++) { assign_scalar(V(i,0), vertices(i,0)); assign_scalar(V(i,1), vertices(i,1)); assign_scalar(V(i,2), vertices(i,2)); } writePLY(filename, vertices, cell_faces); }; #ifndef NDEBUG { // Check for odd cycle. Eigen::VectorXi cell_labels(num_cells); cell_labels.setZero(); Eigen::VectorXi parents(num_cells); parents.setConstant(-1); auto trace_parents = [&](size_t idx) { std::list<size_t> path; path.push_back(idx); while ((size_t)parents[path.back()] != path.back()) { path.push_back(parents[path.back()]); } return path; }; for (size_t i=0; i<num_cells; i++) { if (cell_labels[i] == 0) { cell_labels[i] = 1; std::queue<size_t> Q; Q.push(i); parents[i] = i; while (!Q.empty()) { size_t curr_idx = Q.front(); Q.pop(); int curr_label = cell_labels[curr_idx]; for (const auto& neighbor : cell_adjacency[curr_idx]) { if (cell_labels[std::get<0>(neighbor)] == 0) { cell_labels[std::get<0>(neighbor)] = curr_label * -1; Q.push(std::get<0>(neighbor)); parents[std::get<0>(neighbor)] = curr_idx; } else { if (cell_labels[std::get<0>(neighbor)] != curr_label * -1) { std::cerr << "Odd cell cycle detected!" << std::endl; auto path = trace_parents(curr_idx); path.reverse(); auto path2 = trace_parents(std::get<0>(neighbor)); path.insert(path.end(), path2.begin(), path2.end()); for (auto cell_id : path) { std::cout << cell_id << " "; std::stringstream filename; filename << "cell_" << cell_id << ".ply"; save_cell(filename.str(), cell_id); } std::cout << std::endl; } assert(cell_labels[std::get<0>(neighbor)] == curr_label * -1); } } } } } } #endif size_t outer_facet; bool flipped; Eigen::VectorXi I; I.setLinSpaced(num_faces, 0, num_faces-1); igl::copyleft::cgal::outer_facet(V, F, I, outer_facet, flipped); const size_t outer_patch = P[outer_facet]; const size_t infinity_cell = per_patch_cells(outer_patch, flipped?1:0); Eigen::VectorXi patch_labels(num_patches); const int INVALID = std::numeric_limits<int>::max(); patch_labels.setConstant(INVALID); for (size_t i=0; i<num_faces; i++) { if (patch_labels[P[i]] == INVALID) { patch_labels[P[i]] = labels[i]; } else { assert(patch_labels[P[i]] == labels[i]); } } assert((patch_labels.array() != INVALID).all()); const size_t num_labels = patch_labels.maxCoeff()+1; Eigen::MatrixXi per_cell_W(num_cells, num_labels); per_cell_W.setConstant(INVALID); per_cell_W.row(infinity_cell).setZero(); std::queue<size_t> Q; Q.push(infinity_cell); while (!Q.empty()) { size_t curr_cell = Q.front(); Q.pop(); for (const auto& neighbor : cell_adjacency[curr_cell]) { size_t neighbor_cell, patch_idx; bool direction; std::tie(neighbor_cell, direction, patch_idx) = neighbor; if ((per_cell_W.row(neighbor_cell).array() == INVALID).any()) { per_cell_W.row(neighbor_cell) = per_cell_W.row(curr_cell); for (size_t i=0; i<num_labels; i++) { int inc = (patch_labels[patch_idx] == (int)i) ? (direction ? -1:1) :0; per_cell_W(neighbor_cell, i) = per_cell_W(curr_cell, i) + inc; } Q.push(neighbor_cell); } else { #ifndef NDEBUG for (size_t i=0; i<num_labels; i++) { if ((int)i == patch_labels[patch_idx]) { int inc = direction ? -1:1; assert(per_cell_W(neighbor_cell, i) == per_cell_W(curr_cell, i) + inc); } else { assert(per_cell_W(neighbor_cell, i) == per_cell_W(curr_cell, i)); } } #endif } } } W.resize(num_faces, num_labels*2); for (size_t i=0; i<num_faces; i++) { const size_t patch = P[i]; const size_t positive_cell = per_patch_cells(patch, 0); const size_t negative_cell = per_patch_cells(patch, 1); for (size_t j=0; j<num_labels; j++) { W(i,j*2 ) = per_cell_W(positive_cell, j); W(i,j*2+1) = per_cell_W(negative_cell, j); } } }
void writePLY(std::string &out_file, const carve::point::PointSet *points, bool ascii) { std::ofstream out(out_file.c_str(), std::ios_base::binary); if (!out.is_open()) { std::cerr << "File '" << out_file << "' could not be opened." << std::endl; return; } writePLY(out, points, ascii); }