[英]How do you draw the Mandelbrot Set in Java using SWING/AWT?
[英]Mutli Threaded Mandelbrot Set viewer with Java Swing
在 Java 中尝试使用 Swing 图形的多线程 Mandelbrot 集查看器。 我见过一些使用 BufferedImage 和/或 SwingWorker 的 Mandelbrot Set 程序和其他多线程 Swing 应用程序,但我想知道这种方法是否可以工作/被赎回,如果不能,为什么不呢?
在单击(放大)和按键(缩小)时,我正在打开线程以计算象限中的颜色值(左上、右上、左下、右下),并在private static Color[][] colors = new Color[n][n];
. 然后在组件上调用repaint方法从colors
数组中进行绘制。
这些线程是否与 Event Dispatch Thread 并发执行,如果是,为什么这些线程不发生在 Event Dispatch Thread Swing 组件绘制之前,并带有SwingUtilities.invokeLater
?
没有错误消息,只是在单击和按键时出现不可预测的重绘行为。 谢谢你。
曼德布洛特.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
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;
}
}
复合物.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);
}
}
您在 ComputeThread 中使用 static 字段时犯了一个错误。 这样线程使用相同的 arrays 并且它们相互覆盖。 所以你得到了 1/4 的图像。
重绘与线程同时运行,因此如果它更早完成,则不会绘制整个图像。 您必须通知 JFrame 完成的计算,以便在一切准备就绪后重新绘制。
在效果更好的代码下方:
曼德布洛特.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
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
package mb;
public interface CalcListener
{
public void calcDoneEvent();
}
并且 Complex.java 没有修改。
另一个问题是编码风格。 我通过在 Mandelbrot 中放置 static“me”变量来创建捷径,这是不好的做法。 正确地,您应该继承鼠标和键适配器以拥有能够将侦听器传递给线程的类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.