Beispiel #1
0
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;
}
Beispiel #2
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;
    }
}
Beispiel #3
0
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;
}
Beispiel #4
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);
}
Beispiel #5
0
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);
}
Beispiel #6
0
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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
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);
}
Beispiel #9
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;
}
Beispiel #10
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);
    }
}
Beispiel #11
0
				template<typename CloudType> void operator()(CloudType& cloud) const
				{
					writePLY(*cloud, file);
				}
Beispiel #12
0
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;
      }
Beispiel #14
0
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);
}