包教包会-贝塞尔曲线的绘制原理与应用

说来话长,这一切都得从PhotoShop中的钢笔工具开始提及...git

声明:本文不含复杂数学公式,学渣放心阅读吧😂(我仿佛看到了学渣们留下了激动的泪水)程序员

背景

贝塞尔曲线(Bézier curve)是应用于二维图形应用程序的数学曲线,贝塞尔曲线基于多个点构成。它的应用很是普遍,好比说PS中的钢笔工具所绘画的曲线就是贝塞尔曲线,绘制动画的运动轨迹等等,而最近一次想用到贝塞尔曲线是想作一个 路径动画github

简介

在iOS开发中通常经过UIBezierPath来实现贝塞尔曲线的绘制,平时通常使用绘制二阶和三阶贝塞尔曲线的方法。而咱们要作的远超二三阶的贝塞尔曲线,本文 iOS Demo在原理上实现了N阶贝塞尔曲线的绘制,未使用任何相关API,纯手动绘制贝塞尔曲线,而且能够拖动滑块浏览贝塞尔曲线的绘制过程。算法

本文 iOS Demo 实现如下功能:工具

实现功能 描述
绘制贝塞尔曲线 一、点击空白处设置贝塞尔曲线的点
二、能够设置贝塞尔曲线阶数
三、播放贝塞尔曲线绘制过程
四、拖动滑块,自由查看绘制过程每个瞬间
简易曲线图表 每两个点之间都是用3阶贝塞尔曲线链接(细节待完善)
过山车 一、在空白处绘制贝塞尔曲线
二、过山车沿着绘制的贝塞尔曲线行驶
三、支持多个链接的贝塞尔曲线路径

Demo示例图
动画

8阶贝塞尔曲线绘制过程
8阶贝塞尔曲线绘制过程

贝塞尔曲线的绘制原理

说到绘制原理,若是贴👇这张图,我只能说:什么鬼!!!我看不懂,听不见,你说什么...
路人甲:简单点...说话的方式简单点~网站

失败案例
失败案例

首先提供一个能够动态绘制贝塞尔曲线的网站帮助你更好地理解贝塞尔曲线的绘制。ui

1. 点

贝塞尔曲线点的数量决定了曲线的阶数,通常N个点构成的N-1阶贝塞尔曲线,即3个点为二阶,至少由3个点组成,为何两个点不行,两个点组成的是直线。按顺序,第一个点为 起点 ,最后一个点为 终点 ,其他点都为 控制点spa

A起点、B控制点 、C终点以及绘制的贝塞尔曲线
A起点、B控制点 、C终点以及绘制的贝塞尔曲线

2. 点生线

这里说的线不是贝塞尔曲线,而是各个点按顺序链接起来,造成的直线,如上图ABBC两条线。在这里咱们要将整个曲线的绘制量化为从0~1的过程,用progress为当前过程的进度,progress的区间即0~1。每一条线都须要根据progress生成一个点,以下图,一个点从P0移动到P1,这是这条线从0~1的过程。
3d

根据进度点从起点向终点移动
根据进度点从起点向终点移动

下面是绘制一个二阶贝塞尔曲线过程,先给口诀: 点生线,线生点 😂。由ABC这3个点组成2条线ABBC,2条线根据progress分别生成2个移动的点DE,而DE又连成一条线,始终保持AD:DB=BE:EC

progress为0.3 的连线
progress为0.3 的连线

移动的线
移动的线

DE,DE再根据progress生成点F,只剩一个点,没法构成线,即为最终构成贝塞尔曲线的点。红色点为progress0~1过程当中点F的移动过程,保持AD:DB=BE:EC=DF:FE

progress为0.3 最终的点
progress为0.3 最终的点

点F的移动过程
点F的移动过程

3. 绘制贝塞尔曲线

通过上面 点生线,线生点 的过程 ,咱们拿到了点F在移动中全部点的,将这些点集合链接起来,即造成了贝塞尔曲线。progress自增越慢,点集合的点越多,曲线就越细致。

绘制二阶贝塞尔曲线过程
绘制二阶贝塞尔曲线过程

4. N阶贝塞尔曲线

稍微了解算法的同窗就能发现,其实 点生线,线生点 是一个递归的过程,经过底层的点,一步步推算出最高阶的点。整个推导过程像一个金字塔,底部点的数量最多,每高一阶点的数量就减1,直至最高阶只有1个点。

下面是递归代码:

// 贝塞尔曲线每高一阶  须要递归次数+1
+ (NSArray *)recursionGetsubLevelPointsWithSuperPoints:(NSArray *)points progress:(CGFloat)progress{
    // 获得最终的点 正确结束递归 
    if (points.count == 1) return points;

    NSMutableArray *tempArr = [[NSMutableArray alloc] init];
    for (int i = 0; i < points.count-1; i++) {
        // 第一个点 
        NSValue *preValue = [points objectAtIndex:i];
        CGPoint prePoint = preValue.CGPointValue;
        // 第二个点
        NSValue *lastValue = [points objectAtIndex:i+1];
        CGPoint lastPoint = lastValue.CGPointValue;

        // 两点坐标差
        CGFloat diffX = lastPoint.x-prePoint.x;
        CGFloat diffY = lastPoint.y-prePoint.y;

        // 根据当前progress得出高一阶的点
        CGPoint currentPoint = CGPointMake(prePoint.x+diffX*progress, prePoint.y+diffY*progress);
        [tempArr addObject:[NSValue valueWithCGPoint:currentPoint]];
    }
    // 继续下一次递归过程
    return [self recursionGetsubLevelPointsWithSuperPoints:tempArr progress:progress];
}复制代码

8阶贝塞尔曲线绘制过程:

8阶贝塞尔曲线绘制过程
8阶贝塞尔曲线绘制过程

贝塞尔曲线的应用

光讲原理脱离实践这不是程序员的风格,简单地写了2个贝塞尔曲线的应用,都在本文 iOS Demo 里面,欢迎运行体验。

1. 过山车

经过点击屏幕收集点,将点集合生成贝塞尔曲线,可生成多个相连的贝塞尔曲线。小车按照生成的贝塞尔曲线路径前进。

a. 画路径
经过计算贝塞尔曲线的长度,根据曲线长度分配点的数量,达到点的相对均匀分布,使过山车 匀速前进

画路径
画路径

b. 发车
每一个点都与前面一个点连线,经过计算得出两点的连线与水平造成的夹角,将角度赋予过山车实现 转向功能

发车
发车

2. 简易曲线图表

a. 直线图表
即最简单的两点连成直线。

直线图表
直线图表

b. 曲线图表
曲线图表的曲线所有由3阶贝塞尔曲线构成,整个曲线图不含任何棱角。

曲线图表
曲线图表

拓展

PaintCode
PaintCode

推荐一个 iOS画路径神器 PaintCode,画好图形直接生成代码,用钢笔工具画贝塞尔曲线也十分方便。下图为用钢笔工具画一个圆球(貌似不够圆😆):

生成代码
生成代码

总结

为了准备这一篇文章差很少理解了贝塞尔曲线的绘制原理,可是在细节处,好比说真正意义上贝塞尔曲线点的均匀分布还有待完善,求曲线公式也没有去研究,贝塞尔曲线在复杂的动画方向地应用也是大有做为。

参考

贝塞尔曲线开发的艺术
Android:贝塞尔曲线原理分析

我的水平有限,欢迎提出建议。

相关文章
相关标签/搜索