void PointCloud::initPointGraph(double distance_threshold)
{
  if (distance_threshold == point_graph_threshold_
    && boost::num_edges(*point_graph_) != 0)
    return;

  triangulate();
  point_graph_threshold_ = distance_threshold;

  boost::PointGraph& g_point = *point_graph_;
  g_point = boost::PointGraph(points_num_);

  size_t t = 8;

  for (CGAL::Delaunay::Finite_edges_iterator it = triangulation_->finite_edges_begin();
    it != triangulation_->finite_edges_end(); ++ it)
  {
    const CGAL::Delaunay::Cell_handle& cell_handle  = it->first;
    const CGAL::Delaunay::Vertex_handle& source_handle = cell_handle->vertex(it->second);
    const CGAL::Delaunay::Vertex_handle& target_handle = cell_handle->vertex(it->third);
    double distance_L1 = std::sqrt(CGAL::squared_distance(source_handle->point(), target_handle->point()));
    if (distance_L1 > point_graph_threshold_)
      continue;

    size_t source_id = source_handle->info();
    size_t target_id = target_handle->info();
    assert (source_id < points_num_ && target_id < points_num_);
    WeightedEdge weighted_edge(distance_L1);
    boost::add_edge(source_id, target_id, weighted_edge, g_point);
  }

  return;
}
  double MaxflowInference::segment(PotentialsCache::ConstPtr pc,
                                   Eigen::VectorXi* seg) const
  {
    // -- Check for monkey business and allocate.
    ROS_ASSERT(pc->numEdgePotentials() == model_.eweights_.rows());
    ROS_ASSERT(pc->numNodePotentials() == model_.nweights_.rows());

    if(seg->rows() != pc->numNodes())
      *seg = VecXi::Zero(pc->numNodes());

    for(size_t i = 0; i < pc->node_.size(); ++i) 
      ROS_ASSERT(seg->rows() == pc->node_[i].rows());

    int num_edges = 0;
    for(size_t i = 0; i < pc->edge_.size(); ++i) { 
      ROS_ASSERT(seg->rows() == pc->edge_[i].rows());
      ROS_ASSERT(seg->rows() == pc->edge_[i].cols());
      num_edges += pc->edge_[i].nonZeros();
    }
    
    Graph3d graph(seg->rows(), num_edges);
    graph.add_node(seg->rows());
                        

    Eigen::VectorXd weighted_node(seg->rows());
    SparseMat weighted_edge(seg->rows(), seg->rows());
    pc->applyWeights(model_, &weighted_edge, &weighted_node);
    
    // -- Fill the graph with weighted node potentials.
    for(int i = 0; i < weighted_node.rows(); ++i)
     graph.add_tweights(i, weighted_node(i), 0);
    
    // -- Fill the graph with edge potentials.  Assumes symmetry & upper triangular.
    for(int i = 0; i < weighted_edge.outerSize(); ++i) {
      for(SparseMatrix<double, RowMajor>::InnerIterator it(weighted_edge, i); it; ++it) {
        if(it.col() <= it.row())
          continue;

        ROS_WARN_STREAM_COND(it.value() < 0, "Edgepot weighted sum is negative: " << it.value());
        ROS_FATAL_STREAM_COND(isnan(it.value()), "NaN in edgepot.");
        graph.add_edge(it.row(), it.col(), it.value(), it.value());
      }
    }

    // -- Run graph cuts.
    HighResTimer hrt("maxflow");
    hrt.start();
    double mf = graph.maxflow();
    hrt.stop();
    //cout << hrt.reportMilliseconds() << endl;

    // -- Fill the segmentation.
    for(int i = 0; i < seg->rows(); ++i) {
      if(graph.what_segment(i, Graph3d::SINK) == Graph3d::SINK)
        seg->coeffRef(i) = -1;
      else
        seg->coeffRef(i) = 1;
    }

    return mf;
  }