贝塞尔曲线学习

效果图

图片 1

 

图片 2

前言

由于贝塞尔曲线调控简便且具有极强的叙述本事,它常被用来扭转复杂的平滑曲线;圆形是一种很常用的常见图形,在处理器图形学中也是有那个画圆的算法,本文想追究一下哪些用三阶贝塞尔曲线拟合圆形。
在研究这一个标题时,小编从Stackoverflow上搜到了(4/3)tan(π/(2n))这一个公式,她的几何意义如下图所示。该公式的值表明的现实意思能够描述为:由n段三阶贝塞尔曲线拟合圆形时,曲线端点到该端点前段时间的调节点的超级距离是(4/3)tan(π/(2n))。

图片 3

一起初观望那几个值感觉很意外,想领会他是怎么被演绎出来的,于是花了一些素养去侦查,并和煦求解表明,原本是一批中学生就能够的平面几何运算题。下边给出笔者的求解进度。

有关文化

那部分骨干正是废话,网络都能找到,小编只可是是收拾了以下.建议先不看,用到的时候可以回去探望

贝塞尔曲线学习

求解法力数值

世家把这几个用三阶贝塞尔曲线拟合圆形的数值叫做法力数,大概是因为她的两样取值会影响到拟合的圆形的效果,那么些值决定了贝塞尔曲线拟合圆形的固有误差。小编通过在上面的几何图中增加扶助线、运用平面几何性质来求解该法力数值。

贝塞尔曲线

先来看两组图,有助于掌握什么是贝塞尔曲线图片 4(图片取自维基百科,参照他事他说加以考察链接1)

二回贝塞尔曲线:

图片 5图片 6

P0是起源,P2是极端,P1是调整点

一遍贝塞尔曲线:

图片 7图片 8

 P0是起点,P3是极限,P1是调整点1,P2是决定点2

逐再三再四接全体一些,组成线段

t是比例,在0-第11中学间,正是每条线段的长度都是1

贝塞尔曲线正是最里层的线条在t地方的点所组成的路径

一遍贝塞尔曲线公式:B(t)=(1-t)^3*P0 3(1-t)^2*t*P1 3(1-t)*t^2*P2 t^3*P3,0<=t<=1

B(t)代表曲线上放肆点,P0,1,2,3各自代表决定曲线的4个点,t代表曲线长度为1的放肆取值

APK下载地址

援助线及点地方表达

图片 9

如上海教室命名各点,点O是圆心,P0、P3分别是圆弧(也是贝塞尔曲线)上的源点和终点,P1、P2是贝塞尔曲线的五个调控点, 点M2是线条P1P2的大旨,C1、C2分级是线条P0P1和P2P3的正中,连接OM2并延长与P0P1的延长线交于点F1,过点P1作线段P0P3的垂线交于点F0。
点M1是线段C1M2和C2M2的正中的连线的当心,依据贝塞尔曲线上点的属性可知点M1是圆弧上的点,且是圆弧P0P3的中部,线段P0P3与OF1交于点M0.

别的文化

没接触过贝塞尔曲线的话,恐怕得花些日子整治下,其余的学问就比较轻松了

图片 10

直角三角形,角A的对边a,临边b,斜边c

三角形函数:

sinA=a/c

cosA=b/c

勾股定理:

c^2=a^2 b^2

1.贝塞尔曲线

以下公式中:
B(t)为t时间下 点的坐标;
P0为源点,Pn为巅峰,Pi为调整点

一阶贝塞尔曲线(线段)

图片 11

一阶贝塞尔曲线公式

图片 12

一阶贝塞尔曲线演示

意义:由 P0 至 P1 的连年点, 描述的一条线条

二阶贝塞尔曲线(抛物线)

图片 13

二阶贝塞尔曲线公式

图片 14

二阶贝塞尔曲线演示

规律:由 P0 至 P1 的总是点 Q0,描述一条线条。

由 P1 至 P2 的连日点 Q1,描述一条线条。 由 Q0 至 Q1 的连年点 B(t),描述一条三回贝塞尔曲线。 经验:P1-P0为曲线在P0处的切线。

三阶贝塞尔曲线:

图片 15

三阶贝塞尔曲线公式

图片 16

三阶贝塞尔曲线演示

通用公式:

图片 17

通用贝塞尔曲线公式

高阶贝塞尔曲线
4阶曲线:

图片 18

四阶贝塞尔曲线演示

5阶曲线:

图片 19

五阶贝塞尔曲线演示

表达及推算

三个分包的尺码(或许依靠贝塞尔曲线的数学性质可证明的)是|P2P3|=|P0P1|,大家的指标便是供给出这一个尺寸值,记为l(小写L)。
易知圆弧P0P3被射线OF1对称平分,且OF1与线条P0P3、C1C2和P1P2均垂直相交,又因为点C1、C2和M2是相关线段的中段,轻巧注脚|M1M2| = |M0M2|/4,|P1F0| = |M0M2|,所以|M0M1| = (3/4)|M0M2| = (3/4)|P1F0|。
记|P1F0| = d,圆弧半径为r,∠P0-O-P3为θ,则∠P0-O-M0 = θ/2,|M0M1| = (3/4)d,|OM0| = |OM1| - |M0M1| = r - (3/4)d = r·cos(θ/2),整理等式为
(3/4)d = r - r·cos(θ/2) ····················· ①
因为线段P0P1与圆弧相切于点P0,OP0是圆弧的半径,轻便评释∆P0-F0-P1与∆O-M0-P0相似,∠P1-P0-F0 = θ/2,则
d = l·sin(θ/2) ································ ②
由方程①②联立解得 l = (4/3)·r·(1-cos(θ/2))/sin(θ/2)
若圆弧半径为1,再由上面包车型客车三角函数二倍角公式推导
sin2α = 2·sinα·cosα
cos2α = (cosα)^2 - (sinα)^2 = 1 - 2·(sinα)^2
得到 l = (4/3)tan(θ/4),便是上面图中的值(4/3)tan(π/(2n))

席卷介绍

这几个效应难题就两局地:一是水球分离和融入时候的总是,二是主题圆的抖动

然而实际上网络皆有消除方案了

首先部分是在多少个圆之间加个用贝塞尔曲线组成的path,用一样的颜色,其实是障眼法.见参考链接4

其次部分是用4段叁遍贝塞尔曲线组成的path代替Ellipse,因为Ellipse是颠簸不起来的,那样就足以调整贝塞尔曲线的点来让圆抖动.见参照他事他说加以考查链接3

1.1.生成贝塞尔曲线-迭代法

上边大家用代码来生成贝塞尔曲线,来体现怎样贝塞尔曲线的改造规律:

package com.che.chechengwang.support.view.bezier;

import android.animation.FloatEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * 生成贝塞尔曲线-迭代法
 * <p/>
 * 作者:余天然 on 16/6/14 下午2:10
 */
public class BezierGenerater1 extends View {

    private Paint paint;
    private int centerX, centerY;
    private List<PointF> points;
    private FloatEvaluator evaluator;
    private float fraction;
    private Map<Integer, Integer> colors;
    private List<PointF> destPoints;

    public BezierGenerater1(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        evaluator = new FloatEvaluator();
        startAnim();
    }

    //初始化数据点和控制点的位置
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;
        points = new ArrayList<>();
        points.add(new PointF(centerX - 150, centerY));
        points.add(new PointF(centerX - 50, centerY - 300));
        points.add(new PointF(centerX   50, centerY   300));
        points.add(new PointF(centerX   150, centerY));
        colors = new HashMap<>();
        for (int i = 0; i < points.size(); i  ) {
            colors.put(i, getRanColor());
        }
        destPoints = new ArrayList<>();
        destPoints.add(points.get(0));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //静态的
        drawPoint(canvas, points, Color.BLACK);
        drawLine(canvas, points, Color.GRAY);
        //动态的
        List<PointF> subData = getSubData(points, fraction);
        drawData(canvas, subData, fraction);
    }

    // 绘制数据点
    private void drawPoint(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(20);
        for (int i = 0; i < data.size(); i  ) {
            PointF pointF = data.get(i);
            canvas.drawPoint(pointF.x, pointF.y, paint);
        }
    }

    //绘制基准线
    private void drawLine(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(4);
        for (int i = 0; i < data.size() - 1; i  ) {
            PointF start = data.get(i);
            PointF end = data.get(i   1);
            canvas.drawLine(start.x, start.y, end.x, end.y, paint);
        }
    }

    //绘制路径
    private void drawPath(Canvas canvas, List<PointF> data) {
        Path path = new Path();
        PointF start = data.get(0);
        path.moveTo(start.x, start.y);
        for (int i = 1; i < data.size() - 1; i  ) {
            PointF point = data.get(i);
            path.lineTo(point.x, point.y);
        }
        paint.setColor(Color.RED);
        paint.setStrokeWidth(4);
        canvas.drawPath(path, paint);
    }

    //迭代绘制集合
    private void drawData(Canvas canvas, List<PointF> data, float fraction) {
        if (data.size() == 1) {
            drawPoint(canvas, data, Color.BLACK);
            destPoints.add(data.get(0));
            drawPath(canvas, destPoints);
        } else {
            drawLine(canvas, data, colors.get(data.size() - 1));
            //迭代
            List<PointF> subData = getSubData(data, fraction);
            drawData(canvas, subData, fraction);
        }
    }

    private void startAnim() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(5000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction = animation.getAnimatedFraction();
                invalidate();
            }

        });
        animator.start();
    }

    //生成随机颜色
    private int getRanColor() {
        return 0xff000000 | new Random().nextInt(0x00ffffff);
    }

    //获取子数据源
    private List<PointF> getSubData(List<PointF> data, float fraction) {
        List<PointF> subData = new ArrayList<>();
        for (int i = 0; i < data.size() - 1; i  ) {
            PointF start = data.get(i);
            PointF end = data.get(i   1);
            float x = evaluator.evaluate(fraction, start.x, end.x);
            float y = evaluator.evaluate(fraction, start.y, end.y);
            subData.add(new PointF(x, y));
        }
        return subData;
    }
}

用贝塞尔方程求解法力数

上边用几何运算的议程求解了魔法数,还足以一贯依照贝塞尔曲线方程代入特殊点坐标计算该数值。
用三阶贝塞尔曲线拟合圆形的标题得以简化为思索拟合61%圆弧,如下图圆弧P0P3便是端点为P0、P3,调整点为P1、P2的贝塞尔曲线,它们的坐标分别为P0 = (0,1), P1 = (h,1), P2 = (1,h), P3 = (1,0)

图片 20

大家领会三阶贝塞尔曲线的一般方程如下

图片 21

把地点的点坐标分别代入曲线方程,取t=0.5总括获得点坐标

图片 22

其余依照贝塞尔曲线的数学性质可见曲线方程中t=0.5时的点一定在圆弧上,依据圆形方程定义,可得到上边包车型大巴等式

图片 23

如此那般,轻松解出h的值为 h=(4/3)(sqrt(2)-1) ≈ 0.552284749831

本文由糖果派对电玩城发布于用户反馈,转载请注明出处:贝塞尔曲线学习

您可能还会对下面的文章感兴趣: