hdu 1154& Poj2462 Cutting a Polygon 计算几何模板集合

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1154

用到了直线与线段的关系,直线与线段的交点,点是否在多边形内部,点是否在线段上等的模板

Cutting a Polygon

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 878 Accepted: 256

Description

Given is a simple but not necessarily convex polygon. Given is also a line in the plane. If the polygon is cut along the line then we may get several smaller polygons. Your task is to find the length of the cut, that is the total length of the segments in the intersection of the line and the polygon.

Input

Input consists of a number of cases. The data of each case appears on a number of input lines, the first of which contains two non negative integers n and m giving the number of the vertices of the polygon and the number of cutting lines to consider, 3 <= n <= 1000. The following n lines contain coordinates of the vertices of the polygon; each line contains the x and y coordinates of a vertex. The vertices are given either in clockwise or counterclockwise order. Each of the following m lines of input contains four numbers; these are x and y coordinates of the two points defining the cutting line. If a vertex of the polygon is closer than 1e-8 to the cutting line then we consider that the vertex lies on the cutting line. 

Input is terminated by a line with n and m equal to 0. 

Output


For each cutting line, print the total length of the segments in the intersection of the line and the polygon defined for this test case, with 3 digits after the decimal point. Note: the perimiter of a polygon belongs to the polygon. 

The picture above illustrates the first cutting line for the polygon from the sample.

Sample Input

9 5
0 0
0 2
1 1
2 2
3 1
4 2
5 1
6 2
6 0
-1 2 7.5 1
0 1 6 1
0 1.5 6 1.5
0 2 6 1
0 0 0 2
0 0

Sample Output

2.798
6.000
3.000
2.954
2.000

Source

Waterloo local 2005.06.11

 

 题目大意:

给你一个由n个点构成的多边行,然后一条直线穿过这个多边行,问直线与多边形相交的相邻的点构成线段在多边行内部的长度,即:有多长的线段在多变形内部。

思路,寻找直线与多边形的所有交点,然后判断交点的中点是否在多边形内部,累计求和,

//注意:G++输出要用%0.3f

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EXP exp(1)
#define pppp cout<<endl;
#define EPS 1e-8
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x3f3f3f3f      //1061109567
#define LL_INF 0x3f3f3f3f3f3f3f3f //4557430888798830399
// ios::sync_with_stdio(false);
// 那么cin, 就不能跟C的 scanf,sscanf, getchar, fgets之类的一起使用了。
const int dr[]= {0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]= {-1, 1, 0, 0, -1, 1, -1, 1};
inline int read()//输入外挂
{
    int ret=0, flag=0;
    char ch;
    if((ch=getchar())=='-')
        flag=1;
    else if(ch>='0'&&ch<='9')
        ret = ch - '0';
    while((ch=getchar())>='0'&&ch<='9')
        ret=ret*10+(ch-'0');
    return flag ? -ret : ret;
}
const int maxn=1550;
struct Point
{
    double x;
    double y;

    Point() {}
    Point(double x,double y):x(x),y(y) {}

    Point operator + (const Point &rh) const
    {
        return Point(x+rh.x, y+rh.y);
    }
    Point operator - (const Point &rh)const
    {
        return Point(x-rh.x, y-rh.y);
    }
    bool operator == (const Point &rh)const
    {
        if(x==rh.x && y==rh.y)
            return true;
        return false;
    }
} p[maxn],rec[maxn];
struct Line
{
    Point a;
    Point b;
    Line() {}
    Line(Point a,Point b):a(a),b(b) {}
}line;
int n,m;
double Cross(Point a, Point b)//叉积
{
    return  a.x*b.y-b.x*a.y;
//    double t=a.x*b.y-b.x*a.y;
//    if(t>0)
//        return 1;//锐角
//    else if(t<0)
//        return -1;//钝角
//    else
//        return 0;//平行
}
int CheckLineIntersection(Line L1, Line L2)//判断直线的关系,并返回什么关系
{
    // -1表示直线重合
    // 0表示都平行于y轴,没有交点
    // 1表示L1平行于y轴
    // 2表示L2平行于y轴
    // 3表示不平行于y轴且两条直线也不平行或重合
    // 4表示不平行于y轴但两条直线平行
    int state=0;
    double KL1,KL2;
    if( fabs(L1.a.x-L1.b.x)>EPS )//直线L1不平行于y轴没有斜率
    {
        KL1=(L1.b.y-L1.a.y)/(L1.b.x-L1.a.x);//斜率L1
        state+=1;
    }
    if( fabs(L2.a.x-L2.b.x)>EPS )//直线L1不平行于y轴没有斜率
    {
        KL2=(L2.b.y-L2.a.y)/(L2.b.x-L2.a.x);//斜率L2
        state+=2;
    }
    if(state==0)
    {
        if( fabs(L1.a.x-L2.a.x)<EPS )
            return -1;//表示重合
        return 0;
    }
    else if(state==3)
    {
        if( fabs(KL1-KL2)<EPS )
        {
            double Y1=L1.a.y-L1.a.x*KL1;//直线L1的截距
            double Y2=L2.a.y-L2.a.x*KL2;//直线L2的截距
            if( fabs(Y1-Y2)<EPS )
                return -1;
            return 4;
        }
        return 3;
    }
    else
        return state;
}
Point LineIntersection(Line L1, Line L2, int state)//两条直线的交点,states是两条直线的状态
{
    switch(state)
    {
    case 1://L1存在斜率, L2平行Y轴
    {
        double x=L2.a.x;
        double KL1=(L1.b.y-L1.a.y)/(L1.b.x-L1.a.x);//斜率L1
        double y=(L2.a.x-L1.a.x)*KL1+L1.a.y;
        return Point(x,y);
    }
    case 2://L2存在斜率, L1平行Y轴
    {
        double x=L1.a.x;
        double KL2=(L2.b.y-L2.a.y)/(L2.b.x-L2.a.x);//斜率L2
        double y=(L1.a.x-L2.a.x)*KL2+L2.a.y;
        return Point(x,y);
    }
    case 3://都有斜率
    {
        double KL1=(L1.b.y-L1.a.y)/(L1.b.x-L1.a.x);//斜率L1
        double KL2=(L2.b.y-L2.a.y)/(L2.b.x-L2.a.x);//斜率L2
        double x=(L1.a.x*KL1-L2.a.x*KL2-L1.a.y+L2.a.y)/(KL1-KL2);
        double y=KL1*x-KL1*L1.a.x+L1.a.y;
        return Point(x,y);
    }
    }
}
int CheckLine_SegmentIntersection(Line L, Line S)//直线与线段S的关系,返回什么关系
{
    // -1表示线段直线重合
    // 0表示线段直线平行
    // 1表示线段直线相交
    // 2表示线段直线不相交
    if( fabs(Cross(L.b-L.a, S.b-S.a)) < EPS)//直线与线段平行
    {
        if( fabs(Cross(L.a-S.a, L.b-S.a)) < EPS)//射线与直线重合
            return -1;
        return 0;
    }
    else
    {
        double tmp = Cross(L.b-L.a, S.a-L.a)*Cross(L.b-L.a, S.b-L.a);
        if( tmp < 0.0 || fabs(tmp) < EPS )
            return 1;
        return 2;
    }
}
Point SegmentIntersection(Line S1, Line S2)//线段与线段的交点,states是两条直线的状态
{
    int state=CheckLineIntersection(S1,S2);
    return LineIntersection(S1,S2,state);
}
bool cmp(Point a,Point b)//将交点从左到右排序,从下到上
{
    if(fabs(a.x-b.x)<EPS)
        return a.y<b.y;
    return a.x<b.x;
}
Point mid(Point a,Point b)//返回两个点的重点
{
    return Point((a.x+b.x)/2, (a.y+b.y)/2);
}
bool on_line(Point a, Point b, Point c)//判断点C在线段ab上
{
    if (c.x>=min(a.x,b.x) && c.x<=max(a.x,b.x) && c.y>=min(a.y, b.y) && c.y<=max(a.y,b.y))//去掉在线段上的麻烦
    {
        return (abs((c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y)) <= EPS);
    }
    return false;
}
bool on_flat(Point a)//点是否在多边形内部
{
    int cnt=0;
    Point p1=p[0];
    Point p2;
    for(int i=1; i<=n; ++i)
    {
        p2=p[i];
        if(on_line(p1,p2,a))//表示点在线段上,根据不同要求
        {
            cnt=1;
            break;
        }
        if(a.y>min(p1.y,p2.y))//如果射线交于边的下端点,不算相交,所以是>
        {
            if(a.y<=max(p1.y,p2.y))
            {
                if(a.x<=max(p1.x,p2.x))
                {
                    if(p1.y!=p2.y)//如果射线和边重合,不算
                    {
                        //判断是否满足在这个边的左边,其中(p1.x - p2.x) / (p1.y - p2.y)为tan
                        double tem=(a.y-p2.y)*(p1.x-p2.x)/(p1.y-p2.y)+p2.x;
                        if( p1.x==p2.x || a.x<=tem)
                            cnt++;
                    }
                }
            }
        }
        p1=p2;
    }
    if(cnt&1)
        return true;
    else
        return false;
}
double Dis(Line L)//线段的长度
{
    return sqrt( (L.a.x-L.b.x)*(L.a.x-L.b.x) + (L.a.y-L.b.y)*(L.a.y-L.b.y));
}
double Get_Slove()
{
    int pos=0;
    for(int i=0; i<n; ++i)//寻找直线与多边形边或者点的交点
    {
        int cnt=CheckLine_SegmentIntersection(line,Line(p[i],p[i+1]));
        if(cnt==-1)//重合,相交
        {
            //cout<<i<<" "<<i+1<<"线段与直线重合"<<endl;
            rec[pos++]=p[i];
            rec[pos++]=p[i+1];
        }
        else if(cnt==1)
        {
            rec[pos++]=SegmentIntersection(line, Line(p[i],p[i+1]));
            //cout<<i<<" "<<i+1<<"线段与直线相交"<<endl;
        }
    }
    double ret=0;
//    for(int i=0; i<pos; i++)
//        printf("%lf %lf\n",rec[i].x,rec[i].y);
//    printf("*****\n");
    sort(rec,rec+pos,cmp);
//    for(int i=0; i<pos; i++)
//        printf("%lf %lf\n",rec[i].x,rec[i].y);
//    printf("~~~~~\n");
    for(int i=0; i<pos-1; i++)//计算相交点构成的线段在多边形内部的长度和
    {
        if(on_flat(mid(rec[i],rec[i+1])))
            ret+=Dis(Line(rec[i],rec[i+1]));
    }
    return ret;

}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(!n&&!m)
            break;
        for(int i=0; i<n; ++i)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        p[n]=p[0];
        for(int i=0; i<m; ++i)
        {
            scanf("%lf%lf%lf%lf",&line.a.x,&line.a.y,&line.b.x,&line.b.y);
            printf("%0.3f\n",Get_Slove());
        }
    }
    return 0;
}

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值