CGContext addQuadCurve 添加箭头

CGContext addQuadCurve add an arrow

如果我使用

绘制 quadCurve

ctx.move(至: CGPoint(x: x, y: y))
ctx.addQuadCurve(to: CGPoint(x: x + dist, y: y), control: CGPoint(x: x + dist/2, y: y - arcY))

我想在曲线的顶部画一个箭头来指示方向。我可以获得箭头的 X,并且我知道此时它将是水平箭头。我可以在该 X 值处确定曲线的 Y 值吗?

我并没有坚持使用 addQuadCurve,事实上 addCurve 给出了更好的曲线。只想要一个可以获取箭头的 Y 值并绘制它的解决方案。

圆点代表控制点,我想在曲线的顶部画箭头。我知道 X 值,但 Y 值是多少?

好吧,似乎没有人对这个问题感兴趣,作为 OP,我必须说我不怪他们。然而,尽管可能没有人会读到这篇文章,但我觉得有必要 post 一个对我来说适用于这个特定应用程序的解决方案。

我做的是用'addEllipse'画一个椭圆,然后在椭圆的顶部画箭头(即y = height_of_ellipse/2和x = width_of_ellipse/2 ).我只是用 'fill' 矩形擦掉了椭圆的下半部分。

这是针对非常具体问题的非常具体的解决方案。

这些曲线只是数学,你可以直接计算那个数学。既然你说你更喜欢 addCurve,那么数学就是三次贝塞尔曲线。 Wikipedia 有一篇关于贝塞尔函数的文章,我们可以从那里获取公式:

关于这个公式要记住的重要一点是维度(坐标)都是独立的。如果要计算曲线的 Y 坐标,则只需要控制点的 Y 坐标。理解函数通常从 t=0 到 t=1 计算也很有用,其中 t=0 是起点,t=1 是终点,t=0.5 是中点。 (请注意,t=0.25 是 而不是 四分之一点。以恒定速率沿着这条曲线移动有点复杂。)

那在程序员口语中是什么样子的?

func bezier(t: CGFloat, P0: CGFloat, P1: CGFloat, P2: CGFloat, P3: CGFloat) -> CGFloat {
    return pow(1 - t, 3) * P0
        + 3 * pow(1 - t, 2) * t * P1
        + 3 * (1-t) * pow(t, 2) * P2
        + pow(t, 3) * P3;
}

(这不是一个有效的实现。它旨在尽可能接近正式定义以使其更易于理解。如果您需要一些使其更快的提示,请参阅 Introduction to Fast Bezier。)

假设你的第一个点 (P0) 在 (0,0),你的第一个控制点 (P1) 在 (5,5),你的第二个控制点 (P2) 在 (10, 5) , 而你的最终点 (P3) 在 (10,0) 处,那么曲线中途的点 (t=0.5) 是:

bezier(t: 0.5, P0: 0, P1: 5, P2: 5, P3: 0) // 3.75

在许多情况下,您还需要曲线的导数(切线斜率),这也可以在同一页面上找到:

你可以用它来计算如何画一条与曲线相切的线,在你的情况下这不是必需的,因为你知道它是水平的,但它通常很有用。

如果您决定使用四边形曲线,该方程式也可在同一页面上找到并以相同的方式使用。

贝塞尔曲线没有什么神奇之处。它们只是一个有趣且有用的函数的图,可以通过一些一年级的代数来普遍理解。如果你研究 "Constructing Bézier curves" 部分,并盯着动画看一会儿,大部分的神秘感可能会消失(希望取而代之的是对它们美丽的敬畏感)。