基于BSpline的插值运算

正在做的一个C#项目要用到插值运算,插值运算算法主要是用了BSpline和多项式。

找了一下没有写得特别好的BSpline的C#库,Delphi倒是有一个别人写的不错的例子。

简单改写了一下。

using System;
using System.Collections.Generic;
using System.Text;

namespace BSplineDemo
{
    class Vertex
    {
        Single _X;

        public Single X
        {
            get { return _X; }
            set { _X = value; }
        }
        Single _Y;

        public Single Y
        {
            get { return _Y; }
            set { _Y = value; }
        }
    }

    class BSpline
    {
        const double MaxError = 1e-6;
        List<Vertex> _VertexList=new List<Vertex>();

        //Boolean _Interpolated = false;

        Vertex _firstVertex = new Vertex();
        Vertex _lastVertex = new Vertex();

        public int PointsCount
        {
            get
            {
                return _VertexList.Count;
            }

        }

        public Vertex GetVertex(int Index)
        {
            if (Index == 0)
                return _firstVertex;
            else if (Index == PointsCount + 1)
                return _lastVertex;
            else
                return _VertexList[Index - 1];
        }

        public Vertex Value(Single Parameter)
        {
            //if (!_Interpolated)
            //    Interpolate();

            Vertex v = new Vertex();
            int b, c;
            double dist;
            double mix;
            b = Convert.ToInt32(Math.Truncate((PointsCount - 1) * Parameter));
            for (c = b - 2; c <= b + 3; c++)
            {
                dist = Math.Abs((PointsCount - 1) * Parameter - (c - 1));
                if (dist < 2)
                {
                    if (dist < 1)
                        mix = 4.0 / 6.0 - dist * dist + 0.5 * dist * dist * dist;
                    else
                        mix = (2.0 - dist) * (2.0 - dist) * (2.0 - dist) / 6.0;
                    v.X = Convert.ToSingle(v.X + GetVertex(c).X * mix);
                    v.Y = Convert.ToSingle(v.Y + GetVertex(c).Y * mix);
                }
            }
            return v;
        }

        public void AddVertex(Vertex AVertex)
        {
            _VertexList.Add(AVertex);
            PhantomPoints();

            //_Interpolated = false;
        }

        //B-Splines use phantompoints to interpolate begin and end points
        private void PhantomPoints()
        {
            if (PointsCount > 1)
            {
                _firstVertex.X = 2 * _VertexList[0].X - _VertexList[1].X;
                _firstVertex.Y = 2 * _VertexList[0].Y - _VertexList[1].Y;
                _lastVertex.X = 2 * _VertexList[PointsCount - 1].X - _VertexList[PointsCount - 2].X;
                _lastVertex.Y = 2 * _VertexList[PointsCount - 1].Y - _VertexList[PointsCount - 2].Y;

            }
        }

        public void ClearVertexs()
        {
            _VertexList.Clear();
        }
    }
}
 

调用示例程序

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BSplineDemo
{
    public partial class Form1 : Form
    {
        private BSpline line = new BSpline();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //
            Random rand=new Random();
            line.ClearVertexs();
            for (int i = 0; i < 3; i++)
            {
                Vertex v = new Vertex();
                v.X = rand.Next(0, this.ClientSize.Width);
                v.Y = rand.Next(0, this.ClientSize.Height);

                line.AddVertex(v);
            }

            PaintLine();

        }

        private void PaintLine()
        {
            int j;
            Graphics g = this.CreateGraphics();
            g.Clear(Color.White);
            Pen p=new Pen(Color.Black);
            try
            {
                float X1 = 0, Y1 = 0;
                for (j = 0; j <= 10; j++)
                {
                    Single x = Convert.ToSingle(j / 10.0);   
                    Vertex V = line.Value(x);
                    if (j == 0)
                    {
                        X1 = V.X;
                        Y1 = V.Y;
                    }
                    else
                    {
                        g.DrawLine(p, X1, Y1, V.X, V.Y);
                        X1 = V.X;
                        Y1 = V.Y;
                    }
                }

                for (j = 1; j <= line.PointsCount; j++)
                {
                    Vertex V = line.GetVertex(j);
                    g.DrawEllipse(p, V.X - 2, V.Y - 2, 4, 4);
                }
            }
            finally
            {
                p.Dispose();
                g.Dispose();
            }

        }
    }
}

AttachmentSize
BSplineDemo.rar38.23 KB