理解贝塞尔曲线并应用「Android」

定义

在数学的数值分析领域中,贝塞尔曲线(Bézier curve)是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝塞尔曲面,其中贝塞尔三角是一种特殊的实例。

贝塞尔曲线于1962年,由法国工程师皮埃尔·贝兹(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由保尔·德·卡斯特里奥于1959年运用德卡斯特里奥算法开发,以稳定数值的方法求出贝塞尔曲线。

贝塞尔曲线在平面绘图中有着广泛的应用,一般的矢量图形软件通过它来精确画出曲线。贝塞尔曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。

如何绘制贝塞尔曲线

线性(一阶)贝塞尔曲线

一阶贝塞尔曲线函数中的t会经过由P0至P1的B(t)所描述的曲线。例如当t=0.25时,B(t)即一条由点P0至P1路径的四分之一处。就像由0至1的连续t,B(t)描述一条由P0至P1的直线。

对于一阶贝塞尔曲线,可以很容易根据t的值得出线段上那个点的坐标:

B 1 ( t ) = P 0 + ( P 1 − P 0 ) t B_{1}(t) = P_0 + (P_1 - P_0)t B1(t)=P0+(P1−P0)t

然后可以得出:

B 1 ( t ) = ( 1 − t ) P 0 + t P 1 , t ∈ 0 , 1 B_{1}(t) = (1 - t)P_0 + tP_1,t\in0,1 B1(t)=(1−t)P0+tP1,t∈0,1

我们说的应该是曲线啊,这这这不是直线么?!!先别着急,我们一般说的贝塞尔曲线都不是这种,但是要把曲线绘制出来都离不开它,我们接着往下看。

二阶曲线

这便是最常见的一个标准的二阶贝塞尔曲线,它由三个点 P0、P1、P2组成,其中 P0、P2 为起点或终点,P1 为 控制点 ,Q0Q1为中介点。我们来看看应该怎么绘制这种曲线。

  • 首先我们需要三个点:P0,P1,P2,并连接 P0P1 和 P1P2
  • 在P0P1和P1P2两条线上分别找到两个点Q0、Q1,并使等式 P 0 Q 0 P 0 P 1 = P 1 Q 1 P 1 P 2 \frac{P_0Q_0} {P_0P_1}=\frac{P_1Q_1}{P_1P_2} P0P1P0Q0=P1P2P1Q1成立,连接Q0Q1
  • 在 Q0Q1 上找到一个点T,并使 P 0 Q 0 Q 0 P 1 = P 1 Q 1 Q 1 P 2 = Q 0 T Q 0 Q 1 \frac{P_0Q_0} {Q_0P_1}=\frac{P_1Q_1}{Q_1P_2}=\frac{Q_0T}{Q_0Q_1} Q0P1P0Q0=Q1P2P1Q1=Q0Q1Q0T

到这一步我们便找到了贝塞尔曲线上的一个点,剩下我们要做的工作就是找到所有上述符合条件的点,便可以绘制出贝塞尔曲线,如下图:

我们来看看是怎么收集这些点的

|-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
| | |

结合这两张图我们可以看出来 B 点的变化是由 Q0 和 Q1 两个点共同影响的,这三个点在做同一件事,就是在同一时间内由起点移动到终点,有点区别的是 Q0Q1 这个线段在不断变化。最终点 B 的运动过程就形成了贝塞尔曲线,到这里大家应该就能明白了吧。

总结一下,为建构二次贝塞尔曲线,可以中介点Q0和Q1作为由 0 至 1 的 t:

  • 由P0至P1的连续点Q0,描述一条线性贝塞尔曲线。
  • 由P1至P2的连续点Q1,描述一条线性贝塞尔曲线。
  • 由Q0至Q1的连续点B(t),描述一条二次贝塞尔曲线。

无论多少阶的贝塞尔曲线,它的绘制过程都会进行降阶,四阶降三阶直至降到一阶

三阶及更高阶曲线


三阶贝塞尔曲线

这是一个三阶曲线,正如上文所说,高阶曲线绘制会有一个降阶的一个过程,我们来分析下,是怎样的过程。

  1. 三阶曲线有四个点P0、P1、P2、P3,连接相邻的点便有三条边P0P1、P1P2、P2P3
  2. 在三条边上取三个中介点 Q0、Q1、Q2,与二阶曲线一样满足 P 0 Q 0 P 0 P 1 = P 1 Q 1 P 1 P 2 = P 2 Q 2 P 2 P 3 \frac{P_0Q_0} {P_0P_1}=\frac{P_1Q_1}{P_1P_2}=\frac{P_2Q_2}{P_2P_3} P0P1P0Q0=P1P2P1Q1=P2P3P2Q2,设它们的比值为 U U U
  3. 连接Q0Q1、Q1Q2,并在两条边上取两点 R0、R1,连接R0R1取一点B,需要满足
    Q 0 R 0 Q 0 Q 1 = Q 1 R 1 Q 1 R 2 = R 0 B R 0 R 3 = U \frac{Q_0R_0} {Q_0Q_1}=\frac{Q_1R_1}{Q_1R_2}=\frac{R_0B}{R_0R_3}=U Q0Q1Q0R0=Q1R2Q1R1=R0R3R0B=U,这个点 B 即为三阶曲线的其中一点
  4. 找到所有满足上述条件的点,便是三阶贝塞尔曲线

这时候细心的人变发现,第3步之后不就是二阶曲线的流程吗?确实是,不同的是二阶曲线的两条边是固定的,而此处 Q0、Q1、Q2是中介点,所以会不断变化。

来看看三阶曲线的绘制过程吧:

三阶贝塞尔曲线

于是乎,此处我们可以得出一个结论:n阶曲线可近似理解为由n个一阶曲线与(n-1)阶曲线相互作用而成。

四阶贝塞尔曲线

五阶贝塞尔曲线

在 Android Canvas 里绘制贝塞尔曲线

二阶贝塞尔曲线

在 Android 上绘制曲线一般会用路径工具 Path 来处理,Path 基本可以绘制出任何你想要的线条。所以贝塞尔曲线也不例外,可以通过 Paht#quadTo方法来实现:

java 复制代码
/**
 * Add a quadratic bezier from the last point, approaching control point
 * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
 * this contour, the first point is automatically set to (0,0).
 *
 * @param x1 The x-coordinate of the control point on a quadratic curve
 * @param y1 The y-coordinate of the control point on a quadratic curve
 * @param x2 The x-coordinate of the end point on a quadratic curve
 * @param y2 The y-coordinate of the end point on a quadratic curve
 */
public void quadTo(float x1, float y1, float x2, float y2) {
    nQuadTo(mNativePath, x1, y1, x2, y2);
}

参数是两个点:控制点和结束点。要绘制一个二阶曲线可以这样:

kotlin 复制代码
val path = Path()
path.moveTo(pointA.x, pointA.y)
path.quadTo(pointB.x, pointB.y, pointC.x, pointC.y)

canvas.drawPath(path,paint)

这样便绘制了一个二阶的贝塞尔曲线

三阶贝塞尔曲线

java 复制代码
/**
 * Add a cubic bezier from the last point, approaching control points
 * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
 * made for this contour, the first point is automatically set to (0,0).
 *
 * @param x1 The x-coordinate of the 1st control point on a cubic curve
 * @param y1 The y-coordinate of the 1st control point on a cubic curve
 * @param x2 The x-coordinate of the 2nd control point on a cubic curve
 * @param y2 The y-coordinate of the 2nd control point on a cubic curve
 * @param x3 The x-coordinate of the end point on a cubic curve
 * @param y3 The y-coordinate of the end point on a cubic curve
 */
public void cubicTo(float x1, float y1, float x2, float y2,
                    float x3, float y3) {
    nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}

三阶贝塞尔曲线可以用此方法绘制,三阶曲线有两个控制点,方法参数前两对为控制点位置,最后一对为中点位置,那我们绘制一下:

kotlin 复制代码
val path = Path()
path.moveTo(pointA.x, pointA.y)
path.cubicTo(pointB.x, pointB.y, pointC.x, pointC.y, pointD.x, pointD.y)

canvas.drawPath(path,paint)

以上就是 Android Canvas 绘制二阶、三阶曲线的方法,那如果想绘制更多阶的曲线呢?很不幸,Path 只支持二阶、三阶的贝塞尔曲线绘制,因为多阶的曲线用得确实很少。如果确实有需求,那就需要自己去计算曲线轨迹了,社区里有开源支持,本文就不在赘述了。

相关推荐
deli00739 分钟前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
深蓝AI2 小时前
12000 名工程师的高盛,只用四个月就收回了交易员的 AI 编程权限
人工智能·程序员
wangruofeng5 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
深蓝AI8 小时前
为什么 agent 的 demo 都很惊艳,上了生产就不靠谱
人工智能·程序员
LinXunFeng9 小时前
拿竞品 App 一扫,技术栈全暴露了
android·flutter·开源
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
深蓝AI9 小时前
一个项目五个 AI 同时干活,Claude Code 怎么让它们不互相打架
人工智能·程序员
用户47949283569159 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试