计算几何几何函数库

计算几何几何函数库

导引

  1. 常量定义和包含文件
  2. 基本数据结构
  3. 精度控制
    ㈠ 点的基本运算
  4. 平面上两点之间距离
  5. 判断两点是否重合
  6. 矢量叉乘
  7. 矢量点乘
  8. 判断点是否在线段上
  9. 求一点饶某点旋转后的坐标
  10. 求矢量夹角
    ㈡ 线段及直线的基本运算
  11. 点与线段的关系
  12. 求点到线段所在直线垂线的垂足
  13. 点到线段的最近点
  14. 点到线段所在直线的距离
  15. 点到折线集的最近距离
  16. 判断圆是否在多边形内
  17. 求矢量夹角余弦
  18. 求线段之间的夹角
  19. 判断线段是否相交
    10.判断线段是否相交但不交在端点处
    11.求点关于某直线的对称点
    12.判断两条直线是否相交及求直线交点
    13.判断线段是否相交,如果相交返回交点
    ㈢ 多边形常用算法模块
  20. 判断多边形是否简单多边形
  21. 检查多边形顶点的凸凹性
  22. 判断多边形是否凸多边形
  23. 求多边形面积
  24. 判断多边形顶点的排列方向
  25. 射线法判断点是否在多边形内
  26. 判断点是否在凸多边形内
  27. 寻找点集的graham算法
    10.寻找点集凸包的卷包裹法
    11.凸包MelkMan算法的实现
  28. 凸多边形的直径
    13.求凸多边形的重心
    ===========================================================================
    导引
    /* 需要包含的头文件 /
    #include
    /
    常量定义 /
    const double INF = 1E200;
    const double EP = 1E-10;
    const int MAXV = 300;
    const double PI = 3.14159265;
    /
    基本几何结构 /
    struct POINT
    {
    double x;
    double y;
    POINT(double a=0, double b=0) { x=a; y=b;}
    };
    struct LINESEG
    {
    POINT s;
    POINT e;
    LINESEG(POINT a, POINT b) { s=a; e=b;}
    LINESEG() { }
    };
    // 直线的解析方程 a
    x+by+c=0 为统一表示,约定 a>= 0
    struct LINE
    {
    double a;
    double b;
    double c;
    LINE(double d1=1, double d2=-1, double d3=0) {a=d1; b=d2; c=d3;}
    };
    //线段树
    struct LINETREE
    {
    }
    //浮点误差的处理
    int dblcmp(double d)
    {
    if(fabs(d)<EP)
    return 0 ;
    return (d>0) ?1 :-1 ;
    }
    <一>点的基本运算
    // 返回两点之间欧氏距离
    double dist(POINT p1,POINT p2)
    {
    return( sqrt( (p1.x-p2.x)
    (p1.x-p2.x)+(p1.y-p2.y)(p1.y-p2.y) ) );
    }
    // 判断两个点是否重合
    bool equal_point(POINT p1,POINT p2)
    {
    return ( (abs(p1.x-p2.x)<EP)&&(abs(p1.y-p2.y)<EP) );
    }
    /
    (sp-op)(ep-op)的叉积
    r=multiply(sp,ep,op),得到(sp-op)
    (ep-op)的叉积
    r>0:sp在矢量op ep的顺时针方向;
    r=0:op sp ep三点共线;
    r<0: sp在矢量op ep的逆时针方向 /
    double multiply(POINT sp,POINT ep,POINT op)
    {
    return((sp.x-op.x)
    (ep.y-op.y) - (ep.x-op.x)(sp.y-op.y));
    }
    double amultiply(POINT sp,POINT ep,POINT op)
    {
    return fabs((sp.x-op.x)
    (ep.y-op.y)-(ep.x-op.x)(sp.y-op.y));
    }
    /矢量(p1-op)和(p2-op)的点积
    r=dotmultiply(p1,p2,op),得到矢量(p1-op)和(p2-op)的点积如果两个矢量都非零矢量
    r < 0: 两矢量夹角为锐角;
    r = 0:两矢量夹角为直角;
    r > 0: 两矢量夹角为钝角 /
    double dotmultiply(POINT p1,POINT p2,POINT p0)
    {
    return ((p1.x-p0.x)
    (p2.x-p0.x) + (p1.y-p0.y)
    (p2.y-p0.y));
    }
    /
    判断点p是否在线段l上
    条件:(p在线段l所在的直线上)&& (点p在以线段l为对角线的矩形内) /
    bool online(LINESEG l,POINT p)
    {
    return ((multiply(l.e,p,l.s)==0)
    && ( ( (p.x-l.s.x) * (p.x-l.e.x) <=0 ) && ( (p.y-l.s.y)
    (p.y-l.e.y) <=0 ) ) );
    }
    // 返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置
    POINT rotate(POINT o,double alpha,POINT p)
    {
    POINT tp;
    p.x -=o.x;
    p.y -=o.y;
    tp.x=p.xcos(alpha) - p.ysin(alpha)+o.x;
    tp.y=p.ycos(alpha) + p.xsin(alpha)+o.y;
    return tp;
    }
    /* 返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度)
    角度小于pi,返回正值
    角度大于pi,返回负值
    可以用于求线段之间的夹角 /
    double angle(POINT o,POINT s,POINT e)
    {
    double cosfi,fi,norm;
    double dsx = s.x - o.x;
    double dsy = s.y - o.y;
    double dex = e.x - o.x;
    double dey = e.y - o.y;
    cosfi=dsx
    dex+dsydey;
    norm=(dsx
    dsx+deydey)(dexdex+deydey);
    cosfi /= sqrt( norm );
    if (cosfi >= 1.0 ) return 0;
    if (cosfi <= -1.0 ) return -3.1415926;
    fi=acos(cosfi);
    if (dsxdey-dsydex>0) return fi;// 说明矢量os 在矢量 oe的顺时针方向
    return -fi;
    }
    <二>线段及直线的基本运算
    /* 判断点C在线段AB所在的直线l上垂足P的与线段AB的关系
    本函数是根据下面的公式写的,P是点C到线段AB所在直线的垂足
    AC dot AB
    r = ----------------------
    ||AB||^2
    (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)
    = ----------------------------------------------------
    L^2
    r has the following meaning:
    r=0 P = A
    r=1 P = B
    r<0 P is on the backward extension of AB
    r>1 P is on the forward extension of AB
    0<r<1 P is interior to AB
    /
    double relation(POINT c,LINESEG l)
    {
    LINESEG tl;
    tl.s=l.s;
    tl.e=c;
    return dotmultiply(tl.e,l.e,l.s)/(dist(l.s,l.e)dist(l.s,l.e));
    }
    // 求点C到线段AB所在直线的垂足 P
    POINT perpendicular(POINT p,LINESEG l)
    {
    double r=relation(p,l);
    POINT tp;
    tp.x=l.s.x+r
    (l.e.x-l.s.x);
    tp.y=l.s.y+r
    (l.e.y-l.s.y);
    return tp;
    }
    /* 求点p到线段l的最短距离
    返回线段上距该点最近的点np 注意:np是线段l上到点p最近的点,不一定是垂足 /
    double ptolinesegdist(POINT p,LINESEG l,POINT &np)
    {
    double r=relation(p,l);
    if(r<0)
    {
    np=l.s;
    return dist(p,l.s);
    }
    if(r>1)
    {
    np=l.e;
    return dist(p,l.e);
    }
    np=perpendicular(p,l);
    return dist(p,np);
    }
    // 求点p到线段l所在直线的距离
    //请注意本函数与上个函数的区别
    double ptoldist(POINT p,LINESEG l)
    {
    return abs(multiply(p,l.e,l.s))/dist(l.s,l.e);
    }
    /
    计算点到折线集的最近距离,并返回最近点.
    注意:调用的是ptolineseg()函数 /
    double ptopointset(int vcount, POINT pointset[], POINT p, POINT &q)
    {
    int i;
    double cd=double(INF),td;
    LINESEG l;
    POINT tq,cq;
    for(i=0;i<vcount-1;i++)
    {
    l.s=pointset[i];
    l.e=pointset[i+1];
    td=ptolinesegdist(p,l,tq);
    if(td<cd)
    {
    cd=td;
    cq=tq;
    }
    }
    q=cq;
    return cd;
    }
    /
    判断圆是否在多边形内*/
    bool CircleInsidePolygon(int vcount,POINT center,double radius,POINT polygon[])
    {
    POINT q;
    double d;
    q.x=0;
    q.y=0;
    d=ptopointset(vcount,polygon,center,q);
    if(d<radius||fabs(d-radius)<EP) return true;
    else return false;
    }
    /* 返回两个矢量l1和l2的夹角的余弦 (-1 ~ 1)
    注意:如果想从余弦求夹角的话,注意反余弦函数的值域是从 0到pi /
    double cosine(LINESEG l1,LINESEG l2)
    {
    return(((l1.e.x-l1.s.x)
    (l2.e.x-l2.s.x)+(l1.e.y-l1.s.y)(l2.e.y-l2.s.y))/(dist(l1.e,l1.s)dist(l2.e,l2.s))) );
    }
    // 返回线段l1与l2之间的夹角
    //单位:弧度 范围(-pi,pi)
    double lsangle(LINESEG l1,LINESEG l2)
    {
    POINT o,s,e;
    o.x=o.y=0;
    s.x=l1.e.x-l1.s.x;
    s.y=l1.e.y-l1.s.y;
    e.x=l2.e.x-l2.s.x;
    e.y=l2.e.y-l2.s.y;
    return angle(o,s,e);
    }
    //判断线段u和v相交(包括相交在端点处)
    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(v.s,u.e,u.s)multiply(u.e,v.e,u.s)>=0)&& //跨立实验
    (multiply(u.s,v.e,v.s)multiply(v.e,u.e,v.s)>=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.e)) &&
    (!online(v,u.s)));
    }
    // 判断线段v所在直线与线段u相交
    方法:判断线段u是否跨立线段v
    bool intersect_l(LINESEG u,LINESEG v)
    {
    return multiply(u.s,v.e,v.s)multiply(v.e,u.e,v.s)>=0;
    }
    // 根据已知两点坐标,求过这两点的直线解析方程: a
    x+b
    y+c = 0 (a >= 0)
    LINE makeline(POINT p1,POINT p2)
    {
    LINE tl;
    int sign = 1;
    tl.a=p2.y-p1.y;
    if(tl.a<0)
    {
    sign = -1;
    tl.a=sign
    tl.a;
    }
    tl.b=sign
    (p1.x-p2.x);
    tl.c=sign
    (p1.yp2.x-p1.xp2.y);
    return tl;
    }
    // 根据直线解析方程返回直线的斜率k,水平线返回 0,竖直线返回 1e200
    double slope(LINE l)
    {
    if(abs(l.a) < 1e-20)return 0;
    if(abs(l.b) < 1e-20)return INF;
    return -(l.a/l.b);
    }
    // 返回直线的倾斜角alpha ( 0 - pi)
    // 注意:atan()返回的是 -PI/2 ~ PI/2
    double alpha(LINE l)
    {
    if(abs(l.a)< EP)return 0;
    if(abs(l.b)< EP)return PI/2;
    double k=slope(l);
    if(k>0)
    return atan(k);
    else
    return PI+atan(k);
    }
    // 求点p关于直线l的对称点
    POINT symmetry(LINE l,POINT p)
    {
    POINT tp;
    tp.x=((l.bl.b-l.al.a)p.x-2l.al.bp.y-2l.al.c)/(l.al.a+l.bl.b);
    tp.y=((l.al.a-l.bl.b)p.y-2l.al.bp.x-2l.bl.c)/(l.al.a+l.bl.b);
    return tp;
    }
    // 如果两条直线 l1(a1x+b1y+c1 = 0), l2(a2x+b2y+c2 = 0)相交,返回true,且返回交点p
    bool lineintersect(LINE l1,LINE l2,POINT &p) // 是 L1,L2
    {
    double d=l1.al2.b-l2.al1.b;
    if(abs(d)<EP) // 不相交
    return false;
    p.x = (l2.cl1.b-l1.cl2.b)/d;
    p.y = (l2.al1.c-l1.al2.c)/d;
    return true;
    }
    // 如果线段l1和l2相交,返回true且交点由(inter)返回,否则返回false
    bool intersection(LINESEG l1,LINESEG l2,POINT &inter)
    {
    LINE ll1,ll2;
    ll1=makeline(l1.s,l1.e);
    ll2=makeline(l2.s,l2.e);
    if(lineintersect(ll1,ll2,inter)) return online(l1,inter);
    else return false;
    }
    <三> 多边形常用算法模块
    如果无特别说明,输入多边形顶点要求按逆时针排列
    // 返回多边形面积(signed);
    // 输入顶点按逆时针排列时,返回正值;否则返回负值
    double area_of_polygon(int vcount,POINT polygon[])
    {
    int i;
    double s;
    if (vcount<3)
    return 0;
    s=polygon[0].y*(polygon[vcount-1].x-polygon[1].x);
    for (i=1;i<vcount;i++)
    s+=polygon[i].y*(polygon[(i-1)].x-polygon[(i+1)%vcount].x);
    return s/2;
    }
    // 判断顶点是否按逆时针排列
    // 如果输入顶点按逆时针排列,返回true
    bool isconterclock(int vcount,POINT polygon[])
    {
    return area_of_polygon(vcount,polygon)>0;
    }
    /*射线法判断点q与多边形polygon的位置关系
    要求polygon为简单多边形,顶点时针排列
    如果点在多边形内: 返回0
    如果点在多边形边上:返回1
    如果点在多边形外: 返回2 */
    int insidepolygon(POINT q)
    {
    int c=0,i,n;
    LINESEG l1,l2;
    l1.s=q; l1.e=q;l1.e.x=double(INF);
    n=vcount;
    for (i=0;i<vcount;i++)
    {
    l2.s=Polygon[i];
    l2.e=Polygon[(i+1)%vcount];
    double ee= Polygon[(i+2)%vcount].x;
    double ss= Polygon[(i+3)%vcount].y;
    if(online(l2,q))
    return 1;
    if(intersect_A(l1,l2))
    c++; // 相交且不在端点
    if(online(l1,l2.e)&& !online(l1,l2.s) && l2.e.y>l2.e.y)
    c++;//l2的一个端点在l1上且该端点是两端点中纵坐标较大的那个
    if(!online(l1,l2.e)&& online(l1,l2.s) && l2.e.y<l2.e.y)
    c++;//忽略平行边
    }
    if(c%2 == 1)
    return 0;
    else
    return 2;
    }
    //判断点q在凸多边形polygon内
    // 点q是凸多边形polygon内[包括边上]时,返回true
    // 注意:多边形polygon一定要是凸多边形
    bool InsideConvexPolygon(int vcount,POINT polygon[],POINT q)
    {
    POINT p;
    LINESEG l;
    int i;
    p.x=0; p.y=0;
    for(i=0;i<vcount;i++) // 寻找一个肯定在多边形polygon内的点p:多边形顶点平均值
    {
    p.x+=polygon[i].x;
    p.y+=polygon[i].y;
    }
    p.x /= vcount;
    p.y /= vcount;
    for(i=0;i<vcount;i++)
    {
    l.s=polygon[i];
    l.e=polygon[(i+1)%vcount];
    if(multiply(p,l.e,l.s)multiply(q,l.e,l.s)<0)
    /
    点p和点q在边l的两侧,说明点q肯定在多边形外 */
    return false;
    }
    return true;
    }
    /*寻找凸包的graham 扫描法
    PointSet为输入的点集;
    ch为输出的凸包上的点集,按照逆时针方向排列;
    n为PointSet中的点的数目
    len为输出的凸包上的点的个数 /
    void Graham_scan(POINT PointSet[],POINT ch[],int n,int &len)
    {
    int i,j,k=0,top=2;
    POINT tmp;
    // 选取PointSet中y坐标最小的点PointSet[k],如果这样的点有多个,则取最左边的一个
    for(i=1;i<n;i++)
    if ( PointSet[i].y<PointSet[k].y || (PointSet[i].y==PointSet[k].y)
    && (PointSet[i].x<PointSet[k].x) )
    k=i;
    tmp=PointSet[0];
    PointSet[0]=PointSet[k];
    PointSet[k]=tmp; // 现在PointSet中y坐标最小的点在PointSet[0]
    for (i=1;i<n-1;i++) /
    对顶点按照相对PointSet[0]的极角从小到大进行排序,极角相同
    的按照距离PointSet[0]从近到远进行排序 */
    {
    k=i;
    for (j=i+1;j<n;j++)
    if ( multiply(PointSet[j],PointSet[k],PointSet[0])>0 || // 极角更小
    (multiply(PointSet[j],PointSet[k],PointSet[0])==0) && /极角相等,距离更短 / dist(PointSet[0],PointSet[j])<dist(PointSet[0],PointSet[k]) )
    k=j;
    tmp=PointSet[i];
    PointSet[i]=PointSet[k];
    PointSet[k]=tmp;
    }
    ch[0]=PointSet[0];
    ch[1]=PointSet[1];
    ch[2]=PointSet[2];
    for (i=3;i<n;i++)
    {
    while (multiply(PointSet[i],ch[top],ch[top-1])>=0) top–;
    ch[++top]=PointSet[i];
    }
    len=top+1;
    }
    // 卷包裹法求点集凸壳,参数说明同graham算法
    void ConvexClosure(POINT PointSet[],POINT ch[],int n,int &len)
    {
    int top=0,i,index,first;
    double curmax,curcos,curdis;
    POINT tmp;
    LINESEG l1,l2;
    bool use[MAXV];
    tmp=PointSet[0];
    index=0;
    // 选取y最小点,如果多于一个,则选取最左点
    for(i=1;i<n;i++)
    {
    if(PointSet[i].y<tmp.y||PointSet[i].y == tmp.y&&PointSet[i].x<tmp.x)
    {
    index=i;
    }
    use[i]=false;
    }
    tmp=PointSet[index];
    first=index;
    use[index]=true;
    index=-1;
    ch[top++]=tmp;
    tmp.x-=100;
    l1.s=tmp;
    l1.e=ch[0];
    l2.s=ch[0];
    while(index!=first)
    {
    curmax=-100;
    curdis=0;
    // 选取与最后一条确定边夹角最小的点,即余弦值最大者
    for(i=0;i<n;i++)
    {
    if(use[i])continue;
    l2.e=PointSet[i];
    curcos=cosine(l1,l2); // 根据cos值求夹角余弦,范围在 (-1 – 1 )
    if(curcos>curmax || fabs(curcos-curmax)<1e-6 && dist(l2.s,l2.e)>curdis)
    {
    curmax=curcos;
    index=i;
    curdis=dist(l2.s,l2.e);
    }
    }
    use[first]=false; //清空第first个顶点标志,使最后能形成封闭的hull
    use[index]=true;
    ch[top++]=PointSet[index];
    l1.s=ch[top-2];
    l1.e=ch[top-1];
    l2.s=ch[top-1];
    }
    len=top-1;
    }
    // 求凸多边形的重心,要求输入多边形按逆时针排序
    POINT gravitycenter(int vcount,POINT polygon[])
    {
    POINT tp;
    double x,y,s,x0,y0,cs,k;
    x=0;y=0;s=0;
    for(int i=1;i<vcount-1;i++)
    {
    x0=(polygon[0].x+polygon[i].x+polygon[i+1].x)/3;
    y0=(polygon[0].y+polygon[i].y+polygon[i+1].y)/3; //求当前三角形的重心
    cs=multiply(polygon[i],polygon[i+1],polygon[0])/2;
    //三角形面积可以直接利用该公式求解
    if(abs(s)<1e-20)
    {
    x=x0;y=y0;s+=cs;continue;
    }
    k=cs/s; //求面积比例
    x=(x+k
    x0)/(1+k);
    y=(y+k
    y0)/(1+k);
    s += cs;
    }
    tp.x=x;
    tp.y=y;
    return tp;
    }
    /所谓凸多边形的直径,即凸多边形任两个顶点的最大距离。下面的算法
    仅耗时O(n),是一个优秀的算法。 输入必须是一个凸多边形,且顶点
    必须按顺序(顺时针、逆时针均可)依次输入。若输入不是凸多边形
    而是一般点集,则要先求其凸包。 就是先求出所有跖对,然后求出每
    个跖对的距离,取最大者。点数要多于5个
    /
    void Diameter(POINT ch[],int n,double &dia)
    {
    int znum=0,i,j,k=1;
    int zd[MAXV][2];
    double tmp;
    while(amultiply(ch[0],ch[k+1],ch[n-1]) > amultiply(ch[0],ch[k],ch[n-1])-EP)
    k++;
    i=0;
    j=k;
    while(i<=k && j<n)
    {
    zd[znum][0]=i;
    zd[znum++][1]=j;
    while(amultiply(ch[i+1],ch[j+1],ch[i]) > amultiply(ch[i+1],ch[j],ch[i]) – EP
    && j< n-1)
    {
    zd[znum][0]=i;
    zd[znum++][1]=j;
    j++;
    }
    i++;
    }
    dia=-1.0;
    for(i=0;i<znum;i++)
    {
    printf("%d %d/n",zd[i][0],zd[i][1]);
    tmp=dist(ch[zd[i][0]],ch[zd[i][1]]);
    if(dia<tmp)
    dia=tmp;
    }
    }

猜你喜欢

转载自blog.csdn.net/hayaqi0504/article/details/88975012