贝塞尔曲线:

贝塞尔曲线本质上是由线段和节点组成的,形象的说节点是可拖动的支点,线段像可伸缩的皮筋。一个常规的曲线往往由4个控制点构成(p0,p1,p2,p3),曲线经过起点(p0)和终点(p1)。

构建曲线(由3个控制点构成):

1.首先构造3个控制点(b0,b1,b2),依次连接b0,b1,b2,构成连段线段

2.在

组成的线段上取一点
的距离为t,离
距离(1-t),也就是说利用线性插值可得在

3.同理在

组成的线段上取一点
的距离为t,离
距离(1-t),也就是说利用线性插值可得在

4.通过两个线段的插值获得两个点

,
,连接两个点构成一条直线,在通过上述方式求插值获得

5.重复上述过程,依次取不同的t值(

),多次线性插值,最终得到的就是一个由3个控制点构成的2次贝塞尔曲线。由此可知,4个控制点或更多控制点构建多次贝塞尔曲线都是由这种方式可得。

伯恩斯坦多项式:

将上述2次贝塞尔曲线的计算过程总结可知,

线性贝塞尔曲线:

3次贝塞尔曲线:

其实看到这就已经非常清楚了,最终得到的贝塞尔曲线方程恰好就是一个关于参数 t 的二次方程,如果细心观察的话,其实可以发现控制点的系数是非常有规律的,很像二项系数,因此可以总结规律得到一个任意控制点组成的贝塞尔曲线的方程如下:

Unity实现曲线可视化编辑:

知乎视频​www.zhihu.com

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public static class Bezier
{/// <summary>/// 获取曲线上某点的坐标/// </summary>/// <param name="p0"> 曲线起点 </param>/// <param name="p1"> 曲线终点 </param>/// <param name="p2"> 曲线调整点1 </param>/// <param name="p3"> 曲线调整点2 </param>/// <param name="t"> 曲线插值t </param>/// <returns></returns>public static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t){t = Mathf.Clamp01(t);float oneMinusT = 1f - t;return oneMinusT * oneMinusT * oneMinusT * p0 +3f * oneMinusT * oneMinusT * t * p1 +3f * oneMinusT * t * t * p2 +t * t * t * p3;}/// <summary>/// 获取曲线某点的切线/// </summary>/// <param name="p0"> 曲线起点 </param>/// <param name="p1"> 曲线终点 </param>/// <param name="p2"> 曲线调整点1 </param>/// <param name="p3"> 曲线调整点2 </param>/// <param name="t"> 曲线插值t </param>/// <returns></returns>public static Vector3 GetFirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t){t = Mathf.Clamp01(t);float oneMinusT = 1f - t;return3f * oneMinusT * oneMinusT * (p1 - p0) +6f * oneMinusT * t * (p2 - p1) +3f * t * t * (p3 - p2);}
}

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public enum BezierControlPointMode
{Free,Aligned,Mirrored
}public class BezierSpline : MonoBehaviour
{[SerializeField]public Vector3[] points;[SerializeField]private BezierControlPointMode[] modes;[SerializeField]private bool loop;public bool Loop{get { return loop; }set{loop = value;if (value == true){modes[modes.Length - 1] = modes[0];SetControlPoint(0, points[0]);}}}/// <summary>/// 控制点总数/// </summary>/// <value></value>public int ControlPointCount{get { return points.Length; }}/// <summary>/// 获取控制点坐标/// </summary>/// <param name="index"></param>/// <returns></returns>public Vector3 GetControlPoint(int index){return points[index];}/// <summary>/// 设置控制点坐标/// </summary>/// <param name="index"></param>/// <param name="point"></param>public void SetControlPoint(int index, Vector3 point){if (index % 3 == 0){//获取当前控制点变换增量Vector3 delta = point - points[index];if (loop)//保持环状{//保证所选择的曲线控制点的前后增量一致if (index == 0){points[1] += delta;points[points.Length - 2] += delta;points[points.Length - 1] = point;}else if (index == points.Length - 1){points[0] = point;points[1] += delta;points[index - 1] += delta;}else{points[index - 1] += delta;points[index + 1] += delta;}}else{//适配以交点对称的两个点或一个点的增量if (index > 0){points[index - 1] += delta;}if (index + 1 < points.Length){points[index + 1] += delta;}}points[index] = point;EnforceMode(index);}}public void Reset(){points = new Vector3[]{new Vector3(1f,0f,0f),new Vector3(2f,0f,0f),new Vector3(3f,0f,0f),new Vector3(4f,0f,0f)};modes = new BezierControlPointMode[]{BezierControlPointMode.Free,BezierControlPointMode.Free};}/// <summary>/// 获取当前控制点的控制模式/// </summary>/// <param name="index"></param>/// <returns></returns>public BezierControlPointMode GetControlPointMode(int index){return modes[(index + 1) / 3];}/// <summary>/// 设置当前点的控制模式/// </summary>/// <param name="index"></param>/// <param name="mode"></param>public void SetControlPonitMode(int index, BezierControlPointMode mode){int modeIndex = (index + 1) / 3;modes[modeIndex] = mode;if (loop){//收尾相连if (modeIndex == 0){modes[modes.Length - 1] = mode;}else if (modeIndex == modes.Length - 1){modes[0] = mode;}}EnforceMode(index);}/// <summary>/// 强制修改模式/// </summary>/// <param name="index"></param>private void EnforceMode(int index){int modeIndex = (index + 1) / 3;//确定曲线索引BezierControlPointMode mode = modes[modeIndex];//曲线端点两个点和曲线终点两个点舍弃if (mode == BezierControlPointMode.Free || !loop && (modeIndex == 0 || modeIndex == modes.Length - 1))return;//获取曲线相交的中点int middleIndex = modeIndex * 3;int fixedIndex, enforcedIndex;//修正右侧点if (index <= middleIndex){fixedIndex = middleIndex - 1;if (fixedIndex < 0){fixedIndex = points.Length - 2;}enforcedIndex = middleIndex + 1;if (enforcedIndex >= points.Length){enforcedIndex = 1;}}else{//修正左侧点fixedIndex = middleIndex + 1;if (fixedIndex >= points.Length){fixedIndex = 1;}enforcedIndex = middleIndex - 1;if (enforcedIndex < 0){enforcedIndex = points.Length - 2;}}//将与中心对称的另一个点修正与选择的点Vector3 middle = points[middleIndex];//以中心点对称Vector3 enforcedTangent = middle - points[fixedIndex];if (mode == BezierControlPointMode.Aligned){//以中心点排齐enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middle, points[enforcedIndex]);}//修正points[enforcedIndex] = middle + enforcedTangent;}/// <summary>/// 曲线总数/// </summary>/// <value></value>public int CurveCount{get { return (points.Length - 1) / 3; }}/// <summary>/// 根据插值t获取曲线上对应点坐标/// </summary>/// <param name="t"></param>/// <returns></returns>public Vector3 GetPoint(float t){int i;if (t >= 1f){t = 1f;i = points.Length - 4;//取最后一段曲线}else{//找到当前所属曲线对应的tt = Mathf.Clamp01(t) * CurveCount;i = (int)t;t -= i;i *= 3;}return transform.TransformPoint(Bezier.GetPoint(points[i], points[i + 1], points[i + 2], points[i + 3], t));}/// <summary>/// 根据插值t获取曲线上对应点方向/// </summary>/// <param name="t"></param>/// <returns></returns>public Vector3 GetVelocity(float t){int i;if (t >= 1f){t = 1f;i = points.Length - 4;//取最后一段曲线}else{//找到当前所属曲线对应的tt = Mathf.Clamp01(t) * CurveCount;i = (int)t;t -= i;i *= 3;}//速度矢量return transform.TransformPoint(Bezier.GetFirstDerivative(points[i], points[i + 1], points[i + 2], points[i + 3], t) - transform.position);}/// <summary>/// 根据插值t获取曲线上对应点方向的归一化坐标/// </summary>/// <param name="t"></param>/// <returns></returns>public Vector3 GetDirection(float t){return GetVelocity(t).normalized;}/// <summary>/// 添加一段曲线(起点,终点,2个控制点)/// </summary>public void AddCurve(){//曲线控制点Vector3 point = points[points.Length - 1];Array.Resize(ref points, points.Length + 3);point.x += 1f;points[points.Length - 3] = point;point.x += 1f;points[points.Length - 2] = point;point.x += 1f;points[points.Length - 1] = point;//曲线调整模式Array.Resize(ref modes, modes.Length + 1);modes[modes.Length - 1] = modes[modes.Length - 2];EnforceMode(points.Length - 4);//曲线呈环状if (loop){points[points.Length - 1] = points[0];modes[modes.Length - 1] = modes[0];EnforceMode(0);}}
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;[CustomEditor(typeof(BezierSpline))]
public class BezierSplineInspector : Editor
{//样条曲线private BezierSpline spline;//拖动对象变换private Transform handleTransform;//拖动对象旋转private Quaternion handleRotation;//private const int stepsPerCurve = 10;private float directionScale = 0.5f;private const float handleSize = 0.04f;private const float pickSize = 0.06f;private int selectedIndex = -1;public override void OnInspectorGUI(){spline = target as BezierSpline;//添加loop按钮EditorGUI.BeginChangeCheck();bool loop = EditorGUILayout.Toggle("Loop", spline.Loop);if (EditorGUI.EndChangeCheck()){Undo.RecordObject(spline, "Toggle Loop");EditorUtility.SetDirty(spline);spline.Loop = loop;}if (selectedIndex >= 0 && selectedIndex < spline.ControlPointCount)DrawSelectedPointInspector();//添加曲线按钮    if (GUILayout.Button("Add Curve")){Undo.RecordObject(spline, "Add Curve");spline.AddCurve();EditorUtility.SetDirty(spline);}}private void DrawSelectedPointInspector(){GUILayout.Label("Selected Point");//位移坐标显示EditorGUI.BeginChangeCheck();Vector3 point = EditorGUILayout.Vector3Field("Position", spline.GetControlPoint(selectedIndex));if (EditorGUI.EndChangeCheck()){Undo.RecordObject(spline, "Move Ponit");EditorUtility.SetDirty(spline);spline.SetControlPoint(selectedIndex, point);}//mode枚举EditorGUI.BeginChangeCheck();BezierControlPointMode mode = (BezierControlPointMode)EditorGUILayout.EnumPopup("Mode", spline.GetControlPointMode(selectedIndex));if (EditorGUI.EndChangeCheck()){Undo.RecordObject(spline, "Change Point Mode");spline.SetControlPonitMode(selectedIndex, mode);EditorUtility.SetDirty(spline);}}/// <summary>/// 场景UI/// </summary>private void OnSceneGUI(){spline = target as BezierSpline;handleTransform = spline.transform;handleRotation = Tools.pivotRotation == PivotRotation.Local ? handleTransform.rotation : Quaternion.identity;//曲线起点Vector3 p0 = ShowPoint(0);for (int i = 1; i < spline.ControlPointCount; i += 3){//曲线调整点1Vector3 p1 = ShowPoint(i);//曲线调整点2Vector3 p2 = ShowPoint(i + 1);//曲线终点Vector3 p3 = ShowPoint(i + 2);Handles.color = Color.gray;//控制点之间用直线表示Handles.DrawLine(p0, p1);Handles.DrawLine(p2, p3);//贝塞尔曲线API接口Handles.DrawBezier(p0, p3, p1, p2, Color.white, null, 2f);p0 = p3;//实际贝塞尔曲线显示算法// int lineStep = 50;//决定曲线平滑度// for (int bezierPonitCount = 0; bezierPonitCount < lineStep; bezierPonitCount++)// {//     Handles.color = Color.white;//     //曲线上的某个点//     Vector3 p = spline.GetPoint(bezierPonitCount / (float)lineStep);//     Handles.DrawLine(p0, p);//     //线段的终点作为下一个点的起点//     p0 = p;// }}ShowDirections();}/// <summary>/// 显示曲线某个点的方向/// </summary>private void ShowDirections(){Handles.color = Color.green;Vector3 point = spline.GetPoint(0f);Handles.DrawLine(point, point + spline.GetDirection(0f) * directionScale);//每段曲线显示10个点方向int steps = stepsPerCurve * spline.CurveCount;for (int i = 0; i < steps; i++){point = spline.GetPoint(i / (float)steps);Handles.DrawLine(point, point + spline.GetDirection(i / (float)steps) * directionScale);}}private static Color[] modeColors ={Color.white,Color.yellow,Color.cyan};/// <summary>/// 展示曲线控制点/// </summary>/// <param name="index"></param>/// <returns></returns>private Vector3 ShowPoint(int index){Vector3 point = handleTransform.TransformPoint(spline.GetControlPoint(index));//根据点获取世界空间显示的尺寸float size = HandleUtility.GetHandleSize(point);if (index == 0)size *= 2f;Handles.color = modeColors[(int)spline.GetControlPointMode(index)];if (Handles.Button(point, handleRotation, size * handleSize, size * pickSize, Handles.DotCap)){selectedIndex = index;Repaint();}if (selectedIndex == index){EditorGUI.BeginChangeCheck();//获取当前控制点的坐标point = Handles.DoPositionHandle(point, handleRotation);if (EditorGUI.EndChangeCheck()){Undo.RecordObject(spline, "Move Ponit");EditorUtility.SetDirty(spline);spline.SetControlPoint(index, handleTransform.InverseTransformPoint(point));}}return point;}
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public enum SplineWalkerMode
{Once,Loop,PingPong
}public class SplineWalker : MonoBehaviour
{public SplineWalkerMode mode;public BezierSpline spline;public float duration = 1f;private float progress = 0;private bool gongForward = true;public bool lookForward;void Update(){if (gongForward){progress += Time.deltaTime / duration;if (progress > 1f){if (mode == SplineWalkerMode.Once){progress = 1f;}else if (mode == SplineWalkerMode.Loop){progress -= 1f;}else{progress = 2f - progress;gongForward = false;}}}else{progress -= Time.deltaTime / duration;if (progress < 0f){progress = -progress;gongForward = true;}}Vector3 position = spline.GetPoint(progress);transform.localPosition = position;if (lookForward)transform.LookAt(position + spline.GetDirection(progress));}
}

代码Git:

git@github.com:DengHaiYang1234/BezierCurves.git

参考:

陶程:贝塞尔曲线​zhuanlan.zhihu.com

孙小磊:计算机图形学十:贝塞尔曲线与贝塞尔曲面​zhuanlan.zhihu.com

代码来自这位大佬​catlikecoding.com

css贝塞尔曲线 多个点_贝塞尔曲线实践相关推荐

  1. css贝塞尔曲线 多个点_了解贝塞尔曲线的数学和Python实现示例

    贝塞尔曲线在计算机图形学中被大量使用,通常可以产生平滑的曲线.如果您曾经使用过Photoshop,则可能会发现名为"锚点"的工具,您可以在其中放置锚点并用它们绘制一些曲线,这些也是 ...

  2. java贝塞尔曲线_贝塞尔曲线学习

    贝塞尔曲线学习 1.贝塞尔曲线 以下公式中: B(t)为t时间下 点的坐标: P0为起点,Pn为终点,Pi为控制点 一阶贝塞尔曲线(线段): 一阶贝塞尔曲线公式 一阶贝塞尔曲线演示 意义:由 P0 至 ...

  3. java 贝塞尔曲线_贝塞尔曲线:原理、自定义贝塞尔曲线View、使用!!!

    一.原理 转自:http://www.2cto.com/kf/201401/275838.html Android动画学习Demo(3) 沿着贝塞尔曲线移动的Property Animation Pr ...

  4. 贝塞尔曲线 弯曲动画ios_用贝塞尔曲线弯曲

    贝塞尔曲线 弯曲动画ios by Nash Vail 由Nash Vail 用贝塞尔曲线弯曲 (Nerding Out With Bezier Curves) Since the past few d ...

  5. 【Android UI】贝塞尔曲线 ② ( 二阶贝塞尔曲线公式 | 三阶贝塞尔曲线及公式 | 高阶贝塞尔曲线 )

    文章目录 一.二阶贝塞尔曲线公式 二.三阶贝塞尔曲线 三.高阶贝塞尔曲线 贝塞尔曲线参考 : https://github.com/venshine/BezierMaker 一.二阶贝塞尔曲线公式 二 ...

  6. 多项式曲线——搞清楚贝塞尔曲线、B样条曲线、Nurbs曲线的区别

    多项式曲线--搞清楚贝塞尔曲线.B样条曲线.nurbs曲线的区别 贝塞尔曲线 Bezier曲线定义 Bernstein基函数的性质 Bezier曲线的性质 B样条曲线 B样条曲线定义 B样条基函数的性 ...

  7. R语言使用ggplot2可视化贝塞尔曲线:基于经验数据可视化贝塞尔曲线(Curved Bézier lines with empirical data)、使用curve_intersect函数计算曲线

    R语言使用ggplot2可视化贝塞尔曲线:基于经验数据可视化贝塞尔曲线(Curved Bézier lines with empirical data).使用curve_intersect函数计算曲线 ...

  8. android贝塞尔曲线,一文解析 Android 贝塞尔曲线

    原标题:一文解析 Android 贝塞尔曲线 相信很多同学都知道"贝塞尔曲线"这个词,我们在很多地方都能经常看到.利用"贝塞尔曲线"可以做出很多好看的UI效果, ...

  9. matlab贝塞尔函数特征值,第十一章 贝塞尔函数

    <第十一章 贝塞尔函数>由会员分享,可在线阅读,更多相关<第十一章 贝塞尔函数(96页珍藏版)>请在人人文库网上搜索. 1.第十一章 柱函数,10.1 柱函数,一.柱坐标下的分 ...

最新文章

  1. [core]-ARM Core的分类和总结
  2. 1.9 实例:截取新闻标题
  3. 个子矮s弯如何看点打方向图解_S弯如何看点打方向
  4. java多线程实例_多线程&高并发(全网最新:面试题+导图+笔记)面试手稳心不慌...
  5. WinFormreportViewer报表[矩阵]的使用(一)(附源码示例) 之配餐系统的开发
  6. linux java占用199%,linux分区使用率过高又查询不到被哪些文件占用的问题
  7. 三、synchronized同步锁
  8. pytorch自定义初始化权重
  9. COM口总是有惊叹号怎么办
  10. Julia: eval的一些用法
  11. 睡眠阶段分期——SVM和ELM分别与粒子群算法结合(function)
  12. SAP结转方法:表结法、帐结法
  13. java excel 冻结_Java 冻结或解除冻结Excel中的行和列的方法
  14. blockchain-explorer(pg版) 区块浏览器部署及配置详解
  15. 计算机应该玩什么游戏,电脑玩游戏主要靠什么配置
  16. 关于除去WordPress页脚底部的自带标语
  17. SQL Server SA权限总结
  18. matlab-CPD工具箱的使用
  19. php 创建mssql 表,如何创建mssql数据库
  20. 哈希密码_哈希生日和密码

热门文章

  1. Android json数据解析及简单例子
  2. 一、Linear Regression
  3. CSS的七种基本选择器及其权值
  4. java ceilingentry_java.util.TreeMap.ceilingKey()方法实例
  5. 天津大学考研计算机专业课的教材,天津大学(专业学位)计算机技术研究生考试科目和考研参考书目...
  6. python中again函数怎么用_《“笨方法”学python 》 once again 20170729
  7. mac安装python3.8_mac安装pwntools(python3.8)解决多数问题
  8. python代码段有什么用_25个超有用的Python代码段
  9. nextcloud icon_聊一聊爱车吉利ICON带给我的用车感受
  10. redhat 6.4 mysql_redhat6.4 安装 MySQL 5.6.27