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

{

///

/// 样条曲线。每根样条曲线包含4个控制点

///

public class Spline

{

///

/// 样点数。在点Pk和Pk+1之间,将会生成若干个样点。所以"u"将会从0.00F增长到0.05F.

///

private static readonly int _samplePointCount = 20;

///

/// 在基数算法中的t

///

private static readonly float _tension = 0.0F;

#region 属性

private PointF _startControlPoint;

///

/// "Pk-1"点(起始控制点)

///

public PointF StartControlPoint

{

get

{

return this._startControlPoint;

}

set

{

this._startControlPoint = value;

}

}

private PointF _startPoint;

///

/// "Pk"点(起始点)

///

public PointF StartPoint

{

get

{

return this._startPoint;

}

set

{

this._startPoint = value;

}

}

private PointF _endPoint;

///

/// "Pk+1"点(结束点)

///

public PointF EndPoint

{

get

{

return this._endPoint;

}

set

{

this._endPoint = value;

}

}

private PointF _endControlPoint;

///

/// "Pk+2"点(结束控制点)

///

public PointF EndControlPoint

{

get

{

return this._endControlPoint;

}

set

{

this._endControlPoint = value;

}

}

private PointF[] _ctrlPoints;

///

/// 曲线点(控制点及模拟的样点)

///

public PointF[] CtrlPoints

{

get

{

return this._ctrlPoints;

}

}

private bool _isFirst = false;

///

/// 标识当前样条曲线是否是第一条,如果是m_startControlPoint 和 m_startPoint将会相同。

/// 因为在Pk和Pk+1之间需要4个点来决定样条曲线,所以我们需要在Pk-1点前手动添加一个点。

/// 这样我们才能在Pk-1和Pk+1之间绘制样条曲线。

/// 同样的,最后一根样条曲线的Pk+2点会与它的"Pk+1"点相同,

/// 这样我们才能在Pk+1和Pk+2之间绘制样条曲线。

///

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();

}

}

///

///添加关节。将新控制点添加到控制点列表中,并更新前面的样条曲线。

///

/// 前一根样条曲线

/// 当前点

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();

}

}

}

///

/// 使用基数算法生成样点

///

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;

}

///

/// 绘制样条曲线

///

///

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

///

/// 生成曲线模拟点,该点在startPoint和endPoint之间

///

/// 介于0和1之间的变量

/// 起始点startPoint之前的控制点, 协助确定曲线的外观

/// 目标曲线的起始点startPoint,当u=0时,返回结果为起始点startPoint

/// 目标曲线的结束点endPoint, 当u=1时,返回结果为结束点endPoint

/// 在起结点startPoint之后的控制点, 协助确定曲线的外观

/// 返回介于startPoint和endPoint的点

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;

}

///

/// 计算轴坐标

///

///

///

///

///

///

///

///

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

///

/// 获取样条曲线上的点

///

///

///

///

public static List FetchPoints(PointF[] points)

{

if (points == null || points.Length <= 0)

{

return null;

}

List _splines = new List();

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 _points = new List();

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;

}

}

///

/// Graphics扩展

///

public static class GraphicsExtension

{

///

/// 绘制样条曲线

///

///

///

///

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 _splines = new List();

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#样条曲线命令_C#基数样条曲线的模拟实现(对应Graphics的DrawCurve)相关推荐

  1. c#样条曲线命令_C# chart控件绘制曲线

    标签: 在.NET中以前经常用GDI去绘制,虽然效果也不错,自从.NET 4.0开始,专门为绘制图表而生的Chart控件出现了,有了它,就可以轻松的绘制你所需要的曲线图.柱状图什么的了. using ...

  2. c#样条曲线命令_如何定制CAD功能区界面中的命令?

    在AUTOCAD 高版本中使用了RIBBON功能区界面,到了2015版以后舍弃了传统的菜单工具栏界面,只保留了RIBBON界面,但为了照顾一部分的习惯,可以选择打开菜单栏.不少人仍习惯旧版经典界面,因 ...

  3. C++中当用命令行CTRL+Z+Enter模拟EOF后还想继续输入的探索

    当达到文件尾部EOF(End of File)时,c++会置位ios::failbit和ios::eofbit.进而我们不能再读取内容了,毕竟文章末尾已经没有东西可读了.但是但我们用命令行CTRL+Z ...

  4. eNSP入门常用命令(一)以及模拟双PC双路由互通

    eNSP入门常用命令(一)以及模拟双PC双路由互通 1. 基本命令 system-view 用户进入系统命令 缩写 syssysname 修改系统名命令interface 参数(接口名称)进入接口命令 ...

  5. VTK根据几个点插值成样条曲线,然后以样条曲线为中心线,生成圆柱体管道

    VTK根据几个点插值成样条曲线,然后以样条曲线为中心线,生成圆柱体管道 #include "vtkActor.h" #include "vtkCamera.h" ...

  6. curl命令java_上curl java 模拟http请求

    最近,我的项目要求java模拟http请求,获得dns解决 tcp处理过的信息特定的连接. java api提供urlConnection apache提供的httpClient都不能胜任该需求,二次 ...

  7. c语言退出程序命令_C语言#error命令,阻止程序编译

    #error 指令用于在编译期间产生错误信息,并阻止程序的编译,其形式如下: #error error_message 例如,我们的程序针对 Linux 编写,不保证兼容 Windows,那么可以这样 ...

  8. java 窗口鼠标句柄_c#获取窗体句柄模拟鼠标点击

    [实例简介] c#获取窗体句柄模拟鼠标点击实例2012年8月20日23:48:21,代码清楚,有详细注释,希望对大家有用 [实例截图] [核心代码] 获取窗体句柄模拟鼠标点击 └── 获取窗体句柄模拟 ...

  9. sudo: add-apt-repository:找不到命令_C++腾讯面试题库干货!作为程序员,这些都掌握了,还有什么理由拿不到offer?...

    前面小编发的两篇文章就是相对这些问题的知识点.方便大家套用练习,特别在面试前翻看几次,临时记忆也好.助你轻松拿到大厂offer.记得关注我. 1. C 和 C++ 区别 2. const 有什么用途 ...

  10. wpf如何实现按钮的回车事件_c# – 在WPF中模拟Enter键

    我试图在按钮事件中模拟按键.我可以使用下面的代码来模拟一些键,例如Backspace,但Enter不起作用. 我究竟做错了什么? private void btnEnter_Click(object ...

最新文章

  1. 最新数据分析教程资源合集(案例、数据集、SQL教程、分析样板、面经、简历、PY教程... )...
  2. 在RedHat上安装gcc,java 和 eclipse-rcp
  3. USACO 1.4 Packing Rectangles
  4. Silverlight教程第四部分:使用 Style 元素更好地封装观感 (木野狐译)
  5. Winform中设置BackgroundWorker在取消时关闭后台进程不生效-没有跳出循环
  6. 循环神经网络RNN的基本组件(五)
  7. 纽约佩斯大学孔子学院:中国故事
  8. 用计算机代替人工记账缺陷,会计电算化的实施过程是什么
  9. sparksql 操作hive_Spark SQL 物化视图原理与实践
  10. 《移山之道-VSTS软件开发指南》---读书笔记
  11. 洛谷P3152 正整数序列
  12. linux运行隐藏脚本,linux 下隐藏进程的一种方法及遇到的坑
  13. LeetCode26.删除排序数组中的重复项
  14. VS2017内使用Coin3d第三方库的配置方法(含库与案例)
  15. 关于淘宝自动登陆的尝试——神烦笔记
  16. 建筑的永恒之道----质
  17. Android短信发送,监听,及其工具类封装
  18. 清新漂亮简约好看的个人博客网站模板
  19. Python Requests爬取百思不得姐视频
  20. 百度智能云OCR获取APIKey和SecretKey

热门文章

  1. react 实现图片正在加载中 加载完成 加载失败三个阶段的
  2. 看一看Facebook工程师是怎么评价《第一行代码》的
  3. 极限编程(XP)简介
  4. 中小企业监控体系构建实战--案例分享(内附传送门)
  5. 用友U8库存管理软件,用友U8
  6. VMware 镜像安装
  7. 新路由3 Newifi3 D2 Lede固件
  8. 如何写一份大家都满意的专利说明书
  9. 常见的工业通讯协议有哪些
  10. iOS视频播放器MPMoviePlayerController