如何检测和保存边缘顶点中的循环连通性(空洞检测)?

时间:2017-08-06 21:22:37

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

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

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

(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)

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

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

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

谢谢。

enter image description here

1 个答案:

答案 0 :(得分: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)。如果编码正确则为cnt=2

  4. 检测漏洞

    在常规 STL 中,每个边必须有cnt=1。如果cnt>2则缺少三角形并找到了您的洞。如果cnt>=2您的网格中出现几何误差。

    因此请从edge[]O(n)删除k的所有边缘。

  5. 检测循环

    假设我们的edge[]表格中有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); } 个边缘。现在,对于共享点的每2条边创建三角形。类似的东西:

    O(k.log(k))

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

    另外,为了处理更大的孔,请不要忘记在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; } } 表中添加新边。您可以在处理当前边缘后更新边缘并重复#4 或在运行时合并更改。

  6. [Edit1] C ++示例

    最近我为这个QA做了一些STL编码:

    因此,当我获得所有已经编码的基础设施时,我选择了这一点,结果是:

    STL3D

    edge[],pnt[]类的完整C ++代码和说明位于上面的链接中。我在我的档案中使用了一些球体 STL 网格,并将绿色的孔修补三角形颜色识别出来。结果如下:

    holes

    黑线是线框,红线只是HttpEntity数组的调试图,用于调试......

    正如你所看到的,它甚至适用于比单个三角形更大的孔:) ...