在线段控件上拖动 - 在线计算点

时间:2015-07-16 16:15:25

标签: swift math vector

我有一个由P1和P2定义的线段和一条可以拖动的线上的旋钮。如果拖动位置L远离线段,则拖动也应该起作用。

一个想法是找到垂直线然后找到交叉点。但是,如果拖曳位置L在线段上或通过P1和P2的(无限)线上,则不会起作用。

事实上,在一天结束时,这应该是一个简单的滑块控件,唯一的区别是控件并不总是水平或垂直线段(可以拖动),但可以是任何角度。

2 个答案:

答案 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