检查鼠标是否在贝塞尔曲线上

Check if the mouse is over a Bézier curve

我的方法是遍历曲线并检查鼠标到各个点的距离

但是随着曲线变得更陡峭,这些点会靠得更近,如果鼠标距离阈值太高,它会优先考虑循环中的第一个点,而不是靠近鼠标的点。

有没有办法在里面获得统一积分?或者检查鼠标是否在贝塞尔曲线上并获取曲线中的位置?

主要有两种方法——将曲线细分为小线段和解析解。

对于第二种情况,您必须根据参数 t 为点到曲线的平方距离构建多项式,对其进行微分,并找到结果的零点(5 阶多项式)。然后从指向 t[i], t=0, t=1 的距离中选择最小值。 另一种观点——得到点在曲线上的投影,所以曲线在该点的切线垂直于向量点-曲线点,应该给出相同的表达式。

关于均匀点 - 这是一个相当困难的问题,因为无法解析计算曲线长度。但是细分给出了很好的近似值。

我是这样做的:

  1. 将曲线细分为几块

    块的数量取决于曲线的顺序。由于我通常使用立方体,我凭经验发现 ~8 个块就足够了(对我来说)。

  2. 计算最接近块的点

    所以只需将每个块作为线处理,并计算线上到鼠标位置的最近点(最小垂直距离)。通过为每个块计算它并记住最接近的一个。

    现在我们知道哪个块包含 "closest" 点,所以从直线和垂直线的交点到它通过上一步的鼠标位置,我们应该有一个参数 u=<0,1> 告诉我们在块线上最近的点在哪里,我们也知道块线两端点的曲线参数t (t0,t1)。由此我们可以简单地通过这样做来近似 t 最近点:

    t = t0 + (t1-t0)*u
    

    在图像上 t0=0.25t1=0.375。这有时就足够了,但如果你想要更好的解决方案,那么在这之后只需设置:

    dt = (t1-t0)/4
    t0 = t-dt
    t1 = t+dt
    

    使用t0,t,t1计算2个块的3个端点并再次寻找最近的点。您可以递归执行此操作几次,因为每次迭代都会提高结果的精度

点到直线的垂直距离是通过计算直线和垂直于它的轴之间的交点来计算的。因此,如果直线由端点 p0,p1 定义并且查询点(鼠标)为 q,则 2D 中的轴将为:

dp=p1-p0        // line direction
dq=(dp.y,-dp.x) // axis direction is perpendicular to dp
dq/= |dq|       // normalize
p(u) = p0+dp*u  // point on line
p(v) = q +dq*v  // point on axis
u = <0,1>       // parameter on line
v = <-inf,+inf> // parameter on axis

我们想知道 u,v 来自

p0+dp*u = q +dq*v

这是二维的 2 个线性方程组。在 3D 中,您需要利用叉积来获得 dq 并且系统将包含 3 个方程。解决这个系统会给你 u,v,其中 u 会告诉你最近点在块中的哪个位置,|v| 是垂直距离本身。不要忘记,如果 u 不在 <0,1> 范围内,那么您必须使用直线的更近端点作为最近点。

系统可以通过代数求解(但要注意边缘情况,因为二维方程有 2 个解)或使用逆矩阵...