贝塞尔曲线的认识和SVG应用

441 阅读6分钟

贝塞尔曲线理论

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

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

一阶贝塞尔曲线

也称为线性贝塞尔曲线

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

Bézier_1_big.gif

线性贝塞尔曲线演示动画,t在[0,1]区间

二阶贝塞尔曲线

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

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

Bézier_2_big.gif

二次贝塞尔曲线演示动画,t在[0,1]区间

高阶贝塞尔曲线

为建构高阶曲线,便需要相应更多的中介点。对于三次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2,和由二次曲线描述的点R0、R1所建构:

  • 三阶

Bézier_3_big.gif

  • 四阶

Bézier_4_big.gif

  • 五阶

360px-BezierCurve.gif

SVG Path 绘制贝塞尔曲线

一阶贝塞尔曲线

我们可以利用svg path的M L H V等命令去绘制线性一阶内塞尔曲线

M  x y: 移动到x y
L x y: line to x y 
H x: 水平到 x
V y: 垂直到y
Z: 闭合, 从当前点到开始坐标

下面为4个例子

  • 从20 20开始画一条线到30 30
  • 从20 20 开始水平到40
  • 从20 20 开始垂直到40
  • 从50 50开始直线到 60 60再直线到 50 70,再闭合到原点
<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <path d="M20 20 L 30 30" stroke="red"></path>
    <path d="M20 20 H 40" stroke="red"></path>
    <path d="M20 20 V 40" stroke="red"></path>
    <path  d="M 50 50 L 60 60 L 50 70 Z" stroke="red" fill="transparent"></path>
</svg>

image.png

二阶贝塞尔曲线

二阶贝塞尔曲线需要一个控制点,用来确定起点和终点的曲线斜率。因此它需要两组参数,控制点和终点坐标

Q x1 y1, x y
(or)
q dx1 dy1, dx dy
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 Q 95 10 180 80" stroke="red" fill="transparent"/>
</svg>

image.png

二次贝塞尔曲线还有一个 T 命令,可以通过更简短的参数,延长二次贝塞尔曲线

T x y
(or)
t dx dy

T 命令前面必须是一个 Q 命令,或者是另一个 T 命令,则它的第一个控制点会被假设成前一个命令曲线的第二个控制点的中心对称点(下图的蓝点为根据前面一个Q得出的对称点)。如果 T 单独使用,那么控制点就会被认为和终点是同一个点,所以画出来的将是一条直线

image.png

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 Q 52.5 10, 95 80 T 180 80" stroke="red" fill="transparent"/>
</svg>

可以认为是

d="M 10 80 Q 52.5 10, 95 80 Q 137.5 150, 180 80"

三阶贝塞尔曲线

三次贝塞尔曲线需要定义一个点和两个控制点,所以用 C 命令创建三次贝塞尔曲线,需要设置三组坐标参数:

C x1 y1, x2 y2, x y
(or)
c dx1 dy1, dx2 dy2, dx dy

这里的最后一个坐标 (x,y) 表示的是曲线的终点,另外两个坐标是控制点,(x1,y1) 是起点的控制点,(x2,y2) 是终点的控制点。如果你熟悉代数或者微积分的话,会更容易理解控制点,控制点描述的是曲线起始点的斜率,曲线上各个点的斜率,是从起点斜率到终点斜率的渐变过程

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 10 C 20 20, 40 20, 50 10" stroke="red" fill="transparent"/>
</svg>

image.png

如果要将若干个贝塞尔曲线连起来,从而创建出一条很长的平滑曲线。通常情况下,一个点某一侧的控制点是它另一侧的控制点的对称(以保持斜率不变)。这样,你可以使用一个简写的贝塞尔曲线命令 S,如下所示:

S x2 y2, x y
(or)
s dx2 dy2, dx dy

S 命令可以用来创建与前面一样的贝塞尔曲线,但是,如果 S 命令跟在一个 C 或 S 命令后面,则它的第一个控制点会被假设成前一个命令曲线的第二个控制点的中心对称点。如果 S 命令单独使用,前面没有 C 或 S 命令,那当前点将作为第一个控制点。下面是 S 命令的语法示例,图中左侧红色标记的点对应的控制点即为蓝色标记点。

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="black" fill="transparent"/>
</svg>

image.png

可以看作是

<path d="M 10 80 C 40 10, 65 10, 95 80 C 125 150, 150 150, 180 80" stroke="red" fill="transparent"/>

虽然三次贝塞尔曲线拥有更大的自由度,但是两种曲线能达到的效果总是差不多的。具体使用哪种曲线,通常取决于需求,以及对曲线对称性的依赖程度

DEMO代码

<?xml version="1.0" encoding="UTF-8"?>
<svg width="400px" height="200px" viewBox="0 0 400 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g fill="#636363">
        <!-- 线条的基础使用 -->
        <!-- M move -->
        <!-- L line to -->
        <!-- H herizon -->
        <!-- V verizion -->

        <!-- <path d="M20 20 L 30 30" stroke="red"></path>
        <path d="M20 20 H 40" stroke="red"></path>
        <path d="M20 20 V 40" stroke="red"></path> -->

        <!-- Z 命令会从当前点画一条直线到路径的起点 -->
        <!-- <path  d="M 50 50 L 60 60 L 50 70 Z" stroke="red" fill="transparent"></path> -->

        <!-- 贝塞尔曲线 -->
        <!-- <path d="M 80 20 L 90 20 C 90 20, 100 40, 110 20" stroke="red" fill="transparent"></path> -->

        <!-- 二阶Q使用demo -->
        <!-- <path d="M 10 80 Q 95 10 180 80" stroke="red" fill="transparent"/>
        <path d="M 10 80 L 95 10 L 180 80" stroke="green" fill="transparent"/>

        <text x="10" y="90" font-size="9px">M 10 80</text>
        <text x="95" y="10" font-size="9px">95 10控制点</text>
        <text x="180" y="80" font-size="9px">180 80终点</text>
        <text x="50" y="100" font-size="9px">M 10 80 Q 95 10 180 80</text> -->

        <!-- 二阶T使用demo -->
        <!-- <path d="M 10 80 Q 52.5 10, 95 80 T 180 80" stroke="red" fill="transparent"/>
        <path d="M 10 80 Q 52.5 10, 95 80 Q 137.5 150, 180 80" stroke="red" fill="transparent"/>
        <path d="M 10 80 L 52.5 10 L 95 80" stroke="green" fill="transparent"/>
        <path d="M 95 80 L 137.5 150" stroke="blue" fill="transparent"/>
        <path d="M 137.5 150 L 180 80" stroke="green" fill="transparent"/>

        <text x="10" y="80" font-size="9px">M 10 80</text>
        <text x="52.5" y="10" font-size="9px">52.5 10控制点</text>
        <text x="95" y="80" font-size="9px">第一个落点95 80</text>
        <text x="135" y="160" font-size="9px">52.5 1095 80为中心的对称点137.5 150</text>
        <text x="135" y="170" font-size="9px">使用T根据前一个Q计算出的控制点</text>
        <text x="180" y="80" font-size="9px">T 180 80</text> -->

        <!-- 三阶C使用demo -->
        <!-- <path d="M 10 10 C 20 20, 40 20, 50 10" stroke="red" fill="transparent"/>

        <path d="M 10 10 L 20 20" stroke="green" fill="transparent"/>
        <path d="M 40 20 L 50 10" stroke="green" fill="transparent"/>

        <text x="10" y="10" font-size="5px">M 10 10</text>
        <text x="3" y="25" font-size="5px">20 20控制点</text>
        <text x="40" y="25" font-size="5px">40 20控制点</text>
        <text x="50" y="10" font-size="5px">50 10终点</text> -->
    
        <!-- 三阶S使用demo -->
        <!-- <path d="M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="red" fill="transparent"/>
        <path d="M 10 80 C 40 10, 65 10, 95 80 C 125 150, 150 150, 180 80" stroke="red" fill="transparent"/>

        <path d="M 10 80 L 40 10" stroke="green" fill="transparent"/>
        <path d="M 65 10 L 95 80" stroke="green" fill="transparent"/>
        <path d="M 95 80 L 125 150" stroke="blue" fill="transparent"/>
        <path d="M 150 150 L 180 80" stroke="green" fill="transparent"/>

        <text x="10" y="85" font-size="5px">M 10 80</text>
        <text x="25" y="10" font-size="5px">40 10 控制点</text>
        <text x="60" y="10" font-size="5px">65 10 控制点</text>
        <text x="95" y="80" font-size="5px">95 80 第一个终点</text>
        <text x="90" y="155" font-size="5px">65,10 - 95,80的对称点125,150</text>
        <text x="155" y="150" font-size="5px">S 150 150控制点</text>
        <text x="180" y="80" font-size="5px">180 80终点</text> -->
    </g>
</svg>



拓展阅读