计算三次贝塞尔曲线的控制点

Sim*_*mon 1 java bezier

我正在编写一个在Android平板电脑上显示音乐的应用程序.

当我需要绘制领带和连线时,我已经碰到了 - 将两个音符连接在一起的曲线.

为此,我想使用立方贝塞尔曲线 - 但不知道如何确定两个控制点的位置.

我显然知道开始(A)和结束(B)点(2D),以及我希望曲线走的A-B线的距离.我这样说是因为曲线不一定在水平面上.

任何人都可以帮我确定所需的控制点,以便得到的曲线将通过一个给定的点 - 即距离平面的距离和沿AB平面的偏移量?

请注意,我不是数学专家,并且会喜欢编程类型公式而不是数学公式...请.

这是一个具体的例子:

诽谤的例子

我知道每条线的起点和终点,以及我希望曲线经过的点.

但是,在过去两天用谷歌搜索后,我无法找到正确的公式来确定重现这些曲线所需的两个控制点位置.

Mik*_*ans 10

遗憾的是,如果不依赖数学,就无法做到这一点,因为从你的图像中我们需要绘制的那种曲线取决于它附加的内容.分析给定的图像显示两条曲线不共享"构造"属性:

红线是开始/结束基线,它们的最大平移是适合曲线的,蓝线是从开始到第一个控制的方向,第二个控制到终点,绿线粗略地表示曲线的数学中点(t = 0.5)黑线表示曲线的最大垂直延伸.

为了形成这些曲线,我们将在标准曲线上发布线性代数,并看看它在哪里得到我们.

右边的曲线实际上相对容易构造,因为它是一条非常对称的曲线,我们可以通过缩放标准的半圆贝塞尔曲线来制作曲线:

{ (0,0), (0,0.552), (1,0.552), (1,0) }
Run Code Online (Sandbox Code Playgroud)

这将使曲线直线"向外",所以让它扭曲它以使曲线以微小的角度开始和结束:

{ (0,0), (0.2,0.552), (0.8,0.552), (1,0) }
Run Code Online (Sandbox Code Playgroud)

这是在一个直线单位线上,并且半圆的高度,并且向上,所以我们需要将其缩小到大约四分之一高度,并且可能在y坐标前面贴上一些减号.

{ (0,0), (0.2, +/- 0.138), (0.8, +/- 0.138), (1,0) }
Run Code Online (Sandbox Code Playgroud)

并根据起点p1和终点p4对其进行缩放以匹配所需的线长度,

D = distance(p1, p4)
{ (0,0), (0.2 * D, 0.138 * D), (0.8 * D, 0.138 * D), (D,0) }
Run Code Online (Sandbox Code Playgroud)

然后我们旋转坐标,使它们在正确的角度线上,使用线和水平线之间的角度,并将该角度粘贴在旋转矩阵中:

phi = atan2(p4.y - p1.y, p4.x - p1.x)

{
  (0, 0),
  (0.2 * D * cos(phi) - 0.138 * D * sin(phi), 0.2 * D * sin(phi) + 0.138 * D * cos(phi)),
  (D * cos(phi) - 0.138 * D * sin(phi), D * sin(phi) + 0.138 * D * sin(phi)),
  (D * cos(phi), D * sin(phi)
}
Run Code Online (Sandbox Code Playgroud)

这看起来很"肮脏",但事实并非如此.cos(phi)和sin(phi),如果你已经有phi,只是两个数字,这里没有数学,只是愚蠢的算术.

最后一步是翻译所有坐标,使它们位于页面的正确位置:

{
  (p1.x + 0, p1.y + 0),
  (p1.x + 0.2 * D * cos(phi) - 0.138 * D * sin(phi), p1.y + 0.2 * D * sin(phi) + 0.138 * D * cos(phi)),
  (p1.x + D * cos(phi) - 0.138 * D * sin(phi), p1.y + D * sin(phi) + 0.138 * D * sin(phi)),
  (p1.x + D * cos(phi), p1.y + D * sin(phi)
}
Run Code Online (Sandbox Code Playgroud)

完成.你的第二条曲线很容易制作.

左边的曲线稍微多一些,但只是略微.我们 - 可能不直观地 - 以相同的方式开始,形成与之前完全相同的曲线类型,在旋转之前停止.我们可以观察到,如果我们将图像的曲线平放在水平面上,它实际上是一个规则的缩放半圆,但剪切了右边.所以,让我们这样做:

轮换前:

{ (0,0), (0.2 * D, 0.138 * D), (0.8 * D, 0.138 * D), (D,0) }
Run Code Online (Sandbox Code Playgroud)

水平剪切

float sx = <strength of the shear>
{ (0,0), (0.2 * D + 0.138 * D * sx, 0.138 * D), (0.8 * D + 0.138 * D * sx, 0.138 * D), (D,0) }
Run Code Online (Sandbox Code Playgroud)

旋转:

phi = atan2(p4.y - p1.y, p4.x - p1.x)

{
  (0, 0),
  ((0.2 * D + 0.138 * D * sx) * cos(phi) - 0.138 * D * sin(phi), (0.2 * D + 0.138 * D * sx) * sin(phi) + 0.138 * D * cos(phi)),
  ((D + 0.138 * D * sx) * cos(phi) - 0.138 * D * sin(phi), (D + 0.138 * D * sx) * sin(phi) + 0.138 * D * cos(phi)),
  (D * cos(phi), D * sin(phi)
}
Run Code Online (Sandbox Code Playgroud)

然后最后一个翻译步骤是相同的​​.再次只是插入数字,虽然这次你将不得不玩剪切值,看看哪个看起来最好.

免费参数

我们可以通过改变我们缩放初始半圆的程度来控制曲线的"弯曲"程度.0.25因子相对紧密,0.33相对起泡.我们还可以控制像你离开领带那样的曲线的剪切力.1的剪切是微妙的,1.75的剪切是急剧突然的.

为什么这样做

尽管Bezier曲线的名称中包含单词曲线,但它们是线性插值的线性插值.将线性变换应用于构建曲线的坐标会保留曲线的属性,因此我们可以只使用四个坐标并相信曲线看起来正确,而不是尝试使用完整曲线.

所以我们采用我们知道坐标的曲线的四个坐标,然后应用我们需要的所有变换来获得我们想要的曲线:

(x,y) . scale . (shearx?) . rotation, + (tx,ty)
Run Code Online (Sandbox Code Playgroud)

这是:

|x| . | D 0 | . | 1 shearx | . |cos(phi) -sin(phi)| + |tx|
|y|   | 0 D |   | 0    1   |   |sin(phi)  cos(phi)|   |ty|
Run Code Online (Sandbox Code Playgroud)

矩阵运算可以折叠成单个矩阵(这就是为什么计算机在2D/3D中非常擅长的原因 - 它只是矩阵,所以非常复杂的运算仍然只是应用于一百万个坐标的单个矩阵).

事实上,如果我们将坐标视为三维坐标,我们甚至可以将平移作为矩阵运算,z值始终为1.但这不再与您的问题真正相关.

一个骗子

可以在http://jsfiddle.net/CLbUF/1找到一个逐步实现左边关系的小提琴,但是这并没有将所有操作合并到一个操作中.你需要自己做.