【问题标题】:Zoom On Mandelbrot放大曼德布罗
【发布时间】:2017-06-21 14:06:09
【问题描述】:

我正在想办法在点击时放大我的 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);
        }
    }
}

当前代码将图片框的宽度和高度除以一个值,但我想要它,以便它放大我单击的位置。 如何相对于我点击的位置缩放图片框?

【问题讨论】:

  • 您需要检索并使用鼠标位置。这个答案有解决方案,甚至准备一个图片框stackoverflow.com/questions/7055211/…
  • 忘掉图片框,专注于数据! - 哦,我以前告诉过你,你忽略了它。那么,祝你好运..
  • @TaW:“我以前告诉过你”——在什么情况下?您提供该信息的地方是否存在已删除的问题?了解已经向 OP 提供了哪些建议、更好地理解他们可能已经听到的内容以及他们可能难以理解的内容会有所帮助。
  • @Peter:在this question 中,我告诉他将计算与着色代码分开并存储数据('k 值'),以便以后可以应用/玩颜色。由于无法缩放,他可能无法完全看到计算会变得多么缓慢以及结果是多么宝贵......
  • @TaW:啊,我明白了。感谢您的链接。你是对的......他可能很快就会发现最好只做一次计算。也就是说,无论如何,缩放主要否定了过去结果的有用性。但他肯定不想为了摆弄颜色映射而重新渲染具有大量插入点的字段。

标签: c# windows fractals mandelbrot


【解决方案1】:

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

也就是说,您必须解决两个基本问题才能实现您正在寻找的行为:

  1. 确定鼠标点击的位置。目前,您正在使用Click 事件,该事件仅向您的代码报告控件被点击,而不是在哪里被点击。
  2. 修复您的渲染代码以适应应绘制的边界的变化。目前,您始终将位图中的像素范围映射到以 (-0.72, 0) 为中心的复平面中的范围,仅调整距该中心的渲染距离(即 zoom 变量。

要解决 #1,您需要改为订阅 MouseClick 事件。这会将MouseEventArgs 对象传递给您的处理程序,该处理程序有一个Location 属性,该属性指示控件客户区域内实际单击的点(即报告的坐标相对于控件左上角的原点) )。

要解决 #2,您需要在类中添加一些变量来跟踪您要绘制的相对位置。有很多方法可以做到这一点,但是保持当前将当前“缩放”因子表示为渲染区域的宽度和高度的约定,跟踪正在渲染的空间的中心似乎是有意义的,即鼠标点击的位置,根据绘制的复平面上的位置。

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

没有做的一件事是为代码添加进一步的优化。这些往往会混淆与您的问题直接相关的更改。您可以对代码做很多事情来帮助提高性能,方法是删除冗余计算或使用数学关系来执行计算,其成本比 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();
    }
}

【讨论】:

  • 你能解释一下这行代码是做什么的吗? IProgress progress = new Progress(x => label1.Text = $("{x * 100:0}% done");
  • 这会创建一个Progress&lt;double&gt; 的实例,在这种情况下是一个使用double 的泛型类,当在UI 线程中创建它时,将自动执行在该UI 线程上传递给它的处理程序。在这种情况下,处理程序是 lambda 表达式,它接收状态值 x 并通过将格式化字符串分配给 label1.Text 来格式化它以在屏幕上显示它。通过将整个Progress&lt;double&gt; 实例(类型为IProgress&lt;double&gt;Progress&lt;double&gt; 实现的进度报告接口)传递给有效的方法,它可以随时报告进度。
  • @FlorisMartens:关于您最近提出的编辑,它被正确地拒绝了,因为它偏离了最初的意图。请注意,您修改的元素是原始问题代码的一部分。此外,该问题与这些常量对用户体验的确切影响无关,因此更改它们并没有以任何实质性方式改善答案。请确保您以后不要进行不符合社区准则的编辑;这将变得更加重要,因为您获得了声誉,并且可以在未经审查的情况下进行编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-10
  • 1970-01-01
  • 2017-10-26
  • 2017-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多