简体   繁体   English

带有 Java Swing 的 Mutli Threaded Mandelbrot Set 查看器

[英]Mutli Threaded Mandelbrot Set viewer with Java Swing

Attempting a multi-threaded Mandelbrot Set viewer in Java using Swing graphics.在 Java 中尝试使用 Swing 图形的多线程 Mandelbrot 集查看器。 I've seen a few Mandelbrot Set programs and other multi threaded Swing apps that use BufferedImage and/or SwingWorker, but I'm wondering if this approach can work/be redeemed, and if it cannot, why not?我见过一些使用 BufferedImage 和/或 SwingWorker 的 Mandelbrot Set 程序和其他多线程 Swing 应用程序,但我想知道这种方法是否可以工作/被赎回,如果不能,为什么不呢?

On click (zoom in) and on key press (zoom out) I'm opening threads to calculate color values in quadrants (upper left, upper right, lower left, lower right), and updating these values in private static Color[][] colors = new Color[n][n];在单击(放大)和按键(缩小)时,我正在打开线程以计算象限中的颜色值(左上、右上、左下、右下),并在private static Color[][] colors = new Color[n][n]; . . Then the repaint method is called on the component to draw from the colors array.然后在组件上调用repaint方法从colors数组中进行绘制。

Are these threads executing concurrently with the Event Dispatch Thread, and if so, why aren't these threads occurring ahead of the Event Dispatch Thread Swing component drawing with SwingUtilities.invokeLater in place?这些线程是否与 Event Dispatch Thread 并发执行,如果是,为什么这些线程不发生在 Event Dispatch Thread Swing 组件绘制之前,并带有SwingUtilities.invokeLater

No error messages, just unpredictable redrawing behavior on click and key press.没有错误消息,只是在单击和按键时出现不可预测的重绘行为。 Thank you.谢谢你。

Mandelbrot.java曼德布洛特.java

import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Stack;

public class Mandelbrot extends JPanel {
    private static double size = 4.0; // complex plane is size units in width and length
    private static int n    = 1000; // window is n x n in pixels
    private static int max  = 255; // iterate through hex color values
    private static double x0 = -size/2; // set window origin
    private static double y0 = size/2; // set window origin
    private static double xC = 0; // set complex plane origin
    private static double yC = 0; // set complex plane origin
    private static double scaleFactor = size/n; // translate complex plane to pixels
    private static Stack<double[]> origins = new Stack<double[]>(); // track origins for zooming out
    private static double zoom = 2.0; // modify to alter brightness scheme on zoom
    private static Color[][] colors = new Color[n][n];

    public Mandlebrot() {
        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                double[] center = new double[]{xC, yC}; // rescale
                origins.push(center);
                zoom = (zoom * 2) - (0.5 * zoom);
                xC = x0 + (scaleFactor * e.getX());
                yC = y0 - (scaleFactor * e.getY());
                size = size/2.0;
                scaleFactor = size/n;
                x0 = xC - (size/2);
                y0 = yC + (size/2);
                ComputeThread quadrant1 = new ComputeThread(n, zoom, x0, y0, 0, n/2, 0, n/2, max, scaleFactor, colors);
                ComputeThread quadrant2 = new ComputeThread(n, zoom, x0, y0, n/2, n, 0, n/2, max, scaleFactor, colors);
                ComputeThread quadrant3 = new ComputeThread(n, zoom, x0, y0, 0, n/2, n/2, n, max, scaleFactor, colors);
                ComputeThread quadrant4 = new ComputeThread(n, zoom, x0, y0, n/2, n, n/2, n, max, scaleFactor, colors);
                Thread thread1 = new Thread(quadrant1);
                Thread thread2 = new Thread(quadrant2);
                Thread thread3 = new Thread(quadrant3);
                Thread thread4 = new Thread(quadrant4);
                thread1.start();
                thread2.start();
                thread3.start();
                thread4.start();
                repaint(0, 0, getWidth(), getHeight());
            }
        });
        addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                    double[] center = origins.pop(); // rescale
                    zoom = (zoom + (zoom / 3)) / 2;
                    xC = center[0];
                    yC = center[1];
                    size = size*2.0;
                    scaleFactor = size/n;
                    x0 = xC - (size/2);
                    y0 = yC + (size/2);
                    ComputeThread quadrant1 = new ComputeThread(n, zoom, x0, y0, 0, n/2, 0, n/2, max, scaleFactor, colors);
                    ComputeThread quadrant2 = new ComputeThread(n, zoom, x0, y0, n/2, n, 0, n/2, max, scaleFactor, colors);
                    ComputeThread quadrant3 = new ComputeThread(n, zoom, x0, y0, 0, n/2, n/2, n, max, scaleFactor, colors);
                    ComputeThread quadrant4 = new ComputeThread(n, zoom, x0, y0, n/2, n, n/2, n, max, scaleFactor, colors);
                    Thread thread1 = new Thread(quadrant1);
                    Thread thread2 = new Thread(quadrant2);
                    Thread thread3 = new Thread(quadrant3);
                    Thread thread4 = new Thread(quadrant4);
                    thread1.start();
                    thread2.start();
                    thread3.start();
                    thread4.start();
                    repaint(0, 0, getWidth(), getHeight());
                }
            }
        });
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {           
                g.setColor(colors[j][i]);
                g.drawLine(i, j, i, j);
            }
        }
    }

    public static int mand(Complex z0, int max) {
        Complex z = z0;
        for (int t = 0; t < max; t++) {
            if (z.abs() > zoom) {
                return t;
            }
            z = Complex.ad(Complex.mult(z, z), z0);
        }
        return max;
    }
    
    
    public static void main(String[] args) {

        
        for (int i = 0; i < n; i++) { // initialize array for initial drawing
            for (int j = 0; j < n; j++) {
                double a = x0 + (i * scaleFactor);
                double b = y0 - (j * scaleFactor);
                Complex z0 = new Complex(a, b);
                int gray = max - mand(z0, max);
                Color color = new Color(gray, gray, gray);
                colors[j][i] = color;
            }
        }

        
        SwingUtilities.invokeLater(() -> {
            var panel = new Threading();
            panel.setBackground(Color.WHITE);
            panel.setFocusable(true);
            var frame = new JFrame("Mandelbrot");
            frame.setSize(n, n);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(panel, BorderLayout.CENTER);
            frame.setVisible(true);
        });
        
    }

}

ComputeThread.java ComputeThread.java

import java.awt.Color;

public class ComputeThread implements Runnable {
    
    private static int n;
    private static double x0;
    private static double y0;
    private static int xLow;
    private static int xHigh;
    private static int yLow;
    private static int yHigh;
    private static int max;
    private static double scaleFactor;
    private static double zoom;
    private static Color[][] colors = new Color[1000][1000];
    
    public ComputeThread(int n, double zoom, double x0, double y0, int xLow, int xHigh, int yLow, int yHigh, int max, double scaleFactor, Color[][] colors) {
        this.n = n;
        this.zoom = zoom;
        this.x0 = x0;
        this.y0 = y0;
        this.xLow = xLow;
        this.xHigh = xHigh;
        this.yLow = yLow;
        this.yHigh = yHigh;
        this.max = max;
        this.scaleFactor = scaleFactor;
        this.colors = colors;
    }

    @Override
    public void run() {
        for (int i = xLow; i < xHigh; i++) {
            for (int j = yLow; j < yHigh; j++) {
                double a = x0 + (i * scaleFactor);
                double b = y0 - (j * scaleFactor);
                Complex z0 = new Complex(a, b);
                int gray = max - mand(z0, max);
                Color color = new Color(gray, gray, gray);
                colors[j][i] = color;
            }
        }
    }

    public static int mand(Complex z0, int max) {
        Complex z = z0;
        for (int t = 0; t < max; t++) {
            if (z.abs() > zoom) {
                return t;
            }
            z = Complex.ad(Complex.mult(z, z), z0);
        }
        return max;
    }
    
}

Complex.java复合物.java

import java.lang.Math;

public class Complex {
    double real;
    double imag;
    public Complex(double real, double imag) {
        this.real = real;
        this.imag = imag;
    }
    public static Complex ad(Complex num1, Complex num2) {
        Complex num = new Complex(0.0, 0.0);
        num.real = num1.real + num2.real;
        num.imag = num1.imag + num2.imag;
        return(num);
    }
    
    public static Complex mult(Complex num1, Complex num2) {
        Complex num = new Complex(0.0, 0.0);
        num.real = num1.real * num2.real - num1.imag * num2.imag;
        num.imag = num1.real * num2.imag + num1.imag * num2.real;
        return(num);
    }
    
    public double abs() {
        return Math.hypot(real, imag);
    }
}
  1. You made a mistake by using static fields in ComputeThread.您在 ComputeThread 中使用 static 字段时犯了一个错误。 That way threads use the same arrays and they overwrite each other.这样线程使用相同的 arrays 并且它们相互覆盖。 So you get 1/4 of your image.所以你得到了 1/4 的图像。

  2. Repaint runs concurrently with threads so if it finishes earlier, not the whole image is drawn.重绘与线程同时运行,因此如果它更早完成,则不会绘制整个图像。 You have to inform JFrame about finished calculations so it can repaint when all is ready.您必须通知 JFrame 完成的计算,以便在一切准备就绪后重新绘制。

Below the code that works better:在效果更好的代码下方:

Mandelbrot.java曼德布洛特.java

package mb;

import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.EventListener;
import java.util.Stack;

public class Mandelbrot extends JPanel implements CalcListener {
    private static double size = 4.0; // complex plane is size units in width and length
    private static int n    = 1000; // window is n x n in pixels
    private static int max  = 255; // iterate through hex color values
    private static double x0 = -size/2; // set window origin
    private static double y0 = size/2; // set window origin
    private static double xC = 0; // set complex plane origin
    private static double yC = 0; // set complex plane origin
    private static double scaleFactor = size/n; // translate complex plane to pixels
    private static Stack<double[]> origins = new Stack<double[]>(); // track origins for zooming out
    private static double zoom = 2.0; // modify to alter brightness scheme on zoom
    private static Color[][] colors = new Color[n][n];
    static Mandelbrot me;

    public Mandelbrot() {
        this.me=this;
        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                double[] center = new double[]{xC, yC}; // rescale
                origins.push(center);
                zoom = (zoom * 2) - (0.5 * zoom);
                xC = x0 + (scaleFactor * e.getX());
                yC = y0 - (scaleFactor * e.getY());
                size = size/2.0;
                scaleFactor = size/n;
                x0 = xC - (size/2);
                y0 = yC + (size/2);
                ComputeThread quadrant1 = new ComputeThread(n, zoom, x0, y0, 0, n/2, 0, n/2, max, scaleFactor, colors, me);
                ComputeThread quadrant2 = new ComputeThread(n, zoom, x0, y0, n/2, n, 0, n/2, max, scaleFactor, colors, me);
                ComputeThread quadrant3 = new ComputeThread(n, zoom, x0, y0, 0, n/2, n/2, n, max, scaleFactor, colors, me);
                ComputeThread quadrant4 = new ComputeThread(n, zoom, x0, y0, n/2, n, n/2, n, max, scaleFactor, colors, me);
                Thread thread1 = new Thread(quadrant1);
                Thread thread2 = new Thread(quadrant2);
                Thread thread3 = new Thread(quadrant3);
                Thread thread4 = new Thread(quadrant4);
                thread1.start();
                thread2.start();
                thread3.start();
                thread4.start();
                repaint(0, 0, getWidth(), getHeight());
            }
        });
        addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                    double[] center = origins.pop(); // rescale
                    zoom = (zoom + (zoom / 3)) / 2;
                    xC = center[0];
                    yC = center[1];
                    size = size*2.0;
                    scaleFactor = size/n;
                    x0 = xC - (size/2);
                    y0 = yC + (size/2);
                    ComputeThread quadrant1 = new ComputeThread(n, zoom, x0, y0, 0, n/2, 0, n/2, max, scaleFactor, colors,me);
                    ComputeThread quadrant2 = new ComputeThread(n, zoom, x0, y0, n/2, n, 0, n/2, max, scaleFactor, colors,me);
                    ComputeThread quadrant3 = new ComputeThread(n, zoom, x0, y0, 0, n/2, n/2, n, max, scaleFactor, colors,me);
                    ComputeThread quadrant4 = new ComputeThread(n, zoom, x0, y0, n/2, n, n/2, n, max, scaleFactor, colors,me);
                    Thread thread1 = new Thread(quadrant1);
                    Thread thread2 = new Thread(quadrant2);
                    Thread thread3 = new Thread(quadrant3);
                    Thread thread4 = new Thread(quadrant4);
                    thread1.start();
                    thread2.start();
                    thread3.start();
                    thread4.start();
                    repaint(0, 0, getWidth(), getHeight());
                }
            }
        });
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {           
                g.setColor(colors[j][i]);
                g.drawLine(i, j, i, j);
            }
        }
    }

    public static int mand(Complex z0, int max) {
        Complex z = z0;
        for (int t = 0; t < max; t++) {
            if (z.abs() > zoom) {
                return t;
            }
            z = Complex.ad(Complex.mult(z, z), z0);
        }
        return max;
    }
    
    
    public static void main(String[] args) {

        
        for (int i = 0; i < n; i++) { // initialize array for initial drawing
            for (int j = 0; j < n; j++) {
                double a = x0 + (i * scaleFactor);
                double b = y0 - (j * scaleFactor);
                Complex z0 = new Complex(a, b);
                int gray = max - mand(z0, max);
                Color color = new Color(gray, gray, gray);
                colors[j][i] = color;
            }
        }

        
        SwingUtilities.invokeLater(() -> {
            var panel = new Mandelbrot();
            panel.setBackground(Color.WHITE);
            panel.setFocusable(true);
            var frame = new JFrame("Mandelbrot");
            frame.setSize(n, n);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(panel, BorderLayout.CENTER);
            frame.setVisible(true);
        });
        
    }
    
    public void calcDoneEvent()  
    {
      repaint();
    }
}

ComputeThread.java ComputeThread.java

package mb;

import java.awt.Color;

public class ComputeThread implements Runnable {
    
    private  int n;
    private  double x0;
    private  double y0;
    private  int xLow;
    private  int xHigh;
    private  int yLow;
    private  int yHigh;
    private  int max;
    private  double scaleFactor;
    private  double zoom;
    private  Color[][] colors = new Color[1000][1000];
    private CalcListener listener;
    
    public ComputeThread(int n, double zoom, double x0, double y0, int xLow, int xHigh, int yLow, int yHigh, int max, double scaleFactor, Color[][] colors,CalcListener l) {
        this.n = n;
        this.zoom = zoom;
        this.x0 = x0;
        this.y0 = y0;
        this.xLow = xLow;
        this.xHigh = xHigh;
        this.yLow = yLow;
        this.yHigh = yHigh;
        this.max = max;
        this.scaleFactor = scaleFactor;
        this.colors = colors;
        this.listener = l;
    }

    @Override
    public void run() {
        for (int i = xLow; i < xHigh; i++) {
            for (int j = yLow; j < yHigh; j++) {
                double a = x0 + (i * scaleFactor);
                double b = y0 - (j * scaleFactor);
                Complex z0 = new Complex(a, b);
                int gray = max - mand(z0, max);
                Color color = new Color(gray, gray, gray);
                colors[j][i] = color;
            }
        }
        listener.calcDoneEvent();
    }

    public  int mand(Complex z0, int max) {
        Complex z = z0;
        for (int t = 0; t < max; t++) {
            if (z.abs() > zoom) {
                return t;
            }
            z = Complex.ad(Complex.mult(z, z), z0);
        }
        return max;
    }
    
}

CalcListener.java CalcListener.java

package mb;

public interface CalcListener
{
  public void calcDoneEvent();
}

And Complex.java is without modifications.并且 Complex.java 没有修改。

Another issue is coding style.另一个问题是编码风格。 I made a shortcut by putting static "me" variable in Mandelbrot which is bad practice.我通过在 Mandelbrot 中放置 static“me”变量来创建捷径,这是不好的做法。 Correctly, you should rather inherit Mouse and Key Adapters to own classes that would be capable of passing listener to threads.正确地,您应该继承鼠标和键适配器以拥有能够将侦听器传递给线程的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM