我有一条从a到B的直线和一个半径为R的圆。
用什么算法来检查直线是否与圆相交?它在圆边的哪个坐标上?
我有一条从a到B的直线和一个半径为R的圆。
用什么算法来检查直线是否与圆相交?它在圆边的哪个坐标上?
当前回答
' VB.NET - Code
Function CheckLineSegmentCircleIntersection(x1 As Double, y1 As Double, x2 As Double, y2 As Double, xc As Double, yc As Double, r As Double) As Boolean
Static xd As Double = 0.0F
Static yd As Double = 0.0F
Static t As Double = 0.0F
Static d As Double = 0.0F
Static dx_2_1 As Double = 0.0F
Static dy_2_1 As Double = 0.0F
dx_2_1 = x2 - x1
dy_2_1 = y2 - y1
t = ((yc - y1) * dy_2_1 + (xc - x1) * dx_2_1) / (dy_2_1 * dy_2_1 + dx_2_1 * dx_2_1)
If 0 <= t And t <= 1 Then
xd = x1 + t * dx_2_1
yd = y1 + t * dy_2_1
d = Math.Sqrt((xd - xc) * (xd - xc) + (yd - yc) * (yd - yc))
Return d <= r
Else
d = Math.Sqrt((xc - x1) * (xc - x1) + (yc - y1) * (yc - y1))
If d <= r Then
Return True
Else
d = Math.Sqrt((xc - x2) * (xc - x2) + (yc - y2) * (yc - y2))
If d <= r Then
Return True
Else
Return False
End If
End If
End If
End Function
其他回答
下面是JavaScript的一个很好的解决方案(包括所有必需的数学和实时插图) https://bl.ocks.org/milkbread/11000965
尽管该解决方案中的is_on函数需要修改:
函数is_on(a, b, c) { return Math.abs(距离(a,c) +距离(c,b) -距离(a,b))<0.000001; }
奇怪的是,我可以回答,但不能评论…… 我喜欢Multitaskpro的方法,它可以移动所有东西,使圆的中心落在原点上。不幸的是,他的代码中有两个问题。首先在平方根下的部分,你需要去掉双倍的幂。所以不是:
is underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2));
but:
under Radical = Math.pow(r,2)*(Math.pow(m,2)+1)) - Math.pow(b,2);
在最后的坐标中,他忘记把解移回来。所以不是:
var i1 = {x:t1,y:m*t1+b}
but:
Var i1 = {x:t1+c。x, y: m * t1 + b +陈守惠};
整个函数就变成:
function interceptOnCircle(p1, p2, c, r) {
//p1 is the first line point
//p2 is the second line point
//c is the circle's center
//r is the circle's radius
var p3 = {x:p1.x - c.x, y:p1.y - c.y}; //shifted line points
var p4 = {x:p2.x - c.x, y:p2.y - c.y};
var m = (p4.y - p3.y) / (p4.x - p3.x); //slope of the line
var b = p3.y - m * p3.x; //y-intercept of line
var underRadical = Math.pow(r,2)*Math.pow(m,2) + Math.pow(r,2) - Math.pow(b,2); //the value under the square root sign
if (underRadical < 0) {
//line completely missed
return false;
} else {
var t1 = (-m*b + Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //one of the intercept x's
var t2 = (-m*b - Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //other intercept's x
var i1 = {x:t1+c.x, y:m*t1+b+c.y}; //intercept point 1
var i2 = {x:t2+c.x, y:m*t2+b+c.y}; //intercept point 2
return [i1, i2];
}
}
在此post circle中,通过检查圆心与线段上的点(Ipoint)之间的距离来检查线碰撞,该点表示从圆心到线段的法线N(图2)之间的交点。
(https://i.stack.imgur.com/3o6do.png)
在图像1中显示一个圆和一条直线,向量A指向线的起点,向量B指向线的终点,向量C指向圆的中心。现在我们必须找到向量E(从线起点到圆中心)和向量D(从线起点到线终点)这个计算如图1所示。
(https://i.stack.imgur.com/7098a.png)
在图2中,我们可以看到向量E通过向量E与单位向量D的“点积”投影到向量D上,点积的结果是标量Xp,表示向量N与向量D的直线起点与交点(Ipoint)之间的距离。 下一个向量X是由单位向量D和标量Xp相乘得到的。
现在我们需要找到向量Z(向量到Ipoint),它很容易它简单的向量加法向量A(在直线上的起点)和向量x。接下来我们需要处理特殊情况,我们必须检查是Ipoint在线段上,如果不是我们必须找出它是它的左边还是右边,我们将使用向量最接近来确定哪个点最接近圆。
(https://i.stack.imgur.com/p9WIr.png)
当投影Xp为负时,Ipoint在线段的左边,距离最近的向量等于线起点的向量,当投影Xp大于向量D的模时,距离最近的向量在线段的右边,距离最近的向量等于线终点的向量在其他情况下,距离最近的向量等于向量Z。
现在,当我们有最近的向量,我们需要找到从圆中心到Ipoint的向量(dist向量),很简单,我们只需要从中心向量减去最近的向量。接下来,检查向量距离的大小是否小于圆半径,如果是,那么它们就会碰撞,如果不是,就没有碰撞。
(https://i.stack.imgur.com/QJ63q.png)
最后,我们可以返回一些值来解决碰撞,最简单的方法是返回碰撞的重叠(从矢量dist magnitude中减去半径)和碰撞的轴,它的向量d。如果需要,交点是向量Z。
如果你找到了圆心(因为它是3D的,我想你是指球体而不是圆)和直线之间的距离,然后检查这个距离是否小于可以做到这一点的半径。
碰撞点显然是直线和球面之间最近的点(当你计算球面和直线之间的距离时,会计算出这个点)
点与线之间的距离: http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
基于@Joe Skeen的python解决方案
def check_line_segment_circle_intersection(line, point, radious):
""" Checks whether a point intersects with a line defined by two points.
A `point` is list with two values: [2, 3]
A `line` is list with two points: [point1, point2]
"""
line_distance = distance(line[0], line[1])
distance_start_to_point = distance(line[0], point)
distance_end_to_point = distance(line[1], point)
if (distance_start_to_point <= radious or distance_end_to_point <= radious):
return True
# angle between line and point with law of cosines
numerator = (math.pow(distance_start_to_point, 2)
+ math.pow(line_distance, 2)
- math.pow(distance_end_to_point, 2))
denominator = 2 * distance_start_to_point * line_distance
ratio = numerator / denominator
ratio = ratio if ratio <= 1 else 1 # To account for float errors
ratio = ratio if ratio >= -1 else -1 # To account for float errors
angle = math.acos(ratio)
# distance from the point to the line with sin projection
distance_line_to_point = math.sin(angle) * distance_start_to_point
if distance_line_to_point <= radious:
point_projection_in_line = math.cos(angle) * distance_start_to_point
# Intersection occurs whent the point projection in the line is less
# than the line distance and positive
return point_projection_in_line <= line_distance and point_projection_in_line >= 0
return False
def distance(point1, point2):
return math.sqrt(
math.pow(point1[1] - point2[1], 2) +
math.pow(point1[0] - point2[0], 2)
)