从对角线计算垂直偏移量

14
我正在编写一个音乐显示程序,需要在两个音符之间画一条“连线”。 要清楚起见,“连线”是连接两个音符的曲线。
我知道音符的位置,并计算出曲线的起点A和终点B。
现在,我需要获取偏移量C,给定所需距离,以在二次曲线中使用。 这就是我的数学公式极其有限的知识和理解的地方。
我确实在这里查找答案,但提出的解决方案要么不起作用,要么我无法正确编码它们。
有人能以非数学形式帮助我进行计算吗?

抛物线的确切高度是否有影响? - PandaBearSoup
你想要一条抛物线,通过A、B两点并在C点拐点处达到峰值吗?那么C点的高度是多少? - arynaq
@PandaBearSoup - 抛物线的高度将取决于音符之间的距离,它们不一定是相邻的。 - Simon
3个回答

28
给定线段AB,你可以使用著名的中点公式(A + B)/2找到中点M。现在计算从B到A的向量: p = <p.x, p.y> = AB 将其绕原点逆时针旋转90°以得到垂直向量 n = <n.x, n.y> = < ‒ p.y, p.x >
归一化它: n = <n.x, n.y> / ‖n‖其中 ‖n‖= √(n.x² + n.y²) 为欧几里得范数或长度 C = L(t) = M + t n 使用这个方程-参数形式的线性方程-你可以找到垂直线上的任意数量的点(沿着n的方向)。t是所得点C与M之间的距离。当t = 0时,你会得到M,当t = 1时,你会得到一个距离M沿n 1单位的点等等。这也适用于t的负值,其中获得的点将位于AB的相反侧,即朝向备注。由于t可以是一个小数,你可以通过改变它的值来得到所需的距离和方向从M获得的点。
代码,因为你说你对数学术语不感兴趣 ;)
vec2d calculate_perp_point(vec2d A, vec2d B, float distance)
{
   vec2d M = (A + B) / 2;
   vec2d p = A - B;
   vec2d n = (-p.y, p.x);
   int norm_length = sqrt((n.x * n.x) + (n.y * n.y));
   n.x /= norm_length;
   n.y /= norm_length;
   return (M + (distance * n));
}

由于我不确定您的项目中使用的向量数学库,因此这只是伪代码。

上面的粗体变量是二维向量;大写字母表示点,小写字母表示没有位置的向量


感谢您的时间@legends2k。如果我理解正确,对vec2d对象执行的每个操作都会等同地应用于x和y值? - Simon
@Simon:是的,当你看到 p = B - A 时,它实际上意味着 (p.x, p.y) = (B.x - A.x, B.y - A.y),而像 distance * n 这样的标量和向量的操作实际上是 (distance * n.x, distance * n.y)。很高兴能帮忙 :) - legends2k
还有一个问题,您能告诉我如何调整它以使三次贝塞尔曲线具有两个控制点吗?我应该开另一个问题吗? - Simon
@Simon:所以C1可以是M1 + t nC2可以是M2 + t n;而不是M = (A + B) / 2,它是A和B之间的中点,M1和M2可能是A和B之间的四分之一和四分之三,即M1 = .75 B + .25 AM2 = .25B + .75A。通常,可以使用公式M = (1 - t) B + tA来获得沿着A和B的任何点,其中t是从B的距离。 - legends2k
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/32091/discussion-between-simon-and-legends2k - Simon
显示剩余4条评论

3

我参考了legends2k的出色回答,并在Android上将其转换为Java。这可能会帮助某些人节省时间。

private PointF getPerpendicularPoint(int startX, int startY, int stopX, int stopY, float distance)
{
    PointF M = new PointF((startX + stopX) / 2, (startY + stopY) / 2);
    PointF p = new PointF(startX - stopX, startY - stopY);
    PointF n = new PointF(-p.y, p.x);
    int norm_length = (int) Math.sqrt((n.x * n.x) + (n.y * n.y));
    n.x /= norm_length;
    n.y /= norm_length;
    return new PointF(M.x + (distance * n.x), M.y + (distance * n.y));
}

2

这是一个Swift版本:

func pointNormalToLine(startPoint: CGPoint, endPoint: CGPoint, distance: CGFloat) -> CGPoint {

    let midpoint = CGPoint(x: (startPoint.x + endPoint.x) / 2, y: (startPoint.y + endPoint.y) / 2)
    let p = CGPoint(x: startPoint.x - endPoint.x, y: startPoint.y - endPoint.y)
    var n = CGPoint(x: -p.y, y: p.x)
    let norm_length = sqrt((n.x * n.x) + (n.y * n.y))
    n.x /= norm_length
    n.y /= norm_length
    return CGPoint(x: midpoint.x + (distance * n.x), y: midpoint.y + (distance * n.y))
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接