5
votes

I am beginning to make a mandelbrot set fractal viewer. I am having a lot of issues when it comes to zooming in on the fractal. If you try to zoom, the viewer will just close in on the center. I have done as much as I can to understand this dilemma. How can I zoom in on my fractal in such a way that when I zoom, it will zoom in on the center of the screen, and not the center of the fractal?

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.awt.event.*;



public class Mandelbrot extends JFrame implements ActionListener {

    private JPanel ctrlPanel;
    private JPanel btnPanel;
    private int numIter = 50;
    private double zoom = 130;
    private double zoomIncrease = 100;
    private int colorIter = 20;
    private BufferedImage I;
    private double zx, zy, cx, cy, temp;
    private int xMove, yMove = 0;
    private JButton[] ctrlBtns = new JButton[9];
    private Color themeColor = new Color(150,180,200);

    public Mandelbrot() {
        super("Mandelbrot Set");
        setBounds(100, 100, 800, 600);
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        plotPoints();

        Container contentPane = getContentPane();

        contentPane.setLayout(null);




        ctrlPanel = new JPanel();
        ctrlPanel.setBounds(600,0,200,600);
        ctrlPanel.setBackground(themeColor);
        ctrlPanel.setLayout(null);

        btnPanel = new JPanel();
        btnPanel.setBounds(0,200,200,200);
        btnPanel.setLayout(new GridLayout(3,3));
        btnPanel.setBackground(themeColor);

        ctrlBtns[1] = new JButton("up");
        ctrlBtns[7] = new JButton("down");
        ctrlBtns[3] = new JButton ("left");
        ctrlBtns[5] = new JButton("right");
        ctrlBtns[2] = new JButton("+");
        ctrlBtns[0] = new JButton("-");
        ctrlBtns[8] = new JButton(">");
        ctrlBtns[6] = new JButton("<");
        ctrlBtns[4] = new JButton();

        contentPane.add(ctrlPanel);
        contentPane.add(new imgPanel());
        ctrlPanel.add(btnPanel);

        for (int x = 0; x<ctrlBtns.length;x++){
            btnPanel.add(ctrlBtns[x]);
            ctrlBtns[x].addActionListener(this);
        }

        validate();

    }

    public class imgPanel extends JPanel{
        public imgPanel(){
            setBounds(0,0,600,600);

        }

        @Override
        public void paint (Graphics g){
            super.paint(g);
            g.drawImage(I, 0, 0, this);
        }
    }

    public void plotPoints(){
        I = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        for (int y = 0; y < getHeight(); y++) {
            for (int x = 0; x < getWidth(); x++) {
                zx = zy = 0;
                cx = (x - 320+xMove) / zoom;
                cy = (y - 290+yMove) / zoom;
                int iter = numIter;
                while (zx * zx + zy * zy < 4 && iter > 0) {
                    temp = zx * zx - zy * zy + cx;
                    zy = 2 * zx * zy + cy;
                    zx = temp;
                    iter--;
                }
                I.setRGB(x, y, iter | (iter << colorIter));
            }
        }
    }

    public void actionPerformed(ActionEvent ae){
        String event = ae.getActionCommand();

        switch (event){
        case "up":
            yMove-=100;
            break;
        case "down":
            yMove+=100;
            break;
        case "left":
            xMove-=100;
            break;
        case "right":
            xMove+=100;
            break;
        case "+":
            zoom+=zoomIncrease;
            zoomIncrease+=100;
            break;
        case "-":
            zoom-=zoomIncrease;
            zoomIncrease-=100;
            break;
        case ">":
            colorIter++;
            break;
        case "<":
            colorIter--;
            break;
        }



        plotPoints();
        validate();
        repaint();
    }




    public static void main(String[] args) {
        new Mandelbrot().setVisible(true);
    }
}
2

2 Answers

5
votes

The Mandelbrot set exists in a mathematical plane with natural coordinates. You "view" this with a BufferedImage I using "view port" coordinates. It is all in the mapping between these. You have labeled the viewport coordinates as x and y, and the "real" coordinates into the Mandelbrot space as cx and cy. These are the formulae:

        cx = (x - 320+xMove) / zoom;
        cy = (y - 290+yMove) / zoom;

In order to zoom in and out of a particular "real" spot, you want the amount of your displacement to be constant as you zoom. The problem is that the amount of displacement is being scaled by the zoom amount. Remember cx and cy are the real coordinates in the Mandelbrot plane and x & y are the viewport coordinates. Thus, when looking at the middle of the viewport, as you change zoom, you want cx & cy to remain constant.

My guess is that you want something like:

    cx = ((x - 320) / zoom) + xMove;
    cy = ((y - 290) / zoom) + yMove;

This will make the "movement" in the Mandelbrot plane remain independent of zoom amount. I am assuming that the 320 and 290 is related to viewport size and gives you a zero in the middle of the viewport.

You are going to want the amount that xMove & yMove change on a keystroke to not be a fixed amount (100) but rather an amount that depends on zoom level. As you zoom in a lot you want the amount of movement in the real Mandelbrot plane to be smaller for each keystroke.

2
votes

I was able to fix this problem by having an additional horizontal or vertical shift every time I zoomed in.

Therefore, instead of having:

case "+": //zooming in
zoom+=zoomIncrease;
zoomIncrease+=100;
break;

I will now have

case "+":
initialZoom = zoom;
zoom+=zoomIncrease;
zoomIncrease*=2;
xMove*=2;
yMove*=2;
break;

this basically relocates the image that has been drawn by multiplying its movement on the x and y axes by the factor that the zoom has increased (which is two).