如何在CGAL 2D Arrangement中便捷使用CGAL::simplify?
CGAL 2D Arrangement 边序列简化实现方案
目前CGAL并没有提供直接一键简化所有由2度顶点分隔边序列的工具函数,你需要手动完成提取、简化、替换这三步,但可以通过一套连贯的流程高效实现,不用完全拆分重组整个Arrangement。以下是具体步骤和代码示例:
核心步骤
1. 识别并提取2度顶点连接的边序列
遍历Arrangement中的所有顶点,筛选出度数为2的内部顶点(排除无穷远顶点),然后追踪这些顶点连接的边,形成连续的折线边链。注意要避免重复处理同一个边链。
2. 提取折线点并执行简化
将边链的起点、所有中间2度顶点的坐标、终点组成点序列,调用CGAL::simplify函数传入距离阈值完成简化。建议使用平方距离阈值,避免开方运算提升效率。
3. 替换原边序列为简化后的折线
先删除原边链的所有边,再移除孤立的2度顶点,最后将简化后的折线插入到Arrangement中,确保拓扑结构正确。
代码示例
// 假设使用Exact_predicates_exact_constructions_kernel作为核心内核 typedef CGAL::Exact_predicates_exact_constructions_kernel K; typedef CGAL::Arrangement_2<K> Arrangement; typedef Arrangement::Vertex_handle Vertex_handle; typedef Arrangement::Halfedge_handle Halfedge_handle; typedef CGAL::Point_2<K> Point_2; typedef std::vector<Point_2> Point_sequence; // 提取单个2度顶点所属的完整边链 std::vector<Halfedge_handle> extract_edge_chain(Vertex_handle start_v) { std::vector<Halfedge_handle> chain; Halfedge_handle current_he = start_v->incident_halfedges(); do { chain.push_back(current_he); // 下一个顶点是当前边的目标点的对边的下一条边 Vertex_handle next_v = current_he->target(); if (next_v->degree() != 2) break; // 遇到非2度顶点,终止链 current_he = next_v->incident_halfedges()->opposite()->next(); } while (current_he->source() != start_v); // 回到起点则闭合链 return chain; } // 执行Arrangement简化的主函数 void simplify_arrangement_edges(Arrangement& arr, double distance_threshold) { std::vector<std::vector<Halfedge_handle>> all_chains; std::unordered_set<Vertex_handle> processed_vertices; // 第一步:收集所有未处理的2度顶点对应的边链 for (auto v_it = arr.vertices_begin(); v_it != arr.vertices_end(); ++v_it) { Vertex_handle v = v_it; if (v->degree() == 2 && !v->is_at_infinity() && processed_vertices.find(v) == processed_vertices.end()) { auto chain = extract_edge_chain(v); all_chains.push_back(chain); // 标记链中所有顶点为已处理 for (auto he : chain) { processed_vertices.insert(he->source()); processed_vertices.insert(he->target()); } } } // 第二步+第三步:处理每条边链 for (auto& chain : all_chains) { // 提取原始点序列 Point_sequence original_pts; original_pts.push_back(chain.front()->source()->point()); for (auto he : chain) { original_pts.push_back(he->target()->point()); } // 简化折线:使用平方距离阈值避免开方 Point_sequence simplified_pts; const K::FT squared_threshold = CGAL::square(distance_threshold); CGAL::simplify(original_pts.begin(), original_pts.end(), std::back_inserter(simplified_pts), CGAL::Squared_distance_d<K>(), squared_threshold); // 删除原边链的所有边 std::vector<Vertex_handle> orphaned_vertices; for (auto he : chain) { if (!he->is_fictitious()) { // 提前收集可能孤立的顶点 if (he->source()->degree() == 2) orphaned_vertices.push_back(he->source()); if (he->target()->degree() == 2) orphaned_vertices.push_back(he->target()); arr.remove_edge(he); } } // 删除孤立的2度顶点(边删除后度数变为0) for (auto v : orphaned_vertices) { if (v->degree() == 0) { arr.remove_vertex(v); } } // 插入简化后的折线 if (simplified_pts.size() >= 2) { // 区分闭合多边形和开放折线 if (simplified_pts.front() == simplified_pts.back()) { CGAL::insert_non_intersecting_curve(arr, CGAL::Polygon_2<K>(simplified_pts.begin(), simplified_pts.end())); } else { CGAL::insert_non_intersecting_curve(arr, simplified_pts.begin(), simplified_pts.end()); } } } }
注意事项
- 处理边链时要标记已处理的顶点,避免重复处理同一序列。
- 删除边和顶点的顺序很重要:先删边,再删失去所有边的孤立顶点。
- 插入简化后的折线时,要区分是闭合多边形还是开放折线,调用对应的
insert_non_intersecting_curve重载版本,确保拓扑正确。 - 如果你的Arrangement使用了其他内核或特征类,需要相应调整代码中的类型定义。
内容的提问来源于stack exchange,提问作者Clement
相关产品推荐
相关产品推荐

