0

This is my second post on Mandelbrot fractal conversion from Java to C#. As per my assignment, I need to Draw a mandelbrot fractal on a form, and once it is drawn, allow the user to Zoom in using the mouse, while also drawing a rectangle from the initial click point to the point where the click is released. This is the part of code which i believe is responsible for the rectangle.

private static void Swap<T>(ref T t1, ref T t2)
    {
        T temp = t1;
        t1 = t2;
        t2 = t1;
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        Graphics g1 = e.Graphics;
        g1.DrawImage(bitmap, 0, 0, x1, y1);

        if (action)
        {
            //g.setColor(Color.White);
            if (xe < xs)
            {
                Swap(ref xs, ref xe);
            }

            if (ye < ys)
            {
                Swap(ref ys, ref ye);
            }

            g1.DrawRectangle(Pens.White, xs, ys, (xe - xs), (ye - ys));
            //g1.Dispose();
        }
    }
    //load method here 
    private void Form1_Load(object sender, EventArgs e)
    //while loading
    {
        init();
        start();

    }
    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (action)
        {
            xe = e.X;
            ye = e.Y;
        }
    }

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        action = true;
        // e.consume();
        if (action)
        {
            xs = xe = e.X;
            ys = ye = e.Y;
        }
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        using (Graphics g = this.CreateGraphics())
        {
            Pen pen = new Pen(Color.White);
            g.DrawRectangle(pen, xs, ys, Math.Abs(xs - xe), Math.Abs(ys - ye));

        }

        int z, w;
        if (xs > xe)
        {
            z = xs;
            xs = xe;
            xe = z;
        }
        if (ys > ye)
        {
            z = ys;
            ys = ye;
            ye = z;
        }
        w = (xe - xs);
        z = (ye - ys);
        if ((w < 2) && (z < 2)) initvalues();
        else
        {
            if (((float)w > (float)z * xy)) ye = (int)((float)ys + (float)w / xy);
            else xe = (int)((float)xs + (float)z * xy);
            xende = xstart + xzoom * (double)xe;
            yende = ystart + yzoom * (double)ye;
            xstart += xzoom * (double)xs;
            ystart += yzoom * (double)ys;
        }
        xzoom = (xende - xstart) / (double)x1;
        yzoom = (yende - ystart) / (double)y1;
        mandelbrot();

        this.Invalidate();
    }

What the code does is, draw a rectangle AFTER the dragging is done, and then zoom in with the drawn rectangle still being displayed. What I needed is the rectangle to draw as the mouse is being dragged.

I referred to this question, and solution mentioned there did not help. Java to C# conversion. How do i draw a rectangle on my bitmap?

Any help would be appreciated.

Community
  • 1
  • 1
Bibhushan
  • 11
  • 5
  • What is: bitmap, action, init(), start(), initvalues(), mandelbrot(), xe, xs, ye, ys, xende, xstart, xzoom, yende, ystart, yzoom, ... Can you provide complete code? – Dieter Meemken Nov 29 '15 at 12:00
  • You need to call `Invalidate()` in your `Form1_MouseMove()` method. Otherwise, the form has no way to know that it needs to draw the rectangle. – Peter Duniho Nov 30 '15 at 04:23
  • thank you @PeterDuniho, i checked with the solution you gave on the linked question and it worked but the form started to flicker even when DoubleBuffered was set to true. Now I have managed to solve that issue as well. – Bibhushan Nov 30 '15 at 17:46

1 Answers1

0

Drawing the Rectangle

First of all, it appears that the Graphics.DrawRectangle method is unable to draw a rectangle with negative widths or heights. You will therefore have to write a method that will take two points and produce a rectangle meeting the requirements (positive width and height).

private Rectangle CreateRectangle(Point pt1, Point pt2)
{
    // we use this method to create the rectangle with positive width and height
    int x1 = Math.Min(pt1.X, pt2.X);
    int y1 = Math.Min(pt1.Y, pt2.Y);


    return new Rectangle(x1, y1, Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
}

Second, in your event handler for the MouseDown event, record the position at which the mouse was held down.

private void Form1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
        this.startPoint = e.Location;// record the start position
}

Next, modify your mouse move method to update the variable that holds current location of the mouse. Additionally, make it invalidate the form so that the image is redrawn (along with the rectangle).

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
        // record the current position as the end point if the left button is down
        this.endPoint = e.Location;
        // force a redraw
        this.Invalidate();
    }
}

In the form's Paint event handler, make your code call the CreateRectangle method with the start and end points of the rectangle in order to draw the rectangle on the form.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    // draw the cached Mandelbrot image
    e.Graphics.DrawImage(mandelbrotCache, new Point(0, 0));

    // draw the current rectangle
    e.Graphics.DrawRectangle(rectPen, CreateRectangle(startPoint, endPoint));
}

Finally, in order to remove the rectangle when the mouse button is no longer pressed, set startPoint and endPoint to a value that gets drawn outside the image. This should be done in the MouseUp event handler.

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
    if (e.Button == System.Windows.Forms.MouseButtons.Left)
    {
        // setting the point to -1,-1 makes them get drawn off the screen
        startPoint = new Point(-1, -1);
        endPoint = new Point(-1, -1);

        // force an update so that the rectangle disappears
        this.Invalidate();
    }
}

Addressing the Flickering Issue

In order to stop the form from flickering while you're drawing to it, you will need to enable double buffering on the form. This is done by setting the DoubleBuffered property of the form to true. You can do this anywhere, but I prefer to do it right after the form is created, as below:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        // this reduces the flickering
        this.DoubleBuffered = true;
    }
}

Complete Code:

Here is the complete code for all the steps I detailed above. You can plug in your methods in order to have a working solution.

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private Point startPoint;
        private Point endPoint;
        private Image mandelbrotCache;
        private Pen rectPen;

        public Form1()
        {
            InitializeComponent();

            // this reduces the flickering
            this.DoubleBuffered = true;

            // initialize a dummy image. Cache a copy of your Mandelbrot fractal here
            mandelbrotCache = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            using (var g = Graphics.FromImage(mandelbrotCache))
            {
                var imgRect = new Rectangle(0, 0,
                                            mandelbrotCache.Width,
                                            mandelbrotCache.Height);

                g.FillRectangle(new HatchBrush(HatchStyle.Cross, Color.DarkBlue,
                                Color.LightBlue), imgRect);
            }

            // this is the pen to draw the rectangle with
            rectPen = new Pen(Color.Red, 3);
        }

        private Rectangle CreateRectangle(Point pt1, Point pt2)
        {
            // we use this method to create a rectangle with positive width and height
            int x1 = Math.Min(pt1.X, pt2.X);
            int y1 = Math.Min(pt1.Y, pt2.Y);

            return new Rectangle(x1, y1, Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
                this.startPoint = e.Location;// record the start position
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                // record the current position as the end point if the left button is down
                this.endPoint = e.Location;
                // force a redraw
                this.Invalidate();
            }
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                // setting the point to -1,-1 makes them get drawn off the screen
                startPoint = new Point(-1, -1);
                endPoint = new Point(-1, -1);

                // force an update so that the rectangle disappears
                this.Invalidate();
            }
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            // draw the cached Mandelbrot image
            e.Graphics.DrawImage(mandelbrotCache, new Point(0, 0));

            // draw the current rectangle
            e.Graphics.DrawRectangle(rectPen, CreateRectangle(startPoint, endPoint));
        }
    }
}

Here is a screenshot of a rectangle being drawn.
Note: The left mouse button is still held down. The rectangle disappears immediately the button is released.

Screenshot of a rectangle being drawn on the form

Alex Essilfie
  • 12,339
  • 9
  • 70
  • 108