我认为提供一个不使用ruby2d
gem 的解决方案可能会很有趣,因此请展示如何执行所需的计算。
示例数据
认为
center = [2, 7]
radius = 4
corners = [[1,3], [5,3], [3,1], [3,5]]
这看起来像下面这样,Y
圆的中心和X
正方形的角。
7 Y
6
5 X
4
3 X X
2
1 X
0 1 2 3 4 5
确定矩形的边
选择这些角中的任何一个(比如第一个角):
first, *rest = corners
#=> [[1, 3], [5, 3], [3, 1], [3, 5]]
first
#=> [1, 3]
rest
#=> [[5, 3], [3, 1], [3, 5]]
确定哪个角离 最远c1
:
x1, y1 = first
farthest = rest.max_by { |x,y| (x-x1)**2 + (y-y1)**2 }
#=> [5, 3]
将正方形的边计算为端点数组:
rest.delete(farthest)
#=> [5, 3]
rest
#=> [[3, 1], [3, 5]]
sides = [first,farthest].product(rest)
#=> [[[1, 3], [3, 1]],
# [[1, 3], [3, 5]],
# [[5, 3], [3, 1]],
# [[5, 3], [3, 5]]]
让我们做一个方法来做到这一点。
def sides(corners)
first, *rest = corners
x1, y1 = first
farthest = rest.max_by { |x,y| (x-x1)**2 + (y-y1)**2 }
rest.delete(farthest)
[first,farthest].product(rest)
end
sides(corners)
#=> <as above>
计算与正方形边重合的线的截距和斜率
对于这些边中的每一个,在空间中都有一条与边重合的线。这些线中的每一条都由截距i
和斜率描述,因此对于点b
的任何值,如果 在线上。我们可以计算每条线的截距和斜率。x
[x, y]
y = i + b*x
def compute_line(side)
(ux,uy), (vx,vy) = side
b = ux==uy ? 0.0 : (uy - vy).fdiv(ux - vx)
[uy - b*ux, b]
end
sides.map { |side| compute_line(side) }
#=> [[4.0, -1.0], [2.0, 1.0], [-2.0, 1.0], [8.0, -1.0]]
笔记:
i, b = lines.first
#=> [4.0, -1.0]
i + b*0
#=> 4.0 (the point [0, 4.0])
i + b*1
#=> 3.0 (the point [1, 3.0])
i + b*2
#=> 2.0 (the point [2, 2.0])
i + b*3
#=> 1.0 (the point [3, 1.0])
i + b*4
#=> 0.0 (the point [4, 0.0])
计算圆与边重合的线相交的点
让
cx, cy = center
#=> [2, 7]
假设我们考虑重合线具有截距i
和斜率的一侧s
。然后我们有二次表达式:
(cx-x) 2 + (cy-is*x) 2 = 半径2
通过定义:
e = cy - 我
等式简化为:
cx 2 - 2*cx*x + x 2 + e 2 - 2*e*s*x + s 2 *x 2 = 半径2
或者
(1 + s 2 )*x 2 + 2*(-cx -e*s)*x + cx 2 + e 2 - 半径2 = 0
或者
a x 2 + b x + c = 0
在哪里:
a = (1 + s 2 )
b = -2*(cx + e*s)
c = cx 2 + e 2 - 半径2
一个或多个实根(如果存在)由二次方程给出。首先计算discriminate:
d = b 2 - 4*a*c
如果判别为负,则二次没有实根(只有复根)。这意味着圆不够大,无法与与这一边重合的线相交。
如果判别d
为正,则有两个实根(仅当d
为零时才具有一个实根)。让:
w = d 1/2
根( 的值x
)是:
(-b + w)/(2*a)
和
(-b - w)/(2*a)
让我们将其包装在一个方法中:
def circle_and_line_intersections(center, radius, side)
i, s = compute_line(side)
cx, cy = center
e = cy - i
a = 1 + s**2
b = -2*(cx + e*s)
c = cx**2 + e**2 - radius**2
d = b**2 - 4*a*c
return [] if d < 0
return [-b/(2*a)] if d.zero?
w = Math.sqrt(d)
r1 = (-b + w)/(2*a)
r2 = (-b - w)/(2*a)
[[r1, i + s*r1], [r2, i + s*r2]]
end
sides.map { |side| circle_and_line_intersections(center, radius, side) }
#=> [[[ 0.82287, 3.17712], [-1.82287, 5.82287]],
# [[ 5.89791, 7.89791], [ 1.10208, 3.10208]],
# [],
# [[4.28388, 3.71611], [-1.28388, 9.28388]]]
还需要做以下事情。
确定其中一个交点是否在一侧
这很简单明了:
def on_side?(pt, side)
low, high = side.map(&:first).sort
(low..high).cover?(pt.first)
end
例如:
on_side?([0.82287, 3.17712], sides[0])
#=> false
on_side?([1.10208, 3.10208], sides[1])
#=> true
把它们放在一起
def intersect?(center, radius, corners)
sides(corners).any? do |side|
circle_and_line_intersections(center, radius, side).any? { |pt|
on_side?(pt, side) }
end
end
intersect?(center, radius, corners)
#=> true