The area of the union of circles

本文详细阐述了解决圆交函数问题的方法,并通过实例展示了如何使用该方法来计算特定几何形状的面积。包括圆的排序、圆与圆之间的交点计算、面积的精确计算等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原来的求圆交函数有问题,交上去WA,用了个别人的

#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const double PI=acos(-1.0);
struct Circle{
    double x,y,r,angle;
    int d;
    Circle(){}
    Circle(double xx,double yy){x=xx;y=yy;}
};
struct Node{//入点+1,可以表示覆盖了多少层
    double angle;
    int flag;
    Node(){}
    Node(double a,int b){angle=a;flag=b;}
};
Circle c[1005];
Node p[2010];
int n;
double dist(Circle a,Circle b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int cmp(Circle a,Circle b){
    return a.r<b.r;
}
void init(){
    sort(c,c+n,cmp);
    int i,j;
    for(i=0;i<n;i++){//计算每个圆被覆盖的次数 
    	//c[i].d=1;
    	for(j=i+1;j<n;j++){
    		//if(c[i].x==c[j].x && c[i].y==c[j].y ||c[i].r<eps) continue;
    		if(c[j].r-c[i].r-dist(c[i],c[j])>-eps) c[i].d++;
    	}   
    }
    
}
int getarc(Circle p,Circle q,double &a,double &b){//a是小角,b是大角
    double d=dist(p,q);
	if(p.r + q.r - d < 0) return 0;//外离 
  	if(fabs(p.r-q.r) - d >-eps) return 0;//内含及内切 
    double tmp1=acos((d*d+p.r*p.r-q.r*q.r)/(2*d*p.r));
    double tmp2=atan2(q.y-p.y,q.x-p.x);
    //printf("%f %f %f %f\n",p.x,p.y,q.x,q.y);
    a=tmp2-tmp1;    b=tmp1+tmp2;
    if(b>PI) b-=2*PI;
    if(a<-PI) a+=2*PI;
    return 2;
}
int CirCrossCir(Circle p1,Circle p2,Circle &cp1,Circle &cp2) {//两圆求交点数 ,并用cp1和cp2存储其交点
    double mx = p2.x - p1.x, sx = p2.x + p1.x, mx2 = mx * mx;
    double my = p2.y - p1.y, sy = p2.y + p1.y, my2 = my * my;
    double sq = mx2 + my2, d = -(sq - (p1.r - p2.r)*(p1.r - p2.r)) * (sq - (p1.r + p2.r)*(p1.r + p2.r));//sq为两圆距离的平方和
    if (d + eps < 0) return 0; if (d < eps) d = 0; else d = sqrt(d);
    double x = mx * ((p1.r + p2.r) * (p1.r - p2.r) + mx * sx) + sx * my2;
    double y = my * ((p1.r + p2.r) * (p1.r - p2.r) + my * sy) + sy * mx2;
    double dx = mx * d, dy = my * d; sq *= 2;
    cp1.x = (x - dy) / sq; cp1.y = (y + dx) / sq;
    cp2.x = (x + dy) / sq; cp2.y = (y - dx) / sq;
    if (d > eps) return 2; else return 1;
}
int cmp1(Node a,Node b){
    if(fabs(a.angle-b.angle)>eps) return a.angle<b.angle;
    else return a.flag>b.flag;
}
double cross(Circle a,Circle b){
    return a.x*b.y-a.y*b.x;
}
double getarea(double a,double r){
    return r*r*(a/2.0);
}
void solve(){
    double area=0;
    
    for(int i=0;i<n;i++){
        int cnt=0,flag=0;
        for(int j=0;j<n;j++){
            if(i==j) continue;
            Circle cp1,cp2;
            double a,b;
            //if(getarc(c[i],c[j],a,b)<2) continue;
            if(CirCrossCir(c[i],c[j],cp2,cp1)<2) continue;
            a = atan2(cp1.y - c[i].y, cp1.x - c[i].x);//求圆心指向交点的向量的极角,注意这里atan2(y,x)函数要先y后x
            b = atan2(cp2.y - c[i].y, cp2.x - c[i].x);
            //
            //printf("%f %f\n",a,b);
            p[cnt++]=Node(a,1); p[cnt++]=Node(b,-1);//a是小角,b是大角
            if(a-b>eps) flag++;//记录圆的最左点被覆盖了多少次
        }
        p[cnt++]=Node(-PI,flag);    p[cnt++]=Node(PI,-flag);
        sort(p,p+cnt,cmp1);
        int s=flag+c[i].d;//+1是因为每段圆弧至少被c[i]这个圆覆盖
        for(int j=1;j<cnt;j++){
            if(s==1) {//被覆盖了1次及以上的面积
                Circle a,b;
                a.x=c[i].x+c[i].r*cos(p[j-1].angle); a.y=c[i].y+c[i].r*sin(p[j-1].angle);
                b.x=c[i].x+c[i].r*cos(p[j].angle); b.y=c[i].y+c[i].r*sin(p[j].angle);
                double k=p[j].angle-p[j-1].angle;
                
                //printf("%f %f %f %f  :%f\n",a.x,a.y,b.x,b.y,cross(a,b)*0.5);
                area+=(k-sin(k))*c[i].r*c[i].r*0.5;//弓形面积
                area+=cross(a,b)*0.5;//有向面积
            }
            s+=p[j].flag;
        }
    }
    
    printf("%.5f\n",area);
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE

    scanf("%d",&n);
    for(int i=0;i<n;i++) {
    	scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].r);
    	c[i].d=1;//至少被自己覆盖 
    }
    init();
    solve();
    return 0;
}


在现有基础上更改程序,存在一个PAC,每3s出现0.05s,当他出现的时候,将Disk_center_history更改为记录这个PAC的坐标,且Disk_center_history中的每个点以50r/min进行选转。def update(frame): global Disk_Center_now,current_circle,cont#定义为全局变量 t = frame * interval / 1000 #总帧数*帧间隔(ms)/1000 将当前时间换算为S #计算Disk位置 cony = 574 * math.sin((cont+19)*math.pi/180) conx = 574 * math.cos((cont+19)*math.pi/180) Disk_now_y = -450 + cony Disk_now_x = -250 + conx base_Disk.center = (Disk_now_x, Disk_now_y) #去除之前帧的Disk轨迹 for circle in current_circle: circle.remove() current_circle.clear() #将当前Disk center坐标与time 记录入Disk_Center_history 数组 Disk_Center_history.append((Disk_now_x,Disk_now_y,t)) #清空Disk center 当前坐标 Disk_Center_now=[] #计算每个历史坐标点 for x_hist, y_hist, t_hist in Disk_Center_history: # 计算旋转时间差 delta_t = t - t_hist #德尔塔time theta=Platenw*delta_t #角度变化量=角速度*德尔塔time #极坐标计算,Disk_Center_history位置,随Platen进行顺时针旋转后的新坐标 cos_theta=math.cos(theta) sin_theta=math.sin(theta) x_rotated=x_hist*cos_theta+y_hist*sin_theta y_rotated=-x_hist*sin_theta+y_hist*cos_theta #将当前时间,Disk_Center_history的坐标记录入Disk_Center_now数组 Disk_Center_now.append((x_rotated,y_rotated)) #绘制当前 for center in Disk_Center_now: new_circle=plt.Circle( center, DiskR, edgecolor='#ff7f0e', facecolor='#ffc899', linewidth=0.5, alpha=1, clip_on=True, #裁剪开启 clip_path=clip_circle #裁剪图形为上述Platen定义范围外设置为透明 ) ax.add_patch(new_circle) current_circle.append(new_circle) #创建Disk轨迹列表每次循环初始化为空 circles=[] for center in Disk_Center_now: circles.append(Point(center).buffer(DiskR,resolution=128)) #模拟分辨率为128 #创建Platen platen_circle=Point(Platen).buffer(PlatenR,128) #创建Platen #合所有Disk轨迹 union_area=unary_union(circles).intersection(platen_circle) #计算有效覆盖面积 vaild_area=union_area.area if not union_area.is_empty else 0.0 total_area_perc=(vaild_area/(math.pi*PlatenR*PlatenR))*100 # title设置 ax.set_title(f"Disk Condtion Sweep Simulator\n\n" f"Total time:{total_time} Current time:{t:.2f} Conditon swp area Percent:{total_area_perc:.2f}%", fontsize=14, pad=20) # title设置 return base_Disk , current_circle
03-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值