【问题标题】:How to detect and save cyclic connectivity in edge vertices (hole detection)?如何检测和保存边缘顶点中的循环连通性(孔检测)?
【发布时间】:2017-08-06 21:22:37
【问题描述】:

感谢您花时间阅读我的问题。

我正在努力检测三角形网格中的孔并用新的三角形填充它们。我已经完成了一些部分,以获得边缘顶点列表等。以下是产生孔的顶点/边缘,请查看图像。

(9, 62)         => vertex # 9  and 62 makes an edge (left hole)
(66, 9)         => vertex # 66 and 9  makes an edge (left hole)
(70, 66)        => vertex # 70 and 66 makes an edge (left hole)
(62, 70)        => vertex # 62 and 70 makes an edge (left hole)

(147, 63)       => vertex # 147 and 63 makes an edge (right hole)
(55, 148)
(63, 149)
(149, 55)
(148, 147)

我需要做的第一件事是检查哪些顶点构成一个循环(意味着检测到一个洞),然后保存在一组单独的循环顶点中。

问题是编写这样一个算法来检查给定的图(顶点/边)是否包含多少个循环?然后保存到单独的集合中。

请给我写一些简单的优化算法来解决这个问题。

谢谢。

【问题讨论】:

    标签: algorithm data-structures graph-algorithm stl-algorithm


    【解决方案1】:
    1. 网格

      假设您的 STL 网格有n 三角形,您需要将其转换为索引格式。因此提取所有三角形点并转换为两个单独的表格。一个持有所有点,第二个持有每个三角形的 3 个点索引。假设你得到了m 点和n 三角形。

      您应该对点表(索引)进行排序并使用二分搜索将其从O(n.m) 加速到O(m.log(n))

    2. _edge 结构

      创建包含网格所有边缘的结构。比如:

      struct _edge
       {
       int p0,p1; // used vertexes index
       int cnt;   // count of edge usage
       };
      

      p0<p1.

    3. 创建_edge edge[]O(n)

      它应该是一个包含所有边的列表 (3n),因此遍历所有三角形并为每个三角形添加 3 条边。计数设置为cnt=1 这是O(n)

      现在按p0,p1 对列表进行排序,即O(n.log(n))。之后,只需将所有具有相同 p0,p1 的边相加,将它们的 cnt 相加并删除其中一个。如果编码正确,则为O(n)

    4. 检测漏洞

      在常规STL中,每条边都必须有cnt=2。如果cnt=1 那么三角形丢失了,你找到了你的洞。如果cnt>2 你的网格中有几何错误。

      所以从edge[] 表中删除所有带有cnt>=2 的边,即O(n)

    5. 检测循环

      假设我们在edge[] 表中留下了k 边。现在对于共享一个点的每 2 条边创建三角形。比如:

      for (i=0;i<k;i++)
       for (j=i+1;j<k;j++)
        {
        if ((edge[i].p0==edge[j].p0)||(edge[i].p1==edge[j].p0)) add_triangle(edge[i].p0,edge[i].p1,edge[j].p1);
        if ((edge[i].p0==edge[j].p1)||(edge[i].p1==edge[j].p1)) add_triangle(edge[i].p0,edge[i].p1,edge[j].p0);
        }
      

      如果您对内部循环使用二分搜索,那么这将是O(k.log(k))。此外,您应该避免添加重复的三角形并更正它们的缠绕,因此首先将三角形添加到单独的表中(或记住起始索引),然后删除重复项(或者您可以直接在 add_triangle 中执行此操作)。

      还要处理更大的孔,不要忘记在edge[] 表中添加新边。您可以在处理当前边缘后更新边缘并重复 #4 或在运行时合并更改。

    [Edit1] C++ 示例

    最近我正在为这个 QA 编写 STL 代码:

    因此,当我已经完成了所有基础架构的编码后,我选择试一试,结果如下:

    struct STL3D_edge
        {
        int p0,p1,cnt,dir;
        STL3D_edge()    {}
        STL3D_edge(STL3D_edge& a) { *this=a; }
        ~STL3D_edge()   {}
        STL3D_edge* operator = (const STL3D_edge *a) { *this=*a; return this; }
        //STL3D_edge* operator = (const STL3D_edge &a) { ...copy... return this; }
        int operator == (const STL3D_edge &a) { return ((p0==a.p0)&&(p1==a.p1)); }
        int operator != (const STL3D_edge &a) { return ((p0!=a.p0)||(p1!=a.p1)); }
        void ld(int a,int b) { cnt=1; if (a<=b) { dir=0; p0=a; p1=b; } else { dir=1; p0=b; p1=a; }}
        };
    List<STL3D_edge> edge;
    List<float> pnt;
    void edge_draw()
        {
        int i; STL3D_edge *e;
        glBegin(GL_LINES);
        for (e=edge.dat,i=0;i<edge.num;i++,e++)
            {
            glVertex3fv(pnt.dat+e->p0);
            glVertex3fv(pnt.dat+e->p1);
            }
        glEnd();
        }
    void STL3D::holes()
        {
        //  https://stackoverflow.com/a/45541861/2521214
        int i,j,i0,i1,i2,j0,j1,j2;
        float q[3];
        _fac        *f,ff;
        STL3D_edge  *e,ee,*e0,*e1,*e2;
        ff.attr=31<<5;                          // patched triangles color/id
    
        // create some holes for testing
        if (fac.num<100) return;
        for (i=0;i<10;i++) fac.del(Random(fac.num));
    
        // compute edge table
        edge.allocate(fac.num*3); edge.num=0;
        for (f=fac.dat,i=0;i<fac.num;i++,f++)
            {
            // add/find points to/in pnt[]
            for (i0=-1,j=0;j<pnt.num;j+=3){ vectorf_sub(q,pnt.dat+j,f->p[0]); if (vectorf_len2(q)<1e-6) { i0=j; break; }} if (i0<0) { i0=pnt.num; for (j=0;j<3;j++) pnt.add(f->p[0][j]); }
            for (i1=-1,j=0;j<pnt.num;j+=3){ vectorf_sub(q,pnt.dat+j,f->p[1]); if (vectorf_len2(q)<1e-6) { i1=j; break; }} if (i1<0) { i1=pnt.num; for (j=0;j<3;j++) pnt.add(f->p[1][j]); }
            for (i2=-1,j=0;j<pnt.num;j+=3){ vectorf_sub(q,pnt.dat+j,f->p[2]); if (vectorf_len2(q)<1e-6) { i2=j; break; }} if (i2<0) { i2=pnt.num; for (j=0;j<3;j++) pnt.add(f->p[2][j]); }
            // add edges
            ee.ld(i0,i1); for (e=edge.dat,j=0;j<edge.num;j++,e++) if (*e==ee) { e->cnt++; j=-1; break; } if (j>=0) edge.add(ee);
            ee.ld(i1,i2); for (e=edge.dat,j=0;j<edge.num;j++,e++) if (*e==ee) { e->cnt++; j=-1; break; } if (j>=0) edge.add(ee);
            ee.ld(i2,i0); for (e=edge.dat,j=0;j<edge.num;j++,e++) if (*e==ee) { e->cnt++; j=-1; break; } if (j>=0) edge.add(ee);
            }
    
        // delete even times used edges (to speed up the loops finding)
        for (i0=i1=0,e0=e1=edge.dat;i0<edge.num;i0++,e0++)
         if (int(e0->cnt&1)==1) { *e1=*e0; i1++; e1++; } edge.num=i1;
        // find 2 edges with one comon point (j1)
        for (e0=edge.dat,i0=0;i0<edge.num;i0++,e0++) if (int(e0->cnt&1)==1)
         for (e1=e0+1,i1=i0+1;i1<edge.num;i1++,e1++) if (int(e1->cnt&1)==1)
            {
            // decide which points to use
            j0=-1; j1=-1; j2=-1;
            if (e0->p0==e1->p0) { j0=e0->p1; j1=e0->p0; j2=e1->p1; }
            if (e0->p0==e1->p1) { j0=e0->p1; j1=e0->p0; j2=e1->p0; }
            if (e0->p1==e1->p0) { j0=e0->p0; j1=e0->p1; j2=e1->p1; }
            if (e0->p1==e1->p1) { j0=e0->p0; j1=e0->p1; j2=e1->p0; }
            if (j2<0) continue;
            // add missin triangle
            if (e0->dir)
                {
                vectorf_copy(ff.p[0],pnt.dat+j1);
                vectorf_copy(ff.p[1],pnt.dat+j0);
                vectorf_copy(ff.p[2],pnt.dat+j2);
                }
            else{
                vectorf_copy(ff.p[0],pnt.dat+j0);
                vectorf_copy(ff.p[1],pnt.dat+j1);
                vectorf_copy(ff.p[2],pnt.dat+j2);
                }
            ff.compute();
            fac.add(ff);
            // update edges
            e0->cnt++;
            e1->cnt++;
            ee.ld(j0,j2); for (e=edge.dat,j=0;j<edge.num;j++,e++) if (*e==ee) { e->cnt++; j=-1; break; } if (j>=0) edge.add(ee);
            break;
            }
        }
    

    STL3D 类的完整 C++ 代码和描述在上面的链接中。我使用了我在存档中找到的一些球形 STL 网格,并将孔修补三角形涂成绿色以识别它们。结果如下:

    黑线是线框,红线只是edge[],pnt[] 数组的调试绘制用于调试...

    如您所见,它甚至适用于比单个三角形更大的孔:) ...

    【讨论】:

    • 谢谢,Spektre,根据您的建议,编码很复杂。你有某种示例或伪代码吗?
    • @furqan 不,但是当我有时间的时候,我可以在 C++ 中破坏一些东西......你有任何带有测试孔的 STL 吗? (无论如何都需要将此编码为我为朋友制作的用于 3D 打印的应用程序的一部分)
    • 是的,为什么不呢,在网格上打一个洞真的很简单,你可以使用我的软件 Real3d Renderer (real3d.pk/softwares.html)。转到菜单->编辑->选择和裁剪,然后按开始。使用 Ctrl+鼠标左键,选择三角形并删除它们。
    • 您可以创建一个简单的球体并删除一些三角形。顺便说一句,我也尝试在应用程序中实现孔填充算法,但它仅适用于 10,000 个多边形。它不适用于超过 10,000 个多边形。实现的算法来自 "ennetws" 的回答 (stackoverflow.com/questions/12367801/…)。
    • @furqan 我添加了带有完整代码的edit1 ....这粗略的工作仅适用于没有错误的有效STL文件...我敢打赌,您的10000+多边形网格中有一些错误,或者您在某处遇到堆栈/堆问题...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 2016-09-12
    • 2014-03-30
    • 1970-01-01
    • 2019-06-21
    相关资源
    最近更新 更多