使用二阶贝塞尔曲线画爱心

简介

上一篇这一篇让你彻底搞懂贝塞尔曲线的原理中,我们介绍了贝塞尔曲线的原理。这一篇我们先用二阶贝塞尔曲线来做一个应用 —— 绘制爱心。首先回顾一下二阶贝塞尔曲线。

二阶贝塞尔曲线

如上图所示,经过推导得出的曲线上的点 P 的计算公式如下:


二阶贝塞尔曲线计算公式

爱心绘制控制点

首先我们来看爱心使用二阶贝塞尔曲线如何实现。如下图所示,爱心可以分为4段曲线,分别是 P0-P1,P1-P2,P2-P3和 P3-P0之间的四段曲线,其中 P0-P1和 P1-P2是对称的,P2-P3和 P3-P0也是对称的。


爱心绘制示意图

有了这个基础,我们就可以在各段曲线的中间增加一个控制点,形成二阶贝塞尔曲线所需的三个控制点,如下图所示。


控制点分布

当然,为了让爱心好看,这些点需要仔细调整,尤其是P1-P2和 P2-P3的两段曲线斜接处需要过渡得平滑。

代码实现

代码实现的关键是调节点位参数和封装一个获取二阶贝塞尔曲线的函数方便重复调用,然后就是设置一个 t 变量,通过循环等间隔生成曲线的一些列点就可以绘制出曲线了。下面是根据三个控制点获取二阶贝塞尔曲线在 t 时刻点的位置的方法。

Offset get2OrderBezierPoint(Offset p1, Offset p2, Offset p3, double t) {
  var x = (1 - t) * (1 - t) * p1.dx + 2 * t * (1 - t) * p2.dx + t * t * p3.dx;
  var y = (1 - t) * (1 - t) * p1.dy + 2 * t * (1 - t) * p2.dy + t * t * p3.dy;

  return Offset(x, y);
}

绘制P0-P1的曲线代码如下所示,每段曲线使用了100个点。这里注意,由于使用小数循环可能因为浮点数的偏差导致循环点数少,因此这里的 t 先乘了100倍进行循环,然后再除以100来避免这种情况的出现。

var p0 = Offset(offset1x, center1y);
var p1 = Offset(size.width / 4 - offset1x, side1y);
var p2 = Offset(size.width / 2, center1y);
var path1 = Path();
path1.moveTo(p0.dx, p0.dy);
for (var t = 1; t <= 100; t += 1) {
  var curvePoint = get2OrderBezierPoint(p0, p1, p2, t / 100.0);
  path1.lineTo(curvePoint.dx, curvePoint.dy);
}
canvas.drawPath(path1, paint);

其他曲线段的代码相同,只是控制点不同而已,实现的效果图下,为了方便查看控制点位置和曲线的关系,我们将控制点也画了出来。完整代码已经上传至:CustomPaint 绘制相关代码。

绘制效果

总结

本篇介绍了二阶贝塞尔曲线的实际应用,实际绘制过程中我们发现曲线拼接处(途中的绿色点)的控制点很难调整到过渡平滑,导致调整绘制效果需要花费很多时间调整控制点位置,这种情况实际需要使用三阶贝塞尔曲线来解决。下一篇我们来用三阶贝塞尔曲线重新绘制爱心。

你可能感兴趣的:(使用二阶贝塞尔曲线画爱心)