1

I am new to coding and I have started my journey by learning C# as my first programming language. As an assigment I am trying to code a program that draws a Mandelbrot set, but it is not drawing what it is supposed to and I can't seem to find any issues whilst debugging. It just draws a white square with a few black pixels in the top left.

Here is the most important part of my code (PaintEventArgs method):

        //Magnitude and mandelnum @ start
        int mandelnum = 0;
        double Magnitude = 0; //Magnitude is the distance of (a,b) to middle of my picturebox
        
        //a and b @ start. a and b are all (x,y) pixels of my bitmap reformed with the 
        following formulas: (a*a-b*b+x, 2*a*b+y)

        double a = 0;
        double b = 0;

        int x = 0;
        int y = 0;
        int picboxmiddleX = (50 + pictureBox1.Width) / 2;
        int picboxmiddleY = (110 + pictureBox1.Height) / 2; //the location of picturebox is 
                                                            (50,110)
            //loop through all pixels, calc magnitude for every (x,y) and stop if magnitude is                                
            larger than 2, attach color black or white to ever pixel dependent on even or odd 
            mandelnums.

            for (x = 0; x < pictureBox1.Width; x++)
            {
                for (y = 0; y < pictureBox1.Height; y++)
                {
                while (Magnitude < 2.0) {
                    mandelgetal++;

                    if (mandelgetal < 100)
                    {
                        break;
                    }

                    a = (a * a) - (b * b) + x;
                    b = (2 * a * b) + y;
                    Magnitude = Math.Sqrt(Math.Pow((a - middenpanelX), 2) + Math.Pow((b - 
                    middenpanelY), 2));
                }
                    //pixels with even mandelnum get color white and odd is black
                    if (mandelgetal % 2 == 0)
                    {
                        bm.SetPixel(x, y, Color.White);
                    }
                    else if (mandelgetal % 2 != 0)
                    {
                        bm.SetPixel(x, y, Color.Black);
                    }
                }                        
            }
        pictureBox1.Image = bm;
    }

I hope someone can help!

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • I'm really not knowledgeable in this area, but this `if (mandelgetal < 100) { break; }` looks suspicious to me. What is its value initially? - it's only increasing in the logic shown, so it seems odd that it should break out the first time it's _below_ 100. – 500 - Internal Server Error Oct 04 '21 at 16:40
  • its been a long time since I wrote a mandelbrot renderer... but IIRC you need to scale your X and Y coordinates down quite a bit. Also, if you start off with x=0 and y=0, you will only get one quadrant of the set – Pete Oct 04 '21 at 16:44
  • IIRC the interesting part is between (-2,-2) and (2,2), so you will need to scale quite a bit – Hans Kesting Oct 04 '21 at 16:49
  • Speedup tip: skip the Sqrt and compare to 4.0 – Hans Kesting Oct 04 '21 at 16:50
  • OT if you just tested that mandelgetal is not even, then you don't need to test that again in the else – Hans Kesting Oct 04 '21 at 16:52
  • 1
    Probably want to set `a`, `b`, and `mandelgetal` to 0 ___inside___ the _for_ loop so they don't carry over their values from pixel to pixel. – Wyck Oct 04 '21 at 16:52
  • @500-InternalServerError Yes, that is indeed a mistake, it should be >100. Now that I changed that it is a 400x400 black square... Maybe if I somehow can zoom to the part that Hans Kesting suggested I will find treasure. – peaceprodigy00 Oct 04 '21 at 16:55
  • See [this post](https://stackoverflow.com/a/15147251/380384) on how to use `.LockBits()` instead of `.SetPixel()` for much faster rendering. – John Alexiou Oct 04 '21 at 19:59

2 Answers2

1
  1. The statements

    a = 0.0;
    b = 0.0;
    

    need to be moved inside the pixel loops

  2. The x and y variables need to hold coordinates and not pixel values when used in the Mandelbrot iteration. Remember that mandelbrot coordinates vary between -2 and 2 (depending on zoom) and not 0 to pixel size

  3. picboxmiddleX is not needed, and is incorrectly calculated. If you want to convert from form pixels to control pixels use .PointToScreen() or .PointToClient()

    You can check if the calculation is going to be divergent by seeing if a*a+b*b>4.0. The Magnitude calculation is completely unnecessary.

  4. I see that in the iteration belw you are using the new values of a in the calculation for b.

    a = (a * a) - (b * b) + x;
    b = (2 * a * b) + y;
    

    I suggest using a temporary variable to hold the original a

    ta = a;
    a = (a * a) - (b * b) + x;
    b = (2 * ta * b) + y;
    
  5. If you want to generate a Bitmap to fill the pictureBox then you don't need to subscribe to Paint() events. You do that once at initialization, and then every time the form is resized. Once you create the Bitmap set pictureBox1.Image = bm and it will display.

  6. If you want to draw the fractal at every paint event, then use

    e.Graphics.DrawRectangle(pen, x,y,1,1);
    

    to draw a single pixel.

Here is a sample function that generates a bitmap with the fractal

    public Bitmap GenerateMandelbrot(Size size, double xMin, double yMin, double xMax, double yMax, int iterLimit)
    {
        var bmp = new Bitmap(size.Width, size.Height);
        for (int py = 0; py < size.Height; py++)
        {
            for (int px = 0; px < size.Width; px++)
            {
                // get coordinates of pixel (px, py)
                double x = xMin + ((xMax - xMin) * px) / (size.Width - 1);
                double y = yMin + ((yMax - yMin) * py) / (size.Height- 1);

                double a = x, b = y;
                int iter = 0;
                do
                {
                    // use tuples for iteration
                    (a, b) = (a * a - b * b + x, 2 * a * b + y);
                    iter++;
                } while (iter<=iterLimit && a*a+b*b<4);
                if (iter > iterLimit)
                {
                    // interior color
                    bmp.SetPixel(px, py, Color.Black);
                }
                else
                {
                    // exterior color banded by iteration
                    Color color = iter % 2 == 0 ? Color.DarkOrange : Color.Yellow;
                    bmp.SetPixel(px, py, color);
                }
            }
        }
        return bmp;
    }

and it can be used as follows

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        var bmp = GenerateMandelbrot(pictureBox1.ClientSize, -2.0, -1.6, 1.0, 1.6, 100);
        pictureBox1.Image = bmp;
    }

    ...
}

See GitHub repo for more details and to get ideas on how to move next.

Form1

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
0

Looks to me like it stops calculating as soon as Magnitude goes above/equalto 2.0 - nothing seems to reset it to below that..

As the calcs for pixel values are done in the while loop, if the while loop doesn't run then your pixels will all be white or black depending on what value mandelgetal jammed at..

If you're getting a few black pixels in the top left*, then it seems reasonable to say the while loop runs some small number of times, but then doesn't after (say) 10 pixels, and the rest of the thousands of pixels in the PB are just painted the same color

*in winforms the origin 0,0 is in the top left

Caius Jard
  • 72,509
  • 5
  • 49
  • 80