zoukankan      html  css  js  c++  java
  • 使用Graham扫描法求二维凸包的一个程序

    1 #include <iostream>
    2 #include <cstring>
    3 #include <cstdlib>
    4 #include <cmath>
    5 #include <cstdio>
    6 using namespace std;
    includeall.h
      1 #include "includeall.h"
      2 typedef struct node{//一维链表使用的
      3     double x, y, z;
      4     struct node * next;
      5 }Node;
      6 
      7 class Link{//有头节点的链表类
      8 private:
      9 
     10 public:
     11     Node * data=NULL;
     12 private:
     13     void init()//初始化链表
     14     {
     15         this->data=NULL;
     16         this->data=new Node;
     17         this->data->x=this->data->y=this->data->z=0.0;
     18         this->data->next=NULL;
     19     }
     20     
     21     void destroy()//清除链表占用的空间,包括头节点,所以之后链表不能再继续使用
     22     {
     23         this->clear();
     24         delete this->data;
     25         this->data=NULL;
     26     }
     27     
     28     void check()//检验链表是否有效
     29     {
     30         if(this->data==NULL)
     31         {
     32             cout<<"链表未正确初始化或已经被销毁"<<endl;
     33             exit(0);
     34         }
     35     }
     36     
     37 public:
     38 
     39     void add(double x,double y,double z)//增加一个节点
     40     {
     41         this->check();
     42         Node * tmp=new Node;
     43         tmp->x=x;tmp->y=y;tmp->z=z;
     44         tmp->next=this->data->next;
     45         this->data->next=tmp;
     46     }
     47     
     48     void del(Node * prev)//删除一个节点
     49     {
     50         this->check();
     51         Node * tmp=prev->next;
     52         prev->next=prev->next->next;
     53         delete tmp;
     54     }
     55     
     56     void insert(Node * prev,double x,double y,double z)//在某个元素的后面插入一个节点
     57     {
     58         this->check();
     59         Node * tmp=new Node;
     60         tmp->x=x;tmp->y=y;tmp->z=z;
     61         tmp->next=prev->next;
     62         prev->next=tmp;
     63     }
     64     
     65     int count()//统计链表中节点的数目
     66     {
     67         this->check();
     68         int i=0;
     69         Node * tmp=this->data->next;;
     70         while(tmp!=NULL)
     71         {
     72             tmp=tmp->next;
     73             ++i;
     74         }
     75         return i;
     76     }
     77     
     78     Node * pointOfSpecificElement(int n)//获取指向特定第几个元素的指针
     79     {
     80         Node * tmp=this->data;
     81         while(tmp!=NULL)
     82         {
     83             if(n<=0) break;
     84             --n;
     85             tmp=tmp->next;
     86         }
     87         return tmp;
     88     }
     89     
     90     void clear()//清空链表中的所有元素,不包括头节点
     91     {
     92         this->check();
     93         while(!this->empty())
     94         {
     95             this->del(this->data);
     96         }
     97     }
     98     
     99     void copy(Link * link,int n=1)//将参数链表中从第n个开始的元素拷贝增加到this链表,头节点作为第0个
    100     {
    101         Node * tmp=link->pointOfSpecificElement(n);
    102         while(tmp!=NULL)
    103         {
    104             this->add(tmp->x,tmp->y,tmp->z);
    105             tmp=tmp->next;
    106         }
    107     }
    108     
    109     
    110     bool empty()//链表是否为空
    111     {
    112         this->check();
    113         return (this->data->next==NULL);
    114     }
    115     
    116     Link()
    117     {
    118         this->init();
    119     }
    120     virtual ~Link()
    121     {
    122         this->destroy();
    123     }
    124 };
    Link.class.h
      1 int RightOrLeft(double x1,double y1,double x2,double y2,double x3,double y3)//判断第三个点在前两个点连成的直线的哪个位置,-1 左边,0,直线上,1 右边
      2 {
      3     int result;
      4     if(x1-x2<0.000001 && x1-x2>-0.000001 && y1-y2<0.000001 && y1-y2>-0.000001)
      5     {
      6         return 1;
      7     }
      8     double X=(y3-y1)*(x2-x1)/(y2-y1)+x1;
      9 
     10     if(X-x3<-0.000001)
     11     {
     12         result=1;
     13     }
     14     else if(X-x3>0.000001)
     15     {
     16         result=-1;
     17     }
     18     else{
     19         result=0;
     20     }
     21     if(y2-y1<-0.000001)
     22     {
     23         result=-result;
     24     }
     25     return result;
     26 }
     27 
     28 
     29 /*
     30 *该函数对link的所有点进行二维凸包运算
     31 *link中的每个节点保存了一个点的三维坐标,二维凸包运输只会选取其中的两个坐标进行运算
     32 *具体选取哪两个坐标由该函数的type参数决定
     33 *type的合法取值为 3 5 6,取三代表选取yz坐标(3的二进制为011),取5代表选取xz坐标,取6代表选取xy坐标
     34 *执行完凸包运算之后,link中的节点将被修改,所以如有必要,应该手动在调用该函数之前备份参数链表
     35 *运算执行完之后,link的节点的z坐标将被清零,xy坐标将依据type的取值,对应于原始链表的某两个坐标
     36 *
     37 */
     38 void convelHull(Link * link,int type=5)//对link中的元素进行二维凸包运算,执行后link中只有x,y值有效
     39 {
     40     if(link->count()<1)
     41     {
     42         printf("Warning: no node in link
    ");
     43         return;
     44         //exit(-1);
     45     }
     46     Link * link_tmp=new Link();
     47     {//对利用link的某两个坐标构造link_tmp
     48         Node * tmp=link->pointOfSpecificElement(1);
     49         if(type==3)
     50         {
     51             while(tmp!=NULL)
     52             {
     53                 //此处将link的三维坐标系的某两个映射到函数内使用的link_tmp中,之后的操作将对link_tmp的x,y成员进行操作
     54                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     55                 //可以在此处修改需要对原始的link链表中的哪两个坐标进行凸包运算
     56                 link_tmp->add(tmp->y,tmp->z,0.0);
     57                 //进行凸包运算之后,link的z坐标将被清零,例如此处,link的xy坐标分别对应原始的xz坐标
     58                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     59                 tmp=tmp->next;
     60             }
     61         }
     62         else if(type==5)
     63         {
     64             while(tmp!=NULL)
     65             {
     66                 //此处将link的三维坐标系的某两个映射到函数内使用的link_tmp中,之后的操作将对link_tmp的x,y成员进行操作
     67                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     68                 //可以在此处修改需要对原始的link链表中的哪两个坐标进行凸包运算
     69                 link_tmp->add(tmp->x,tmp->z,0.0);
     70                 //进行凸包运算之后,link的z坐标将被清零,例如此处,link的xy坐标分别对应原始的xz坐标
     71                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     72                 tmp=tmp->next;
     73             }
     74         }
     75         else if(type==6)
     76         {
     77             while(tmp!=NULL)
     78             {
     79                 //此处将link的三维坐标系的某两个映射到函数内使用的link_tmp中,之后的操作将对link_tmp的x,y成员进行操作
     80                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     81                 //可以在此处修改需要对原始的link链表中的哪两个坐标进行凸包运算
     82                 link_tmp->add(tmp->x,tmp->y,0.0);
     83                 //进行凸包运算之后,link的z坐标将被清零,例如此处,link的xy坐标分别对应原始的xz坐标
     84                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     85                 tmp=tmp->next;
     86             }
     87         }
     88         else{
     89             printf("参数不符合规范
    ");
     90             exit(-1);
     91         }
     92     }//link_tmp已经构造完成
     93     {//将link_tmp中y坐标最小的点放到链表的第一个位置
     94         Node * tmp=link_tmp->pointOfSpecificElement(0);
     95         Node * tmp2=tmp;
     96         while(tmp!=NULL && tmp->next!=NULL)
     97         {
     98             if(tmp->next->y<tmp2->next->y)
     99             {
    100                 tmp2=tmp;
    101             }
    102             tmp=tmp->next;
    103         }//tmp2指向y坐标最小的节点的前一个节点
    104         link_tmp->add(tmp2->next->x,tmp2->next->y,tmp2->next->z);
    105         link_tmp->del(tmp2);
    106     }//y坐标最小的元素已经是链表的第一个节点
    107     Node trans;//保存此时平移的距离,以便于最后把它们平移回来
    108     {//所有点平移,使link_tmp第一个节点的坐标是坐标原点
    109         trans.x=link_tmp->pointOfSpecificElement(1)->x;
    110         trans.y=link_tmp->pointOfSpecificElement(1)->y;
    111         //trans.z=link_tmp->pointOfSpecificElement(1)->z;
    112         Node * tmp=link_tmp->pointOfSpecificElement(1);
    113         while(tmp!=NULL)
    114         {
    115             tmp->x-=trans.x;
    116             tmp->y-=trans.y;
    117             //tmp->z-=trans.z;
    118             tmp=tmp->next;
    119         }
    120     }//所有点的平移完成
    121     {//从第二个点开始按与第一个点的距离从小到大排序
    122         Node * tmp1=link_tmp->pointOfSpecificElement(0);
    123         Node * tmp2=tmp1->next;
    124         while(tmp2!=NULL)
    125         {
    126             //使用z成员保存其到原点的距离
    127             tmp2->z=sqrt((tmp2->x* tmp2->x)+(tmp2->y*tmp2->y));
    128             tmp2=tmp2->next;
    129         }
    130         tmp2=tmp1->next;
    131         //int count=0;
    132         while(tmp2!=NULL && tmp2->next!=NULL)
    133         {//保证只有第一个点在坐标原点
    134             if(tmp2->next->z==0.0)
    135             {
    136                 link_tmp->del(tmp2);
    137                 //++count;
    138                 continue;
    139             }
    140             tmp2=tmp2->next;
    141         }
    142         //printf("去重次数:%d
    ",count);
    143         /*if(link_tmp->count()<3)
    144         {
    145             printf("对链表进行去重操作之后导致链表中剩余元素不足三个,无法进行后续运算
    ");
    146             exit(-1);
    147         }*/
    148         tmp2=tmp1->next->next;
    149         while(tmp2!=NULL)
    150         {
    151             Node * minDis=tmp2;
    152             Node * tmp3=tmp2;
    153             while(tmp3!=NULL)
    154             {
    155                 if(tmp3->z-minDis->z<-0.000001)
    156                 {
    157                     minDis=tmp3;
    158                 }
    159                 tmp3=tmp3->next;
    160             }
    161             if(minDis!=tmp2)
    162             {
    163                 Node tmp;
    164                 tmp.x=minDis->x;
    165                 tmp.y=minDis->y;
    166                 tmp.z=minDis->z;
    167                 minDis->x=tmp2->x;
    168                 minDis->y=tmp2->y;
    169                 minDis->z=tmp2->z;
    170                 tmp2->x=tmp.x;
    171                 tmp2->y=tmp.y;
    172                 tmp2->z=tmp.z;
    173             }
    174             tmp2=tmp2->next;
    175         }
    176     }//按照距离排序完成
    177     /*{//输出排序完的点的顺序
    178         Node * tmp=link_tmp->pointOfSpecificElement(1);
    179         printf("距离排序后的点(%d):
    ",link_tmp->count());
    180         while(tmp!=NULL)
    181         {
    182             printf("%.10f %.10f %.10f
    ",tmp->x+trans.x,tmp->y+trans.y,tmp->z);
    183             tmp=tmp->next;
    184         }
    185     }*/
    186     {//从第二个点开始按与第一个点的幅角从小到大排序
    187         Node * tmp1=link_tmp->pointOfSpecificElement(1);
    188         Node * tmp2=tmp1->next;
    189         while(tmp2!=NULL)
    190         {
    191             //使用z成员保存其幅角
    192             tmp2->z=acos((tmp2->x/tmp2->z));
    193             tmp2=tmp2->next;
    194         }
    195         tmp2=tmp1->next;
    196         while(tmp2!=NULL)
    197         {
    198             Node * tmp3=tmp2->next;
    199             Node * tmp4=tmp1->next;
    200             while(tmp3!=NULL)
    201             {
    202                 if(tmp4->next->z-tmp4->z<-0.000001)
    203                 {
    204                     Node tmp;
    205                     tmp.x=tmp4->x;
    206                     tmp.y=tmp4->y;
    207                     tmp.z=tmp4->z;
    208                     tmp4->x=tmp4->next->x;
    209                     tmp4->y=tmp4->next->y;
    210                     tmp4->z=tmp4->next->z;
    211                     tmp4->next->x=tmp.x;
    212                     tmp4->next->y=tmp.y;
    213                     tmp4->next->z=tmp.z;
    214                 }
    215                 tmp4=tmp4->next;
    216                 tmp3=tmp3->next;
    217             }
    218             tmp2=tmp2->next;
    219         }
    220     }//按照幅角排序完成
    221     /*{//输出排序完的点的顺序
    222         Node * tmp=link_tmp->pointOfSpecificElement(1);
    223         printf("幅角排序后的点(%d):
    ",link_tmp->count());
    224         while(tmp!=NULL)
    225         {
    226             printf("%.10f %.10f %.10f
    ",tmp->x+trans.x,tmp->y+trans.y,tmp->z);
    227             tmp=tmp->next;
    228         }
    229     }*/
    230     {//对其进行求凸包运算
    231         Link * stk_tmp=new Link();
    232         Node * tmp=link_tmp->pointOfSpecificElement(1);
    233         stk_tmp->add(tmp->x,tmp->y,0.0);
    234         tmp=tmp->next;
    235         if(tmp!=NULL) stk_tmp->add(tmp->x,tmp->y,0.0);
    236         if(link_tmp->count()>=3)
    237         {
    238             Node * stkTop=stk_tmp->pointOfSpecificElement(1);//指向栈顶元素
    239             Node * stkNext=stkTop->next;//指向栈顶的下一个元素
    240             Node * current=tmp->next;//指向当前点
    241             //int count1=0,count2=0;
    242             while(1)
    243             {
    244                 if(RightOrLeft(stkNext->x,stkNext->y,stkTop->x,stkTop->y,current->x,current->y)==1)
    245                 {
    246                     //++count1;
    247                     stk_tmp->del(stk_tmp->pointOfSpecificElement(0));
    248                     stkTop=stk_tmp->pointOfSpecificElement(1);
    249                     stkNext=stkTop->next;
    250                 }
    251                 else{
    252                     //++count2;
    253                     stk_tmp->add(current->x,current->y,0.0);
    254                     //cout<<"入栈"<<current->x+trans.x<<" "<<current->y+trans.y<<endl;
    255                     stkTop=stk_tmp->pointOfSpecificElement(1);
    256                     stkNext=stkTop->next;
    257                     if(current->next==NULL) break;
    258                     else{
    259                         current=current->next;
    260                     }
    261                 }
    262             }//end of while
    263         }
    264         //printf("入栈次数:%d
    ",count2);
    265         //printf("出栈次数:%d
    ",count1);
    266         //现在栈 stk_tmp中保存有凸包上的点
    267         {//对凸包上的点平移到原位置
    268             Node * tmp=stk_tmp->pointOfSpecificElement(1);
    269             while(tmp!=NULL)
    270             {
    271                 tmp->x+=trans.x;
    272                 tmp->y+=trans.y;
    273                 //tmp->z+=trans.z;
    274                 tmp=tmp->next;
    275             }
    276         }//平移到原位置完成
    277         delete link_tmp;
    278         link->clear();
    279         link->copy(stk_tmp);
    280         delete stk_tmp;
    281     }//凸包运算完成
    282 }// end of function convelHull
    convelHull.func.h
    #include "includeall.h"
    #include "Link.class.h"
    #include "convelHull.func.h"
    
    char * filename1=(char *)"./data/凸包问题输入.asc";
    char * filename2 = (char *)"./data/tubao_out.asc";
    void constructBody(Link * link,char * filename)//由点云文件构造点云模型
    {
        FILE * fp;
        if (fp = fopen(filename, "r"), fp == NULL)
        {
            cout << "文件打开失败,文件路径:" << filename << endl;
            exit(0);
        }
        fseek(fp,0,SEEK_SET);
        float x,y,z;
        int count=0;
        while(!feof(fp))
        {
            fscanf(fp,"%f %f %f
    ",&x,&y,&z);
            
            ++count;
            if(count>=1)
            {
                link->add(x*1,y*1,z*1);
                count=0;
            }
        }
        fclose(fp);
    }
    void saveToFile(Link * link,char * filename)//将一个链表中的内容保存到指定的文件中
    {
        FILE * fp=NULL;
        if(fp=fopen(filename,"w"),fp==NULL) exit(0);
        Node * tmp=link->data->next;
        while(tmp!=NULL)
        {
            fprintf(fp,"%.5f %.5f %.5f
    ",tmp->x,tmp->y,tmp->z);
            tmp=tmp->next;
        }
        fclose(fp);
    }
    
    int main()
    {
        Link * link=new Link();
    
        /*  link->add(0,0,-1);
         link->add(0,0,0);
         link->add(0,0,1);
         link->add(0,0,2);
         
         link->add(-1,0,-1);
         link->add(-1,0,0);
         link->add(-1,0,1);
         link->add(-1,0,2);
         
         link->add(-2,0,-1);
         link->add(-2,0,0);
         link->add(-2,0,1);
         link->add(-2,0,2);
         
         link->add(1,0,-1);
         link->add(1,0,0);
         link->add(1,0,1);
         link->add(1,0,2);
         
         link->add(2,0,-1);
         link->add(2,0,0);
         link->add(2,0,1);
         link->add(2,0,2);
    
         link->add(-2,0,-2);
         link->add(-1,0,-2);
         link->add(0,0,-2);
         link->add(1,0,-2);
         link->add(2,0,-2);
         link->add(3,0,-2);
         link->add(5,0,0);
         link->add(-5,0,0); */
         
        /*  link->add(0,0,0);
         link->add(1,0,1);
         link->add(2,0,2);
         link->add(3,0,3);
         link->add(4,0,4);
         link->add(5,0,5);
         link->add(6,0,6);
         */
        //constructBody(link,filename1);
        link->add(1,1,0);
        link->add(2,2,0);
        link->add(3,3,0);
        Node * tmp=link->pointOfSpecificElement(1);
        printf("未进行凸包运算的点(%d):
    ",link->count());
        while(tmp!=NULL)
        {
            printf("%f %f %f
    ",tmp->x,tmp->y,tmp->z);
            tmp=tmp->next;
        }
        printf("
    ");
        convelHull(link,6);
        saveToFile(link,filename2);
        tmp=link->pointOfSpecificElement(1);
        printf("进行过凸包运算的点(%d):
    ",link->count());
        while(tmp!=NULL)
        {
            printf("%f %f %f
    ",tmp->x,tmp->y,tmp->z);
            tmp=tmp->next;
        }
        printf("
    ");
        printf("test: %d
    ",RightOrLeft(0,0,1,1,-1,0));
        printf("test: %d
    ",RightOrLeft(0,0,-1,1,1,0));
        printf("test: %d
    ",RightOrLeft(0,0,-1,-1,-1,0));
        printf("test: %d
    ",RightOrLeft(0,0,1,-1,0,-1));
        return 0;
    }
    main.cpp

     就当是记录一下,程序写得不完美

  • 相关阅读:
    SpringDataJpa
    #pragma pack(n)的使用
    jquery中的ajax方法参数
    rapidjson的使用
    Linux下Qt安装
    jsoncpp 0.5 ARM移植
    GoAhead2.5移植到ARM教程
    Qt 4.7.2移植到ARM教程
    虚函数与纯虚函数的区别
    海康、大华IpCamera RTSP地址和格式
  • 原文地址:https://www.cnblogs.com/vanwoos/p/4947130.html
Copyright © 2011-2022 走看看