展开3d凸多边形

时间:2017-04-28 19:36:35

标签: math 3d geometry

我有一个由点和三角形组成的凸多面体(三角形的法线在多边形外面):

enter image description here

输入结构是:

class Vector {
  float X, Y, Z;
};
class Triangle {
  int pointIndexes[3];
  Vector normal;
};
class Polyhedron{
  vector<Vector> points;
  vector<Triangle> triangles;
};

我想通过沿着一定距离的法线移动三角形来扩展多边形。 如何计算具有良好性能的移动点的新3D坐标?

=&GT;我在O(n ^ 3)中有一个实现:我沿着它们的法线移动所有平面(三角形)然后,我通过测试所有平面交叉点找到所有点(3个不平行的平面给出交叉点)。这个解决方案有效,但效果非常差。

=&GT;我也尝试将点顺序移动到他所附三角形的所有法线上,但它没有给出正确的结果。

1 个答案:

答案 0 :(得分:1)

我很好奇,所以我尝试在 C ++ 中对此进行编码,这里有一些见解:

  1. <强>结构

    struct _pnt
        {
        int i0,i1,i2;   // neighbor triangles (non parallel)
        double pos[3];  // position
        _pnt()      {}
        _pnt(_pnt& a)   { *this=a; }
        ~_pnt() {}
        _pnt* operator = (const _pnt *a) { *this=*a; return this; }
        //_pnt* operator = (const _pnt &a) { ...copy... return this; }
        };
    
    struct _fac
        {
        int i0,i1,i2;       // triangle
        double nor[3];      // normal
        double mid[3],d;    // mid point x,y,z and normal d
        _fac()      {}
        _fac(_fac& a)   { *this=a; }
        ~_fac() {}
        _fac* operator = (const _fac *a) { *this=*a; return this; }
        //_fac* operator = (const _fac &a) { ...copy... return this; }
        };
    

    因此,我将3个相邻非平行三角形的索引i0,i1,i2添加到每个点。我还添加了每个三角形的mid点和d正常偏移以加速计算,但两者都可以在需要时计算,因此您不需要将它们添加到网格本身。

    < / LI>
  2. <强>预先计算

    因此,您需要为nor,d,mid假设O(n)三角形和n点的每个面预先计算m。并且每个点的邻接索引在O(m)中计算,因此整个事件是O(n+m)。容易计算邻接,首先清除所有点的i0,i1,i2。然后循环遍历所有面,如果少于3个法线并且没有法线平行,则每个面都为其每个点添加索引。

  3. <强>偏移

    现在只需通过midnormal*offset_step重新计算所有面的d来完成偏移。之后,您遍历所有点并计算您获得索引的3个平面的交集。所以这也是O(n+m)

    我懒得导出交叉方程,所以我使用了3x3逆矩阵。因为我的矩阵是4x4,所以最后一行和列未使用。请注意我的矩阵是OpenGL,所以它们被转置...这就是为什么法线被装入如此奇怪的原因。

  4. 这里是我的C ++来源:

    //---------------------------------------------------------------------------
    struct _pnt
        {
        int i0,i1,i2;   // neighbor triangles (non parallel)
        double pos[3];  // position
        _pnt()      {}
        _pnt(_pnt& a)   { *this=a; }
        ~_pnt() {}
        _pnt* operator = (const _pnt *a) { *this=*a; return this; }
        //_pnt* operator = (const _pnt &a) { ...copy... return this; }
        };
    //---------------------------------------------------------------------------
    struct _fac
        {
        int i0,i1,i2;       // triangle
        double nor[3];      // normal
        double mid[3],d;    // mid point x,y,z and normal d
        _fac()      {}
        _fac(_fac& a)   { *this=a; }
        ~_fac() {}
        _fac* operator = (const _fac *a) { *this=*a; return this; }
        //_fac* operator = (const _fac &a) { ...copy... return this; }
        };
    //---------------------------------------------------------------------------
    class mesh
        {
    public:
        List<_pnt> pnt;
        List<_fac> fac;
    
        mesh()      {}
        mesh(mesh& a)   { *this=a; }
        ~mesh() {}
        mesh* operator = (const mesh *a) { *this=*a; return this; }
        //mesh* operator = (const mesh &a) { ...copy... return this; }
    
        void icosahedron(double r)  // init mesh with icosahedron of radius r
            {
            // points
            double a=r*0.525731112119133606;
            double b=r*0.850650808352039932;
            _pnt p; p.i0=-1; p.i1=-1; p.i2=-1; pnt.num=0;
            vector_ld(p.pos,-a,0.0, b); pnt.add(p);
            vector_ld(p.pos, a,0.0, b); pnt.add(p);
            vector_ld(p.pos,-a,0.0,-b); pnt.add(p);
            vector_ld(p.pos, a,0.0,-b); pnt.add(p);
            vector_ld(p.pos,0.0, b, a); pnt.add(p);
            vector_ld(p.pos,0.0, b,-a); pnt.add(p);
            vector_ld(p.pos,0.0,-b, a); pnt.add(p);
            vector_ld(p.pos,0.0,-b,-a); pnt.add(p);
            vector_ld(p.pos, b, a,0.0); pnt.add(p);
            vector_ld(p.pos,-b, a,0.0); pnt.add(p);
            vector_ld(p.pos, b,-a,0.0); pnt.add(p);
            vector_ld(p.pos,-b,-a,0.0); pnt.add(p);
            // triangles
            _fac f; fac.num=0; vector_ld(f.nor,0.0,0.0,0.0);
            f.i0= 0; f.i1= 4; f.i2= 1; fac.add(f);
            f.i0= 0; f.i1= 9; f.i2= 4; fac.add(f);
            f.i0= 9; f.i1= 5; f.i2= 4; fac.add(f);
            f.i0= 4; f.i1= 5; f.i2= 8; fac.add(f);
            f.i0= 4; f.i1= 8; f.i2= 1; fac.add(f);
            f.i0= 8; f.i1=10; f.i2= 1; fac.add(f);
            f.i0= 8; f.i1= 3; f.i2=10; fac.add(f);
            f.i0= 5; f.i1= 3; f.i2= 8; fac.add(f);
            f.i0= 5; f.i1= 2; f.i2= 3; fac.add(f);
            f.i0= 2; f.i1= 7; f.i2= 3; fac.add(f);
            f.i0= 7; f.i1=10; f.i2= 3; fac.add(f);
            f.i0= 7; f.i1= 6; f.i2=10; fac.add(f);
            f.i0= 7; f.i1=11; f.i2= 6; fac.add(f);
            f.i0=11; f.i1= 0; f.i2= 6; fac.add(f);
            f.i0= 0; f.i1= 1; f.i2= 6; fac.add(f);
            f.i0= 6; f.i1= 1; f.i2=10; fac.add(f);
            f.i0= 9; f.i1= 0; f.i2=11; fac.add(f);
            f.i0= 9; f.i1=11; f.i2= 2; fac.add(f);
            f.i0= 9; f.i1= 2; f.i2= 5; fac.add(f);
            f.i0= 7; f.i1= 2; f.i2=11; fac.add(f);
            // precompute stuff
            compute();
            }
        void compute()  // compute normals and neighbor triangles info
            {
            int i,j,k;
            _fac *f,*ff;
            _pnt *p;
            double a[3],b[3];
            const double nor_dot=0.001;     // min non parallel dor product of normals
            // normals
            for (f=fac.dat,i=0;i<fac.num;i++,f++)
                {
                vector_sub(a,pnt.dat[f->i1].pos,pnt.dat[f->i0].pos);
                vector_sub(b,pnt.dat[f->i2].pos,pnt.dat[f->i0].pos);
                vector_mul(a,b,a);
                vector_one(f->nor,a);
                }
            // mid points
            for (f=fac.dat,i=0;i<fac.num;i++,f++)
                {
                // mid point of triangle as start point
                vector_copy(a,  pnt.dat[f->i0].pos);
                vector_add (a,a,pnt.dat[f->i1].pos);
                vector_add (a,a,pnt.dat[f->i2].pos);
                vector_mul (f->mid,a,0.33333333333);
                f->d=vector_mul(f->mid,f->nor);
                }
            // neighbors
            for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
                {
                p->i0=-1;
                p->i1=-1;
                p->i2=-1;
                }
            for (f=fac.dat,i=0;i<fac.num;i++,f++)
                {
                for (p=pnt.dat+f->i0;p->i2<0;)
                    {
                    if (p->i0>=0) { ff=fac.dat+p->i0; if (fabs(vector_mul(f->nor,ff->nor))<=nor_dot) break; } else { p->i0=i; break; }
                    if (p->i1>=0) { ff=fac.dat+p->i1; if (fabs(vector_mul(f->nor,ff->nor))<=nor_dot) break; } else { p->i1=i; break; }
                                                                                                                     p->i2=i; break;
                    }
                for (p=pnt.dat+f->i1;p->i2<0;)
                    {
                    if (p->i0>=0) { ff=fac.dat+p->i0; if (fabs(vector_mul(f->nor,ff->nor))<=nor_dot) break; } else { p->i0=i; break; }
                    if (p->i1>=0) { ff=fac.dat+p->i1; if (fabs(vector_mul(f->nor,ff->nor))<=nor_dot) break; } else { p->i1=i; break; }
                                                                                                                     p->i2=i; break;
                    }
                for (p=pnt.dat+f->i2;p->i2<0;)
                    {
                    if (p->i0>=0) { ff=fac.dat+p->i0; if (fabs(vector_mul(f->nor,ff->nor))<=nor_dot) break; } else { p->i0=i; break; }
                    if (p->i1>=0) { ff=fac.dat+p->i1; if (fabs(vector_mul(f->nor,ff->nor))<=nor_dot) break; } else { p->i1=i; break; }
                                                                                                                     p->i2=i; break;
                    }
                }
            }
        void draw() // render mesh
            {
            int i;
            _fac *f;
            glBegin(GL_TRIANGLES);
            for (f=fac.dat,i=0;i<fac.num;i++,f++)
                {
                glNormal3dv(f->nor);
                glVertex3dv(pnt.dat[f->i0].pos);
                glVertex3dv(pnt.dat[f->i1].pos);
                glVertex3dv(pnt.dat[f->i2].pos);
                }
            glEnd();
            }
        void draw_normals(double r) // render mesh normals as line of size r
            {
            int i;
            double a[3];
            _fac *f;
            for (f=fac.dat,i=0;i<fac.num;i++,f++)
                {
                // normal endpoints
                vector_mul (a,r,f->nor);
                vector_add (a,a,f->mid);
                glBegin(GL_LINES);
                glVertex3dv(f->mid);
                glVertex3dv(a);
                glEnd();
                // wireframe
                glBegin(GL_LINE_LOOP);
                glVertex3dv(pnt.dat[f->i0].pos);
                glVertex3dv(pnt.dat[f->i1].pos);
                glVertex3dv(pnt.dat[f->i2].pos);
                glEnd();
                }
            }
        void offset(double d)   // offset triangles by d in normal direction
            {
            int i,j;
            _fac *f;
            _pnt *p;
            double a[3],m[16];
            // translate mid points
            for (f=fac.dat,i=0;i<fac.num;i++,f++)
                {
                vector_mul(a,d,f->nor);
                vector_add(f->mid,f->mid,a);
                f->d=vector_mul(f->mid,f->nor);
                }
            // recompute points as intersection of 3 planes
            for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
             if (p->i2>=0)
                {
                matrix_one(m);
                for (f=fac.dat+p->i0,j=0;j<3;j++) m[0+(j<<2)]=f->nor[j]; a[0]=f->d;
                for (f=fac.dat+p->i1,j=0;j<3;j++) m[1+(j<<2)]=f->nor[j]; a[1]=f->d;
                for (f=fac.dat+p->i2,j=0;j<3;j++) m[2+(j<<2)]=f->nor[j]; a[2]=f->d;
                matrix_inv(m,m);
                matrix_mul_vector(p->pos,m,a);
                }
            }
        };
    //---------------------------------------------------------------------------
    

    这里预览(左边是源,然后几次应用偏移)

    preview

    也适用于消极步骤。这个问题看起来像这样:

    // globals
    mesh obj;
    // init
    obj.icosahedron(0.5);
    // render   
    glFrontFace(GL_CW); // for gluPerspective
    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);
    glColor3f(0.5,0.5,0.5);
    obj.draw();
    glDisable(GL_LIGHTING);
    glColor3f(0.0,0.9,0.9);
    glLineWidth(2.0);
    obj.draw_normals(0.2);
    glLineWidth(1.0);
    // on mouse wheel
    if (WheelDelta>0) obj.offset(+0.05);
    if (WheelDelta<0) obj.offset(-0.05);
    

    我也使用我的动态列表模板:


    List<double> xxx;double xxx[];相同
    xxx.add(5);5添加到列表的末尾
    xxx[7]访问数组元素(安全)
    xxx.dat[7]访问数组元素(不安全但快速直接访问)
    xxx.num是数组的实际使用大小
    xxx.reset()清除数组并设置xxx.num=0
    xxx.allocate(100)100项目预分配空间

    矢量和矩阵数学源可以在这里找到: