首先回忆一下初中直线方程式:
根据两点式整理得一般式:
(y1-y2)x+(x2-x1)y+x1y2-x2y1=0
由 AX+BY+C=0 得 A=y1-y2 B=x2-x1 C=x1y2-x2y1
有了以上公式,在地图中,只要给出2个点的屏幕像素坐标,圆的圆心坐标和半径(像素),就可以根据半径和圆心到直线的距离比较求得直线和圆是否有相交。
/**
* 计算线段和圆的交点
*
* @param p1 直线上的第一个点
* @param p2 直线上的第二个点
* @param center 圆心
* @param radius 圆半径
* @return 两个交点的坐标数组,如果没有交点则返回null
*/
public static Point[] findLineCircleIntersections(Point p1, Point p2, Point center, double radius) {
// 将坐标平移至圆心处
double x1 = p1.x - center.x;
double y1 = p1.y - center.y;
double x2 = p2.x - center.x;
double y2 = p2.y - center.y;
//AX+BY+C=0 A=y1-y2 B=x2-x1 C=x1y2-x2y1
double dx = x2 - x1; //B
double dy = y2 - y1; //A
double dr = Math.sqrt(dx * dx + dy * dy); //A^2+B^2
double D = x1 * y2 - x2 * y1; //C
// 计算判别式 点到直线的距离d^2 = C^2 / (A^2+B^2)
// C^2 = D^2
// A^2+B^2 = dr^2 当直线与圆相切时 d^2=radius * radius
// radius * radius * dr * dr = D * D 则 radius * radius * dr * dr - D * D=0
//所以当discriminant=0时相切 如果圆与直线相交 则d^2<radius * radius
double discriminant = radius * radius * dr * dr - D * D;
// 判别式大于等于0,意味着直线与圆相交
double discriminant = radius * radius * dr * dr - D * D;
// 判别式大于等于0,意味着直线与圆相交
if (discriminant >= 0) {
double sqrtDiscriminant = Math.sqrt(discriminant);
double signDy = (dy < 0) ? -1 : 1;
int ix1 = (int) ((D * dy + signDy * dx * sqrtDiscriminant) / (dr * dr));
int iy1 = (int) ((-D * dx + Math.abs(dy) * sqrtDiscriminant) / (dr * dr));
Point intersectionPoint1 = new Point(ix1 + center.x, iy1 + center.y);
// 如果判别式大于0,可能存在两个交点
if (discriminant > 0) {
int ix2 = (int) ((D * dy - signDy * dx * sqrtDiscriminant) / (dr * dr));
int iy2 = (int) ((-D * dx - Math.abs(dy) * sqrtDiscriminant) / (dr * dr));
Point intersectionPoint2 = new Point(ix2 + center.x, iy2 + center.y);
return new Point[]{intersectionPoint1, intersectionPoint2};
} else {
return new Point[]{intersectionPoint1};
}
} else {
return null; // 无交点
}
}