CGAL join_vertex source code analysis

location: CGAL\boost\graph\Euler_operations.h

http://doc.cgal.org/latest/BGL/group__PkgBGLEulerOperations.html#ga585a3324ef493cc1340da5cb364b772c

/**  
 * joins the two vertices incident to `h`, (that is `source(h, g)` and
 * `target(h, g)`) and removes `source(h,g)`. Returns the predecessor
 * of `h` around the vertex, i.e., `prev(opposite(h,g))`.  The
 * invariant `join_vertex(split_vertex(h,g),g)` returns `h`.  The
 * time complexity is linear in the degree of the vertex removed.
 *
 * \image html join_vertex.svg
 *
 * \tparam Graph must be a model of `MutableFaceGraph`
 *
 * \param g the graph
 * \param h the halfedge which incident vertices are joint
 *
 * \returns `prev(opposite(h,g))`
 *
 * \pre The size of the faces incident to `h` and `opposite(h,g)` is at least 4.
 *
 * \post `source(h, g)` is invalidated
 * \post `h` is invalidated 
 * 
 * \sa `split_vertex()`
 */
template<typename Graph>
typename boost::graph_traits<Graph>::halfedge_descriptor
join_vertex(typename boost::graph_traits<Graph>::halfedge_descriptor h,
            Graph& g)
{
  typedef typename boost::graph_traits<Graph>              Traits;
  typedef typename Traits::halfedge_descriptor             halfedge_descriptor;
  typedef typename Traits::vertex_descriptor               vertex_descriptor;
  typedef Halfedge_around_target_iterator<Graph>           halfedge_around_vertex_iterator;

  halfedge_descriptor hop = opposite(h, g)
    , hprev = prev(hop, g)
    , gprev = prev(h, g)
    , hnext = next(hop, g)
    , gnext = next(h, g);
  vertex_descriptor v_to_remove = target(hop, g)
    , v = target(h, g);

  // this assertion fires needlessly
  // CGAL_precondition(std::distance(
  //                     halfedges_around_face(e, g).first,
  //                     halfedges_around_face(e, g).second) >= 4);

  CGAL_assertion( halfedge(v_to_remove, v, g).first == h );

  halfedge_around_vertex_iterator ieb, iee;
  for(boost::tie(ieb, iee) = halfedges_around_target(hop, g); ieb != iee; ++ieb) {
    CGAL_assertion( target(*ieb,g) == v_to_remove);
    set_target(*ieb ,v , g);
  }

  set_next(hprev, hnext, g);
  set_next(gprev, gnext, g);
  set_halfedge(v, gprev, g);
  // internal::set_constant_vertex_is_border(g, v);

  remove_edge(edge(h, g), g);
  remove_vertex(v_to_remove, g);

  return hprev;
}


CGAL join_vertex source code analysis_第1张图片


let's explain the codes line by line:

  halfedge_descriptor hop = opposite(h, g)
    , hprev = prev(hop, g)
    , gprev = prev(h, g)
    , hnext = next(hop, g)
    , gnext = next(h, g);
  vertex_descriptor v_to_remove = target(hop, g)
    , v = target(h, g);

these lines do some preparing work as our picture depicts.


  halfedge_around_vertex_iterator ieb, iee;
  for(boost::tie(ieb, iee) = halfedges_around_target(hop, g); ieb != iee; ++ieb) {
    CGAL_assertion( target(*ieb,g) == v_to_remove);
    set_target(*ieb ,v , g);
  }

these sentences set the target of the half-edges I mark red to v. At this part, I had been confused for a while. I mean here we just set the target of halfedges tp, Ap, Bp, bp, qp, but what about their opposite, i.e. pt, pA, pB, pb, pq. Another problem is why there don't change the data structure after halfedge pq collapsing, for example, when the triangle Apt, ABp become Aqt, ABq, shouldn't we do some fix work to guarantee the topology of the graph is valid.


okay, let me tackle these problems one by one.

first, let us recall some basic knowledge about the data structure of  halfedge graph.


Each edge is represented by two halfedges with opposite orientation. Each halfedge stores a reference to an incident face and to an incident vertex. Additionally, it stores a reference to the next and previous halfedge incident to its incident face. For each face and each vertex an incident halfedge is stored.

CGAL join_vertex source code analysis_第2张图片


so each halfedge only cares about its target vertex. After setting the target of halfedges tp, Ap, Bp, bp, qp, we don't need to care about their opposite, because the target vertex of those edges stay the same.  First problem solved.


the answer to the second problem is we don't need to do any fix work. Because the topology of triangles related with vertex p will stay the same after the halfedge pq collapsing. For instance, the previous and next halfedges of pt are Ap and tA, after halfedge pq collasped, the previous and next halfedges of qt are Aq and tA, The relative relationship between them aren't actually changed. So we just leave them as they are.


Go back to our analysis, 


  set_next(hprev, hnext, g);
  set_next(gprev, gnext, g);


these two lines are to keep the neighbors relationship  after pq removed.

the set_next actually does bilateral work

template<class Gt, class I, CGAL_HDS_PARAM_, class A>
void
set_next(typename boost::graph_traits< CGAL::Polyhedron_3<Gt,I,HDS,A> >::halfedge_descriptor h1
         , typename boost::graph_traits< CGAL::Polyhedron_3<Gt,I,HDS,A> >::halfedge_descriptor h2
         , CGAL::Polyhedron_3<Gt,I,HDS,A>&)
{
  typedef typename CGAL::Polyhedron_3<Gt,I,HDS,A>::Halfedge::Base Sneak;
  static_cast<Sneak&>(*h1).set_next(h2);
  static_cast<Sneak&>(*h2).set_prev(h1);
}



set_halfedge(v, gprev, g);

this line just guarantees the incident halfedge of v is valid.


  remove_edge(edge(h, g), g);
  remove_vertex(v_to_remove, g);

Finally, we remove the edge pq(note here is edge not halfedge), then we remove vertex p. Done!


你可能感兴趣的:(CGAL join_vertex source code analysis)