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

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

大家好,又见面了,我是全栈君。

说来话长,这一切都得从PhotoShop中的钢笔工具开始说起…

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

背景

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

简介

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

本文 iOS Demo 实现以下功能:

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

Demo示例图

8阶贝塞尔曲线绘制过程

贝塞尔曲线的绘制原理

说到绘制原理,如果贴?这张图,我只能说:什么鬼!!!我看不懂,听不见,你说什么…
路人甲:简单点…说话的方式简单点~

失败案例

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

1. 点

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

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

2. 点生线

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

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

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

progress为0.3 的连线

移动的线

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

progress为0.3 最终的点

点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阶贝塞尔曲线绘制过程

贝塞尔曲线的应用

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

1. 过山车

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

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

画路径

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

发车

2. 简易曲线图表

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

直线图表

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

曲线图表

拓展

PaintCode

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

生成代码

总结

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

参考

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

个人水平有限,欢迎提出建议。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108399.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Linux下安装libiconv

    Linux下安装libiconv1、下载libiconv库wgethttp://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz2.解压,然后进入解压后的目录tarzxvflibiconv-1.13.1.tar.gz&&cdlibiconv-1.13.13.配置libiconvsudo./configure–prefix=/usr/local或者sudo./configure–prefix=…

    2022年10月23日
    0
  • Python中两List的Sql左连接实现

    内联接比较容易,之前做过,用两List的关键字进行if过滤即可;左(右)连接稍微麻烦一些,但稍思考一下,还是可以满足条件的。/Users/nisj/PycharmProjects/BiDataProc/Demand/hadoopStat/SqlLeftJoin.py#-*-coding=utf-8-*-importsysreload(sys)sys.setdefaultenc

    2022年4月16日
    79
  • 助臂_有所臂助

    助臂_有所臂助第十章助臂  一个好汉三个帮,程序员同样如此。  我怀着异样的心情去了天龙。也许是ANGEL的出现感染了我,给我感觉到了压迫,我比往常更努力的投入到工作中去,我必须尽快完成眼前这个工程,然后全心投入到智能杀毒软件的研究中去。  思考再三后,我还是把DENNIS信里的内容告诉了李珏和赵思聪,他两当场就被震住了。  “真的可能写出智能化的病毒吗?”赵思聪不可思议的问道。  我点了点头,无奈的说道:

    2022年8月31日
    2
  • DM368开发 –IPNC 设置过程[通俗易懂]

    DM368开发 –IPNC 设置过程[通俗易懂]第一步: IPNC 安装:安装软件包,解压后包含以下组件:1. IPNC_RDK_DM36x_Version3.1.0.tar.gz:这是linux tar文件,需要解压在linux机器。这是主要tar文件IPNC RDK安装。这个包含在以下文件 预构建二进制文件Dm36x平台 配套文献 源代码构建IPNC的RDK所需 硬件包和BoM,图表和Gerber文件        实用程序文件像凝

    2022年8月13日
    2
  • Java——从键盘输入数组

    Java——从键盘输入数组补充知识JavaScanner类java.util.Scanner是Java5的新特征,我们可以通过Scanner类来获取用户的输入。toString()方法用于返回以一个字符串表示的Number对象值。目录补充知识一、从键盘获取不限制长度的数组输出结果二、从键盘获取限制长度的数组输出结果三、参考一、从键盘获取不限制长度的数组importjava.util.Scanner;publicclassInputArrayNoLimitLength{ publicst

    2022年6月26日
    28
  • 时间复杂度是什么_时间复杂度的表示方法

    时间复杂度是什么_时间复杂度的表示方法-宝宝为啥听不懂他们在讨论的时间复杂度0.0-我怎么知道这个算法运行得比那个算法快0.0-我究竟会不会超时0.0-我为什么还会超时0.0-时间复杂度怎么算0.0在别人还不会求时间复杂度的时候而你会了是不是很酷在别人都会求时间复杂度的时候而你不会是不是很尴尬千里之行始于足下希望这篇文章能祝你一臂之力=w= 此篇详解,希望能帮助各位稍微解决一下不解=w=…

    2025年6月6日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号