一、 问题描述
编写程序,实现如下功能:给定平面上两个圆,根据圆心位置和半径,判别它们是否有公切线。如果有的话,绘制两个圆的所有公切线。 要求:
1、用鼠标交互绘制两个圆,程序获取圆心坐标和半径;
2、通过对两个圆心连线的几何变换绘制所有的公切线。
二、 求解思路
使用ginput函数获取用户选择的两个圆的圆心和圆环上一点以确定圆的位置和半径,首先判断两个圆的半径是否相同,在两个圆半径相同时,若两圆外离,则有4条切线,其中两条外切线直接由圆心的连线顺着圆心连线在两圆心的垂线移动半径的长度得到,两个内切线由比例关系,获取两连线的中心,根据比例关系获得使得线与圆相切时的角度,利用数学关系获得顺时针和逆时针旋转所获得的切线。若两个圆的半径不同,则分外离,相交,内含(外切与内切包含在外离和相交的情形中,即所画的有两条切线重合)。
外离时,两个圆有四条切线,内切线和半径相同时的画法相同,外切线需要将两圆心连线朝着半径小的圆的方向延长,根据几何关系计算出切线与延长线的交点以及切线与延长线的夹角,利用数学表达式得到旋转后的延长线上两点的坐标(一点直接为两线的交点,另外一点选取大圆的圆心),即获得了大圆圆心绕两线交点的点,连结此点与两线交点即可获得切线,由于顺时针旋转和逆时针旋转,所以有两条外切线。
相交时,只有外切线,画法与外离时的画法相同
内含时,没有切线。
三、 程序代码
%Main.m
clear;
clc;
close all
axis([0 100 0 100])
ax=axes('XLim',[0,100],'YLim',[0,100]);
set(gcf,'Renderer','opengl')
axis equal
[h1,x_long1,y_long1,r1]=draw_circle;
set(h1,'Color','g','LineWidth',2)
[h2,x_long2,y_long2,r2]=draw_circle;
set(h2,'Color','g','LineWidth',2)
%默认h1为半径大的圆
hm=max(r1,r2);
if isequal(hm,r2)
[h1,h2]=swap(h1,h2);
[x_long1,x_long2]=swap(x_long1,x_long2);
[y_long1,y_long2]=swap(y_long1,y_long2);
[r1,r2]=swap(r1,r2);
end
hx(1,:)=get(h1,'Xdata');
hy(1,:)=get(h1,'Ydata');
hx(2,:)=get(h2,'Xdata');
hy(2,:)=get(h2,'Ydata');
d=sqrt((hx(1,1)-hx(2,1))^2+(hy(1,1)-hy(2,1))^2);
line_ab=line([hx(1,1),hx(2,1)],[hy(1,1),hy(2,1)]);
set(line_ab,'Color','k','LineWidth',1)
if r1==r2
if d>(r1+r2)
draw_x(r1,r2,hx,hy)
draw_nei(r1,r2,hx,hy)
elseif d<(r1+r2) && d>=(r1-r2)
draw_x(r1,r2,hx,hy)
end
elseif d>(r1+r2)
draw_wai(r1,r2,hx,hy)
draw_nei(r1,r2,hx,hy)
elseif d<(r1+r2) && d>=(r1-r2)
draw_wai(r1,r2,hx,hy)
end
zoom on
%draw_circle.m
function[h,x_long,y_long,r]=draw_circle
h=draw_line;
hold on
h1=get(h,'Xdata');
h2=get(h,'Ydata');
r=sqrt((h1(2)-h1(1))^2+(h2(2)-h2(1))^2);
x_long=zeros(1,360);
y_long=zeros(1,360);
figure(1)
for i=1:360
x_long(i)=h1(1)+r*cos(i*180/pi);
y_long(i)=h2(1)+r*sin(i*180/pi);
plot(x_long(i),y_long(i),'r.','MarkerSize',1);
pause(0.002)
hold on
end
end
function h=draw_line
x1=zeros(2,2);
x1(1,:)=draw_point;
x1(2,:)=draw_point;
h=line(x1(:,1),x1(:,2));
end
function x1=draw_point
x1=roundn(ginput(1),-1);
text(x1(1),x1(2),['(',num2str(x1(1)),',',num2str(x1(2)),')']);
end
%画外切线
draw_wai.m
function draw_wai(r1,r2,hx,hy)
[theta,point]=prep_wai(r1,r2,hx,hy);
kx=point(1)+(hx(1,1)-point(1))*cos(theta)-(hy(1,1)-point(2))*sin(theta);
ky=point(2)+(hy(1,1)-point(2))*cos(theta)+(hx(1,1)-point(1))*sin(theta);
lineq=line([kx point(1)],[ky point(2)]);
set(lineq,'LineWidth',3)
kx=point(1)+(hx(1,1)-point(1))*cos(theta)+(hy(1,1)-point(2))*sin(theta);
ky=point(2)+(hy(1,1)-point(2))*cos(theta)-(hx(1,1)-point(1))*sin(theta);
lineq1=line([kx point(1)],[ky point(2)]);
set(lineq1,'LineWidth',3)
end
function [theta,point]=prep_wai(r1,r2,hx,hy)
d=sqrt((hx(1,1)-hx(2,1))^2+(hy(1,1)-hy(2,1))^2);
dp=d/(r1/r2-1);
theta=asin(r2/dp);
point=zeros(1,2);
point(1)=hx(2,1)+(hx(2,1)-hx(1,1))*dp/d;
point(2)=hy(2,1)+(hy(2,1)-hy(1,1))*dp/d;
point=roundn(point,-1);
plot(point(1),point(2),'r','MarkerSize',3)
text(point(1),point(2),['(',num2str(point(1)),',',num2str(point(2)),')'])
end
%画内切线
draw.nei.m
function draw_nei(r1,r2,hx,hy)
[thetas,points]=prep_nei(r1,r2,hx,hy);
px1=points(1)+(hx(1,1)-points(1))*cos(thetas)-(hy(1,1)-points(2))*sin(thetas);
py1=points(2)+(hy(1,1)-points(2))*cos(thetas)+(hx(1,1)-points(1))*sin(thetas);
px2=points(1)+(hx(2,1)-points(1))*cos(thetas)-(hy(2,1)-points(2))*sin(thetas);
py2=points(2)+(hy(2,1)-points(2))*cos(thetas)+(hx(2,1)-points(1))*sin(thetas);
lineqs=line([px1 px2],[py1 py2]);
set(lineqs,'LineWidth',3)
px10=points(1)+(hx(1,1)-points(1))*cos(thetas)+(hy(1,1)-points(2))*sin(thetas);
py10=points(2)+(hy(1,1)-points(2))*cos(thetas)-(hx(1,1)-points(1))*sin(thetas);
px20=points(1)+(hx(2,1)-points(1))*cos(thetas)+(hy(2,1)-points(2))*sin(thetas);
py20=points(2)+(hy(2,1)-points(2))*cos(thetas)-(hx(2,1)-points(1))*sin(thetas);
lineqs1=line([px10 px20],[py10 py20]);
set(lineqs1,'LineWidth',3)
end
function [thetas,points]=prep_nei(r1,r2,hx,hy)
d=sqrt((hx(1,1)-hx(2,1))^2+(hy(1,1)-hy(2,1))^2);
d1=d/(r2/r1+1);
thetas=asin(r1/d1);
points=zeros(1,2);
points(1)=hx(1,1)+(hx(2,1)-hx(1,1))*d1/d;
points(2)=hy(1,1)+(hy(2,1)-hy(1,1))*d1/d;
points=roundn(points,-1);
plot(points(1),points(2),'r','MarkerSize',3)
text(points(1),points(2),['(',num2str(points(1)),',',num2str(points(2)),')'])
end
%画两个圆半径相等时的外切线
draw_x.m
function draw_x(r1,r2,hx,hy)
d=sqrt((hx(1,1)-hx(2,1))^2+(hy(1,1)-hy(2,1))^2);
fx=(hx(2,1)-hx(1,1))/d;
fy=(hy(2,1)-hy(1,1))/d;
xnew1=hx(1,1)-fy*r1;
ynew1=hy(1,1)+fx*r1;
xnew2=hx(2,1)-fy*r1;
ynew2=hy(2,1)+fx*r1;
line1=line([xnew1,xnew2],[ynew1,ynew2]);
set(line1,'LineWidth',3)
xnew3=hx(1,1)+fy*r1;
ynew3=hy(1,1)-fx*r1;
xnew4=hx(2,1)+fy*r1;
ynew4=hy(2,1)-fx*r1;
line2=line([xnew3,xnew4],[ynew3,ynew4]);
set(line2,'LineWidth',3)
end
四、 实验结果
两个圆半径相等时外切
两个圆半径相等时相交
两个圆外离时
两个圆相交时
两个圆内含时
五、 实验心得
- 注意分类讨论,针对特殊情况
- 注意合理使用函数,减少代码量
- 合理使用数学表达式