我有一个由P1和P2定义的线段和一条可以拖动的线上的旋钮。如果拖动位置L远离线段,则拖动也应该起作用。
一个想法是找到垂直线然后找到交叉点。但是,如果拖曳位置L在线段上或通过P1和P2的(无限)线上,则不会起作用。
事实上,在一天结束时,这应该是一个简单的滑块控件,唯一的区别是控件并不总是水平或垂直线段(可以拖动),但可以是任何角度。
答案 0 :(得分:1)
让我们将V定义为向量P2-P1。所以有一条线由P1 + t V定义(对于所有实数t),你的线段(称之为S)是该线的子集,其中0≤t≤1。
你想在S中找到最接近你的拖曳点L的点。对于任何t,从L到线上t点的距离是
sqrt((P1x + t Vx - Lx)^2 + (P1y + t Vy - Ly)^2)).
要找到最接近L的点,我们希望找到最小化此距离的t。实际上,最小化距离的平方
就足够了(P1x + t Vx - Lx)^2 + (P1y + t Vy - Ly)^2
有时称为 quadrance 。为了找到最小化四分之一的t,我们取四边形相对于t的导数,将其设置为零,并求解t:
Solve[D[(P1x + t Vx - Lx)^2 + (P1y + t Vy - Ly)^2, t] == 0, t]
如果您将其输入Mathematica,您就会得到答案
{{t->(Lx Vx - P1x Vx + Ly Vy - P1y Vy) / (Vx^2 + Vy^2)}}
但那可能是任何实数。您需要将其限制在0 ... 1的范围内,以确保您的线段中有一个点。
在斯威夫特:
extension CGPoint {
func closestPointOnLineSegment(start p1: CGPoint, end p2: CGPoint) -> CGPoint {
let v = CGPointMake(p2.x - p1.x, p2.y - p1.y)
var t: CGFloat = (self.x * v.x - p1.x * v.x + self.y * v.y - p1.y * v.y) / (v.x * v.x + v.y * v.y)
if t < 0 { t = 0 }
else if t > 1 { t = 1 }
return CGPointMake(p1.x + t * v.x, p1.y + t * v.y)
}
}
使用示例:
let knobPoint = dragPoint.closestPointOnLineSegment(start: p1, end: p2)
答案 1 :(得分:0)
您需要在线P1P2上找到L点的投影 如果表示向量 P = P1P2,向量 M = P1L,则需要投影
L' = P * DotProduct( M , P )/ DotProduct( P < / strong>, P )
可能你真的需要找到P1L'和P1P2的比率,然后
t = DotProduct( M , P )/ DotProduct( P , P )