模拟系列(二)——模拟闪电

要求

简要模拟闪电的形成过程,要求插值渲染,用C# WinForm实现(网格)。

思路

逻辑部分

  1. 网格电场模型是一种图结构,基于C#的特性,设计Node类(结点)和Wire类(连线),且为泛型。
  2. 设计超类Unit(继承自Node,管理Node的事件)。
  3. 网格模型的计算需要状态的更替,故设计Status类(状态)和Mutable类(原状态和新状态)。
  4. 每个电路对象(结点,单元,连线)拥有一个唯一编号,故设计Markable类(基于UUID)。
  5. 结点和单元可以处理事件(单击、绘图等),增加接口IDraw。
  6. 单元的绘制过程使用插值接口IInterpolating,边界为双线性插值,中间为更平滑的双立方插值。
  7. 为介质设计介质计算接口IMedia,用于计算传导电流以及判定介质是否击穿。

界面部分

  1. 采用DirectUI思想绘制,API采用Gdi+的Graphics,构造图元方法参考自@vczh的C++ GUI Library/GacLib。
  2. 构造渲染器工厂GraphicsRendererFactory和图元工厂GraphicsElementFactory,渲染器存放绘制句柄(对Gdi+而言不需要存放句柄,且Gdi+对象有缓存机制),图元存放图形的属性。
  3. 每个元器件拥有独立的IGraphicsElement图元绘制接口集合以绘制图像。
  4. 插值函数采用双线性和双立方插值,双线性只需4个网格,而双立方需要16个网格,分层渲染(Estimate Test),渲染网格越小,颜色梯度越大,则耗时越多。
  5. 由于插值算法为CPU密集型,故本身效率不高,有优化空间,参考渐变着色为相对着色(按最值设置区间)。

核心算法

采用状态更新机制,类似状态机,并行优化。

public virtual void Update()
        {
            _nodes.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.NodeToWire));
            _wires.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.NodeToWire));
            _wires.Values.AsParallel().ForAll(a => a.Update());
            _wires.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.WireToNode));
            _nodes.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.WireToNode));
            _nodes.Values.AsParallel().ForAll(a => a.Update());
        }

测试

PS:由于仿真算法本身的不足,击穿及传导等各种物理参数很难调整完全,故仿真结果差强人意,帧率太低。由于绘图采用异步的async/await机制,故要求.NET 4.5。

云内电荷迁移:

image_thumb9

可以看出,电荷慢慢迁移到云的边界处,导致边界处场强增大,颜色梯度增大。

image_thumb17

云的左上角出现一对电荷量相反且电荷非常集中的正反电荷,导致其他区域的颜色梯度接近为中等,呈现绿色。这对集中的正反电荷导致云与空气的接触面被击穿。

image_thumb20

云的四周布满电荷,导致云内部被击穿,同时,由于巨大的场强,地上产生感应电流。

image_thumb24

电场逐渐增大,地上附近的空气被击穿,产生电流,向上延伸;云附近的空气被击穿,向四周延伸。地下是零电位导体。

至于如何使得感应电流成为一条有鲜明轮廓的折线(闪电轮廓),还有待研究。

 

源码

https://github.com/bajdcc/SimuCircult

类库:SimuElectricity

界面:WinSE

原文地址:https://www.cnblogs.com/bajdcc/p/4766307.html