Reputation: 11
I am trying to figure out a way to zoom in on my Mandelbrot set on click. I have it so when I click it slightly zooms in but it doesn't move the Mandelbrot accordingly.
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);
}
}
}
The current code divides the picturebox width and height by a value, but I want to have it so it zooms in on where I click. How could I scale the picturebox in relation to where I click?
Upvotes: 0
Views: 2721
Reputation: 70671
It would be helpful to understand what guidance has already been offered to you. I infer from this comment that there is probably an existing question you asked and then deleted, in which this problem was discussed. Without knowing what help has already been offered and what specifically it was you were unable to understand, it's hard to know how best to present a new answer.
That said, there are two fundamental problems you have to solve to achieve the behavior you're looking for:
Click
event, which only reports to your code that the control was clicked, not where it was clicked.zoom
variable.To address #1, you need to subscribe to the MouseClick
event instead. This will pass a MouseEventArgs
object to your handler, which has a Location
property that indicates the point within the control's client area that was actually clicked (i.e. the coordinates reported are relative to an origin at the top-left of the control).
To address #2, you need to add some variables to your class to keep track of the relative position where you want to draw. There are lots of ways to do this, but staying with your current convention of representing the current "zoom" factor as the width and height of the rendered area, it seems to make sense to track the center of the space being rendered, i.e. the location the mouse was clicked, in terms of the location on the complex plane that was drawn.
I have a strong objection to programs that lock up the UI while they do long-running tasks. So in the process of revising your code example, I modified it so that it would render the image in a worker thread, and would report the rendering progress by providing a status message on a Label
that I added to the form. (This involved making your Mandelbrot()
method an async
method…I went ahead and used await
when calling it to suppress compiler warnings, but of course in this context the await
s are not strictly needed.)
One thing I did not do was to add further optimizations to the code. These tend to obfuscate the changes that are directly related to your question. There are a number of things you could do to your code to help improve performance, by removing redundant calculations or by using mathematical relationships to perform computations less expensively than the literal translation of the Mandelbrot algorithm.
There is lots on the Internet about how to render Mandelbrot images, so you can look around to find that sort of advice, once you have gotten the basic idea working. Besides, for now, with only 255 iterations per pixel and the relatively small bitmap, performance isn't a serious consideration.
Here is the version of your code I came up with:
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();
}
}
Upvotes: 3