检测鼠标是否在线python上

时间:2015-12-14 12:50:26

标签: python python-2.7 line collision-detection

对于我的A-Level项目,我正在创建一个dijktras算法程序。我已经创建了节点,文本框,按钮等所有正确的悬停检测(使用鼠标),但是,我尝试使用arctan为弧/线/连接器创建悬停检测;通过获得两个连接位置的x之差(与我们所做的相反)的差异,然后从一个位置(点a)到鼠标位置并进行比较。然而,差异似乎总是越大,越接近指向a,使得我设置的极限很容易检测到比点b更接近点a的悬停(特别是当y的差异超过x的差异非常小时)这会产生一些错误,因为我希望程序在整条线上均匀地检测到它。有没有更好的方法可以进行线路悬停检查?

3 个答案:

答案 0 :(得分:2)

计算距离线的距离。

对于直线(来自维基百科https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line)点(x0,y0)与线(x1,1)的距离 - >(x2,y2)为:

formula for distance of point (x0,y0) from line (x1,1)->(x2,y2)

还有其他公式用于计算点与直线的距离,但这个公式很方便,水平/垂直线没有特殊情况。

对于圆弧,请使用((从鼠标到圆心的距离) - (圆的半径))。

这些都会为您提供一致且独立于鼠标位置的度量,因此您可以将鼠标悬停的阈值定义为相对较小的值。你应该处理鼠标在多行/弧范围内的特殊情况 - 没有比在这些情况下随机选择一个的UI差,因为用户总是想选择一个不是随机选择的。

更新:多搜索一下,我发现这个Shortest distance between a point and a line segment - woolie的python答案看起来很简洁。这计算了通过点v和w,v-> w的线与通过点p的垂线的交点,该值以值t在0和1之间沿着从v到w的线变化 - 所以它容易确定p处的鼠标光标在线段v-> w上,因为在这种情况下0.0 <= t <= 1.0或超出段v-> w的任一端,因为在这种情况下t&lt; ; 0.0或t> 1.0

HTH 巴尼

答案 1 :(得分:1)

另一种方法是从该行创建一个边界框。简单地将线偏移一些固定的垂直距离。也许您已经有逻辑来检测鼠标是否在任意矩形内,但如果没有,请参见此处:Finding whether a point lies inside a rectangle or not

答案 2 :(得分:1)

我喜欢barny的建议使用线点距离,但我假设当你说“line”时你的意思是“线段”。在这种情况下,您不能只使用线距离公式,因为它可能会将鼠标位置投影到与线段共线的点上,但实际上并不位于端点之间。

以下是另一种解释此问题的实现方法。

import math

def magnitude(A):
    return math.sqrt(A.x**2 + A.y**2)

#https://en.wikipedia.org/wiki/Dot_product
def dot_product(A,B):
    return A.x*B.x + A.y*B.y

#the family of vectors parallel to vector B is defined by
#V(f) = B*f
#where f is a scalar value. when f is between 0 and 1, the vector's length is between 0 and B's magnitude.
#this returns the f value of the vector projection of A onto B.
#https://en.wikipedia.org/wiki/Vector_projection
def scalar_projection_ratio(A,B):
    length = dot_product(A,B) / magnitude(B)
    return length / magnitude(B)

#finds the vector with the same angle and magnitude as line segment ab.
def vectorize(a,b):
    return Vector(b.x - a.x, b.y - a.y)

def clamp(x, left, right):
    if x < left: return left
    if x > right: return right
    return x

#finds the point lying between `a` and `b` which is closest to `p`.
def closest_point_to_line_segment(a,b,p):
    B = vectorize(a,b)
    P = vectorize(a,p)

    f = scalar_projection_ratio(P, B)

    #if f is less than 0 or greater than 1, the vector projection of P onto AB does not lie between A and B.
    #so we must clamp it to that range.

    f = clamp(f, 0, 1)
    return Point(a.x + f*B.x, a.y + f*B.y)

def distance_to_line_segment(a,b,p):
    t = closest_point_to_line_segment(a,b,p)
    return magnitude(vectorize(t,p))

def is_over_line(a, b, p):
    return distance_to_line_segment(a,b,p) < 20 #or whatever tolerance is appropriate

此代码假设您有VectorPoint类,它们只是x&amp;的简单容器。 y值。您可以使用元组或任何其他您喜欢的数据结构;它只需要一些名义上的变化。

为了验证我在上面的代码中没有输错,我用它来制作一个快速热图,显示每个像素到红线段的距离。

enter image description here

最暗的值代表最短的距离,所以这似乎证实了这种方法的有效性。