放大Mandelbrot

时间:2017-06-21 14:06:10

标签: c# windows fractals mandelbrot

我想找到一种方法来放大点击我的Mandelbrot设置。我有它所以当我点击它稍微放大但它不会相应地移动Mandelbrot。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Numerics;

namespace Project_V2
{
    public partial class FractalGen : Form
    {
        public double zoom = 2.4;
        public FractalGen()
        {
            InitializeComponent();
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            zoom -= 0.3;
            Mandelbrot();
        }

        private void button1_Click(object sender, EventArgs e)
        {
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Mandelbrot();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {

        }

        private void Mandelbrot()
        {
            Bitmap bm = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            DateTime StartT = DateTime.Now;
            for (int x = 0; x < pictureBox1.Width; x++)
            {
                for (int y = 0; y < pictureBox1.Height; y++)
                {
                    double a = (double)(x - (pictureBox1.Width / 1.25)) / (double)(pictureBox1.Width / zoom);
                    double b = (double)(y - (pictureBox1.Height / 2)) / (double)(pictureBox1.Height / zoom);
                    Complex C = new Complex(a, b);
                    Complex Z = new Complex(0, 0);
                    int u = 0;
                    do
                    {
                        u++;
                        Z = Z * Z;
                        Z = Z + C;
                        double Mag = Complex.Abs(Z);
                        if (Mag > 2.0) break;
                    } while (u < 255);
                    Color rgbInside = Color.FromArgb(0, 0, 0);
                    Color rgbOutside = Color.FromArgb(u >= 127 ? 255 : 2 * u, u >= 127 ? (u - 127) : 0, 0);
                    bm.SetPixel(x, y, u < 255 ? rgbOutside : rgbInside);
                }
            }
            pictureBox1.Image = bm;
            DateTime EndT = DateTime.Now;
            string Time = Convert.ToString((EndT - StartT).TotalSeconds);
            textBox1.Text = "Time Taken: " + Time + " Seconds";
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            zoom = 2.4;
            Mandelbrot();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            saveFileDialog1.ShowDialog();
        }

        private void saveFileDialog1_FileOk(object sender, CancelEventArgs e)
        {
            string name = saveFileDialog1.FileName;
            pictureBox1.Image.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Png);
        }
    }
}

当前代码将图片框的宽度和高度除以一个值,但我想拥有它以便放大我点击的位置。 我如何根据我点击的位置缩放图片框?

1 个答案:

答案 0 :(得分:3)

了解已经提供给您的指导会很有帮助。我从this comment推断出你可能存在一个问题然后被删除,其中讨论了这个问题。如果不知道已经提供了哪些帮助以及您无法理解的具体内容,就很难知道如何最好地提出新答案。

尽管如此,为了实现您正在寻找的行为,您必须解决两个基本问题:

  1. 确定单击鼠标的位置。目前,您正在使用Click事件,该事件仅向您的代码报告已单击控件,而不是其中被点击。
  2. 修复渲染代码以适应应绘制范围的变化。目前,您始终将位图中的像素范围映射到以(-0.72,0)为中心的复平面中的范围,仅调整渲染中心的距离(即zoom变量。
  3. 要解决#1,您需要订阅MouseClick事件。这会将MouseEventArgs对象传递给您的处理程序,该处理程序具有Location属性,指示实际单击的控件客户区内的点(即报告的坐标是相对于原点的在控件的左上角。)

    要解决#2问题,您需要在类中添加一些变量,以跟踪您想要绘制的相对位置。有很多方法可以做到这一点,但坚持你当前的代表当前&#34; zoom&#34;因子作为渲染区域的宽度和高度,根据绘制的复平面上的位置,跟踪被渲染空间的中心,即鼠标点击的位置似乎是有意义的。

    我强烈反对在执行长时间运行的任务时锁定UI的程序。因此,在修改代码示例的过程中,我对其进行了修改,以便在工作线程中呈现图像,并通过在我添加到表单的Label上提供状态消息来报告呈现进度。 (这涉及使Mandelbrot()方法成为async方法......我继续使用await来调用它来抑制编译器警告,但当然在这种情况下await s并非严格需要。)

    我做的一件事做的是为代码添加进一步的优化。这些往往会混淆与您的问题直接相关的更改。您可以通过删除冗余计算或使用数学关系以比Mandelbrot算法的字面转换更低成本执行计算,从而为您的代码做些许多事情来帮助提高性能。

    互联网上有很多关于如何呈现Mandelbrot图像的内容,所以一旦你掌握了基本的想法,你就可以环顾四周寻找那种建议。此外,目前,每个像素只有255次迭代和相对较小的位图,性能不是一个重要的考虑因素。

    以下是我提出的代码版本:

    public partial class Form1 : Form
    {
        double zoom = 2.4;
        double centerX = -0.72, centerY = 0;
    
        public Form1()
        {
            InitializeComponent();
        }
    
        private async void pictureBox1_MouseClick(object sender, MouseEventArgs e)
        {
            double minX = centerX - zoom / 2, minY = centerY - zoom / 2;
            centerX = minX + (double)e.Location.X / pictureBox1.Width * zoom;
            centerY = minY + (double)e.Location.Y / pictureBox1.Height * zoom;
            zoom -= 0.3;
            await Mandelbrot();
        }
    
        private async void Form1_Load(object sender, EventArgs e)
        {
            await Mandelbrot();
        }
    
        private async Task Mandelbrot()
        {
            IProgress<double> progress = new Progress<double>(x => label1.Text = $"{x * 100:0}% done");
    
            DateTime StartT = DateTime.UtcNow;
            pictureBox1.Image = await Task.Run(() => _GenerateBitmap(progress));
            DateTime EndT = DateTime.UtcNow;
            string Time = Convert.ToString((EndT - StartT).TotalSeconds);
            textBox1.Text = "Time Taken: " + Time + " Seconds";
        }
    
        private Bitmap _GenerateBitmap(IProgress<double> progress)
        {
            Bitmap bm = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            double minX = centerX - zoom / 2, minY = centerY - zoom / 2;
    
            for (int x = 0; x < pictureBox1.Width; x++)
            {
                for (int y = 0; y < pictureBox1.Height; y++)
                {
                    double a = minX + (double)x / pictureBox1.Width * zoom;
                    double b = minY + (double)y / pictureBox1.Height * zoom;
                    Complex C = new Complex(a, b);
                    Complex Z = new Complex(0, 0);
                    int u = 0;
                    do
                    {
                        u++;
                        Z = Z * Z;
                        Z = Z + C;
                        double Mag = Complex.Abs(Z);
                        if (Mag > 2.0) break;
                    } while (u < 255);
                    Color rgbInside = Color.FromArgb(0, 0, 0);
                    Color rgbOutside = Color.FromArgb(u >= 127 ? 255 : 2 * u, u >= 127 ? (u - 127) : 0, 0);
                    bm.SetPixel(x, y, u < 255 ? rgbOutside : rgbInside);
                }
    
                progress.Report((double)x / pictureBox1.Width);
            }
    
            return bm;
        }
    
        private async void button1_Click_1(object sender, EventArgs e)
        {
            zoom = 2.4;
            await Mandelbrot();
        }
    }