令人惊叹的 Pickover 生物形态





5.00/5 (14投票s)
复数平面中无限的生物形状分形集
引言
我在这里将要介绍的算法是在80年代末为研究员Clifford Pickover开发的。它允许生成大量的生物形态,看起来像某种微生物生物,或者也许是抽象绘画。
该过程非常简单。我们从一些复变函数F(z)开始,例如,Sin(z) + z2 + c,其中c是一个常数,并且,使用Julia集的标准收敛研究,我们递归地处理该函数,迭代一定次数,或者直到我们发现该函数发散,例如,模数大于给定值。
关键在于Pickover修改了算法,添加了关于结果的实部和虚部大小的最终条件,但他犯了一个错误,混淆了OR和AND运算符,正是由于这个错误,我们才能获得如此惊人的结果。
该项目是用C#编写的,使用Visual Studio 2015。
使用代码
这个演示应用程序实际上只是一个幻灯片,包含一组十个函数中随机选择的图像。 c常数是随机选择的,这是即使使用相同的函数和相同的复平面子集也能获得许多不同形状的关键。
首先,我在一个名为_limits的数组中为这十个函数定义了xmin、xmax、ymin、ymax的界限。
private double[,] _limits = new double[10, 4] { { 2, 4, -5, -3 },
{ -2, 2, -2, 2 },
{ -1.7, 1.7, -1.7, 1.7 },
{ -3, 3, -3, 3 },
{ -5, 5, -5, 5 },
{ -10, 10, -10, 10 },
{ -3, 3, -3, 3 },
{ -1.5, 1.5, -1.5, 1.5 },
{ -1.2, 1.2, -1.2, 1.2 },
{ -8, 8, -8, 8 }};
为了处理复数,我选择了System.Numerics命名空间中的Complex结构。方法Fz实现了我选择的十个函数。实际上,您不必费很多心思来定义这些函数。几乎所有你写的都能正常工作
private Complex Fz(Complex z, Complex c, int f)
{
switch (f)
{
case 0:
return Complex.Sin(z) + Complex.Pow(z, 2) + c;
case 1:
return Complex.Pow(z, 2) + Complex.Pow(z, 6) + c;
case 2:
return Complex.Pow(z, 2) + Complex.Pow(z, 5) + c;
case 3:
return Complex.Pow(z, 3) + c;
case 4:
return Complex.Sin(z) + Complex.Pow(z, 5) + c;
case 5:
return Complex.Sin(z) - Complex.Cos(z) + Complex.Pow(z, 2) + c;
case 6:
return Complex.Pow(z, 3) - Complex.Cos(z) + Complex.Pow(z, 2) + c;
case 7:
return Complex.Pow(z, 6) - Complex.Pow(z, 4) + Complex.Sinh(z) + c;
case 8:
return Complex.Pow(z, 10) - Complex.Sinh(z) + c;
default:
return Complex.Cos(z) + Complex.Pow(z, 4) + c;
}
}
这是执行计算并绘制结果的方法。我选择使用async / await异步编程,以便在显示图片时保持应用程序的响应性。
我没有使用Bitmap类的SetPixel方法,而是通过使用一个包含像素颜色的整数数组来使用优化的方法。在过程结束时,将此数据复制到Bitmap中,使用单个操作。
private Task<Bitmap> BiomorphAsync(Complex c, int f)
{
return Task.Run(() =>
{
int[] bmpbits = new int[640 * 480];
for (int ix = 0; ix < bmpbits.Length; ix++)
{
bmpbits[ix] = Color.White.ToArgb();
}
double xmin = _limits[f, 0];
double xmax = _limits[f, 1];
double ymin = _limits[f, 2];
double ymax = _limits[f, 3];
double px = (xmax - xmin) / 639;
double py = (ymax - ymin) / 479;
for (int p = 0; p < 640; p++)
{
for (int q = 0; q < 480; q++)
{
Complex z = new Complex(xmin + p * px, ymin + q * py);
for (int k = 1; k < 50; k++)
{
z = Fz(z, c, f);
if (z.Magnitude > 100)
{
break;
}
}
if (Math.Abs(z.Real) < 100 || Math.Abs(z.Imaginary) < 100)
{
bmpbits[p + 640 * (479 - q)] = Color.Black.ToArgb();
}
}
}
Bitmap bmp = new Bitmap(640, 480);
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, 640, 480),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppRgb);
Marshal.Copy(bmpbits, 0, bd.Scan0, bmpbits.Length);
bmp.UnlockBits(bd);
return bmp;
});
}
该过程在表单的Load事件中启动,并循环直到触发FormClosing事件。 c常数是随机创建的,并且函数也以相同的方式选择。
private async void frmBio_Load(object sender, EventArgs e)
{
_stopBio = false;
Random rnd = new Random();
while (!_stopBio)
{
pbImage.Image = await BiomorphAsync(new Complex(rnd.NextDouble() * (rnd.Next(3) + 1),
rnd.NextDouble() * (rnd.Next(3) + 1)),
rnd.Next(9));
await WaitmsAsync(1000);
}
}
private void frmBio_FormClosing(object sender, FormClosingEventArgs e)
{
_stopBio = true;
}
现在,让算法自己说话吧
就这样了,感谢您的阅读!