opengl:凸包算法

准备工作

判断点在有向线段的左侧

可以通过叉积判断,如下为k在有向线段ab的左侧代码描述:

double multiply(Point a, Point b, Point k)
{
    double x1 = b.x-a.x;
    double y1 = b.y-a.y;
    double x2 = k.x-a.x;
    double y2 = k.y-a.y;
    return x1*y2-x2*y1;
}

bool toLeft(Point a, Point b, Point k)
{
    return multiply(a,b,k)>0; 
}

判断点在三角形的内部

给三角形abc定义一定的次序,按照一般习惯,假设abc是逆时针的,则判断k是否在三角形内部,只需要判断k是否在有向线段ab,bc,ac的左侧:

bool inTriangle(Point a, Point b, Point c, Point k)
{
    bool abLeft = toLeft(a,b,k);
    bool bcLeft = toLeft(b,c,k);
    bool caLeft = toLeft(c,a,k);
    return (abLeft==bcLeft)&&(bcLeft==caLeft);
}

几种典型的算法

极点算法

凸包上的顶点称为极点,极点有一个特性,总可以找到过极点的一条直线使得其他所有的顶点,在这个直线的一侧。所以极点不可能在某一个顶点三角形的内部,则可以在初始化时,标示所有的顶点为极点,然后遍历所有的顶点组成的三角形,排除掉三角形内部的顶点,则剩下的顶点则为凸包的极点。该算法时间复杂度为O(N^4),算法描述如下:

极边算法

凸包上的边称为极边,所有的顶点都在极边的一侧,所以可以遍历所有的边,检查它是否为极边,算法时间复杂度为O(N^3),算法描述如下:

GiftWrapping算法

两个相邻的极边之间有一个共同的极点,所以一条极边的尾端也是另一条极边的顶端。如果已知一个极点,则可以寻找以该极点作为顶端的极边的尾端极点。方法是任取一个点作为候选点,如果下一个点在已知点与候选点组成的有向线段的右端,则把这个点作为候选点,这样不断的更新。因为最下的点肯定是一个极点,所以可把最下点作为初始点。算法的复杂度为O(N*W)(W是凸包的边数),算法描述如下:

Graham Scan算法

算法需要借助一次排序,和两个栈:

下图描述了整个流程:

opengl实现

geometry.h文件:

#include <GL/glut.h>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;


class Point
{
public:
    Point(){};
    Point(double a,double b,double c):x(a),y(b),z(c),extreme(true){};
public:
    double x;
    double y;
    double z; //平面凸包,此项为0
    bool extreme;   //EE算法中用到,标识该点是否为极点
    pair<double,double> ref; //Graham Scan算法中,排序的参考点
    bool operator < (const Point &a);

};

class Edge
{
public :
    Edge(Point a,Point b):s(a),e(b){};
    Point s,e;
};

enum  PLOTMODE
{
    EP=0,
    EE,
    GW,
    GS
};

double multiply(Point a, Point b, Point c);

bool toLeft(Point a, Point b, Point c);

bool inTriangle(Point a, Point b, Point c, Point k);

 void EPAlgorithm(vector<Point> &list);

 vector<Edge> EEAlgorithm(vector<Point> list);

 vector<Edge> GWAlgorithm(vector<Point> list);

 stack<Point> GSAlgorithm(vector<Point>list);

geometry.cpp文件:

#include "Geometry.h"

bool Point::operator<(const Point &a)
{
    Point p = Point(ref.first,ref.second,0);
    return toLeft(p,*this,a);
}

double multiply(Point a, Point b, Point k)
{
    double x1 = b.x-a.x;
    double y1 = b.y-a.y;
    double x2 = k.x-a.x;
    double y2 = k.y-a.y;
    return x1*y2-x2*y1;
}

bool toLeft(Point a, Point b, Point k)
{
    return multiply(a,b,k)>0; 
}

bool inTriangle(Point a, Point b, Point c, Point k)
{
    bool abLeft = toLeft(a,b,k);
    bool bcLeft = toLeft(b,c,k);
    bool caLeft = toLeft(c,a,k);
    return (abLeft==bcLeft)&&(bcLeft==caLeft);
}

//极点算法
void EPAlgorithm(vector<Point> &list)
{
    //三重循环遍历所有三角形
    for(int i=0;i<list.size();i++)
    {
        for(int j=i+1;j<list.size();j++)
        {
            for(int k=j+1;k<list.size();k++)
            {
                for(int m=0;m<list.size();m++)
                {
                    if(!list[m].extreme)
                        continue;
                    if(m==i||m==j||m==k)
                        continue;
                    if(inTriangle(list[i],list[j],list[k],list[m]))
                        list[m].extreme = false;
                }
            }
        }
    }
}

//极边算法
vector<Edge> EEAlgorithm(vector<Point> list)
{
    vector<Edge> res;
    //二重循环遍历所有边
    for(int i=0;i<list.size();i++)
    {
        for(int j=i+1;j<list.size();j++)
        {
            bool left = true, right = true;
            for(int k=0;k<list.size();k++)
            {
                if(k!=i&&k!=j)
                    (toLeft(list[i],list[j],list[k])>0?left:right) = false;
            }
            if(left|right)
                res.push_back(Edge(list[i],list[j]));
        }
    }
    return res;
}

//GiftWrapping算法
vector<Edge> GWAlgorithm(vector<Point> list)
{
    vector<Point> listCopy = list; 
    vector<Edge> res;
    if(listCopy.size()<=2)
        return res;
    int ltl = 0;
    //找出lowest-then-leftest的点
    for(int i=1;i<listCopy.size();i++)
    {
        if(listCopy[i].y<listCopy[ltl].y||(listCopy[i].y==listCopy[ltl].y&&listCopy[i].x<listCopy[ltl].x))
            ltl = i;
    }
    int p = ltl;
    //找出下一条极边
    while(1)
    {
        int q = -1;
        for(int i=0;i<listCopy.size();i++)
        {
            if(i!=p&&(q<0||!toLeft(listCopy[p],listCopy[q],listCopy[i])))
                q = i;
        }
        res.push_back(Edge(listCopy[p],listCopy[q]));
        if(q==ltl)
            break;
        p = q;
    }
    return res;
}

Point getPoint(stack<Point>s, int num)
{
    stack<Point> temp = s;
    for(int i=0;i<num;i++)
    {
        temp.pop();
    }
    return temp.top(); 
}

stack<Point> GSAlgorithm(vector<Point>list)
{
    vector<Point> listCopy = list;
    stack<Point> S,T;
    if(listCopy.size()<3)
        return S;
    int ltl = 0;
    //找出lowest-then-leftest的点
    for(int i=1;i<listCopy.size();i++)
    {
        if(listCopy[i].y<listCopy[ltl].y||(listCopy[i].y==listCopy[ltl].y&&listCopy[i].x<listCopy[ltl].x))
            ltl = i;
    }
    //给所有定点附加ref属性
    for(int i=0;i<listCopy.size();i++)
    {
        listCopy[i].ref = pair<double,double>(listCopy[ltl].x,listCopy[ltl].y);
    }
    S.push(listCopy[ltl]);
    listCopy.erase(listCopy.begin()+ltl);
    //对定点进行排序
    sort(listCopy.begin(),listCopy.end());
    //构造初始的S和T
    S.push(listCopy[0]);
    for(int i = listCopy.size()-1;i>=1;i--)
    {
        T.push(listCopy[i]);
    }
    while(!T.empty())
    {
        while(!toLeft(getPoint(S,1),getPoint(S,0),getPoint(T,0)))
        {
            S.pop();
        }
        S.push(getPoint(T,0));
        T.pop();
    }
    return S;
}

convexHull.cpp文件:

#include <iostream>
#include <vector>
#include <GL/glut.h>
#include "Geometry.h"

using namespace std;

GLsizei width = 600,height = 600;
vector<Point> list;

PLOTMODE mode ;

void init()
{
    glClearColor(0.0f,0.0f,0.0,1.0f);
    glViewport(0,0,width,height);
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(0,width,0,height);
}

//选择菜单
void selectMenu(GLint option)
{
    switch (option)
    {
    case 1:
        list.clear();
        glutPostRedisplay();
        break;
    case 2:
        mode = EP;
        glutPostRedisplay();
        break;
    case 3:
        mode = EE;
        glutPostRedisplay();
        break;
    case 4:
        mode = GW;
        glutPostRedisplay();
        break;
    case 5:
        mode = GS;
        glutPostRedisplay();
        break;
    default:
        break;
    }
}

void plotPoints(vector<Point> list)
{
    glBegin(GL_POINTS);
    for(int i=0;i<list.size();i++)
    {
        glVertex2i(list[i].x,list[i].y);
    }
    glEnd();
}

void plotEP(vector<Point>list)
{
    EPAlgorithm(list);
    glPointSize(3.0);
    glBegin(GL_POINTS);
    for(int i=0;i<list.size();i++)
    {
        if(list[i].extreme)
            glVertex2d(list[i].x,list[i].y);
    }
    glEnd();
    glPointSize(1.0);
}

void plotEE(vector<Point>list)
{
    vector<Edge> edge = EEAlgorithm(list);
    glBegin(GL_LINES);
    for(int i=0;i<edge.size();i++)
    {
        glVertex2d(edge[i].s.x,edge[i].s.y);
        glVertex2d(edge[i].e.x,edge[i].e.y);
    }
    glEnd();
}

void plotGW(vector<Point>list)
{
    vector<Edge> edge = GWAlgorithm(list);
    glBegin(GL_LINES);
    for(int i=0;i<edge.size();i++)
    {
        glVertex2d(edge[i].s.x,edge[i].s.y);
        glVertex2d(edge[i].e.x,edge[i].e.y);
    }
    glEnd();
}

void plotGS(vector<Point> list)
{
    stack<Point> s = GSAlgorithm(list);
    glBegin(GL_LINE_LOOP);
    {
        while (!s.empty())
        {
            glVertex2d(s.top().x,s.top().y);
            s.pop();
        }
    }
    glEnd();
}

void displayFunc()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0,0.0,0.0);
    glPointSize(1.0);
    plotPoints(list);
    cout<<mode<<endl;
    switch (mode)
    {
    case EE:
        plotEE(list);
        break;
    case EP:
        plotEP(list);
        break;
    case GW:
        plotGW(list);
        break;
    case GS:
        plotGS(list);
        break;
    default:
        break;
    }
    glFlush();
}

void reshapeFunc(GLint newWidth,GLint newHeight)
{
    glViewport(0,0,newWidth,newHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0,GLdouble(newWidth),0.0,GLdouble(newHeight));
    width = newWidth;
    height = newHeight;
}

void mouseFunc(GLint button, GLint action, GLint x,GLint y)
{
    if(button==GLUT_LEFT_BUTTON&&action==GLUT_DOWN)
    {
        list.push_back(Point(x,height-y,0));
        glutPostRedisplay();
    }
}

int main(int argc,char* argv[])
{
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(width,height);
    glutCreateWindow("Convex Hull");

    init();
    glutCreateMenu(selectMenu);
    glutAddMenuEntry("清除点",1);
    glutAddMenuEntry("极点算法",2);
    glutAddMenuEntry("极边算法",3);
    glutAddMenuEntry("GiftWrapping算法",4);
    glutAddMenuEntry("Graham Scan算法",5);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    glutDisplayFunc(displayFunc);
    glutReshapeFunc(reshapeFunc);
    glutMouseFunc(mouseFunc);

    glutMainLoop();
}

你可能感兴趣的:(C++,OpenGL,凸包,图形学)