目录
1 两圆求交
1.1 思路
可以将两圆的位置关系分为3种情况:相交、相离、包含(这里我将外部相切的情况归为相离,内部相切归为包含。)
我们知道两圆的圆心坐标以及半径,就可以根据圆心距与两圆半径距离进行相交情况判断。伪代码如下
%dis为圆心距
if(dis >= r1+r2||abs(r1-r2) >= dis)
%相离、包含
else
%相交
end
1.2 求交点
首先考虑最一般的情况,及两圆相交,有两个交点。如何求相交点呢?
第一种方法是联立两圆方程求解,这种方法比较暴力,鄙人喜欢温柔一点的。
第二种方式是几何方法:
- 求圆心向量AB与X轴夹角θ
- 求向量AC与向量AB的夹角α
联立方程,求出AD
AD2+ CD2= r12
DB2+ CD2= r22
r12+ r22=AB2
α=acos(AD/AC)
上述方程没有考虑到∠ACB是否为90°,所以有问题。这里采用余弦定理
α=acos((r12+AB2-r22)/2*r1*AB) - 那么C点和E点坐标也就知道了。
具体代码如下
%两圆心连线与X轴夹角,逆时针为正
theta = atan2d(y2-y1,x2-x1);
ad = (r1^2-r2^2+dis^2)/(2*dis);
%交点与圆心1连线的向量与两圆心连线的夹角
alpha = acosd(ad/r1);
crosspoint1 = [x1+r1*cosd(theta+alpha);y1+r1*sind(theta+alpha)];
crosspoint2 = [x1+r1*cosd(theta-alpha);y1+r1*sind(theta-alpha)];
crosspoint = [crosspoint1 crosspoint2];
1.3 选取探测点
找到交点(图中红点)后下一步就是找两圆重合部分,这里以找到重合部分的边界(图中红线)为目标。
我们知道一个圆上2个点可以将圆分为2段弧,3个点可以分为3段弧。现在相交圆上有两段弧,我们可以在每段弧中间再取一个点,作为试探点(上图中蓝点),检查该点是否同时满足距离两圆圆心距离小于其圆的半径,若满足则说明该试探点所在的弧为重合部分的边界(即重合弧)。
遍历两圆上的试探点,我们就得到了所有重合弧的信息啦!弧的表示方法有很多种,这里我用5个参数表示一段弧。
x %弧所在圆的坐标
y
r%弧所在圆的半径
startAngle%弧的起始角
plusAngle%弧的增加角
圆弧的表示
1.4 相离 包含情况
需要注意的是,当两圆为相离或包含关系时是没有交点的,但是怎么确定包含关系时的重合边界呢?
这里可以将整个圆当做一条弧,规定当圆上没有相交点时,人为的增加一个探测点。然后再进行探测点遍历就可以啦
相离情况
包含情况
2 多圆求交
多个圆相交也是上面的思路,首先对所有圆进行遍历,两两求交点,根据交点求每一个圆上的探测点;再对所有圆进行遍历,检验探测点是否满足条件,满足的话该探测点所在的弧即为重合的边界(重合弧)。
3 圆环求交
3.1 思路
圆如何求交我们已经搞清楚了,两个圆环求交呢?
首先考虑一个圆环是由两个同心圆组成的,一个内圆一个外圆。我们完全可以把一个圆环看成两个圆,从而两个圆环求交的问题就转化成了4个圆求交的问题。
3.2 操作
需要注意的是,探测点与内外圆的判断是不一样的。
- 对于外圆,重合部分的探测点到该圆圆心的距离应<=该圆半径
- 对于内圆,重合部分的探测点到该圆圆心的距离应>=该圆半径
其他部分都一样
3 多圆环求交
想必你已经会了
4 matlab代码
使用了面向对象编程思想,将圆封装成了一个类,包含了圆的位置坐标,半径,交点等属性。
完整程序下载链接: matlab 多圆/圆环求交.
觉得有帮助的话就给我个赞吧!!!
main函数
clear all
% circle = 5*rand(3,4);%随机生成10个圆
%3圆环 前两行为XY坐标,第三行为半径,第四行为内外圆标志
circle = [0,0,4,4,2,2;
0,0,0,0,3,3;
1,6,1,6,1,6;
-1,1,-1,1,-1,1];
%2圆环
% circle = [0,0,5,5;
% 0,0,0,0;
% 3,6,3,6;
% -1,1,-1,1];
%3外圆
% circle = [-1,1,0;
% 0,0,2;
% 3,3,3;
% 1,1,1];
[~,col] = size(circle);
%-------交点列表 用于画图-------
crossPointsList = [];
%---------弧列表初始化----------
arcList = [];
%---------圆列表初始化----------
cirList(col) = Circle();%建立类对象数组
for i = 1:col
cirList(i).cirNum = i;
cirList(i).cirPro = circle(1:3,i) ;
cirList(i).cirType = circle(4,i) ;
end
%---------遍历所有圆,两两求交--------
for i = 1:col
for j = i+1:col
%两圆交点
crossPoint = ArcIntersection(cirList(i),cirList(j));
%如果有交点
if ~isempty(crossPoint)
%将交点放入交点列表
crossPointsList(:,end+1:end+2) = crossPoint;
end
end
PlotCir(cirList(i));%画圆
hold on;
cirList(i).CrossPointsAngle();
%画出两交点的中心点
if ~isempty(cirList(i).middlePoints)
plot(cirList(i).middlePoints(1,:), ...
cirList(i).middlePoints(2,:), ...
'b.','MarkerSize',25);
hold on;
end
end
%--------画出交点-----------
if isempty(crossPointsList)
fprintf('无任何交点\n');
else
plot(crossPointsList(1,:),crossPointsList(2,:),'r.','MarkerSize',25);
hold on;
end
%% 判断哪些弧属于重合弧
for i = 1:col
arcFlag = (1:cirList(i).numArc);
for j = 1:col
%跳过自身判断
if (i == j)
continue
end
pos = cirList(i).middlePoints - cirList(j).cirPro(1:2,:);
d = (pos(1,:).^2+pos(2,:).^2).^0.5;
%如果是外圆
if cirList(j).cirType == 1
arcOder_temp = find(d <= cirList(j).cirPro(3)); %满足要求的弧的序号
else %如果是内圆
arcOder_temp = find(d >= cirList(j).cirPro(3)); %满足要求的弧的序号
end
arcFlag = intersect(arcFlag,arcOder_temp);
end
%如果没有n次相交弧
if isempty(arcFlag)
fprintf('%d号圆没有n次相交弧\n', cirList(i).cirNum);
continue;
else
arcList_temp = ones(3,size(arcFlag,2)).*cirList(i).cirPro;
arcList_temp(4,:) = cirList(i).cirArcList(1,arcFlag);
arcList_temp(5,:) = cirList(i).cirArcList(2,arcFlag);
arcList = [arcList arcList_temp];
end
end
%画圆弧
PlotArc(arcList,20)
title('三圆环求交')
xlabel('x')
ylabel('y')
%axis([-5 5 -5 5])
Circle类
classdef Circle < handle
%圆类
properties
%圆的编号
cirNum
%圆的属性 x y r
cirPro
%圆的类型 1代表外环 -1代表内圆 0代表默认值
cirType
%圆的交点列表 交点的 x y
cirCrossPoints
%圆弧列表 第一行是弧的起点角度 第二行是弧转过的角度(逆时针)
cirArcList
%圆弧数量
numArc
%交点中间点 x y angle
middlePoints
end
methods
%构造函数
function obj = Circle(ciecle,type)
if nargin == 0
obj.cirPro = zeros(3,1);
obj.cirType = 0;
elseif nargin == 1
obj.cirPro = ciecle;
obj.cirType = 0;
elseif nargin == 2
obj.cirPro = ciecle;
obj.cirType = type;
end
end
%求交点与圆心连线 与 X轴夹角
function CrossPointsAngle(obj)
xr = obj.cirPro(1);
yr = obj.cirPro(2);
r = obj.cirPro(3);
if ~isempty(obj.cirCrossPoints)
vector = [obj.cirCrossPoints(1,:)-xr;obj.cirCrossPoints(2,:)-yr];
%向量与X轴夹角
angle = atan2d(vector(2,:),vector(1,:));
%根据夹角排序
angleOrder = CrossPointsSort(angle);
%生成圆弧列表
obj.cirArcList(1,:) = angleOrder;
obj.cirArcList(2,:) = [diff(angleOrder),angleOrder(1)-angleOrder(end)+360];
%计算圆弧数量
obj.numArc = size(obj.cirArcList,2);
%生成每个段圆弧的中间的探测点
middleAngle = obj.cirArcList(1,:) + obj.cirArcList(2,:)/2;
obj.middlePoints = [xr+cosd(middleAngle).*r;yr+sind(middleAngle).*r];
else %如果一个圆和其他圆没有交点,规定一个默认探测点
obj.cirArcList(1,:)=0;
obj.cirArcList(2,:)=360;
obj.numArc = 1;
%生成每个段圆弧的中间的探测点
middleAngle = obj.cirArcList(1,:) + obj.cirArcList(2,:)/2;
obj.middlePoints = [xr+cosd(middleAngle).*r;yr+sind(middleAngle).*r];
%fprintf('%d号圆与其他圆均无交点\n',obj.cirNum);
end
end
end
end
%圆的交点排序 交点与X轴夹角从小到大排序
function angleOrder = CrossPointsSort(angle)
array = find(angle<0);
%将小于0°的角度 转为大于0°
angle(array)=angle(array)+360;
%对圆的交点列表进行排序 因为是行排序 用sortrows时需要转置
tempAngle = sortrows(angle',1);
angleOrder = tempAngle';
end