Try Again

计算几何知识点简记

什么是计算几何

  由函数逼近论、微分几何、代数几何、计算数学等形成的边缘学科,研究几何形体的计算机表示、分析和综合。它是计算机图形学、计算机辅助设计的数学基础。

笛卡尔坐标系点,边的定义以及基础运算

struct Point{
    double x,y;
    Point(){}
    Point(const double &a,const double &b):x(a),y(b){}
};
struct Lineseg{
    Point s,e;
    Lineseg(){}
    Lineseg(const Point &a,const Point &b):s(a),e(b){}
};
Point operator + (Point a,Point b) {return Point(a.x+b.x,a.y+b.y);}
Point operator - (Point a,Point b) {return Point(a.x-b.x,a.y-b.y);}
Point operator * (Point a,double x){return Point(a.x*x,a.y*x);}
Point operator / (Point a,double x){return Point(a.x/x,a.y/x);}
bool  operator < (const Point &a,const Point &b){return a.x<b.x|| (a.x==b.x) && a.y<b.y;}
bool  operator ==(const Point &a,const Point &b){return !dcmp(a.x-b.x) && !dcmp(a.y-b.y);}
bool  operator !=(const Point &a,const Point &b){return !(a==b);}
double operator * (Point a,Point b){return a.x*b.y-a.y*b.x;}//向量叉积

 

矢量的概念

  如果一条线段的端点是有次序之分的,我们把这种线段称为有向线段(directed segment)。如果有向线段p1p2的起点p1 在坐标原点,我们可以把这个有向线段称为矢量(vector)p2。

向量Q=Q1-Q2

设二维矢量P = ( x1, y1 ),Q = ( x2 , y2 )
则矢量加法定义为: P + Q = ( x1 + x2 , y1 + y2 )
矢量减法定义为: P - Q = ( x1 - x2 , y1 - y2 )。
显然有性质 P + Q = Q + P,P - Q = - ( Q - P )。

矢量叉积

  矢量叉积是与直线和线段相关算法的核心部分

  定义:设矢量P = ( x1, y1 ),Q = ( x2, y2 ),
    则P × Q = x1*y2 - x2*y1

    几何意义:矢量叉积为由(0,0)、p1、p2和p1+p2所组成的平行四边形的带符号的面积,其结果是一个标量。显然有性质 P × Q = - ( Q × P ) 和 P × ( - Q ) = - ( P × Q )。

  |P×Q|=|P|×|Q|sinθ

  |P×Q|等于以P和Q为两边的平行四边形的面积

叉积的几何意义

double operator * (Point a,Point b){return a.x*b.y-a.y*b.x;}//向量叉积
double multiply(Point a,Point b,Point c){
    return (b-a)*(c-a);
}

 

矢量叉积的作用

通过符号判断两矢量之间相互之间的顺时针关系(右手螺旋定理)

若 P × Q > 0 , 则P在Q的顺时针方向。
若 P × Q < 0 , 则P在Q的逆时针方向。
若 P × Q = 0 , 则P与Q共线,但可能同向也可能反向

计算多边形面积

化简可得

double area(int n)//求面积//要求是凸多边形,且坐标点逆时针排列
{
    double s=0.0;
    for(int i=0;i<n;i++)
        s+=p[i]*p[(i+1)%n];
    return s/2.0;
}

 

矢量叉积的作用-判断凹凸多边形

   把多边形的任何一条边向两方延长,如果多边形的其他各边都在延长所得直线的同一旁,这样的多边形叫做凸多边形
方法:

  只需依次计算多边形相邻两边矢量的叉积
    (1)全部为零,则多边形各边共线;
    (2)部分为正,部分为负,则为凹多边形;
    (3)全部大于零或等于零,则为凸多边形;
    (4)全部小于零或等于零,则为凸多边形。

矢量叉积的作用-折线段的拐向判断

折线段的拐向判断方法可以直接由矢量叉积的性质推出。对于有公共端点的线段p0p1和p1p2,通过计算(p2 - p0) × (p1 - p0)的符号便可以确定折线段的拐向:
  若(p2 - p0) × (p1 - p0) > 0,则p0p1在p1点拐向右侧后得到p1p2。
  若(p2 - p0) × (p1 - p0) < 0,则p0p1在p1点拐向左侧后得到p1p2。
  若(p2 - p0) × (p1 - p0) = 0,则p0、p1、p2三点共线。

矢量叉积的作用-判断点是否在线段上

  设点为Q,线段为P1P2 ,判断点Q在该线段上的依据是:( Q - P1 ) × ( P2 - P1 ) = 0 且 Q 在以 P1,P2为对角顶点的矩形内。前者保证Q点在直线P1P2上,后者是保证Q点不在线段P1P2的延长线或反向延长线上

bool online(Lineseg l,Point p){//判断点是否在线上
    return multiply(l.s,p,l.e)==0 && (p.x-l.s.x)*(p.x-l.e.x)<=0 && (p.y-l.s.y)*(p.y-l.e.y)<=0;
}

矢量叉积的作用-判断两直线相交

快速排除法

  设以线段 P1P2 为对角线的矩形为R, 设以线段 Q1Q2 为对角线的矩形为T,如果R和T不相交,显然两线段不会相交。

跨立实验

如果两线段相交则两线段必然相互跨立对方。

当 ( P1 - Q1 ) × ( Q2 - Q1 ) = 0 时,说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线,但是因为已经通过快速排斥试验,所以 P1 一定在线段 Q1Q2上;同理,( Q2 - Q1 ) ×(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2上。

所以判断P1P2跨立Q1Q2的依据是:( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) >= 0。
同理判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >= 0。

//判断两线段是否相交,内交或者端点相交返回true
bool intersect(Lineseg u,Lineseg v)
{
    return 
    (max(u.s.x,u.e.x)>=min(v.s.x,v.e.x)) &&
    (max(v.s.x,v.e.x)>=min(u.s.x,u.e.x)) &&
    (max(u.s.y,u.e.y)>=min(v.s.y,v.e.y)) &&
    (max(v.s.y,v.e.y)>=min(u.s.y,u.e.y)) &&//快速排斥实验
    (multiply(u.s,v.s,u.e)*multiply(u.s,u.e,v.e))>=0 &&//跨立实验//注意顺序
    (multiply(v.s,u.s,v.e)*multiply(v.s,v.e,u.e))>=0;
}
//判断u和v相交,且交点不是端点
bool intersect_A(Lineseg u,Lineseg v)
{
    return intersect(u,v)&&
           (!(online(u,v.s)))&&
           (!(online(u,v.e)))&&
           (!(online(v,u.s)))&&
           (!(online(v,u.e)));
}
bool intersect_L(Lineseg u,Lineseg v)//判断线段v是否跨立u所在的直线
{
    return multiply(u.s,v.s,u.e)*multiply(u.s,u.e,v.e)>=0;
}

矢量叉积-判断点与多边形的关系

 射线法

  从目标点出发 , 向某一方向发出一条射线 , 如果该射线和四边形交点个数是奇数,则该点在四边形内部,若为偶数,则不在内部。

  做射线L的方法是:设P'的纵坐标和P相同,横坐标为正无穷大(很大的一个正数),则P和P'就确定了射线L。

  同时要注意特殊情况。

//射线法判断点Q与多边形polygon的位置关系,要求polygon是简单多边形,顶点逆时针排列
//如果点在多边形内返回2,点在多边形上返回1,点在多边形外返回0
int insidepolygon(int n,Point polygon[],Point p)
{
    int num=0,i;//n为顶点数
    Lineseg line;
    if(n==1) return ((fabs(polygon[0].x-p.x)<esp) && (fabs(polygon[0].y-p.y)<esp));
    else if(n==2)
    {
        Lineseg side;
        side.s=polygon[0];
        side.e=polygon[1];
        return online(side,p);
    }
    line.s=p;
    line.e.y=p.y;
    line.e.x=INF;
    for(int i=0;i<n;i++)
    {
        Lineseg side;
        side.s=polygon[i];
        side.e=polygon[(i+1)%n];
        if(online(side,p)) return true;//点在多边形上
        if(fabs(side.s.y-side.e.y)<esp) continue;//平行x轴不做考虑
        if(online(line,side.s))
        {
            if(side.s.y>side.e.y) num++;//此两条语句不能合起来写
        }
        else if(online(line,side.e))
        {
            if(side.e.y>side.s.y) num++;
        }
        else if(intersect(line,side)) num++;
        
    }
    return num&1?2:0;
}

矢量叉积作用-凸多边形排序

对于凸多边形,如果不是按照逆时针顺序排列,可以通过矢量叉积来进行排序、

//多边形点逆时针排序,注意是凸多边形
void adjustThePoint(int n)
{
    vector<Point> p_vector;
    if(n<3) return ;
    p_vector.push_back(p[0]);
    p_vector.push_back(p[1]);
    for(int i=2;i<n;i++)
    {
        int pos=p_vector.size()-1;
        while(pos>0)
        {
            if(multiply(p[0],p[i],p[pos])<0)
            {
                p_vector.insert(p_vector.begin()+pos+1,p[i]);
                break;
            }
            pos--;
        }
        if(!pos) p_vector.insert(p_vector.begin()+1,p[i]);
    }
    int pos=0;
    for(auto &t:p_vector)
        p[pos++]=t;
}

 

posted @ 2018-07-24 22:19  十年换你一句好久不见  阅读(562)  评论(0编辑  收藏  举报