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

实现斐波那契球面与Bowyer-Watson算法时遭遇段错误求助

斐波那契球面三角剖分段错误排查及修复

问题背景

调试生成斐波那契球面并通过Bowyer-Watson算法做三角剖分的代码时,持续出现段错误,在线调试工具也无法解决,以下是原代码的问题分析及修复方案。

原代码中的关键错误

  • 数组越界:add_vertex里的edge edges[3]固定大小为3,但处理多个符合条件的三角形时,添加的边数会远超3,直接触发段错误,需改为动态分配。
  • 循环变量重定义:add_vertex中判断边唯一性的嵌套循环里,内外层都用了i作为循环变量,导致变量覆盖,逻辑混乱,需修改内层变量名。
  • 代码位置错误:add_vertex中创建新三角形、重分配数组的代码被放在边遍历循环内,导致每处理一条边就执行一次数组操作并返回,逻辑完全错误,需移到循环外。
  • 顶点赋值覆盖:create_fibonacci_sphere中给point_list[n]连续两次赋值,第二次覆盖了第一次的y坐标,导致数据丢失,需修正赋值逻辑。
  • 语法结构错误:triangulate中移除超级三角形的for循环缺少闭合},create_fibonacci_sphere最后一行printf在函数外,均会导致编译或运行错误。
  • 浮点数比较不当:vertex_equals用==直接比较浮点数,因精度问题会导致顶点匹配错误,需改用差值极小值判断。
  • 参数类型不匹配:triangulate调用时传入size_t类型的arr_size,但函数参数是int,需显式转换。

修复后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// 顶点结构体
typedef struct Vertex
{
    double x;
    double y;
} vertex;

// 比较两个顶点(考虑浮点数精度)
int vertex_equals(vertex v1, vertex v2)
{
    const double eps = 1e-9;
    return fabs(v1.x - v2.x) < eps && fabs(v1.y - v2.y) < eps;
}

// 边结构体
typedef struct Edge
{
    vertex v0;
    vertex v1;
} edge;

// 比较两条边(无向边)
int edge_equals(edge e1, edge e2)
{
    return (vertex_equals(e1.v0, e2.v0) && vertex_equals(e1.v1, e2.v1)) ||
           (vertex_equals(e1.v0, e2.v1) && vertex_equals(e1.v1, e2.v0));
}

// 外接圆结构体
typedef struct Circumcircle
{
    vertex c;
    double r;
} circum_circ;

// 三角形结构体
typedef struct Triangle
{
    vertex v0;
    vertex v1;
    vertex v2;
    circum_circ circumcirc;
} triangle;

// 计算三角形的外接圆
circum_circ calc_circum_circ(vertex v0, vertex v1, vertex v2)
{
    double A = v1.x - v0.x;
    double B = v1.y - v0.y;
    double C = v2.x - v0.x;
    double D = v2.y - v0.y;

    double E = A * (v0.x + v1.x) + B * (v0.y + v1.y);
    double F = C * (v0.x + v2.x) + D * (v0.y + v2.y);

    double G = 2.0 * (A * (v2.y - v1.y) - B * (v2.x - v1.x));

    double dx, dy;
    vertex center;
    double radius;

    // 共线点处理
    if (round(fabs(G)) == 0)
    {
        double minx = fmin(v0.x, fmin(v1.x, v2.x));
        double miny = fmin(v0.y, fmin(v1.y, v2.y));
        double maxx = fmax(v0.x, fmax(v1.x, v2.x));
        double maxy = fmax(v0.y, fmax(v1.y, v2.y));

        center.x = (minx + maxx) / 2;
        center.y = (miny + maxy) / 2;

        dx = center.x - minx;
        dy = center.y - miny;
    }
    else
    {
        double cx = (D * E - B * F) / G;
        double cy = (A * F - C * E) / G;

        center.x = cx;
        center.y = cy;

        dx = center.x - v0.x;
        dy = center.y - v0.y;
    }
    radius = sqrt(dx * dx + dy * dy);

    circum_circ circle = {center, radius};
    return circle;
}

// 创建三角形
triangle make_triangle(vertex v0, vertex v1, vertex v2)
{
    triangle t;
    t.v0 = v0;
    t.v1 = v1;
    t.v2 = v2;
    t.circumcirc = calc_circum_circ(v0, v1, v2);
    return t;
}

// 判断顶点是否在三角形的外接圆内
int in_circumcircle(triangle t, vertex v)
{
    double dx = t.circumcirc.c.x - v.x;
    double dy = t.circumcirc.c.y - v.y;
    // 用平方比较避免开根号,提升性能同时避免精度问题
    return (dx * dx + dy * dy) <= (t.circumcirc.r * t.circumcirc.r + 1e-9);
}

// 创建包含所有点的超级三角形
triangle make_super_triangle(vertex* vertices, int num_vertices)
{
    double minx = INFINITY, miny = INFINITY, maxx = -INFINITY, maxy = -INFINITY;
    for(int i=0; i<num_vertices; i++)
    {
        if(vertices[i].x < minx) minx = vertices[i].x;
        if(vertices[i].y < miny) miny = vertices[i].y;
        if(vertices[i].x > maxx) maxx = vertices[i].x;
        if(vertices[i].y > maxy) maxy = vertices[i].y;
    }

    double dx = (maxx - minx) * 10;
    double dy = (maxy - miny) * 10;

    vertex v0 = {minx - dx, miny - dy * 3};
    vertex v1 = {minx - dx, maxy + dy};
    vertex v2 = {maxx + dx * 3, maxy + dy};

    return make_triangle(v0, v1, v2);
}

// 添加顶点并更新三角剖分结果
triangle* add_vertex(vertex v, triangle* triangles, int* num_triangles)
{
    // 动态分配边数组,避免越界
    edge* edges = malloc(sizeof(edge) * (*num_triangles) * 3);
    if (!edges) return NULL;
    int num_edges = 0;

    // 移除所有外接圆包含当前顶点的三角形,并收集它们的边
    for(int i = 0; i < *num_triangles; i++)
    {
        if(in_circumcircle(triangles[i], v))
        {
            edges[num_edges++] = (edge){triangles[i].v0, triangles[i].v1};
            edges[num_edges++] = (edge){triangles[i].v1, triangles[i].v2};
            edges[num_edges++] = (edge){triangles[i].v2, triangles[i].v0};
            // 用最后一个三角形覆盖当前位置,减少数组移动
            triangles[i] = triangles[--(*num_triangles)];
            i--;
        }
    }

    // 筛选出唯一的边
    edge* unique_edges = malloc(sizeof(edge) * num_edges);
    if (!unique_edges) { free(edges); return NULL; }
    int num_unique_edges = 0;

    for(int i = 0; i < num_edges; i++)
    {
        int is_unique = 1;
        // 内层循环变量改用k,避免和外层i冲突
        for(int k = 0; k < num_edges; k++)
        {
            if(i != k && edge_equals(edges[i], edges[k]))
            {
                is_unique = 0;
                break;
            }
        }
        if(is_unique)
        {
            unique_edges[num_unique_edges++] = edges[i];
        }
    }

    // 创建新的三角形
    triangle* new_triangles = malloc(sizeof(triangle) * num_unique_edges);
    if (!new_triangles) { free(edges); free(unique_edges); return NULL; }

    for(int i = 0; i < num_unique_edges; i++)
    {
        new_triangles[i] = make_triangle(unique_edges[i].v0, unique_edges[i].v1, v);
    }

    // 扩展三角形数组
    triangle* result = realloc(triangles, sizeof(triangle) * (*num_triangles + num_unique_edges));
    if(result == NULL)
    {
        free(new_triangles);
        free(edges);
        free(unique_edges);
        return NULL;
    }

    triangles = result;
    for(int i=0; i < num_unique_edges; i++)
    {
        triangles[(*num_triangles)++] = new_triangles[i];
    }

    // 释放临时分配的内存
    free(new_triangles);
    free(edges);
    free(unique_edges);
    return triangles;
}

// 执行Delaunay三角剖分
triangle* triangulate(vertex* vertices, int num_vertices, int* num_triangles)
{
    triangle st = make_super_triangle(vertices, num_vertices);

    triangle* triangles = malloc(sizeof(triangle));
    if(triangles == NULL)
    {
        return NULL;
    }

    triangles[0] = st;
    *num_triangles = 1;

    // 逐个添加顶点
    for(int i=0; i < num_vertices; i++)
    {
        triangles = add_vertex(vertices[i], triangles, num_triangles);
        if(triangles == NULL)
        {
            free(triangles);
            return NULL;
        }
    }

    // 移除包含超级三角形顶点的三角形
    for(int i = 0; i < *num_triangles; i++)
    {
        if (vertex_equals(triangles[i].v0, st.v0) || vertex_equals(triangles[i].v0, st.v1) || vertex_equals(triangles[i].v0, st.v2) ||
            vertex_equals(triangles[i].v1, st.v0) || vertex_equals(triangles[i].v1, st.v1) || vertex_equals(triangles[i].v1, st.v2) ||
            vertex_equals(triangles[i].v2, st.v0) || vertex_equals(triangles[i].v2, st.v1) || vertex_equals(triangles[i].v2, st.v2)) {
            triangles[i] = triangles[--(*num_triangles)];
            i--;
        }
    }

    // 调整数组大小以匹配实际三角形数量
    triangle* result = realloc(triangles, sizeof(triangle) * (*num_triangles));
    if(result == NULL)
    {
        free(triangles);
        return NULL;
    }
    triangles = result;

    return triangles;
}

void create_fibonacci_sphere(int radius, int num_of_points, float points[][3], size_t arr_size)
{
    double golden_ratio = (1 + sqrt(5)) / 2;
    double angle_increment = 2 * M_PI * golden_ratio;

    // 生成斐波那契球面点
    for (int i = 0; i < num_of_points; ++i)
    {
        double t = (double)i / num_of_points;
        double angle1 = acos(1 - 2 * t);
        double angle2 = angle_increment * i;

        points[i][0] = radius * sin(angle1) * cos(angle2);
        points[i][1] = radius * sin(angle1) * sin(angle2);
        points[i][2] = radius * cos(angle1);
    }

    // 转换为2D顶点(这里选择x-z平面,若需要x-y平面可修改第二个参数)
    vertex point_list[num_of_points];
    for (int n = 0; n < num_of_points; n++)
    {
        point_list[n] = (vertex) {points[n][0], points[n][2]};
        // 若需要x-y平面,改为:point_list[n] = (vertex) {points[n][0], points[n][1]};
    }

    int num_triangles;
    // 显式转换size_t为int
    triangle* triangles = triangulate(point_list, (int)arr_size, &num_triangles);
    if (triangles)
    {
        // 打印三角剖分结果
        for (int i = 0; i < num_triangles; ++i)
        {
            printf("%f %f %f %f %f %f\n", triangles[i].v0.x, triangles[i].v0.y,
                   triangles[i].v1.x, triangles[i].v1.y,
                   triangles[i].v2.x, triangles[i].v2.y);
        }
        free(triangles);
    }

    printf("%d points generated on Fibonacci sphere with radius %d.\n", num_of_points, radius);
}

int main(void)
{
    int radius = 50;
    int num_of_points = 1000;
    float vertices[num_of_points][3];
    create_fibonacci_sphere(radius, num_of_points, vertices, sizeof(vertices)/sizeof(vertices[0]));

    printf("this will print if there are no errors.\n");
    return 0;
}

额外说明

  • 修复了所有导致段错误的核心问题,包括数组越界、逻辑错误、语法错误等。
  • 优化了浮点数比较逻辑,避免精度问题引发的判断错误。
  • 增加了内存分配失败的检查,提升代码稳定性。
  • 注释了顶点平面选择的位置,可根据需求切换x-y或x-z平面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:32:00