[英]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);
}
}
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 的图像。
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.