您的位置:首页 > 其它

使用自制CtlPointsCurve.dll组件实现样条曲线拟合

2005-12-14 04:20 585 查看
对于现在数据采集重绘处理,很多情况下都用到了曲线重绘,而曲线生成的方法也有多种.
最简单的一种就是两点一线法,简单来说就是对每个采集点做直线连接,使到其趋向一曲线,这种方法的好处是保证数据的真实性,但同时也引申出很多问题.首先,采集点必须要足够多,否则的话就成了线段组,而并非曲线了,这样对于一些无法多点采集的技术领域,或者采集成本很高的技术领域来说,便无法满足其分析需求了.因此,产生了样条曲线的计算方法.





在样条曲线的计算法则中,最复杂的可以算是B样条多次曲线拟合,其思路是在当前采样点的前提下,根据其计算公式,推算出对于采样点符合条件的曲线目标点,然后再在目标点间连线.这样就可以在采样点少的情况下,画出趋向曲线的点了.

理论的东西就说那么多,至于计算法则,我相信数学书上比我写的要详细很多,接下来就介绍在程序里面的实现,特别是VB里面的实现.

关于曲线拟合的VC代码,网络上面已经可以找到很多,推荐大家可以到codeguru里面找找,有位Tom Chan的控制点曲线计算(Control Point Curve)的类已经封装得很好了,特别对于不关心具体算法的用户可以放心使用,而至于VB上面实现的代码,到目前为止在下还没有找到.于是引申自己编写的念头.
在实现过程中,发觉VB对数的处理能力,特别是数组的处理能力实在不敢恭维(是因为VB里面的数组都为SALFARRAY的原因吧).

于是想到可以借助Tom Chan的计算类来实现.首先使用ATL编写ACTIVEX COM DLL,该DLL里封装了Tom Chan的计算方法,然后给予VB接口的调用,这样不但可以加快运算的速度,也可以减低VB做算法的负担.(要知道,如果用VB里面的安全数组来做算法的话,对CPU实在是一种浪费)

下图为在下的程序演示:



至于该组件--CtlPointsCurve.dll ,可以Email给在下(Xeden3@hotmail.com).

现在,我们具体看看如何使用这个CtlPointsCurve.dll.

首先,我们必须要给一个画线的空间给他,当然,PICTUREBOX是最好的选择。
然后我们定义两个按键,一个用于绘出曲线,一个清空,以便重新绘制。

Private Declare Function Polyline Lib "gdi32" (ByVal hdc As Long, lpPoint As POINTAPI, ByVal nCount As Long) As Long
Private Declare Function Ellipse Lib "gdi32" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long

定义两个API Ellipse()用于在PICTUREBOX绘制鼠标点击的采样点,Polyline()用于画出最后目标点连线。

Dim ctlPointCurveMathMaker As New CTLPOINTSCURVELib.CtlPointsCurveMath
Dim posSrc(5000) As CTLPOINTSCURVELib.Point
Private Type POINTAPI
X As Long
Y As Long
End Type

ctlPointCurveMathMaker对象,作为计算样条曲线用。

posSrc定义最大的采样点,内部结构与POINTAPI相同,现在设定的最大采样值是5000点。POINTAPI 结构,用于表示2D坐标。

m_Count当前采样点数。

由于样条计算是在CTLPOINTSCURVELib.CtlPointsCurveMath内部进行,所以必须把API的2D坐标结构POINTAPI转化到CTLPOINTSCURVELib.Point内,同样,生成目标点的坐标也要转换到API的2D坐标结构POINTAPI,以便绘制。

Private Sub cmdClear_Click()
Picture1.Cls
m_Count = 0
End Sub

清空

Private Sub Picture1_MouseDown(Buton As Integer, Shift As Integer, X As Single, Y As Single)
Picture1.ForeColor = vbBlue
posSrc(m_Count).pixX = X
posSrc(m_Count).pixY = Y
m_Count = m_Count + 1
Ellipse Picture1.hdc, X, Y, X + 5, Y + 5
Picture1.Refresh
End Sub

当鼠标点击Picture1后,把鼠标当前坐标作为采样点赋值到posSrc(m_Count)内,并且绘制一个半径为5pixel的圆。

Private Sub cmdRun_Click()
Dim posDest() As CTLPOINTSCURVELib.Point
Dim ptAPI() As POINTAPI
Dim ptCount As Long
ctlPointCurveMathMaker.InputCtlPoints m_Count, posSrc(0)
ctlPointCurveMathMaker.Generate
ptCount = ctlPointCurveMathMaker.GetCurveCount
ReDim posDest(ptCount - 1)
ReDim ptAPI(ptCount - 1)
ctlPointCurveMathMaker.GetCurve ptCount, posDest(0)
For i = 0 To ptCount - 1
ptAPI(i).X = posDest(i).pixX
ptAPI(i).Y = posDest(i).pixY
Next i
Picture1.ForeColor = vbRed
Polyline Picture1.hdc, ptAPI(0), ptCount
Picture1.Refresh
End Sub

Dim posDest() As CTLPOINTSCURVELib.Point 定义生成的目标点的坐标。
Dim ptAPI() As POINTAPI 当然,这个就是把posDest转化过来,用于绘制曲线用的。ctlPointCurveMathMaker.InputCtlPoints m_Count, posSrc(0) 加入采样点,m_Count为采样数。posSrc(0)为采样点。

ctlPointCurveMathMaker.Generate 开始计算样条曲线。
ptCount = ctlPointCurveMathMaker.GetCurveCount 获取目标点数目。
ReDim posDest(ptCount - 1)
ReDim ptAPI(ptCount - 1)

好了,我们现在根据得到的ptCount,重新定义posDest和ptAPI的大小。
ctlPointCurveMathMaker.GetCurve ptCount, posDest(0) 读取目标点。

For i = 0 To ptCount - 1
ptAPI(i).X = posDest(i).pixX
ptAPI(i).Y = posDest(i).pixY
Next i
把目标点转化到ptAPI里面,然后调用Polyline Picture1.hdc, ptAPI(0), ptCount绘制出来。

Picture1内容,也把当前采样点数m_Count归0。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: