文档章节

曲线间平滑计算方法和一个Spline的实现

梦想游戏人
 梦想游戏人
发布于 2017/08/20 22:32
字数 2048
阅读 44
收藏 0
点赞 0
评论 0

Unity曲线编辑器和bezier曲线插值-----该篇是对上篇的曲线间平滑过渡问题的展开。

上面的曲线间平滑多度强烈依赖于2条二阶曲线粘合处的平滑。

1.5点线性平滑处理,大致思想是通过历史点信息 通过线性预测方法得出插值的点,该方法如果不加以校正的话,会蝴蝶效应,例如依据的点是越来越偏离真实点。图中 A B就是根据历史p0 p1 p2 一定方法(一般可以是曲率做为参考依据)计算出来

2.利用三阶bezier曲线的,一个三点一线特性,大致是第一条曲线的第二个控制点和第一条曲线的终点和第二条曲线的第一个控制点,三点在一条线上的话,连接处就是平滑的,但是在实践的时候发现1个问题,虽然连接处平滑了,但是曲线 的曲率变化可能会偏差很大,导致过渡后曲率偏离过大, 也会导致抖动问题,看起来像是拐角突然变大or小。也依赖于拖动曲线的时候的让曲线间曲率变化不大。

如图p2 p3 p4 在同一条线上。这个特性在editor时候可以做自动微调计算,让他们在同一个线段上

 

3.一片论文提到的方法 http://www.ixueshu.com/document/720e4fbfdf8eec55318947a18e7f9386.html

4.之所以不平滑是因为他们交合处角度,位置的偏差导致,平滑计算可以针对角度和位置分别进行插值处理

5.RungeKutta 算法 可用在mspline 等曲线计算中,解决步长带来的精度的问题

6.欧拉渐进法euler,也可以实现和5一样的效果,是一阶的 RungeKutta 算法

(https://www.zhihu.com/question/34780726   赵恒的回答)

 

------------------------------------------------------------------------

M-Spline方法 的一个实现, 这种方法和bezier方法区别是,这种方法生成的曲线是过控制点的,并且每个点会影响全局生成的曲线信息,因此各自的适用情况不一样。而且比例是0-1全局的,因此一个完整的曲线不适用于过大的曲线描述,受限于float精度。。

using System;
using System.Runtime.InteropServices;
using UnityEngine;
using System.Collections;

public class MSplineScript : MonoBehaviour
{
    public static MSplineScript ins = null;

    protected Vector3[] dtbl;
    protected Vector3[] ftbl;
    public bool isMaked;
    private float length_;
    protected int lengthDiv = 8;
    public Vector3[] posTbl;
    protected float[] sectionLength;
    protected Method useMethod = Method.ImprovedEuler;
    void Awake()
    {
        ins = this;
    }
    ArrayList mpoints = new ArrayList();
    void Start()
    {
        ins = this;
        var ps = this.GetComponentsInChildren<Transform>();
        var vs = new Vector3[ps.Length];
        int i = 0;
        foreach (var p in ps)
        {
            vs[i] = p.position;
            ++i;
        }
        this.Make(vs);

        mpoints.Clear();
        var pp = this.GetComponentsInChildren<MPont>();
        foreach (var p in pp)
        {
            for (float iter = 0f; iter < 1f; iter += 0.001f)
            {
                if (Vector3.Distance(this.GetPoint(iter), p.transform.position) < 1f)
                // 注意这个1f的距离取决于 曲线的精度,如果曲线很长,那么这个1可能不够
                {
                    p.rate = iter;
                    break;
                }
            }
            mpoints.Add(p);
        }
    }
    public BezierDirection GetDirection(float rate)
    {
        for (int i = mpoints.Count - 2; i >= 0; i--)
        {
            var p = mpoints[i] as MPont;
            if (rate >= p.rate)
            {
                return p.direction;
            }
        }
        return BezierDirection.None;
    }
    protected float CalcSectionLength(int section)
    {
        Vector3 slope;
        float num2;
        float num3;
        int num = section;
        float num6 = 1f / ((float)this.lengthDiv);
        float num7 = 0f;
        switch (this.useMethod)
        {
            case Method.Euler:
                for (int i = 0; i <= this.lengthDiv; i++)
                {
                    float rate = i * num6;
                    slope = this.GetSlope(num, rate);
                    slope.Scale(slope);
                    num2 = Mathf.Sqrt((slope.x + slope.y) + slope.z);
                    num7 += num6 * num2;
                }
                return num7;

            case Method.ImprovedEuler:
                for (int j = 0; j <= (this.lengthDiv - 1); j++)
                {
                    float num9 = j * num6;
                    slope = this.GetSlope(num, num9);
                    slope.Scale(slope);
                    num2 = Mathf.Sqrt((slope.x + slope.y) + slope.z);
                    slope = this.GetSlope(num, (j + 1) * num6);
                    slope.Scale(slope);
                    num3 = Mathf.Sqrt((slope.x + slope.y) + slope.z);
                    num7 += (num6 * (num2 + num3)) * 0.5f;
                }
                return num7;

            case Method.RungeKutta:
                for (int k = 0; k <= this.lengthDiv; k++)
                {
                    float num11 = k * num6;
                    slope = this.GetSlope(num, num11);
                    slope.Scale(slope);
                    num2 = Mathf.Sqrt((slope.x + slope.y) + slope.z) * num6;
                    slope = this.GetSlope(num, num11 + (0.5f * num2));
                    slope.Scale(slope);
                    num3 = Mathf.Sqrt((slope.x + slope.y) + slope.z) * num6;
                    slope = this.GetSlope(num, num11 + (0.5f * num3));
                    slope.Scale(slope);
                    float num4 = Mathf.Sqrt((slope.x + slope.y) + slope.z) * num6;
                    slope = this.GetSlope(num, num11 + num4);
                    slope.Scale(slope);
                    float num5 = Mathf.Sqrt((slope.x + slope.y) + slope.z) * num6;
                    num7 += ((num2 + (2f * (num3 + num4))) + num5) / 6f;
                }
                return num7;
        }
        return num7;
    }

    public void GetAllRateToSectionRate(float rate, out int section, out float sectionRate)
    {
        rate = Mathf.Clamp(rate, 0f, 1f);
        section = 0;
        sectionRate = 0f;
        if (rate <= 0f)
        {
            section = 0;
            sectionRate = 0f;
        }
        else if (rate >= 1f)
        {
            section = this.posTbl.Length - 1;
            sectionRate = 0f;
        }
        else
        {
            float length = this.Length;
            float num2 = rate * length;
            float num3 = 0f;
            if (this.posTbl != null)
            {
                for (int i = 0; i < this.posTbl.Length; i++)
                {
                    float sectionLength = this.GetSectionLength(i);
                    if ((num3 + sectionLength) > num2)
                    {
                        section = i;
                        float num6 = num2 - num3;
                        sectionRate = num6 / sectionLength;
                        break;
                    }
                    num3 += sectionLength;
                }
            }
        }
    }

    public Vector3 GetPoint(float rate)
    {
        int num;
        float num2;
        this.GetAllRateToSectionRate(rate, out num, out num2);
        return this.GetPoint(num, num2);
    }

    public Vector3 GetPoint(int section, float rate)
    {
        if (!this.isMaked)
        {
            return Vector3.zero;
        }
        if (section >= this.posTbl.Length)
        {
            throw new IndexOutOfRangeException();
        }
        if (this.ftbl == null)
        {
            return Vector3.zero;
        }
        if (this.ftbl.Length == 0)
        {
            return Vector3.zero;
        }
        int index = section;
        Vector3 scale = new Vector3(rate, rate, rate);
        Vector3 vector = this.ftbl[index + 1] - this.ftbl[index];
        vector.Scale(scale);
        Vector3 vector2 = Vector3.Scale(this.ftbl[index], new Vector3(3f, 3f, 3f));
        vector += vector2;
        vector.Scale(scale);
        vector += this.dtbl[index + 1];
        vector2 = Vector3.Scale(this.ftbl[index], new Vector3(2f, 2f, 2f));
        vector -= vector2;
        vector -= this.ftbl[index + 1];
        vector.Scale(scale);
        return (vector + this.posTbl[index]);
    }

    public int GetSection(float rate)
    {
        int num;
        float num2;
        this.GetAllRateToSectionRate(rate, out num, out num2);
        return num;
    }

    public float GetSectionLength(int section)
    {
        if ((this.sectionLength != null) && ((section < this.sectionLength.Length) && (this.sectionLength != null)))
        {
            return this.sectionLength[section];
        }
        return 0f;
    }

    public float GetSectionRate(int section)
    {
        if ((section >= 0) && (section < (this.posTbl.Length - 1)))
        {
            return (this.GetSectionLength(section) / this.Length);
        }
        return 0f;
    }

    public float GetSectionRateToAllRate(int section, float rate)
    {
        if (section < 0)
        {
            return 0f;
        }
        if (section > (this.posTbl.Length - 1))
        {
            return 1f;
        }
        float num = 0f;
        for (int i = 0; i < this.posTbl.Length; i++)
        {
            if (i < section)
            {
                num += this.GetSectionRate(i);
            }
            else
            {
                return (num + (this.GetSectionRate(i) * rate));
            }
        }
        return num;
    }

    public Vector3 GetSlope(float rate)
    {
        int num;
        float num2;
        this.GetAllRateToSectionRate(rate, out num, out num2);
        return this.GetSlope(num, num2);
    }

    public Vector3 GetSlope(int section, float rate)
    {
        if (section >= this.posTbl.Length)
        {
            throw new IndexOutOfRangeException();
        }
        if (this.ftbl == null)
        {
            return Vector3.zero;
        }
        if (this.ftbl.Length == 0)
        {
            return Vector3.zero;
        }
        int index = section;
        Vector3 a = new Vector3(rate, rate, rate);
        Vector3 vector = this.ftbl[index + 1] - this.ftbl[index];
        vector.Scale(Vector3.Scale(a, new Vector3(3f, 3f, 3f)));
        Vector3 vector2 = Vector3.Scale(this.ftbl[index], new Vector3(6f, 6f, 6f));
        vector += vector2;
        vector.Scale(a);
        vector += this.dtbl[index + 1];
        vector2 = Vector3.Scale(this.ftbl[index], new Vector3(2f, 2f, 2f));
        vector -= vector2;
        return (vector - this.ftbl[index + 1]);
    }

    public void Make(Vector3[] points)
    {
        int num2;
        this.posTbl = new Vector3[points.Length];
        points.CopyTo(this.posTbl, 0);
        Vector3[] vectorArray = new Vector3[points.Length];
        this.dtbl = new Vector3[2 * points.Length];
        this.ftbl = new Vector3[2 * points.Length];
        int length = points.Length;
        Vector3[] vectorArray2 = vectorArray;
        Vector3[] dtbl = this.dtbl;
        Vector3[] ftbl = this.ftbl;
        ftbl[0].x = ftbl[length - 1].x = ftbl[0].y = ftbl[length - 1].y = ftbl[0].z = ftbl[length - 1].z = 0f;
        for (num2 = 0; num2 < (length - 1); num2++)
        {
            dtbl[num2 + 1] = this.posTbl[num2 + 1] - this.posTbl[num2];
        }
        ftbl[1] = dtbl[2] - dtbl[1];
        vectorArray2[1].x = vectorArray2[1].y = vectorArray2[1].z = 4f;
        for (num2 = 1; num2 < (length - 2); num2++)
        {
            Vector3 scale = new Vector3(-1f / vectorArray2[num2].x, -1f / vectorArray2[num2].y, -1f / vectorArray2[num2].z);
            Vector3 vector = ftbl[num2];
            vector.Scale(scale);
            ftbl[num2 + 1] = dtbl[num2 + 2] - dtbl[num2 + 1];
            ftbl[num2 + 1] += vector;
            vectorArray2[num2 + 1] = new Vector3(4f, 4f, 4f);
            vectorArray2[num2 + 1] += scale;
        }
        ftbl[length - 2].x -= ftbl[length - 1].x;
        ftbl[length - 2].y -= ftbl[length - 1].y;
        ftbl[length - 2].z -= ftbl[length - 1].z;
        for (num2 = length - 2; num2 > 0; num2--)
        {
            ftbl[num2].x = (ftbl[num2].x - ftbl[num2 + 1].x) / vectorArray2[num2].x;
            ftbl[num2].y = (ftbl[num2].y - ftbl[num2 + 1].y) / vectorArray2[num2].y;
            ftbl[num2].z = (ftbl[num2].z - ftbl[num2 + 1].z) / vectorArray2[num2].z;
        }
        this.sectionLength = new float[this.posTbl.Length];
        for (num2 = 0; num2 < this.posTbl.Length; num2++)
        {
            this.sectionLength[num2] = this.CalcSectionLength(num2);
        }
        this.isMaked = true;
        this.length_ = 0f;
    }

    protected void GetAxisAndAngleOfPath(float rate, out Vector3 axis, out float angle)
    {
        Vector3 slope = GetSlope(rate);
        if (slope.magnitude < 0.001f)
        {
            axis = Vector3.up;
            angle = 0f;
        }
        else
        {
            Quaternion.LookRotation(slope).ToAngleAxis(out angle, out axis);
            if (axis.y < 0f)
            {
                axis.Scale(new Vector3(-1f, -1f, -1f));
                angle = 360f - angle;
            }
        }
    }


    void OnDrawGizmos()
    {
        return;
        Start();
        for (float t = 0f; t < 1f; t += 0.01f)
        {
            Debug.DrawLine(this.GetPoint(t), this.GetPoint(t + 0.01f));
        }
    }
    public float Length
    {
        get
        {
            if (!Application.isPlaying || (this.length_ == 0f))
            {
                float num = 0f;
                if (this.posTbl != null)
                {
                    for (int i = 0; i < (this.posTbl.Length - 1); i++)
                    {
                        num += this.GetSectionLength(i);
                    }
                }
                this.length_ = num;
            }
            return this.length_;
        }
    }


    //-----------------------------------------------------------------------------

    public float GetInvR(float nowRate)
    {
        Vector3 vector;
        Vector3 vector2;
        float num2;
        float num3;
        float num4 = 5f;
        float rate = Mathf.Clamp01(nowRate - ((num4 * 0.5f) / Length));
        float num6 = Mathf.Clamp01(nowRate + ((num4 * 0.5f) / Length));
        this.GetAxisAndAngleOfPath(rate, out vector, out num2);// 直接坐标切线
        this.GetAxisAndAngleOfPath(num6, out vector2, out num3);
        float num7 = num3 - num2;// 当前2个点的世界坐标原点 切线的角度,  角度是基于世界坐标原点而来
        if (num7 > 180f)
        {
            num7 -= 360f;
        }
        if (num7 < -180f)
        {
            num7 += 360f;
        }
        float num8 = 30f;
        float num9 = 0.2f;
        float num = Mathf.Atan(num7 * num9) / 1.570796f;// 角度变化量*0.2
        float invR = (num * (1f / num8));
        return invR;
    }

    [HideInInspector]
    public bool CalcRate = false;

    public float rate;
    public void Update()
    {
        ins = this;
        if (CalcRate == false) return;

        var ps = this.GetComponentsInChildren<Transform>();
        var vs = new Vector3[ps.Length];
        int i = 0;
        foreach (var p in ps)
        {
            vs[i] = p.position;
            ++i;
        }
        this.Make(vs);

        GameObject.Find("CalcRate").transform.position = GetPoint(rate);
    }

    float nowRate = 0f;

    public float IteratePathPosisionRatio(float nowRate, float sideOffset, float length)
    {
        float num = length / this.Length;
        Vector3 point = this.GetPoint(nowRate, sideOffset);
        for (int i = 0; i < 10; i++)
        {
            Vector3 vector3 = this.GetPoint(nowRate + num, sideOffset) - point;
            if (vector3.magnitude == 0f)
            {
                break;
            }
            float num3 = length / vector3.magnitude;
            float num4 = 0.05f;
            if (Mathf.Abs((float)(num3 - 1f)) <= num4)
            {
                break;
            }
            num *= ((num3 - 1f) * 0.75f) + 1f;
        }
        float b = nowRate + num;
        return Mathf.Min(1f, b);
    }
    public Vector3 GetPoint(float rate, float sideOffsetRate)
    {
        int num;
        float num2;
        this.GetAllRateToSectionRate(rate, out num, out num2);
        return this.GetPoint(num, num2, sideOffsetRate);
    }

    public Vector3 GetPoint(int section, float sectionRate, float sideOffsetRate)
    {
        Vector3 point;
        if (sideOffsetRate == 0f)
        {
            point = this.GetPoint(section, sectionRate);
        }
        else
        {
            Vector3 normalized = this.GetSlope(section, sectionRate).normalized;
            Vector3 vector3 = Vector3.Cross(Vector3.up, normalized);
            float num = this.GetWidth(section, sectionRate, sideOffsetRate) * sideOffsetRate;
            point = ((Vector3)(vector3 * num)) + this.GetPoint(section, sectionRate);
        }
        point.y = 0.1f;
        return point;
    }

    public float GetWidth(float rate, float side)
    {
        int num;
        float num2;
        this.GetAllRateToSectionRate(rate, out num, out num2);
        return this.GetWidth(num, num2, side);
    }

    public float GetWidth(int section, float rate, float side)
    {
        return 0.2f;

        /*    
           float from = this.pointList[section].getWidth(side);
           if (section < (this.posTbl.Length - 1))
           {
               from = Mathf.Lerp(from, this.pointList[section + 1].getWidth(side), rate);
           }
           return from;*/
    }





    protected enum Method
    {
        Euler,
        ImprovedEuler,
        RungeKutta
    }
}

Method 表示使用何种方法进行 曲线渐进计算 欧拉法,改进的欧拉法,Rungekutta方法。。皆是上述提到的方法。

© 著作权归作者所有

共有 人打赏支持
梦想游戏人
粉丝 34
博文 402
码字总数 115594
作品 0
成都
Convert BSpline Curve to Arc Spline in OpenCASCADE

Abstract. The paper based on OpenCASCADE algorithms to approximate the NURBS curve to arc spline. The method is most useful in numerical control to drive the cutter along straig......

eryar ⋅ 2016/11/23 ⋅ 0

图像放大方法概述

影视制作领域,往往会涉及到将低分辨率的图像放大为高分辨的图像的问题,有时候还会涉及到 非正方形像素到正方形像素的调整问题。本章将在图像放大算法,像素宽高比调整方法以及图像序列放大...

自由的角马 ⋅ 2015/01/10 ⋅ 0

用canvas绘制一个曲线动画——深入理解贝塞尔曲线

摘要:在前端开发中,贝赛尔曲线无处不在:这篇文章我准备从实现一个非常简单的曲线动画效果入手,帮助大家彻底地弄懂什么是贝塞尔曲线,以及它有哪些特性,文章中有一点点数学公式,但是都非...

hujiulong/blog ⋅ 01/04 ⋅ 0

SplineLibrary 2.3 发布,样条曲线生成工具

SplineLibrary 2.3 发布,此版本更新内容如下: 添加了一个样条曲线校准框架 shape preservingspline suite ,space spline curves, B-splines, regression splines,tension splines, smoot......

oschina ⋅ 2014/01/26 ⋅ 2

【18-03-24】Matlab 数据分析

[18-03-24] Matlab 数据分析 1° 数据插值 一般地,从各种试验得来的数据总是有一定的数量,而利用插值技术能够从有限的数据中获取系统整体的状态,因此,数据插值在各行各业,特别是信号处理...

千阳Weston ⋅ 03/25 ⋅ 0

贝塞尔曲线仿美拍直播刷礼物

我特别喜欢看明星的直播,比如演技精湛吴亦凡,百花影帝李易峰,铁血硬汉黄子韬,男人本色是鹿晗,从不吸毒柯震东,一米八零黄晓明,从未整容是杨颖,阖家欢乐王宝强。啊,这些明星我特别喜欢...

翻滚吧李博 ⋅ 2017/11/29 ⋅ 0

Android一个包含表格的图标库

之前有写过一个图表lib,但是开发的速度,大多很难跟上产品需求变化的脚步,所以修改了下原先的图表库,支持图表下面能整合table显示对应的类目,用曲线替换了折线,支持多曲线的显示,增加了...

WelliJhon ⋅ 01/30 ⋅ 0

一、R语言可视化--ggplot2之快速作图qplot()

转载来自:http://www.cnblogs.com/lizhilei-123/p/6722116.html ggplot2之快速作图qplot() qplot()的意思是快速作图,利用它可以很方便的创建各种复杂的图形,其他系统需要好几行代码才能解...

u011596455 ⋅ 03/15 ⋅ 0

微软AI面试题有多难?这里有一份样卷

     大数据文摘作品   编译:张南星、卫青、钱天培      究竟什么样的AI人才能被微软这样的巨头聘用呢?   是不是要码力超群,上来就能徒手写个AlphaGo呢?还是要眼光毒辣,当场...

大数据文摘 ⋅ 05/13 ⋅ 0

RootFinder 2.3 发布,平方根计算技术

RootFinder 2.3 发布,此版本更新内容如下: 添加l基础 spline 库扩展,b spline 函数,创建基于 spline discout curve 的功能,创建 基于 spline 的 forward curve 的功能,和 canned produ...

oschina ⋅ 2014/01/24 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Thrift RPC实战(二) Thrift 网络服务模型

TServer类层次体系 TSimpleServer/TThreadPoolServer是阻塞服务模型 TNonblockingServer/THsHaServer/TThreadedSelectotServer是非阻塞服务模型(NIO) 1 TServer抽象类的定义 内部静态类Args的...

lemonLove ⋅ 12分钟前 ⋅ 0

vim命令用法

第五章 vim命令 vim和vi几乎是一样的,唯一的区别就是当编辑一个文本时,使用vi不会显示颜色,而使用vim会显示颜色。 vim有三个模式:一般模式,编辑模式,命令模式。 系统最小化安装时没有安...

弓正 ⋅ 13分钟前 ⋅ 0

MyBatis源码解读之配置

1. 目的 本文主要介绍MyBatis配置文件解析,通过源码解读mybatis-config.xml(官方默认命名)、Mapper.xml 与Java对象的映射。 2. MyBatis结构 查看大图 MyBatis结构图,原图实在太模糊了,所以...

无忌 ⋅ 17分钟前 ⋅ 0

Ignite的jdbc与网格的连接方式的查询性能对比

环境: 数据量100万 Ignite2.5 Windows10 8g jdbc方式连接 import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; i......

仔仔1993 ⋅ 32分钟前 ⋅ 0

收集自网络的wordpress 分页导航的代码教程(全网最全版)

wordpress 分页导航是用来切换文章的一个功能,添加了 wordpress 分页导航后,用户即可自由到达指定的页面数浏览分类文章,而这样的一个很简单功能却有很多朋友在用插件:WP-PageNavi,插件的...

Rhymo-Wu ⋅ 48分钟前 ⋅ 0

微服务 WildFly Swarm 入门

Hello World 就像前面章节中的其他框架一样,我们希望添加一些基本的 Hello-world 功能,然后在其上逐步添加更多的功能。让我们从在我们的项目中创建一个 HolaResources 开始。您可以使用您的...

woshixin ⋅ 55分钟前 ⋅ 0

Maven的安装和Eclipse的配置

1. 下载Maven 下载地址 2. 解压压缩包,放到自己习惯的硬盘中 此处我将其放到了 D:\Tools 目录下。 3. 配置环境变量 右键此电脑 -> 属性 -> 高级系统设置 -> 环境变量。 在系统变量中新建,变...

影狼 ⋅ 今天 ⋅ 0

python pip使用国内镜像的方法

国内源 清华:https://pypi.tuna.tsinghua.edu.cn/simple 阿里云:http://mirrors.aliyun.com/pypi/simple/ 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/ 华中理工大学:http://......

良言 ⋅ 今天 ⋅ 0

对于url变化的spa应该如何使用微信jssdk

使用vue单页面碰上微信jssdk config验证失败的坑。第一次成功 之后切换页面全部失败,找到了解决方法,第一次验证成功后保存验证信息 切换页面时验证信息直接拿来用,加一个wx.error() 失败时...

孙冠峰 ⋅ 今天 ⋅ 0

Spring Cloud Gateway 一般集成

SCF发布,带来很多新东西,不过少了点教程,打开方式又和以前的不一样,比如这个SCG,压根就没有入门指导,所以这里写一个,以备后用。 一、集成 pom.xml <dependency> <groupI...

kut ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部