题意
https://www.acwing.com/problem/content/description/3037/
求以原点为圆心的圆与一个简单多边形的面积交
思路
将多边形每条边与圆心相连求出其围成的有向三角形面积(三角剖分思想) 相加即为答案
求次面积交要考虑的状态如下:(设多边形某条边上的两端点为Point a,b,如果线段ab与圆有交点 设交点为pa pb)
1.a b均在圆内 S = Cross(a,b)/2;
2.a b均在圆外
①ab与圆心O的距离 >= R, S = aOb的扇形面积
②距离 < R,S = paOPb的扇形面积 + aOpa的有向S△ + pbOb的有向S△
3.a在圆内 b在圆外 S = bOpb的扇形 + aOpb的有向S△
4.b在圆内 a在圆外 S = paOa的扇形 + paOb的有向S△
以上,在通过扇形面积公式 线段与圆心间的关系处理细节即可
求出交就能就并~
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
int n;
double R;
struct Point{
double x,y;
Point(double x = 0,double y = 0) : x(x),y(y) {}
}p[55],O;
typedef Point Vector;
int dcmp(double x){//equal ?= 0
if(fabs(x) < eps) return 0;
else return x < 0 ? -1:1;
}
//运算符重载
Vector operator + (Vector A, Vector B){ return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (Vector A, Vector B){ return Vector(A.x-B.x, A.y-B.y); }
Vector operator * (Vector A, double p){ return Vector(A.x*p, A.y*p); }
Vector operator / (Vector A, double p){ return Vector(A.x/p, A.y/p); }
bool operator == (const Point &a, const Point &b){
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
double Dot(Vector A, Vector B){ return A.x*B.x + A.y*B.y; }
double Cross(Vector A, Vector B){ return A.x*B.y - A.y*B.x; }
double dis(Point A,Point B){
return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y));
}
double Length(Vector A){return sqrt(Dot(A,A));}//计算向量的长度
Point rotate(Point a, double b){
return {a.x * cos(b) + a.y * sin(b), -a.x * sin(b) + a.y * cos(b)};
}
Vector norm(Vector a){//将向量a标准化
return a / Length(a);
}
//P点再P1P2上 叉积==0
bool OnSeg(Point P,Point P1,Point P2){
return dcmp(Cross(P1 - P,P2 - P)) == 0 && dcmp(Dot(P1 - P,P2 - P)) <= 0;
}
//判断共线
bool Collinear(Point P1,Point P2,Point P3,Point P4){
double A = Cross(P1 - P2,P3 - P2);
double B = Cross(P1 - P2,P4 - P2);
if(!dcmp(A) && !dcmp(B)) return 1;
return 0;
}
//两参数方程求交点,(点,向量,点,向量)
Point Getlinenode(Point P,Point v,Point Q,Point w){
//线1(P1,P2) 线2(P3,P4) (P1,P1-P2,P3,P3-P4)
Point u = P - Q;
double t = Cross(w,u) / Cross(v,w);
return P + v * t;
}
double get_sector(Point a,Point b){//Oa Ob扇形面积
double angle = acos(Dot(a,b) / Length(a) / Length(b));//aOb夹角
if(dcmp(Cross(a,b)) < 0) angle *= -1.0;//面积是有向的
return R * R * angle / 2.0;//面积
}
//求圆与直线的交点
double get_C_L_Ist(Point a,Point b,Point &pa,Point &pb){//ab 与 O 相交于 papb
Point e = Getlinenode(a,b - a,O,rotate(b - a,pi / 2.0));//e是O垂直于ab的垂足
double mind = dis(O,e);//垂线的距离
if(!OnSeg(e,a,b)){
mind = min(dis(O,a),dis(O,b));//O到线段ab的最短距离
}
if(dcmp(R - mind) <= 0) return mind;//ab均在圆外 返回最短距离 不比再求交点
double len = sqrt(R * R - dis(O,e)*dis(O,e));//欲加在e上的向量长度 勾股
pa = e + norm(a - b) * len;
pb = e + norm(b - a) * len;
return mind;
}
double get_circle_triangle_area(Point a,Point b){
double disa = dis(a,O),disb = dis(b,O);
if(dcmp(R - disa) >= 0 && dcmp(R - disb) >= 0) return Cross(a,b)/2.0;//a b在圆内 等于S△
if(Collinear(a,O,b,O)) return 0.0;//O a b三点共线
Point pa,pb;//ab与圆的两个交点
double mind = get_C_L_Ist(a,b,pa,pb);//线段ab与O的最近距离
if(dcmp(R - mind) <= 0) return get_sector(a,b);//扇形aOb
if(dcmp(R - disa) >= 0) return get_sector(pb,b)+Cross(a,pb)/2.0;
if(dcmp(R - disb) >= 0) return get_sector(a,pa)+Cross(pa,b)/2.0;
//扇形+△ 注意有向!
return get_sector(a,pa)+get_sector(pb,b)+Cross(pa,pb)/2.0;//俩扇形+△
}
double work(){
double ans = 0.0;
for(int i = 0;i < n; i++){
ans += get_circle_triangle_area(p[i],p[(i + 1) % n]);
}
return fabs(ans);
}
int main()
{
O.x = 0,O.y = 0;
while(~scanf("%lf %d",&R,&n)){
for(int i = 0;i < n; i++) scanf("%lf %lf",&p[i].x,&p[i].y);
printf("%.2f\n",work());
}
}