C#基數樣條曲線的類比實現(對應Graphics的DrawCurve)

來源:互聯網
上載者:User

標籤:c#   drawcurve   基數樣條曲線   樣條曲線   

C#的繪圖函數中有一個繪製樣條曲線的函數DrawCurve,當只傳入Pen和Point數組時,採用的是基數樣條曲線繪製。如果只是繪製樣條曲線,那這個函數已經滿足了。但是項目中要求不但要繪製曲線,還要將曲線以方格的形式類比來實現。為此,就必須知道樣條曲線是如何繪製的,才有辦法知道都有哪些點,然後再用格子來類比。

起初,使用了很粗暴的方法,即使用DrawCurve在記憶體中繪製到Image中,然後從Image中取出黑白點,然後形成黑白點的矩陣,進而利用這些矩陣點對應到像素點來繪製方格。做了簡單的實現,但效果不理想。原因有幾個。

1.不斷使用記憶體繪製到Image中需要消耗大量的記憶體。

2.利用像素點來採集矩陣的點時,難以確定一個採集的範圍。

3.至少需要對像素進行X和Y的雙重迴圈遍曆才能達成,這樣時間複雜度會隨著X、Y的增加而不斷加大。

後面找到了一份類比實現,經過改造,初步達成了目的。來看看類比實現的和DrawCurve的擬合,見。


註:圖中黑色部分使用DrawCurve來繪製,黑色線中間的白色部分採用的是類比繪製。從測試的結果來看,符合程度比較理想。下面是實現的代碼。

using System;using System.Collections.Generic;using System.Drawing;using System.Linq;using System.Text;namespace SplineTest{    /// <summary>        /// 樣條曲線。每根樣條曲線包含4個控制點    /// </summary>    public class Spline    {               /// <summary>        /// 樣點數。在點Pk和Pk+1之間,將會產生若干個樣點。所以"u"將會從0.00F增長到0.05F.        /// </summary>        private static readonly int _samplePointCount = 20;        /// <summary>        /// 在基數演算法中的t        /// </summary>        private static readonly float _tension = 0.0F;        #region 屬性        private PointF _startControlPoint;        /// <summary>        /// "Pk-1"點(起始控制點)        /// </summary>        public PointF StartControlPoint        {            get            {                return this._startControlPoint;            }            set            {                this._startControlPoint = value;            }        }        private PointF _startPoint;        /// <summary>        ///  "Pk"點(起始點)        /// </summary>        public PointF StartPoint        {            get            {                return this._startPoint;            }            set            {                this._startPoint = value;            }        }                private PointF _endPoint;        /// <summary>        /// "Pk+1"點(結束點)        /// </summary>        public PointF EndPoint        {            get            {                return this._endPoint;            }            set            {                this._endPoint = value;            }        }                private PointF _endControlPoint;        /// <summary>        /// "Pk+2"點(結束控制點)        /// </summary>        public PointF EndControlPoint        {            get            {                return this._endControlPoint;            }            set            {                this._endControlPoint = value;            }        }                private PointF[] _ctrlPoints;        /// <summary>        /// 曲線點(控制點及類比的樣點)        /// </summary>        public PointF[] CtrlPoints        {            get            {                return this._ctrlPoints;            }        }        private bool _isFirst = false;        /// <summary>        /// 標識當前樣條曲線是否是第一條,如果是m_startControlPoint 和 m_startPoint將會相同。        /// 因為在Pk和Pk+1之間需要4個點來決定樣條曲線,所以我們需要在Pk-1點前手動添加一個點。        /// 這樣我們才能在Pk-1和Pk+1之間繪製樣條曲線。        /// 同樣的,最後一根樣條曲線的Pk+2點會與它的"Pk+1"點相同,        /// 這樣我們才能在Pk+1和Pk+2之間繪製樣條曲線。        /// </summary>        public bool IsFirst        {            get            {                return this._isFirst;            }            set            {                this._isFirst = value;            }        }        #endregion        public Spline()        {            _startControlPoint = new PointF();            _startPoint = new PointF();            _endPoint = new PointF();            _endControlPoint = new PointF();            _ctrlPoints = new PointF[_samplePointCount + 1];            for (int i = 0; i < _ctrlPoints.Length; i++)            {                _ctrlPoints[i] = new PointF();            }        }        /// <summary>        ///添加關節。將新控制點添加到控制點列表中,並更新前面的樣條曲線。        /// </summary>        /// <param name="prevSpline">前一根樣條曲線</param>        /// <param name="currentPoint">當前點</param>        public void AddJoint(Spline prevSpline, PointF currentPoint)        {            //前一根樣條曲線(prevSpline)為null,說明控制點列表中只有一個點,所以4個控制點樣同。            //當第2個及之後的控制點添加到控制點列表中時,那第1根樣條曲線的Pk+1和Pk+2點需要更新            if (null == prevSpline)            {                this._startControlPoint = currentPoint;                this._startPoint = currentPoint;                this._endPoint = currentPoint;                this._endControlPoint = currentPoint;                this._isFirst = true;            }            else//前一根樣條曲線不為null,所以更新前一根樣條曲線的控制點列表,同時更新當前樣條曲線的控制點列表。            {                //前一根樣條曲線是第1根樣條曲線,更新它的Pk+1和Pk+2點                if (true == prevSpline._isFirst)                {                    this._startControlPoint = prevSpline.StartControlPoint;                    this._startPoint = prevSpline.StartPoint;                    this._endPoint = currentPoint;                    this._endControlPoint = currentPoint;                    GenerateSamplePoint();                    return;                }                else///前一根樣條曲線不是第1根樣條曲線,僅更新它的Pk+2點                {                    prevSpline.EndControlPoint = currentPoint;                    prevSpline.GenerateSamplePoint();                    //類比當前樣條曲線的樣點                    this._startControlPoint = prevSpline._startPoint;                    this._startPoint = prevSpline._endPoint;                    this._endPoint = currentPoint;                    this._endControlPoint = currentPoint;                    GenerateSamplePoint();                }            }        }        /// <summary>        /// 使用基數演算法產生樣點        /// </summary>        public void GenerateSamplePoint()        {            PointF startControlPoint = this.StartControlPoint;            PointF startPoint = this.StartPoint;            PointF endPoint = this.EndPoint;            PointF endControlPoint = this.EndControlPoint;            float step = 1.0F / (float)_samplePointCount;            float uValue = 0.00F;            for (int i = 0; i < _samplePointCount; i++)            {                PointF pointNew = GenerateSimulatePoint(uValue, startControlPoint, startPoint, endPoint, endControlPoint);                this.CtrlPoints[i] = pointNew;                uValue += step;            }            this.CtrlPoints[_ctrlPoints.Length - 1] = endPoint;        }        /// <summary>        /// 繪製樣條曲線        /// </summary>        /// <param name="g"></param>        public void Draw(Graphics g, Pen pen)        {            for (int i = 0; i < _ctrlPoints.Length - 1; i++)            {                PointF lastPoint = _ctrlPoints[i];                PointF nextPoint = _ctrlPoints[i + 1];                g.DrawLine(pen, lastPoint, nextPoint);            }        }        #region GenerateSimulatePoint        /// <summary>        /// 產生曲線類比點,該點在startPoint和endPoint之間        /// </summary>        /// <param name="u">介於0和1之間的變數</param>        /// <param name="startControlPoint">起始點startPoint之前的控制點, 協助確定曲線的外觀</param>        /// <param name="startPoint">目標曲線的起始點startPoint,當u=0時,返回結果為起始點startPoint</param>        /// <param name="endPoint">目標曲線的結束點endPoint, 當u=1時,返回結果為結束點endPoint</param>        /// <param name="endControlPoint">在起結點startPoint之後的控制點, 協助確定曲線的外觀</param>        /// <returns>返回介於startPoint和endPoint的點</returns>        private PointF GenerateSimulatePoint(float u,                                PointF startControlPoint,                                PointF startPoint,                                PointF endPoint,                                PointF endControlPoint)        {            float s = (1 - _tension) / 2;            PointF resultPoint = new PointF();            resultPoint.X = CalculateAxisCoordinate(startControlPoint.X, startPoint.X, endPoint.X, endControlPoint.X, s, u);            resultPoint.Y = CalculateAxisCoordinate(startControlPoint.Y, startPoint.Y, endPoint.Y, endControlPoint.Y, s, u);            return resultPoint;        }        /// <summary>        /// 計算軸座標        /// </summary>        /// <param name="a"></param>        /// <param name="b"></param>        /// <param name="c"></param>        /// <param name="d"></param>        /// <param name="s"></param>        /// <param name="u"></param>        /// <returns></returns>        private float CalculateAxisCoordinate(float a, float b, float c, float d, float s, float u)        {            float result = 0.0F;            result = a * (2 * s * u * u - s * u * u * u - s * u)                   + b * ((2 - s) * u * u * u + (s - 3) * u * u + 1)                   + c * ((s - 2) * u * u * u + (3 - 2 * s) * u * u + s * u)                   + d * (s * u * u * u - s * u * u);            return result;        }        #endregion        /// <summary>        /// 擷取樣條曲線上的點        /// </summary>        /// <param name="g"></param>        /// <param name="pen"></param>        /// <param name="points"></param>        public static List<PointF> FetchPoints(PointF[] points)        {            if (points == null || points.Length <= 0)            {                return null;            }            List<Spline> _splines = new List<Spline>();            Spline splineNew = null;            Spline lastNew = null;            foreach (PointF nowPoint in points)            {                if (null == _splines || 0 == _splines.Count)                {                    splineNew = new Spline();                    splineNew.AddJoint(null, nowPoint);                    _splines.Add(splineNew);                }                else                {                    splineNew = new Spline();                    lastNew = _splines[_splines.Count - 1] as Spline;                    splineNew.AddJoint(lastNew, nowPoint);                    _splines.Add(splineNew);                };            }            List<PointF> _points = new List<PointF>();            foreach (Spline spline in _splines)            {                if (spline.IsFirst)                {                    continue;                }                foreach (PointF point in spline.CtrlPoints)                {                    if (_points.Contains(point))                    {                        continue;                    }                    _points.Add(point);                }                           }            return _points;        }    }    /// <summary>    /// Graphics擴充    /// </summary>    public static class GraphicsExtension    {        /// <summary>        /// 繪製樣條曲線        /// </summary>        /// <param name="g"></param>        /// <param name="pen"></param>        /// <param name="points"></param>        public static void DrawSpline(this Graphics g, Pen pen, PointF[] points)        {            if (g == null)            {                return;            }            if (pen == null)            {                return;            }            if (points == null || points.Length <= 0)            {                return;            }            List<Spline> _splines = new List<Spline>();            Spline splineNew = null;            Spline lastNew = null;            foreach (PointF nowPoint in points)            {                if (null == _splines || 0 == _splines.Count)                {                    splineNew = new Spline();                    splineNew.AddJoint(null, nowPoint);                    _splines.Add(splineNew);                }                else                {                    splineNew = new Spline();                    lastNew = _splines[_splines.Count - 1];                    splineNew.AddJoint(lastNew, nowPoint);                    _splines.Add(splineNew);                }            }                        Spline spline = null;            for (int i = 0; i < _splines.Count; i++)            {                spline = _splines[i];                if (spline.IsFirst)                {                    continue;                }                spline.Draw(g, pen);            }        }    }}
注:

1.Spline部分最核心的演算法是CalculateAxisCoordinate,網上有很多類似的實現,但都不理想,這個是比較理想的一個。

2.為了便於在Graphics中直接調用,這裡對Graphics增加了一個擴充方法DrawSpline,這樣就可以像調用DrawCurve一樣調用,即g.DrawSpline(pen,points).

3.在繪製出樣條曲線後,還需要能得到其所類比的點,於是在Spline中增加了一個FetchPoints的方法。

轉載請註明出處http://blog.csdn.net/xxdddail/article/details/47662983。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

C#基數樣條曲線的類比實現(對應Graphics的DrawCurve)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.