You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何遍历CGAL球面排列中每个面的边界并输出顶点坐标

解决CGAL球面排列面顶点XYZ坐标获取问题

需求说明

基于球面三角形创建CGAL球面排列,该排列将球面分为两个面(球面三角形面及其补面),需输出两个面的顶点XYZ坐标,示例输出如下:

Face 0: // the spherical triangle face
0 1 0
0 0 1
1 0 0

Face 1: // the complement face
1 0 0
0 0 1
0 1 0

现有代码

#include <list>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Arrangement_on_surface_2.h>
#include <CGAL/Arr_geodesic_arc_on_sphere_traits_2.h>
#include <CGAL/Arr_spherical_topology_traits_2.h>
#include "arr_print.h"

typedef CGAL::Exact_predicates_exact_constructions_kernel         Kernel;
typedef CGAL::Arr_geodesic_arc_on_sphere_traits_2<Kernel>         Geom_traits;
typedef Geom_traits::Point_2                                      Point;
typedef Geom_traits::Curve_2                                      Curve;
typedef CGAL::Arr_spherical_topology_traits_2<Geom_traits>        Topol_traits;
typedef CGAL::Arrangement_on_surface_2<Geom_traits, Topol_traits> Arrangement;


int main()
{
    Geom_traits traits;
    Arrangement arr(&traits);
    auto ctr_p = traits.construct_point_2_object();
    auto ctr_cv = traits.construct_curve_2_object();

    std::list<Curve> arcs;
    arcs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 1, 0)));
    arcs.push_back(ctr_cv(ctr_p(0, 1, 0), ctr_p(0, 0, 1)));
    arcs.push_back(ctr_cv(ctr_p(0, 0, 1), ctr_p(1, 0, 0)));

    CGAL::insert(arr, arcs.begin(), arcs.end());

    typename Arrangement::Face_const_iterator fit;
    for(fit = arr.faces_begin(); fit != arr.faces_end(); ++fit)
    {
        if (fit->is_unbounded()) continue; 
        
        // How to print the XYZ position of each vertex
        // from each face.

    }
    

   return 0;
}

遇到的问题

尝试用逆时针边界(CCB)迭代器遍历面边界时出现类型转换错误,错误代码如下:

Arrangement::Inner_ccb_const_iterator hc_it;
for (hc_it = fit->inner_ccbs_begin(); hc_it != fit->inner_ccbs_end(); ++hc_it) {
    Arrangement::Halfedge_handle he_handle = hc_it; // error here (can't convert)
    // how can I get the vertex data?
}

正确实现方法

核心修正点

  1. 不跳过无界面:补面属于无界面,是需要输出的目标之一;
  2. 使用外边界迭代器:球面三角形面和补面都没有内部洞,应使用outer_ccb()而非inner_ccbs;
  3. 正确遍历CCB半边:CCB迭代器直接指向半边对象,无需类型转换即可访问顶点。

完整修正后的代码

将原main函数中的遍历部分替换为以下代码:

int face_idx = 0;
typename Arrangement::Face_const_iterator fit;
for(fit = arr.faces_begin(); fit != arr.faces_end(); ++fit)
{
    // 输出面标识与类型注释
    std::cout << "Face " << face_idx << ": ";
    fit->is_unbounded() ? std::cout << "// the complement face" : std::cout << "// the spherical triangle face";
    std::cout << std::endl;

    // 获取面的外边界CCB迭代器
    Arrangement::Ccb_halfedge_const_iterator hc_it = fit->outer_ccb();
    const Arrangement::Ccb_halfedge_const_iterator start = hc_it;

    // 遍历CCB所有半边,避免重复输出起始顶点
    do {
        // 获取当前半边的起点顶点
        const Point& p = hc_it->vertex()->point();
        // 转换为double格式输出XYZ坐标
        std::cout << CGAL::to_double(p.x()) << " "
                  << CGAL::to_double(p.y()) << " "
                  << CGAL::to_double(p.z()) << std::endl;
        ++hc_it;
    } while (hc_it != start);

    std::cout << std::endl;
    face_idx++;
}

代码说明

  • CCB遍历逻辑:球面排列中每个面的外边界是闭合逆时针环,用do-while循环确保完整遍历所有顶点且不重复输出起始点;
  • 坐标提取:通过vertex()->point()获取球面点,用CGAL::to_double()将精确的Kernel::FT类型转换为可打印的double值;
  • 面类型区分:通过is_unbounded()判断是球面三角形面(有界)还是补面(无界),对应输出注释。

内容的提问来源于stack exchange,提问作者Cedric Martens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 12:17:33