C# 和 Windows Forms 中的分形






4.79/5 (17投票s)
2006年7月31日
4分钟阅读

70137

2163
用 C# 和 Windows Forms 编写的交互式探索分形集的项目。
引言
这个用 C# 编写的项目允许用户交互式地探索曼德布洛特集,并保存生成的图片以及视图窗口的坐标。图片可以保存为位图文件,或复制到剪贴板。视图窗口信息可以保存到 XML 文件或复制到剪贴板。
用户界面描述
程序启动时,主图像尚未计算。要概览曼德布洛特集,只需单击顶部工具栏中的“计算”按钮。除了主图像外,窗口的右上角还有两个面板:“预览”和“鸟瞰图”。
预览
预览提供曼德布洛特集当前视图的小图像。预览始终是最新的,而主图像出于性能原因,需要点击“计算”才能刷新。
鸟瞰图
鸟瞰图提供曼德布洛特集的概览,并且永不改变。可以在鸟瞰图窗口上拖动鼠标来选择新的视图窗口。此时,预览会更新,视图窗口的数值也会更新。主图像仍需要点击“计算”才能反映更改。鸟瞰图有助于缩小视图,或快速缩放到集合的另一部分。
主图像
它显示了视图窗口的图像,并自动缩放以适应可用空间。用户可以在主图像上拖动鼠标来选择新的视图窗口。与鸟瞰图不同,新的视图窗口是相对于当前选择的视图窗口的。
数值
窗口顶部有一些控件用于编辑数值。以下是它们的描述。
图像大小
“宽度”和“高度”表示曼德布洛特集例程创建的位图的像素大小。为避免出现奇怪的图像,宽度和高度的比例必须与曼德布洛特集视图窗口的比例相同。“自动调整”复选框允许程序在用户更改视图窗口时更改“高度”值(如果需要)。由于用户可以手动更改“宽度”和“高度”的值,“调整比例”按钮强制更新“高度”值。一个注释显示了当前的比例,供那些想手工计算的人参考。
曼德布洛特集窗口
XMin
、XMax
、YMin
和 YMax
定义了曼德布洛特集的视图窗口。这些值可以手动编辑,其他控件会自动更新。一个注释显示了当前视图窗口的比例。
计算参数
最大迭代次数设置了一个给定点被归类为曼德布洛特集外部的点之前所需的迭代次数。尝试几个不同的值可能会很有趣。请注意,生成的图像是每像素 24 位,因此最大迭代次数将产生类似的结果(模 256)。
工具栏命令
- 计算:重新生成主图像
- 保存:将主图像保存到文件,格式为 Windows 位图。还将参数保存到具有相同名称的 XML 文件中
- 重置:将所有参数恢复到默认值
- 复制:将主图像复制到剪贴板
- 复制参数:将参数复制到剪贴板
一些代码
位图生成过程如下
public System.Drawing.Bitmap GetBitmap(
int pImageWidth,
int pImageHeight,
double pXMin,
double pXMax,
double pYMin,
double pYMax,
int pMaxIterationCount)
{
try
{
int i = 0;
int j = 0;
int[][] lValues = new int[pImageHeight][];
for (i = 0; i < pImageHeight; i++)
{
lValues[i] = new int[pImageWidth];
}
CalculateLevels(
lValues,
pImageWidth,
pImageHeight,
pXMin,
pXMax,
pYMin,
pYMax,
pMaxIterationCount);
System.Drawing.Bitmap lBitmap = new System.Drawing.Bitmap
(pImageWidth, pImageHeight,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte lRed = 0;
byte lGreen = 0;
byte lBlue = 0;
int lPixelByteCount = 4;
int lAlphaPos = 3;
int lRedPos = 2;
int lGreenPos = 1;
int lBluePos = 0;
int lPixelCount = pImageHeight * pImageWidth;
int lPixelPos = 0;
m_ProgressBar.Minimum = 0;
m_ProgressBar.Maximum = lPixelCount;
m_ProgressBar.Value = 0;
System.Drawing.Imaging.BitmapData lBitmapData =
new System.Drawing.Imaging.BitmapData();
lBitmap.LockBits(
new System.Drawing.Rectangle(
0,
0,
pImageWidth,
pImageHeight),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb,
lBitmapData);
unsafe
{
System.Byte* lStartPtr = (System.Byte*)((void*)lBitmapData.Scan0);
int lEndToStart = lBitmapData.Stride -
pImageWidth * lPixelByteCount;
System.Byte* lPtr = lStartPtr;
for (j = 0; j < pImageHeight; j++)
{
for (i = 0; i < pImageWidth; i++)
{
GetColorFromLevel(
lValues[j][i],
pMaxIterationCount,
out lRed,
out lGreen,
out lBlue);
lPtr[lAlphaPos] = 255;
lPtr[lRedPos] = lRed;
lPtr[lGreenPos] = lGreen;
lPtr[lBluePos] = lBlue;
lPtr += lPixelByteCount;
lPixelPos++;
}
lPtr += lEndToStart;
m_ProgressBar.Value = lPixelPos;
System.Windows.Forms.Application.DoEvents();
}
}
lBitmap.UnlockBits(lBitmapData);
return lBitmap;
}
catch
{
return null;
}
}
此函数获取一个描述曼德布洛特集视图窗口的值数组。然后它创建适当大小的位图,锁定它,并在推断出初始数组的颜色后填充它。
颜色推断
这里使用了一个非常简单的算法。将势能与最大可用值进行比较。然后将比例分成 8 个区间,这些区间将按以下顺序进行:黑色到红色 - 红色到黄色 - 黄色到绿色 - 绿色到青色 - 青色到蓝色 - 蓝色到品红色 - 品红色到白色 - 白色到黑色
示例 XML 文件
<?xml version="1.0" encoding="utf-16"?>
<State xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<XMin>-0.040557444936069775</XMin>
<XMax>-0.040016368521333778</XMax>
<YMin>0.98423178672189038</YMin>
<YMax>0.98491968869265967</YMax>
<ImageWidth>1200</ImageWidth>
<ImageHeight>1526</ImageHeight>
<MaxIterationCount>100</MaxIterationCount>
</State>
使用此软件计算的示例图像
注意:这些图像已缩小以满足尺寸要求。
已知问题
当用户单击 NumericUpDown
控件来增加或减少 XMin
、XMax
、YMin
或 YMax
的值时,预览会更新。当用户按住鼠标时会抛出异常。经过一定毫秒数后,控件开始加速,并且 NumericUpDown
控件抱怨一些内部计时器。我认为花费时间修复此问题不合适。
参考文献
对于那些对分形感兴趣的人,我推荐 Barnsley、Devaney、Mandelbrot(本人)、Peitgen、Saupe 和 Voss 编写的书《分形图像科学》。我的副本于 1988 年由 Springer Verlag 出版。