在CGAL - SourceCode - box_intersection_d源码阅读 一文中介绍了如何利用box intersection快速找出相交对。
并在PaperImpl- Fast Software for Box Intersections 介绍了如何利用c++实现box intersection。
在CGAL - SourceCode - Intersection_of_triangle_meshes 源码阅读一文中已经介绍了两个三角网格之间相交线的具体计算实现。但是对于相交线计算出来之后如何重新构建网格,并没有进一步展开。文本为Surface_intersection_visitor_for_corefinement代码阅读的记录。
代码位置:CGAL\Polygon_mesh_processing\internal\Corefinement\Visitor.h
实现的代码入口函数为:finalize
,如下:
void finalize(Intersection_nodes<TriangleMesh, VertexPointMap, Predicates_on_constructions_needed>& nodes, const TriangleMesh& tm1, const TriangleMesh& tm2, const VertexPointMap& vpm1, const VertexPointMap& vpm2) { nodes.all_nodes_created(); TriangleMesh* tm1_ptr = const_cast<TriangleMesh*>(&tm1); TriangleMesh* tm2_ptr = const_cast<TriangleMesh*>(&tm2); std::map<TriangleMesh*, VertexPointMap> vpms; vpms[tm1_ptr] = vpm1; vpms[tm2_ptr] = vpm2; vertex_descriptor null_vertex = Graph_traits::null_vertex(); const Node_id nb_nodes = nodes.size(); // we reserve nb_nodes+3 because we use the last three entries for the // face triangulation mesh_to_node_id_to_vertex[tm1_ptr].resize(nb_nodes+3, null_vertex); mesh_to_node_id_to_vertex[tm2_ptr].resize(nb_nodes+3, null_vertex); //store for each triangle face which boundary is intersected by the other surface, //original vertices (and halfedges in the refined mesh pointing on these vertices) typedef boost::unordered_map<face_descriptor,Face_boundary> Face_boundaries; std::map<TriangleMesh*,Face_boundaries> mesh_to_face_boundaries; //0) For each triangle mesh, collect original vertices that belongs to the intersection. // From the graph of constraints, extract intersection edges that are incident to such vertices. In case // there exists another original vertex adjacent to the first one found, this halfedge must be // marked on the boundary (and possibly update an_edge_per_polyline). // This is done first to avoid halfedges stored to be modified in the steps following. for (typename Mesh_to_vertices_on_intersection_map::iterator it=mesh_to_vertices_on_inter.begin(); it!=mesh_to_vertices_on_inter.end(); ++it) { // ...... } //1) First split halfedges cut by the intersection polyline(s) for (typename std::map<TriangleMesh*,On_edge_map>::iterator it=on_edge.begin(); it!=on_edge.end(); ++it) { // ...... } //2)triangulation of the triangle faces containing intersection point in their interior // and also those with intersection points only on the boundary. for (typename std::map<TriangleMesh*,On_face_map>::iterator it=on_face.begin(); it!=on_face.end(); ++it) { // ... } nodes.finalize(); // additional operations output_builder(nodes, input_with_coplanar_faces, is_node_of_degree_one, mesh_to_node_id_to_vertex); }
从代码注释中发现,该实现的主要有三个步骤:
an_edge_per_polyline
)。首先执行这一步操作,可以避免存储的半边在以下步骤中被修改。如果只需要对原理有个简单的认识,那么看到这里就可以结束了。
首先需要了解的数据结构是:Mesh_to_vertices_on_intersection_map
。
typedef std::vector<Node_id> Node_ids; // 存储了node id的数组 typedef boost::unordered_map<face_descriptor,Node_ids> On_face_map; // 三角形面片和对应nodes的关系 typedef boost::unordered_map<edge_descriptor,Node_ids> On_edge_map; // 三角形边和对应nodes的关系 //to keep the correspondance between node_id and vertex_handle in each mesh typedef std::vector<vertex_descriptor> Node_id_to_vertex; // nodeid和vertex对应关系 typedef std::map<TriangleMesh*, Node_id_to_vertex > Mesh_to_map_node; //to handle coplanar halfedge of polyhedra that are full in the intersection typedef std::multimap<Node_id,halfedge_descriptor> Node_to_target_of_hedge_map; // nodeid和半边的关系 typedef std::map<TriangleMesh*,Node_to_target_of_hedge_map> Mesh_to_vertices_on_intersection_map; // 每个网格上,交点和半边的映射关系
然后看一下第一步的具体实现:
for (typename Mesh_to_vertices_on_intersection_map::iterator it=mesh_to_vertices_on_inter.begin(); it!=mesh_to_vertices_on_inter.end(); ++it) // 对每个mesh进行遍历 { TriangleMesh& tm=*it->first; // Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm]; Node_to_target_of_hedge_map& nodes_to_hedge=it->second; for(typename Node_to_target_of_hedge_map::iterator it_node_2_hedge=nodes_to_hedge.begin(); it_node_2_hedge!=nodes_to_hedge.end(); ++it_node_2_hedge) // 对node进行遍历 { Node_id node_id_of_first=it_node_2_hedge->first; // 获取节点的邻域节点,如果邻域节点为空,则为孤立点 std::vector<Node_id>& neighbors=graph_of_constraints[node_id_of_first]; if ( !neighbors.empty() ) { // 遍历邻域点 BOOST_FOREACH(Node_id node_id, neighbors) { //if already done for the opposite if (node_id >= node_id_of_first) continue; typename Node_to_target_of_hedge_map::iterator it_node_2_hedge_two = nodes_to_hedge.find(node_id); // 相邻的点也在半边上 if ( it_node_2_hedge_two!=nodes_to_hedge.end() ) //a full edge is on intersection { //get the corresponding halfedge with vertex corresponding to node_id_of_first halfedge_descriptor hedge=it_node_2_hedge->second; halfedge_descriptor start=hedge; bool did_break=false; while ( source(hedge,tm) != target(it_node_2_hedge_two->second,tm) ) { hedge=opposite(next(hedge,tm),tm); if (tm1_ptr==tm2_ptr && hedge==start) { // ... 特殊情况处理 } } if (did_break) continue; // 构建关联的边,放到marks_on_edges,output_builder内部。 std::pair<Node_id,Node_id> edge_pair(node_id,node_id_of_first); call_put(marks_on_edges,tm,edge(hedge,tm),true); // output_builder用来针对布尔关系,构建网格 output_builder.set_edge_per_polyline(tm,edge_pair,hedge); } } } #ifdef CGAL_COREFINEMENT_DEBUG else { std::cout << "X1: Found an isolated point" << std::endl; } #endif } }
// on_edge的类型是:std::map<TriangleMesh*,On_edge_map> on_edge; // 表示每个mesh,对应边和交点之间的关系 for (typename std::map<TriangleMesh*,On_edge_map>::iterator it=on_edge.begin(); it!=on_edge.end(); ++it) { TriangleMesh& tm=*it->first; const VertexPointMap& vpm=vpms[&tm]; On_edge_map& on_edge_map=it->second; On_face_map& on_face_map=on_face[&tm]; Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm]; for(typename On_edge_map::iterator it2=on_edge_map.begin(); it2!=on_edge_map.end(); ++it2) { //获取被split的半边 halfedge_descriptor hedge=halfedge(it2->first,tm); //indices of the nodes to be inserted Node_ids& node_ids=it2->second; CGAL_assertion( std::set<Node_id>(node_ids.begin(), node_ids.end()) .size()==node_ids.size() ); //对边上的交点进行排序 sort_vertices_along_hedge(node_ids,hedge,tm,vpm,nodes); //save original face and nodes for face of hedge (1) // 保存hedge的面的原始面和nodes(1) if ( !is_border(hedge,tm) ){ face_descriptor f=face(hedge,tm); typename Face_boundaries::iterator it_face = face_boundaries.find(f); if (it_face==face_boundaries.end()) it_face=face_boundaries.insert(std::make_pair(f,Face_boundary(hedge,tm))).first; it_face->second.copy_node_ids(hedge,node_ids.begin(),node_ids.end()); } //save original face and nodes for face of hedge->opposite (2) // 保存hedge对边的面的原始面和nodes typename Face_boundaries::iterator opposite_original_info=face_boundaries.end(); halfedge_descriptor hedge_opp = opposite(hedge,tm); if ( !is_border(hedge_opp,tm) ){ face_descriptor f=face(hedge_opp,tm); opposite_original_info=face_boundaries.find(f); if (opposite_original_info==face_boundaries.end()) opposite_original_info=face_boundaries.insert(std::make_pair(f,Face_boundary(hedge_opp,tm))).first; opposite_original_info->second.copy_node_ids(hedge_opp,node_ids.rbegin(),node_ids.rend()); } /// ...... //do split the edges //split的关键代码如下: CGAL_assertion_code(vertex_descriptor expected_src=source(hedge,tm)); BOOST_FOREACH(std::size_t node_id, node_ids) { // Split new hedge halfedge_descriptor hnew = Euler::split_edge(hedge, tm); CGAL_assertion(expected_src==source(hnew,tm)); /// New vertex vertex_descriptor vnew=target(hnew,tm); // user_visitor.new_vertex_added(node_id, vnew, tm); // NODE_VISITOR_TAG nodes.call_put(vpm, vnew, node_id, tm); // register the new vertex in the output builder // 将得到的新的vertex注册到output_builder中 output_builder.set_vertex_id(vnew, node_id, tm); // 建立新创建的vertex和node之间的关系 node_id_to_vertex[node_id]=vnew; if (first){ first=false; hedge_incident_to_src=next(opposite(hedge,tm),tm); } //update marker tags. If the edge was marked, then the resulting edges in the split must be marked if ( hedge_is_marked ) call_put(marks_on_edges,tm,edge(hnew,tm),true); CGAL_assertion_code(expected_src=vnew); } /// ...... } }
TODO:这部分代码很复杂,暂时只能理解是用来做三角剖分的,具体怎么做需要先了解单纯的三角剖分要怎么实现,在后续的文章中,会对这一块进行学习,还有再对这部分内容进行补充。