问题描述:有圆O1,O2,已知两圆圆心,半径,绘制两圆外公切线。 效果如下图所示:
问题可转换为求四个切点的坐标。B1为例,大致思路如下:
设x=x2-x1,y=y2-y1,d为O1,O2两点的距离
d方=x方+y方,能看明白吧?
设一个点P,P为O1O2与A1B1的交点。设l为PO2距离
则l=d*r/(R-r)
由于d是可表达的,自然l是可表达的
设B1P长为s
由于三角形O2B1P为直角三角形,B1为直角。
r方+S方=l方
所以这里s也是可表达的。所以B1到O1O2这个轴的距离为 s*r/l
设B1到O1O2轴的垂点为M,则O2M=r*r/l
上面两步用到了相似三角形原理。
剩下就简单了,M的坐标为 (x2+O2M*(x2-x1)/d,y2+O2M*(y2-y1)/d)
最后我们已知M点,已知过M点的一条直线O1O1,知道垂线的长度,自然就知道这个端点B1了,表达式太复杂,就不写了
这个程序主要是思路,
public struct TyphoonCircle
{
private PointF center;
private int radius;
/// <summary>
/// 获取或设置台风中心点坐标
/// </summary>
public PointF Center
{
get
{
return this.center;
}
set
{
this.center = value;
}
}
{
private PointF center;
private int radius;
/// <summary>
/// 获取或设置台风中心点坐标
/// </summary>
public PointF Center
{
get
{
return this.center;
}
set
{
this.center = value;
}
}
/// <summary>
/// 获取或设置台风中心点坐标
/// </summary>
public int Radius
{
get
{
return this.radius;
}
set
{
this.radius = value;
}
}
/// 获取或设置台风中心点坐标
/// </summary>
public int Radius
{
get
{
return this.radius;
}
set
{
this.radius = value;
}
}
/// <summary>
/// 根据中心点坐标和风场半径建立台风圈实体结构体
/// </summary>
/// <param name="center">中心点坐标</param>
/// <param name="radius">风场半径</param>
public TyphoonCircle(PointF center,int radius)
{
this.center = center;
this.radius = radius;
}
}
/// 根据中心点坐标和风场半径建立台风圈实体结构体
/// </summary>
/// <param name="center">中心点坐标</param>
/// <param name="radius">风场半径</param>
public TyphoonCircle(PointF center,int radius)
{
this.center = center;
this.radius = radius;
}
}
/// <summary>
/// 计算 circle1 与 circle2 的外公切线与circle2的交点坐标
/// </summary>
/// <param name="circle1">台风风圈</param>
/// <param name="circle2">台风风圈</param>
/// <returns>PointF</returns>
private PointF[] ReturnCutPoints(TyphoonCircle circle1, TyphoonCircle circle2)
{
//切点
PointF[] CutPoints = new PointF[2];
if (circle1.Radius != circle2.Radius)
CutPoints = CalculateForDifferentRadius(circle1, circle2);
return CutPoints;
} //ReturnCutPoints
/// 计算 circle1 与 circle2 的外公切线与circle2的交点坐标
/// </summary>
/// <param name="circle1">台风风圈</param>
/// <param name="circle2">台风风圈</param>
/// <returns>PointF</returns>
private PointF[] ReturnCutPoints(TyphoonCircle circle1, TyphoonCircle circle2)
{
//切点
PointF[] CutPoints = new PointF[2];
if (circle1.Radius != circle2.Radius)
CutPoints = CalculateForDifferentRadius(circle1, circle2);
return CutPoints;
} //ReturnCutPoints
/// <summary>
/// 在半径不相等的情况下,计算 circle1 和 circle2 两圆的外公切线与 circle2 的交点坐标
/// </summary>
/// <param name="circle1">台风风圈</param>
/// <param name="circle2">台风风圈</param>
private PointF[] CalculateForDifferentRadius(TyphoonCircle circle1, TyphoonCircle circle2)
{
//令circle1,circle2的外公切线交与点P
//circle1的圆心为O1,circle2的圆心为O2
//切线于circle1的两个焦点分别记作A1,A2
//切线于circle2的两个焦点分别记作B1,B2
//过点 B1 引 O1O2 的垂线,垂足为 M
// O1O2 的斜率为k
/// 在半径不相等的情况下,计算 circle1 和 circle2 两圆的外公切线与 circle2 的交点坐标
/// </summary>
/// <param name="circle1">台风风圈</param>
/// <param name="circle2">台风风圈</param>
private PointF[] CalculateForDifferentRadius(TyphoonCircle circle1, TyphoonCircle circle2)
{
//令circle1,circle2的外公切线交与点P
//circle1的圆心为O1,circle2的圆心为O2
//切线于circle1的两个焦点分别记作A1,A2
//切线于circle2的两个焦点分别记作B1,B2
//过点 B1 引 O1O2 的垂线,垂足为 M
// O1O2 的斜率为k
//切点
PointF[] CutPoints = new PointF[2];
PointF[] CutPoints = new PointF[2];
float deltaX = circle2.Center.X - circle1.Center.X;
float deltaY = circle2.Center.Y - circle1.Center.Y;
float deltaY = circle2.Center.Y - circle1.Center.Y;
// O1 与 O2 距离
float distance = 0;
float distance = 0;
// P 与 O2 距离
float lengthA;
float lengthA;
// P 与 B1 或 B2点的距离
float lengthB;
float lengthB;
// B1 到 O1O2的距离
float lengthC;
float lengthC;
// O2 到 M 的距离
float lengthD;
float lengthD;
//用于记录 M 点的坐标
PointF M = new PointF();
PointF M = new PointF();
// O1O2 直线方程的斜率k
float k;
float k;
// O1O2 直线方程中的常数
float b;
float b;
distance = (float)(Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2)));
lengthA = distance * circle1.Radius / (circle2.Radius - circle1.Radius);
lengthA = distance * circle1.Radius / (circle2.Radius - circle1.Radius);
lengthB = (float)(Math.Sqrt(Math.Pow(lengthA, 2) - Math.Pow(circle1.Radius, 2)));
lengthC = lengthB * circle1.Radius / lengthA;
lengthD = circle1.Radius * circle1.Radius / lengthA;
lengthC = lengthB * circle1.Radius / lengthA;
lengthD = circle1.Radius * circle1.Radius / lengthA;
M.X = circle1.Center.X + lengthD * -deltaX / distance;
M.Y = circle1.Center.Y + lengthD * -deltaY / distance;
M.Y = circle1.Center.Y + lengthD * -deltaY / distance;
k = (circle1.Center.Y - circle2.Center.Y) / (circle1.Center.X - circle2.Center.X);
b = circle1.Center.Y - k * circle1.Center.X;
b = circle1.Center.Y - k * circle1.Center.X;
float Y = 0;
float X = 0;
float X = 0;
Y = (float)((k * M.X + M.Y * k * k + b + Math.Abs(lengthC) * Math.Sqrt(k * k + 1)) / (k * k + 1));
X = M.X - (Y - M.Y) * k;
CutPoints[0].X = X;
CutPoints[0].Y = Y;
X = M.X - (Y - M.Y) * k;
CutPoints[0].X = X;
CutPoints[0].Y = Y;
Y = (float)((k * M.X + M.Y * k * k + b - Math.Abs(lengthC) * Math.Sqrt(k * k + 1)) / (k * k + 1));
X = M.X - (Y - M.Y) * k;
CutPoints[1].X = X;
CutPoints[1].Y = Y;
X = M.X - (Y - M.Y) * k;
CutPoints[1].X = X;
CutPoints[1].Y = Y;
return CutPoints;
} //CalculateForDifferentRadius
} //CalculateForDifferentRadius