Python 检测鼠标是否在直线上

Python 检测鼠标是否在直线上,python,python-2.7,line,collision-detection,Python,Python 2.7,Line,Collision Detection,对于我的A级项目,我正在创建一个dijktras算法程序。我已经创建了节点、文本框、按钮等,所有这些都具有正确的悬停检测(使用鼠标),但是,我尝试使用arctan为弧/线/连接器创建悬停检测;通过获得两个连接位置的y差和x差(与我们所做的相反),然后从一个位置(点a)到鼠标位置,并进行比较。但是,距离a点越近,差异似乎越大,因此根据我设置的限制,它可以轻松检测到接近a点而不是b点的悬停(特别是当y与x之间的差值非常小时。这会产生一些错误,因为我希望程序能在整条直线上均匀地检测到。有没有更好的方法

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

另一种方法是从直线上创建一个边界框。只需将直线偏移一些固定的垂直距离即可lar距离。可能您已经有了检测鼠标是否位于任意矩形内的逻辑,但如果没有,请参见此处:

另一种方法是从直线创建边界框。只需将直线偏移一定的垂直距离。可能您已经有了检测鼠标是否位于任意矩形内的逻辑,但如果没有t、 请参见此处:

计算与直线的距离

对于一条直线(来自维基百科),点(x0,y0)与直线(x1,1)->(x2,y2)的距离为:

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

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

这两种方法都可以提供一致且独立于鼠标位置的度量值,因此您可以将鼠标悬停的阈值定义为相对较小的值。您应该处理鼠标位于多条直线/圆弧范围内的特殊情况-没有什么比在这些情况下随机选择一条的UI更糟糕的了,因为er总是希望选择一个不是随机选择的

更新:再搜索一点,我发现了这个——woolie的python答案看起来很好,很简洁。这计算了通过点v和w的线的交点,v->w与通过点p的垂线的交点,t的值在0和1之间,沿v到w的直线变化——因此很容易确定位于p的鼠标光标位于线段v->w上,因为在本例中为0.0 1.0


巴尼

计算与直线的距离

对于一条直线(来自维基百科),点(x0,y0)与直线(x1,1)->(x2,y2)的距离为:

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

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

这两种方法都可以提供一致且独立于鼠标位置的度量值,因此您可以将鼠标悬停的阈值定义为相对较小的值。您应该处理鼠标位于多条直线/圆弧范围内的特殊情况-没有什么比在这些情况下随机选择一条的UI更糟糕的了,因为er总是希望选择一个不是随机选择的

更新:再搜索一点,我发现了这个——woolie的python答案看起来很好,很简洁。这计算了通过点v和w的线的交点,v->w与通过点p的垂线的交点,t的值在0和1之间,沿v到w的直线变化——因此很容易确定位于p的鼠标光标位于线段v->w上,因为在本例中为0.0 1.0


Barny

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

下面是一个替代实现,可以解释这一点

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
导入数学
def震级(A):
返回math.sqrt(A.x**2+A.y**2)
#https://en.wikipedia.org/wiki/Dot_product
def dot_产品(A、B):
返回A.x*B.x+A.y*B.y
#平行于向量B的向量族定义如下:
#V(f)=B*f
#其中f是标量值。当f介于0和1之间时,向量的长度介于0和B的大小之间。
#这将返回A到B的向量投影的f值。
#https://en.wikipedia.org/wiki/Vector_projection
def标量投影比(A,B):
长度=点积(A,B)/量级(B)
返回长度/幅度(B)
#查找与线段ab具有相同角度和幅值的向量。
def矢量化(a、b):
返回向量(b.x-a.x,b.y-a.y)
def卡箍(x、左、右):
如果x<左:返回左
如果x>右:返回右
返回x
#查找位于'a'和'b'之间最接近'p'的点。
def最近的_点_到_线_段(a、b、p):
B=矢量化(a,B)
P=矢量化(a,P)
f=标量投影比(P,B)
#如果f小于0或大于1,则P在AB上的向量投影不在A和B之间。
#所以我们必须把它限制在这个范围内。
f=夹具(f,0,1)
返回点(a.x+f*B.x,a.y+f*B.y)
def距离_至_线_段(a、b、p):
t=最接近直线段的点(a、b、p)
返回幅度(矢量化(t,p))
def在管路(a、b、p)上方:
返回距离_至_线_段(a、b、p)<20#或任何合适的公差
此代码假定您有
向量
类,它们只是x&y值的简单容器。您可以使用元组或任何其他数据结构