贝塞尔曲线开发的艺术

本文详细介绍了贝塞尔曲线的开发艺术,包括二阶和三阶贝塞尔曲线的模拟,以及它们在圆滑绘图、曲线变形、波浪效果和路径动画中的应用。文章还探讨了贝塞尔曲线的进阶内容,如计算曲线上任意点的坐标,以及如何通过贝塞尔曲线进行矩形、切线和圆的拟合。提供了在线模拟工具和源代码供读者参考学习。
摘要由CSDN通过智能技术生成
                       

贝塞尔曲线开发的艺术

一句话概括贝塞尔曲线:将任意一条曲线转化为精确的数学公式。

很多绘图工具中的钢笔工具,就是典型的贝塞尔曲线的应用,这里的一个网站可以在线模拟钢笔工具的使用:

http://bezier.method.ac/

这里写图片描述

贝塞尔曲线中有一些比较关键的名词,解释如下:

  • 数据点:通常指一条路径的起始点和终止点
  • 控制点:控制点决定了一条路径的弯曲轨迹,根据控制点的个数,贝塞尔曲线被分为一阶贝塞尔曲线(0个控制点)、二阶贝塞尔曲线(1个控制点)、三阶贝塞尔曲线(2个控制点)等等。

要想对贝塞尔曲线有一个比较好的认识,可以参考WIKI上的链接:

https://en.wikipedia.org/wiki/B%C3%A9zier_curve

这里写图片描述

贝塞尔曲线模拟

在Android中,一般来说,开发者只考虑二阶贝塞尔曲线和三阶贝塞尔曲线,SDK也只提供了二阶和三阶的API调用。对于再高阶的贝塞尔曲线,通常可以将曲线拆分成多个低阶的贝塞尔曲线,也就是所谓的降阶操作。下面将通过代码来模拟二阶和三阶的贝塞尔曲线是如何绘制和控制的。

贝塞尔曲线的一个比较好的动态演示如下所示:

http://myst729.github.io/bezier-curve/

这里写图片描述

二阶模拟

二阶贝塞尔曲线在Android中的API为:quadTo()和rQuadTo(),这两个API在原理上是可以互相转换的——quadTo是基于绝对坐标,而rQuadTo是基于相对坐标,所以后面我都只以其中一个来进行讲解。

先来看下最终的效果:

这里写图片描述

从前面的介绍可以知道,二阶贝塞尔曲线有两个数据点和一个控制点,只需要在代码中绘制出这些辅助点和辅助线即可,同时,控制点可以通过onTouchEvent来进行传递。

package com.xys.animationart.views;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;/** * 二阶贝塞尔曲线 * <p/> * Created by xuyisheng on 16/7/11. */public class SecondOrderBezier extends View {
        private Paint mPaintBezier;    private Paint mPaintAuxiliary;    private Paint mPaintAuxiliaryText;    private float mAuxiliaryX;    private float mAuxiliaryY;    private float mStartPointX;    private float mStartPointY;    private float mEndPointX;    private float mEndPointY;    private Path mPath = new Path();    public SecondOrderBezier(Context context) {        super(context);    }    public SecondOrderBezier(Context context, AttributeSet attrs) {        super(context, attrs);        mPaintBezier = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaintBezier.setStyle(Paint.Style.STROKE);        mPaintBezier.setStrokeWidth(8);        mPaintAuxiliary = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaintAuxiliary.setStyle(Paint.Style.STROKE);        mPaintAuxiliary.setStrokeWidth(2);        mPaintAuxiliaryText = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaintAuxiliaryText.setStyle(Paint.Style.STROKE);        mPaintAuxiliaryText.setTextSize(20);    }    public SecondOrderBezier(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mStartPointX = w / 4;        mStartPointY = h / 2 - 200;        mEndPointX = w / 4 * 3;        mEndPointY = h / 2 - 200;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        mPath.reset();        mPath.moveTo(mStartPointX, mStartPointY);        // 辅助点        canvas.drawPoint(mAuxiliaryX, mAuxiliaryY, mPaintAuxiliary);        canvas.drawText("控制点", mAuxiliaryX, mAuxiliaryY, mPaintAuxiliaryText);        canvas.drawText("起始点", mStartPointX, mStartPointY, mPaintAuxiliaryText);        canvas.drawText("终止点", mEndPointX, mEndPointY, mPaintAuxiliaryText);        // 辅助线        canvas.drawLine(mStartPointX, mStartPointY, mAuxiliaryX, mAuxiliaryY, mPaintAuxiliary);        canvas.drawLine(mEndPointX, mEndPointY, mAuxiliaryX, mAuxiliaryY, mPaintAuxiliary);        // 二阶贝塞尔曲线        mPath.quadTo(mAuxiliaryX, mAuxiliaryY, mEndPointX, mEndPointY);        canvas.drawPath(mPath, mPaintBezier);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_MOVE:                mAuxiliaryX = event.getX();                mAuxiliaryY = event.getY();                invalidate();        }        return true;    }}
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

三阶模拟

三阶贝塞尔曲线在Android中的API为:cubicTo()和rCubicTo(),这两个API在原理上是可以互相转换的——quadTo是基于绝对坐标,而rCubicTo是基于相对坐标,所以后面我都只以其中一个来进行讲解。

有了二阶的基础,再来模拟三阶就非常简单了,无非是增加了一个控制点而已,先看下效果图:

这里写图片描述

代码只需要在二阶的基础上添加一些辅助点即可,下面只给出一些关键代码,详细代码请参考Github:

    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        mPath.reset();        mPath.moveTo(mStartPointX, mStartPointY);        // 辅助点        canvas.drawPoint(mAuxiliaryOneX, mAuxiliaryOneY, mPaintAuxiliary);        canvas.drawText("控制点1", mAuxiliaryOneX, mAuxiliaryOneY, mPaintAuxiliaryText);        canvas.drawText("控制点2", mAuxiliaryTwoX, mAuxiliaryTwoY, mPaintAuxiliaryText);        canvas.drawText("起始点", mStartPointX, mStartPointY, mPaintAuxiliaryText);        canvas.drawText("终止点", mEndPointX, mEndPointY, mPaintAuxiliaryText);        // 辅助线        canvas.drawLine(mStartPointX, mStartPointY, mAuxiliaryOneX, mAuxiliaryOneY, mPaintAuxiliary);        canvas.drawLine(mEndPointX, mEndPointY, mAuxiliaryTwoX, mAuxiliaryTwoY, mPaintAuxiliary);        canvas.drawLine(mAuxiliaryOneX, mAuxiliaryOneY, mAuxiliaryTwoX, mAuxiliaryTwoY, mPaintAuxiliary);        // 三阶贝塞尔曲线        mPath.cubicTo(mAuxiliaryOneX, mAuxiliaryOneY, mAuxiliaryTwoX, mAuxiliaryTwoY, mEndPointX, mEndPointY);        canvas.drawPath(mPath, mPaintBezier);    }
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

模拟网页

如下所示的网页,模拟了三阶贝塞尔曲线的绘制,可以通过拖动曲线来获取两个控制点的坐标,而起始点分别是(0,0)和(1,1)。

http://cubic-bezier.com/

这里写图片描述

通过这个网页,也可以比较方便的获取三阶贝塞尔曲线的控制点坐标。

贝塞尔曲线应用

圆滑绘图

当在屏幕上绘制路径时,例如手写板,最基本的方法是通过Path.lineTo将各个触点连接起来,而这种方式在很多时候会发现,两个点的连接是非常生硬的,因为它毕竟是通过直线来连接的,如果通过二阶贝塞尔曲线来将各个触点连接,就会圆滑的多,不会出现太多的生硬连接。

先来看下代码,非常简单的绘制路径代码:

package com.xys.animationart.views;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;/** * 圆滑路径 * <p/> * Created by xuyisheng on 16/7/19. */public class DrawPadBezier extends View {
        private float mX;    private float mY;    private float offset = ViewConfiguration.get(getContext()).getScaledTouchSlop();    private Paint mPaint;    private Path mPath;    public 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值