这个实例基于以下规则实现了 一维空间下的温度扩散的模拟:
- 温度从高到低扩散;
- 每一轮热量传递量为1;
- 温度可扩散到周围2个元胞中;
示例如下:
刚开始如第一幅图,在中间位置有个高温点,然后热量不断向两端扩散。最后如第二张图所示,热量达到平衡。
WPF的界面代码如下:
<Window x:Class="CellularAutomata.WinTemperatureDiffusionSimulation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CellularAutomata"
mc:Ignorable="d"
Title="WinTemperatureDiffusionSimulation" Height="450" Width="800">
<UniformGrid Name="uGrid0">
</UniformGrid>
</Window>
C# 的后端模拟代码如下:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace CellularAutomata
{
/// <summary>
/// WinTemperatureDiffusionSimulation.xaml 的交互逻辑
/// 温度扩散模拟实现
/// </summary>
public partial class WinTemperatureDiffusionSimulation : Window
{
private static readonly SolidColorBrush LiveColor = Brushes.Blue;
private static readonly SolidColorBrush DeadColor = Brushes.Red;
private const int ColCount = 1;
private const int RowCount = 11;
private const int MinNum_Live = 2;
private const int MaxNum_Live = 6;
private const int MinNum_Procreation = 3;
/// <summary>
/// 最小温度差,小于这个数就不发生热量传递
/// </summary>
private const int MinNum_TemperatureDifference = 1;
private static readonly int[][] AroundIdxList = new int[][]
{
new int[]{ -1,-1},
new int[]{ -1,0},
new int[]{ -1,1},
new int[]{ 0,-1},
//new int[]{ 0,0},
new int[]{ 0,1},
new int[]{ 1,-1},
new int[]{ 1,0},
new int[]{ 1,1},
};
private readonly int[,] list;
private readonly TextBlock[,] ctrllist;
private readonly DispatcherTimer timer;
private int showTurn;
private readonly Random random;
public WinTemperatureDiffusionSimulation()
{
InitializeComponent();
// 初始化控件列表和 后台记录温度值的数组
random = new Random(DateTime.Now.Millisecond);
uGrid0.Rows = RowCount;
uGrid0.Columns = ColCount;
list = new int[RowCount, ColCount];
ctrllist = new TextBlock[RowCount, ColCount];
for (int i = 0; i < RowCount; i++)
{
for (int j = 0; j < ColCount; j++)
{
TextBlock txt = new TextBlock
{
Foreground = Brushes.White,
TextAlignment = TextAlignment.Center,
};
uGrid0.Children.Add(txt);
ctrllist[i, j] = txt;
}
}
// 设定初始高温点
list[RowCount / 2, ColCount / 2] = 400;
//list[0, 0] = 70;
UpdateCtrl();
// 开始演变
timer = new DispatcherTimer
{
Interval = new TimeSpan(0, 0, 0, 0, 10)
};
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
// 计算这一轮温度的变化值
int[,] templist = new int[RowCount, ColCount];
for (int i = 0; i < RowCount; i++)
{
for (int j = 0; j < ColCount; j++)
{
UpdateTemperature(templist, i, j);
}
}
// 更新温度
for (int i = 0; i < RowCount; i++)
{
for (int j = 0; j < ColCount; j++)
{
list[i, j] += templist[i, j];
}
}
// 每演变N轮更新一次界面
if (showTurn++ >= 10)
{
showTurn = 0;
// 更新界面
UpdateCtrl();
}
}
public void UpdateTemperature(int[,] templist, int rowIdx, int colIdx)
{
// 简易版的模拟。使用遍历的方式 只能模拟一维空间,2维不能模拟
// 遍历周遭元胞,计算热量传递量。
// 为了避免重复计算,只考虑从高到低的传递。
foreach (int[] item in AroundIdxList)
{
int tempCIdx = colIdx + item[0];
if (tempCIdx < 0 || tempCIdx >= ColCount)
{
continue;
}
int tempRIdx = rowIdx + item[1];
if (tempRIdx < 0 || tempRIdx >= RowCount)
{
continue;
}
int diff;
// diff = HeatConduction(list[rowIdx, colIdx], list[tempRIdx, tempCIdx]);
// 本轮开始前有温度差 且本轮计入的热量传递后还有温度差时 才发生热量传递。
if (list[rowIdx, colIdx] - list[tempRIdx, tempCIdx] > MinNum_TemperatureDifference
&& list[rowIdx, colIdx] + templist[rowIdx, colIdx] - list[tempRIdx, tempCIdx]
> MinNum_TemperatureDifference)
{
diff = HeatConduction(list[rowIdx, colIdx], list[tempRIdx, tempCIdx]);
templist[rowIdx, colIdx] -= diff;
templist[tempRIdx, tempCIdx] += diff;
}
}
}
public int HeatConduction(int tempHeiht, int tempLow)
{
double diff = (tempHeiht - tempLow);
// 有温度差
if (diff > MinNum_TemperatureDifference)
{
// 简易版热量传递为1 ,
return 1;
// 复杂版则根据热量差值 决定传递量(未完成)
//return (int)Math.Ceiling(0.35 * Math.Pow(diff, 0.9));
}
else
{
return 0;
}
}
public void UpdateCtrl()
{
// 从内存ctrllist的值更新,每个元胞的温度值和颜色
for (int i = 0; i < RowCount; i++)
{
for (int j = 0; j < ColCount; j++)
{
ctrllist[i, j].Text = list[i, j].ToString();
//byte value;
//double dValue = Math.Abs(list[i, j]);
//double maxV = 100;// 最高温度
//dValue = 0xFF - (maxV - dValue) / (maxV / 0xFF);
//value = (byte)dValue;
if (list[i, j] >= 200)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0x00));
}
else if (list[i, j] >= 100)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(0xFF, 40, 40));
}
else if (list[i, j] >= 50)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(0xFF, 130, 130));
}
else if (list[i, j] >= 20)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(0, 0, 0));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(0xFF, 190, 190));
}
else if (list[i, j] > 0)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(0, 0, 0));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(0xFF, 220, 220));
}
else if (list[i, j] == 0)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(0, 0, 0));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(0xFF, 0xFF, 0xFF));
}
else if (list[i, j] > -20)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(0, 0, 0));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(220, 220, 0xFF));
}
else if (list[i, j] > -50)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(190, 190, 0xFF));
}
else if (list[i, j] > -100)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(130, 130, 0xFF));
}
else if (list[i, j] > -200)
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(40, 40, 0xFF));
}
else
{
ctrllist[i, j].Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
ctrllist[i, j].Background = new SolidColorBrush(Color.FromRgb(0x00, 0x00, 0xFF));
}
}
}
}
}
}