65.9K
CodeProject 正在变化。 阅读更多。
Home

ATL 7.0 ActiveX 控件模拟 PS(粒子群)动态系统示例

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.86/5 (4投票s)

2004年11月15日

7分钟阅读

viewsIcon

54250

downloadIcon

1616

关于 ATL 7.0 和动态系统仿真的文章。

Sample Image

引言

该控件模拟了一个粒子群动态系统。每个粒子具有不同的权重和电荷。因此,它们在运动时会根据其不断变化的的速度和加速度,对彼此施加不同的吸引力和排斥力。系统还模拟了阻尼力和其他衰减特性,以便它们最终能够稳定下来。

该系统中的粒子还具有背景和前景颜色、图片、字体、文本、形状等属性。控件会检测鼠标事件并改变相应粒子的颜色。当用户单击其中一个粒子时,控件可以触发多个事件。实际上,我把它做成了一个非常实用的按钮控件,就像你在 Britica 2000 中可能看到的,它是一个出色的导航工具,关键字按钮像从管道中吹出的气泡一样一个接一个地弹出。

该控件还演示了双缓冲等加速动画的技术。系统定时器被用作此模拟的引擎,而不是空闲线程。

除了编程方面的内容,该控件还讨论并解决了计算机仿真中离散时间采样的一些问题。如果您学过高等微积分,也许可以和我一起探讨一下 :-)

快速了解如何使用该控件

为了简单起见,我们先用 VB 来演示用法(感谢微软)。创建一个新项目,导入组件 SmartGravityBubbleCtrl。然后将其拖到窗体上并放置在任何您喜欢的位置。现在,在对象浏览器中,您会看到两个类:CSmartGravityBubbleCtrlCSmartBubble。第一个是我们动态系统的容器,即我们粒子的“游乐场”,后者是我们的“玩家”——粒子本身。当您将控件拖到窗体上时,您就已经为我们的玩家设置好了游乐场。接下来您需要做的是创建 CSmartBubble 对象(玩家)并将它们添加到游乐场中。源代码如下:

Dim b As CSmartBubble 
Set b = New CSmartBubble 
b.Shape = RoundedRect 
b.id = 1 
CSmartGravityBubble1.AddBubble b

很简单,对吧?电子和重力参数经过了仔细的调优,您无需指定除默认值以外的任何值。但是,如果您想要一些特别的东西,可以随意指定您自己的参数。您可以添加任意多的粒子,看着它们移动和跳跃,直到它们都“累了”并进入一个优美而有规律的模式。但有一点您需要注意:在此版本中,添加到容器中的每个 CSmartBubble 对象都必须具有不同的 ID 值。这可能是一个愚蠢的想法,因为我在编写控件时决定每个气泡都通过其 ID 进行识别,这样当用户单击其中一个时,我就可以触发一个事件,并用此 ID 告诉客户端哪个被单击了。您可以去除源代码中的保护代码。

还有什么?

该控件不仅仅是一个代码玩具。它功能齐全,具有许多属性和事件。详细的描述和参考已包含在源代码包中。

此外,还有两个演示项目:demo1xmlDemo,都用 VB6 编写。demo1 处理控件的 LeftButtonDown 事件,并在用户单击的位置添加一个粒子,处理程序中的代码如下:

Private Sub Bubble1_OnLeftButtonDown(ByVal X As Long, ByVal Y As Long)
  Dim b As CSmartBubble
  Set b = New CSmartBubble
  b.xPos = X 'set the coordinates of the particle to where the user clicks
  b.yPos = Y
  b.Width = 50 'set the size of the particle
  b.Height = 20
  b.Text = "Green" 'text
  b.ElectronicCharge = 1
  b.Shape = RoundedRect
  b.id = id + 1
  b.Weight = 2
  b.TextHighlightColor = RGB(255, 255, 255)
  'color when the particle is highlighted by mouse

  id = id + 1 'ensures that we get unique id for every particle
  Bubble1.AddBubble b 
End Sub

xmlDemo 则要复杂得多,我用它来模仿 Britica 2000 的知识导航器。它用于导航树形结构,例如磁盘目录,并产生迷人的效果。我用 XML 编写了一个树形结构,并使用 Microsoft XML 4.0 进行了解析。这个树形结构的内容主要是关于我自己的自我介绍。

初始化时,程序会创建一个在 XML 文件根节点中指定了文本和样式的粒子,并将该根粒子固定在控件的中心。然后,它的子节点会以 500 毫秒的延迟时间,由定时器触发,一个接一个地弹出。整个过程看起来就像用管道吹泡泡一样。当用户单击其中一个粒子,并且该粒子也是一个父节点时,它就会被固定到中心,所有其他气泡都会被消除,同时它的子节点会持续地被弹出。要编译并运行此演示,您必须安装 MS XML 4.0。

粒子群解释

该动态系统的基本思想源自库仑定律和牛顿万有引力定律。我们赋予每个粒子正电荷和质量,因此它们会因电荷而相互排斥,同时因重力而相互吸引。这些力使系统具有动态性。根据库仑定律,两个粒子 p1 和 p2 之间的电场力为

Sample Image

公式是矢量形式的,我省略了库仑常数。Q1 和 Q2 是它们的电荷,P1 和 P2 是它们的位移矢量。粒子之间的引力原本也是同一种形式,但这不会使系统稳定,因为排斥力总是大于引力,所以粒子会越跑越远,系统会不稳定,但这也不是我想要的。因此,我使用了以下形式的引力:

Sample Image

W1 和 W2 是两个粒子的权重。

显然,当粒子之间的距离增加时,排斥力会减小,而引力会增大。因此,如果粒子之间的距离太小,排斥力会将它们推开;而当距离超过某个值时,引力就会占主导地位并将它们拉得更近。

这两种力之间的关系可以如下展示:

Sample Image

很明显,它们有交点,那就是粒子应该停止的地方。好吧,有人可能会简单地认为,只要我们在每个采样时间点实现这些力并计算加速度和速度,系统就会运行良好并达到我们期望的稳定状态。然而,这是错误的,因为我们讨论的力甚至不符合能量守恒定律,即系统的能量会随着时间的推移越来越大,粒子会越来越疯狂地加速,直到最终飞出我们的视线。

我们仿真的系统在现实世界中并不存在。我们应该给它衰减特性,以逐渐降低其能量并使其稳定。阻尼力是我首先选择的。就像在空气中运动的物体一样,其阻尼力可以表示为:

Sample Image

S 是该物体的速度矢量,k 是一个常数。前面的负号表示阻尼力与速度方向相反。

阻尼力的模拟使整个过程更加真实。但这仍然无法稳定系统。因此,我们使用另一种方法,该方法通过仿真时间的函数来衰减引力和排斥力。随着时间的推移,引力和排斥力以与仿真时间平方根成比例的速度衰减。当一个粒子被添加到容器中时,仿真时间将重置为零,系统重新开始仿真。

实现

关于理论部分已经说了不少。现在让我们来看看源代码。我喜欢在 Visual Studio .Net 中使用 ATL7 开发 ActiveX 控件,因为一切都变得简单明了,特别是对 COM 事件的支持。我只需要在 COM 类之前输入几个属性,然后说:“完成!”。然后一切都像魔法一样完成了。源代码太多,无法在此详细解释。我只给出几点,剩下的留给您去检查:整个设计使用了简单的容器模式,因为我在 STL 中找不到适合我粒子的容器。

该控件使用了双缓冲技术以提高性能。STL 中的复数模板类用于矢量计算。使用了一位 Code Project 贡献者的辅助类,并对其进行了扩展,以实现控件的字体和图片属性。您可以在 VCUE 命名空间中找到它。控件中的所有背景图片都实现为 IPicture 接口,以便于使用并支持透明图片,如 GIF。仿真由定时器驱动,而不是空闲线程。您可以轻松地对此进行修改。

数学上的挑战

我们都知道速度是加速度的积分,距离是速度的积分。在我们的仿真中,积分实际上是离散样本的总和。由于我们需要进行两次积分,而且当粒子靠近时,它们的加速度可能会非常大,因此求和与积分之间的偏差可能会非常大,导致系统看起来不稳定。所以您可能会在我的程序中看到很多“四舍五入”和“截断”的代码。这些代码很难理解,您可以忽略它们。

© . All rights reserved.