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

基于半边数据结构拆分网格分离组件时遇栈溢出问题求解

解决半边结构下大规模网格拆分的栈溢出问题

我需要在单个网格中对分离的几何组件进行分组,当前采用半边数据结构(half-edge data structure)。思路是选取一条半边,遍历边找到配对的孪生半边,再遍历其关联面,若面已访问则跳过。但由于网格规模过大,当前递归实现出现栈溢出(stackoverflow),附上代码如下:

public Mesh[] SplitDisjointMeshes()
{
    var output = new List<Mesh>();
    var visited = new bool[_faces.Count];

    for(var i = 0; i < _halfedges.Count; i++)
    {
        if (visited[_halfedges[i].AdjacentFace]) continue;

        var faces = new List<int>();
        GetDisjointMeshPart(i, ref visited, faces);
        // Todo: Build new mesh from all connected faces
        
    }

    return null;
}

private void GetDisjointMeshPart(int i, ref bool[] visited, List<int> faces)
{
    int he_first, he_current;

    he_first = i;
    he_current = he_first;

    do
    {
        he_current = _halfedges[he_current].NextHalfedge;

        var tmp = _halfedges.GetPairHalfedge(he_current);

        var face = _halfedges[tmp].AdjacentFace;

        if (face == -1 || visited[face]) continue; // Not found or aready visited
        faces.Add(face);
        visited[face] = true;
        GetDisjointMeshPart(tmp, ref visited, faces);
    }
    while (he_current != he_first);
}

核心解决方案:递归改迭代(DFS/BFS)

递归的调用栈深度受限于网格连通分量的大小,大规模网格必然触发栈溢出。改用迭代式的深度优先搜索(DFS)或广度优先搜索(BFS),用手动维护的栈/队列替代系统调用栈,就能彻底解决这个问题,同时保证O(N)时间复杂度(每个半边、面只会被访问一次)。

优化后的迭代实现代码

public Mesh[] SplitDisjointMeshes()
{
    var output = new List<Mesh>();
    var visited = new bool[_faces.Count];

    // 遍历所有面,避免重复处理半边
    for (int faceIdx = 0; faceIdx < _faces.Count; faceIdx++)
    {
        if (visited[faceIdx]) continue;

        var connectedFaces = new List<int>();
        // 手动维护栈,存储需要处理的半边索引
        var stack = new Stack<int>();

        // 找到当前面的任意一条半边作为起点
        int startHalfedge = _faces[faceIdx].FirstHalfedge;
        stack.Push(startHalfedge);
        visited[faceIdx] = true;
        connectedFaces.Add(faceIdx);

        while (stack.Count > 0)
        {
            int currentHe = stack.Pop();
            int heFirst = currentHe;
            int heCurrent = heFirst;

            do
            {
                // 遍历当前面的所有半边,找孪生半边对应的相邻面
                int twinHe = _halfedges[heCurrent].GetPairHalfedge();
                int adjacentFace = _halfedges[twinHe].AdjacentFace;

                if (adjacentFace != -1 && !visited[adjacentFace])
                {
                    visited[adjacentFace] = true;
                    connectedFaces.Add(adjacentFace);
                    // 将相邻面的起始半边压入栈,处理其连通区域
                    stack.Push(_faces[adjacentFace].FirstHalfedge);
                }

                heCurrent = _halfedges[heCurrent].NextHalfedge;
            } while (heCurrent != heFirst);
        }

        // 这里实现从connectedFaces构建新Mesh的逻辑
        // Mesh newMesh = BuildMeshFromFaces(connectedFaces);
        // output.Add(newMesh);
    }

    return output.ToArray();
}

关键优化点说明

  • 替换递归为手动栈:用Stack<int>存储待处理的半边索引,完全避开系统调用栈的深度限制
  • 遍历面而非半边:原代码遍历所有半边会导致大量重复检查,改为遍历面,每个连通分量只处理一次,效率更高
  • O(N)复杂度保证:每个面、半边仅被访问一次,时间复杂度与网格规模线性相关
  • 避免重复标记:访问面时立即标记为已访问,防止重复入栈处理

可选:BFS实现(队列替代栈)

如果担心栈的内存占用(极端情况下栈大小可能接近网格规模),可以改用队列实现BFS,逻辑几乎一致,只是把Stack换成Queue:

// 替换栈为队列
var queue = new Queue<int>();
queue.Enqueue(startHalfedge);
// ...
while (queue.Count > 0)
{
    int currentHe = queue.Dequeue();
    // 后续逻辑与DFS完全相同
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 13:25:18