两圆的外切线与内切线的切点算法
最近想画两球⽔滴效果所有在⽹上找两圆外切点和内切点的算法,找了很久没有找到所以⾃⼰写了⼀个⼯具类来计算两圆的公切线点。具体效果如下图:
根据CircleUtils类的getCircleTangentPointOut⽅法返回外切点坐标[r1p1, r1p2, r2p1, r2p2],依次为左边圆的两个切点坐标和右边两个切点坐标。
根据CircleUtils类的getCircleTangentPointIn⽅法返回外切点坐标[r1p1, r1p2, r2p1, r2p2],依次为左边圆的两个切点坐标和右边两个切点坐标。
根据获取坐标绘制线段和点,如下代码
@Override
protected void onDraw(Canvas canvas) {
mPaint.tColor(0XFF00FF00);
mPaint.tStyle(Paint.Style.FILL);
float r1 = 300;
float r2 = 200;
canvas.drawCircle(mDownX, mDownY, r1, mPaint);
canvas.drawCircle(mCurrentX, mCurrentY, r2, mPaint);
PointF[] points2 = CircleTangentPointOut(new PointF(mDownX, mDownY), r1, new PointF(mCurrentX, mCurrentY), r2); if (points2 != null) {
mPaint.tColor(0XFF0000FF);
mPaint.tStyle(Paint.Style.STROKE);
canvas.drawLine(points2[0].x, points2[0].y, points2[2].x, points2[2].y, mPaint);
canvas.drawLine(points2[1].x, points2[1].y, points2[3].x, points2[3].y, mPaint);
}
PointF[] points1 = CircleTangentPointIn(new PointF(mDownX, mDownY), r1, new PointF(mCurrentX, mCurrentY), r2); if (points1 != null) {
mPaint.tColor(0XFF0000FF);
mPaint.tStyle(Paint.Style.STROKE);
canvas.drawLine(points1[0].x, points1[0].y, points1[3].x, points1[3].y, mPaint);
canvas.drawLine(points1[1].x, points1[1].y, points1[2].x, points1[2].y, mPaint);
}
if (points2 != null) {
mPaint.tColor(0XFFFF0000);
mPaint.tStyle(Paint.Style.FILL);
for (PointF p : points2) {
canvas.drawCircle(p.x, p.y, 5, mPaint);
牛肉炒青椒怎么做}
}
if (points1 != null) {
mPaint.tColor(0XFFFF0000);
mPaint.tStyle(Paint.Style.FILL);
for (PointF p : points1) {
canvas.drawCircle(p.x, p.y, 5, mPaint);
}
}
}
CircleUtils⼯具类如下代码
public class CircleUtils {
//返回两圆外切点坐标
蔺相如读音
public static PointF[] getCircleTangentPointOut(PointF c1, float r1, PointF c2, float r2){
float centerLine = getPointDistance(c1, c2);
if(centerLine > Math.abs(r1-r2)){
//计算外切
PointF[] points = new PointF[8];
//圆⼼连线与圆1的交点
PointF r1Point = ratioPoint(c1, c2, r1/centerLine);
points[6] = r1Point;
//圆⼼连线与圆2的交点
PointF r2Point = ratioPoint(c1, c2, (centerLine-r2)/centerLine);
points[7] = r2Point;
/
/两元交点连线和两圆焦点在左边圆的⾓度
float angleR1 = getAngle(r1, centerLine, r2);
牵牛花儿歌
//两元交点连线和两圆焦点在右边圆的⾓度
float angleR2 = getAngle(r2, centerLine, r1);
//外切线与圆⼼连线的⾓度(0~90度之间的⾓度)
float angle = (float) Math.acos(Math.abs(r1-r2)/centerLine);
float angle = (float) Math.acos(Math.abs(r1-r2)/centerLine);
赤城山//两圆的交点
points[4] = rotatePoint(r1Point, c1, angleR1);
points[5] = rotatePoint(r2Point, c2, angleR2);
if(r1>=r2){
/
/切线与第⼀个圆的交点
points[0] = rotatePoint(r1Point, c1, angle);
points[1] = rotatePoint(r1Point, c1, -angle);
做最好的自己图片//切线与第⼆个圆的交点
points[2] = rotatePoint(r2Point, c2, -(float) (Math.PI-angle));
points[3] = rotatePoint(r2Point, c2, (float) (Math.PI-angle));
}el{
//切线与第⼀个圆的交点
points[0] = rotatePoint(r1Point, c1, (float) (Math.PI-angle));
points[1] = rotatePoint(r1Point, c1, -(float) (Math.PI-angle));
//切线与第⼆个圆的交点
points[2] = rotatePoint(r2Point, c2, -angle);
points[3] = rotatePoint(r2Point, c2, angle);
}
天空蓝
return points;
}
return null;
}
//返回两圆内切点坐标
public static PointF[] getCircleTangentPointIn(PointF c1, float r1, PointF c2, float r2){ float centerLine = getPointDistance(c1, c2);
if(centerLine > r1+r2){
//计算内切
PointF[] points = new PointF[7];
//内切线焦点
points[4] = new PointF((c1.x*r2+c2.x*r1)/(r1+r2), (c1.y*r2+c2.y*r1)/(r1+r2));
float l1 = centerLine*r1/(r1+r2);
float l2 = centerLine*r2/(r1+r2);
//圆⼼连线与圆1的交点
points[5] = ratioPoint(c1, points[4], r1/l1);
float angle = (float) Math.acos(r1/l1);
//第1个圆的切点
points[0] = rotatePoint(points[5], c1, angle);红泥巴村
points[1] = rotatePoint(points[5], c1, -angle);
/
/圆⼼连线与圆2的交点
points[6] = ratioPoint(points[4], c2, (l2-r2)/l2);
//第2个圆的切点
points[2] = rotatePoint(points[6], c2, -angle);
points[3] = rotatePoint(points[6], c2, angle);
return points;
}
return null;
}
//根据点 a, b, c位置距离为ab, bc, ac获取b点在ac上的垂点d,返回垂点d
public static PointF getVerticalPoint( PointF a, PointF b, PointF c){
float ab =getPointDistance(a, b);
float ac =getPointDistance(a, c);
float bc =getPointDistance(b, c);
return getVerticalPoint(ab, ac, bc, a, c);
}
public static PointF getVerticalPoint(float ab,float ac, float bc, PointF a, PointF c){
float angle = getAngle(ab, ac, bc);
float ratio = (float) (s(angle)*ab)/ac;
return ratioPoint(a, c, ratio);
}
//返回两点之间的距离
public static float getPointDistance(PointF a, PointF b){
return (float) Math.sqrt(Math.pow(a.x-b.x,2)+Math.pow(a.y-b.y, 2));
}
神态描写的词语//根据点 a, b, c位置距离为ab, bc, ac获取a点⾓度
public static float getAngle(float ab,float ac, float bc){
return (float) Math.acos((ab*ab+ac*ac-bc*bc)/(2*ab*ac));
}
//获取⼀个点,起始点到该点长度除以起始点到结束点长度的⽐例为ratio
public static PointF ratioPoint(PointF startPoint, PointF endPoint, float ratio){ if(startPoint == null){
startPoint = new PointF(0, 0);
}
PointF ret = new PointF();
float x = endPoint.x-startPoint.x;
float y = endPoint.y-startPoint.y;
ret.x = x*ratio+startPoint.x;
ret.y = y*ratio+startPoint.y;
return ret;
}
//空间⼀个点围绕center点旋转angle⾓度后的位置
public static PointF rotatePoint(PointF point, PointF center, float angle){
if(center == null){
center = new PointF(0, 0);
}
PointF ret = new PointF();
//获取相对位置
float x = point.x-center.x;
float y = point.y-center.y;
//根据选择矩阵旋转后加上中⼼点位置
ret.x = (float) ((s(angle)-y*Math.sin(angle))+center.x);
ret.y = (float) ((x*Math.sin(angle)+s(angle))+center.y);
return ret;
}
}