• 计算点、线、面等元素之间的交点、交线、封闭区域面积和闭合集(续9)


     

    介绍完平面上凸包各种求取方法后,讲解三维顶点集合凸包的方法。

    过程:

    3DConvexHull( int argc, char *argv[] )

    {  
        ReadVertices();                      //
    读取所有顶点

        DoubleTriangle();                    //构造初始三角形

        ConstructHull();                     //构建三维顶点集合的凸包

    }

        
        
    //首先找到3个不共线的顶点,以相反的顺序建成三角形的两个面。接着找到不在三角面上的第四个顶点。在三角面上以逆时针顺序存储顶点,建立到新顶点的3个面。

    void    DoubleTriangle( void )

    {

        tVertex v0, v1, v2, v3, t;

        tFace    f0, f1 = NULL;

        tEdge    e0, e1, e2, s;

        int      vol;

        v0 = vertices;

        while ( Collinear( v0, v0->next, v0->next->next ) )//比较连续存储的3个顶点

            if ( ( v0 = v0->next ) == vertices )     //提取下一个顶点

                printf("所有顶点共线");

        v1 = v0->next;       //找到不共线的顶点

        v2 = v1->next;       //找到不共线的顶点

        v0->mark = PROCESSED;    //标记顶点处理过

        v1->mark = PROCESSED;    //标记顶点处理过

        v2->mark = PROCESSED;    //标记顶点处理过

        f0 = MakeFace( v0, v1, v2, f1 );

        f1 = MakeFace( v2, v1, v0, f0 );//以相反的顶点顺序构建第二个面

        f0->edge[0]->adjface[1] = f1;

        f0->edge[1]->adjface[1] = f1;

        f0->edge[2]->adjface[1] = f1;        //指出面的邻接面

        f1->edge[0]->adjface[1] = f0;

        f1->edge[1]->adjface[1] = f0;

        f1->edge[2]->adjface[1] = f0;        //指出面的邻接面

        //准备找到不在面上的第四个顶点来构建体

        v3 = v2->next;

        vol = VolumeSign( f0, v3 );      //通过计算体积来判断是否第四个顶点不在面上

        while ( !vol )   {               //当体积为0的话,继续循环

            if ( ( v3 = v3->next ) == v0 )

                printf("所有顶点是共面的");

            vol = VolumeSign( f0, v3 );

        }

        vertices = v3;                   //记录V3为现在被增加的顶点

    }

    //每次考察一个顶点,如果构成凸包中新顶点,则新建面和边,如处在现阶段的凸包之内,则不作其他处理;

    void    ConstructHull( void )

    {

        tVertex v, vnext;

        int         vol;

        bool        changed;

        v = vertices;

        do {

            vnext = v->next;     //取下一个顶点来处理

            if ( !v->mark ) {    //如果此顶点的标记为“还从未处理过”

                v->mark = PROCESSED;//将此顶点标记为“已经被处理过”

                changed = AddOne( v );//将新顶点加入,处在现凸包内,或构成新凸包的一个顶点

                CleanUp( &vnext );

            }

            v = vnext;

        } while ( v != vertices );

    }

    通过体积来计算此顶点与其他面的关系,若构成的体都为凹,则不予考虑。否则,可说此顶点位于某些面之外。若从此顶点能观察到某边的两个邻接面,则此边将被删除。若从此顶点只能观察到一个面,则将创建新面。

    bool    AddOne( tVertex p )

    {

        tFace f;

        tEdge e, temp;

        int       vol;

        bool     vis = FALSE;

        f = faces;

        do {

            vol = VolumeSign( f, p );        //计算每个面和新增加的顶点构成的锥体的体积

       

            if ( vol < 0 ) {

                f->visible = VISIBLE;  //此面被标记为可见

                vis = TRUE;              //标识顶点已处于面之外

            }

            f = f->next;             //取得下一个面

        } while ( f != faces );      //对所有面循环处理

       

        if ( !vis ) {                //没有一个面可见,则顶点位于体内,不将作为凸包顶点

            p->onhull = !ONHULL;    //顶点作标记

            return FALSE;

        }

        e = edges;

        do {

            temp = e->next;          //取下一条边

            if ( e->adjface[0]->visible && e->adjface[1]->visible )

                e->delete = REMOVED;//如果这条边的两个邻接面都可见,则标记为删除此条边

            else if ( e->adjface[0]->visible || e->adjface[1]->visible )

                e->newface = MakeConeFace( e, p ); //构建新面

            e = temp;

        } while ( e != edges );      //所有边循环处理

        return TRUE;

    }

    //计算体积,此结果的正负可判断顶点与面的“可见”关系。

    int VolumeSign( tFace f, tVertex p )

    {

        double vol;

        int     voli;

        double ax, ay, az, bx, by, bz, cx, cy, cz;

        ax = f->vertex[0]->v[X] - p->v[X];

        ay = f->vertex[0]->v[Y] - p->v[Y];

        az = f->vertex[0]->v[Z] - p->v[Z];       //顶点坐标相减构成向量

        bx = f->vertex[1]->v[X] - p->v[X];

        by = f->vertex[1]->v[Y] - p->v[Y];

        bz = f->vertex[1]->v[Z] - p->v[Z];

        cx = f->vertex[2]->v[X] - p->v[X];

        cy = f->vertex[2]->v[Y] - p->v[Y];

        cz = f->vertex[2]->v[Z] - p->v[Z];

        vol =   ax * (by*cz - bz*cy)

            + ay * (bz*cx - bx*cz)

            + az * (bx*cy - by*cx);

           //体积计算是通过法向量与一条边向量的点乘,因为点乘结果表示两个模与向量夹角的余弦,其中法向量的模是三角面上两条边的模乘上夹角的正弦,即为面积。而剩下的那个模乘上夹角的余弦,即为高。

        return vol;

    }

                      
        当一条边的一个邻接面对新加入的顶点是“可见”的时候,构建一个新面和两条新边

    tFace   MakeConeFace( tEdge e, tVertex p )

    {

        tEdge new_edge[2];

        tFace new_face;

        int       i, j;

        //准备构建两条新边

        for ( i=0; i < 2; ++i )

            //如果新边已经被创建过,直接拷贝过来即可

            if ( !( new_edge[i] = e->endpts[i]->duplicate) ) {

                //如果新边没有被创建过,则构建

                new_edge[i] = MakeNullEdge();

                new_edge[i]->endpts[0] = e->endpts[i];

                new_edge[i]->endpts[1] = p;

                e->endpts[i]->duplicate = new_edge[i];// 顶点标记这条新边

            }

            //创建新的面

            new_face = MakeNullFace();  

            new_face->edge[0] = e;           //构成面的原来那条边

            new_face->edge[1] = new_edge[0];//构成面的第一条新边

            new_face->edge[2] = new_edge[1]; //构成面的第二条新边

            MakeCcw( new_face, e, p );       //以逆时针方向建立面上的顶点顺序

            //将新建立的边和面联系起来

            for ( i=0; i < 2; ++i )

                for ( j=0; j < 2; ++j

                         if ( !new_edge[i]->adjface[j] ) {

                         new_edge[i]->adjface[j] = new_face;

                         break;      //新边和新面一旦联系,就跳出循环

                     }

                     return new_face;//处理结束,返回新建立的面

    }

  • 相关阅读:
    适用于实数范围的中缀表达式的 +
    Django官方文档学习2——数据库及模板
    github命令
    千行代码入门Python
    Notepad++配置Python运行环境
    Python常用网页字符串处理技巧
    requests设置headers,proxies,cookies
    Django官方文档学习1——第一个helloworld页面
    笔记本键盘上没有break键的解决方案
    Python beautifulsoup模块
  • 原文地址:https://www.cnblogs.com/wuhanhoutao/p/1119238.html
Copyright © 2020-2023  润新知