使用自己的 Complex 在 JavaFX 中设置 Mandelbrot class
Mandelbrot Set in JavaFX using own Complex class
我正在尝试使用我自己的 Complex class 在 Java 中生成一个 Mandelbrot 集。它需要可缩放并具有着色功能,我还没有实现。我正在使用我的老师编写的算法,但出于某种原因,我得到的图像与 Mandelbrot 集不同。我尝试了各种修改,但无法找到解决问题的方法。
CanvasFX.java
package mandelbrot;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class CanvasFX extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Mandelbrot Set");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package mandelbrot;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.BlendMode;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.image.WritableImage;
import javafx.scene.image.PixelWriter;
public class Controller extends Complex {
public Canvas canvas;
private GraphicsContext gc;
private double x1, y1, x2, y2;
public void initialize() {
final int size = 512;
gc = canvas.getGraphicsContext2D();
clear(gc);
Complex a = new Complex(-2.5, 2.5);
Complex b = new Complex(2.5, -2.5);
WritableImage wr = new WritableImage(size, size);
drawMandelbrot(wr, a, b,512, 512);
}
private void clear(GraphicsContext gc) {
gc.setFill(Color.WHITE);
gc.setGlobalBlendMode(BlendMode.SRC_OVER);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
private void rect(GraphicsContext gc) {
double x = x1;
double y = y1;
double width = x2 - x1;
double height = y2 - y1;
if (width < 0) {
x = x2;
width = -width;
}
if (height < 0) {
y = y2;
height = -height;
}
gc.strokeRect(x + 0.5, y + 0.5, width, height);
}
public void mouseMoves(MouseEvent mouseEvent) {
double x = mouseEvent.getX();
double y = mouseEvent.getY();
gc.setGlobalBlendMode(BlendMode.DIFFERENCE);
gc.setStroke(Color.WHITE);
rect(gc);
x2 = x;
y2 = y;
rect(gc);
}
public void mousePressed(MouseEvent mouseEvent) {
x1 = mouseEvent.getX();
y1 = mouseEvent.getY();
x2 = x1;
y2 = y1;
}
public void mouseReleased(MouseEvent mouseEvent) {
rect(gc);
System.out.format("%f %f %f %f\n", x1, y1, x2, y2);
}
private void drawMandelbrot(WritableImage wr, Complex a, Complex b, int height, int width) {
final int maxIterations = 200;
final double pixelWidth = (b.re() - a.re()) / width;
final double pixelHeight = (b.im() - a.im()) / height;
int iteration;
PixelWriter pw = wr.getPixelWriter();
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
Complex z = new Complex(); // z = 0
Complex c = new Complex(); // c = 0
c.setRe(a.re() + pixelWidth * column); // set real part of c
c.setIm(a.im() + pixelHeight * row); // set imaginary part of c
iteration = 0;
while (Complex.abs(z) <= 4 && iteration < maxIterations) {
z.mul(z); // z to the power of 2
z.add(c); // z plus c
iteration++;
}
if (iteration < maxIterations)
pw.setArgb(column, row, 0xFFFFFFFF);
else
pw.setArgb(row, column, 0xFF0000FF);
}
}
gc.setGlobalBlendMode(BlendMode.SRC_OVER);
gc.drawImage(wr, 0, 0, 512, 512);
}
}
sample.fxml
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.*?>
<GridPane fx:controller="mandelbrot.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10"
vgap="10" stylesheets="/tutorial/main.css">
<Canvas fx:id="canvas" GridPane.columnIndex="0" GridPane.rowSpan="6" width="512" height="512"
onMouseDragged="#mouseMoves" onMousePressed="#mousePressed" onMouseReleased="#mouseReleased"/>
</GridPane>
Complex.java
package mandelbrot;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class Complex implements Field<Complex> {
public double r, i; // Real and imaginary parts
public Complex() {
this.r = 0;
this.i = 0;
}
public Complex(double real) {
this.r = real;
this.i = 0;
}
public Complex(double real, double imaginary) {
this.r = real;
this.i = imaginary;
}
public Complex(Complex c) {
this.r = c.r;
this.i = c.i;
}
public Complex(String s) {
boolean isPositive = true;
if(s.charAt(0) == '-') { // Check if the first part is negative
isPositive = false;
s = s.substring(1); // Remove first minus from the string
}
if(!(s.contains("+") || s.contains("-"))) { // Check if the complex number consists only of real or imaginary part
if(s.contains("i")) { // If consists only of imaginary part
// If imaginary part is equal to 1
if(s.charAt(0) == 'i') this.i = Double.parseDouble((isPositive ? "+" : "-") + "1");
else this.i = Double.parseDouble((isPositive ? "+" : "-") +
s.substring(0, s.length() - 1));
}
else this.r = Double.parseDouble((isPositive ? "+" : "-") + s);
}
else {
boolean isImaginaryPositive = true;
if (s.contains("-")) isImaginaryPositive = false; // Check if the imaginary part if negative
String[] split = s.split("[+-]"); // Split the string to real and imaginary part
this.r = Double.parseDouble((isPositive ? "+" : "-") + split[0]);
// If imaginary part is equal to 1
if(split[1].charAt(0) == 'i') this.i = Double.parseDouble((isImaginaryPositive ? "+" : "-") + "1");
else this.i = Double.parseDouble((isImaginaryPositive ? "+" : "-") +
split[1].substring(0, split[1].length() - 1));
}
}
@Override
public Complex add(Complex b) {
this.r = this.r + b.r;
this.i = this.i + b.i;
return this;
}
@Override
public Complex sub(Complex b) {
this.r = this.r - b.r;
this.i = this.i - b.i;
return this;
}
@Override
public Complex mul(Complex b) {
double real = this.r;
double imaginary = this.i;
this.r = real * b.r - imaginary * b.i;
this.i = real * b.i + imaginary * b.r;
return this;
}
@Override
public Complex div(Complex b) throws IllegalArgumentException {
if(b.r == 0 && b.i == 0) throw new IllegalArgumentException("Can't divide by 0.");
double real = this.r;
double imaginary = this.i;
double divisor = b.r * b.r + b.i * b.i;
this.setRe((real * b.r + imaginary * b.i) / divisor);
this.setIm((imaginary * b.r - real * b.i) / divisor);
return this;
}
public double abs() {
return Math.hypot(r, i);
}
public double sqrAbs() {
return Math.sqrt(abs());
}
public double phase() {
if(this.r == 0 && this.i == 0) return -1;
double phi = 0;
if(this.r == 0) {
if(this.i > 0) {
phi = Math.PI / 2;
}
if(this.i < 0) {
phi = -(Math.PI / 2);
}
}
if(this.r > 0) {
phi = Math.atan(this.i / this.r);
}
if(this.r < 0) {
phi = Math.atan(this.i / this.r) + Math.PI;
}
return phi;
}
public double re() {
return r;
}
public double im() {
return i;
}
public static Complex add(Complex a, Complex b) {
Complex c = new Complex();
c.setRe(a.r + b.r);
c.setIm(a.i + b.i);
return c;
}
public static Complex sub(Complex a, Complex b) {
Complex c = new Complex();
c.setRe(a.r - b.r);
c.setIm(a.i - b.i);
return c;
}
public static Complex mul(Complex a, Complex b) {
Complex c = new Complex();
c.setRe(a.r * b.r - a.i * b.i);
c.setIm(a.r * b.i + a.i * b.r);
return c;
}
public static Complex div(Complex a, Complex b) throws IllegalArgumentException {
if(b.r == 0 && b.i == 0) throw new IllegalArgumentException("Can't divide by 0.");
Complex c = new Complex();
double divisor = b.r * b.r + b.i * b.i;
c.setRe((a.r * b.r + a.i * b.i) / divisor);
c.setIm((a.i * b.r - a.r * b.i) / divisor);
return c;
}
public static double abs(Complex a) {
return Math.hypot(a.r, a.i);
}
public static double sqrtabs(Complex a) {
return Math.sqrt(abs(a));
}
public static double phase(Complex a) {
if(a.r == 0 && a.i == 0) return -1;
double phi = 0;
if(a.r == 0) {
if(a.i > 0) {
phi = Math.PI / 2;
}
if(a.i < 0) {
phi = -(Math.PI / 2);
}
}
if(a.r > 0) {
phi = Math.atan(a.i / a.r);
}
if(a.r < 0) {
phi = Math.atan(a.i / a.r) + Math.PI;
}
return phi;
}
public static double re(Complex a) {
return a.r;
}
public static double im(Complex a) {
return a.i;
}
@Override
public String toString() {
NumberFormat formatter = new DecimalFormat("#0.00");
if(i == 0) return formatter.format(r);
if(r == 0) return formatter.format(i) + "i";
if(i > 0) return formatter.format(r) + "+" + formatter.format(i) + "i";
return formatter.format(r) + formatter.format(i) + "i";
}
static Complex valueOf(String s) {
Complex complex = new Complex(s);
return complex;
}
void setRe(double real) {
this.r = real;
}
void setIm(double imaginary) {
this.i = imaginary;
}
void setVal(Complex c) {
this.r = c.r;
this.i = c.i;
}
void setVal(double real, double imaginary) {
this.r = real;
this.i = imaginary;
}
}
Output
1) 在你的 Complex-class 中替换
@Override
public Complex mul(Complex b) {
double real = this.r;
double imaginary = this.i;
this.r = real * b.r - imaginary * b.i;
this.i = real * b.i + imaginary * b.r;
return this;
}
和
@Override
public Complex mul(Complex b) {
double real = this.r;
double imaginary = this.i;
double real_b = b.r;
double imaginary_b = b.i;
this.r = real * real_b - imaginary * imaginary_b;
this.i = real * imaginary_b + imaginary * real_b;
return this;
}
或者在您的 drawMandelbrot 方法中替换
z.mul(z);
与
z = Complex.mul(z, z);
这导致:
2) 如评论中所述,将 drawMandelbrot 方法替换为 maxIterations-case
pw.setArgb(row, column, 0xFF0000FF);
与
pw.setArgb(column, row, 0xFF0000FF);
这导致:
我正在尝试使用我自己的 Complex class 在 Java 中生成一个 Mandelbrot 集。它需要可缩放并具有着色功能,我还没有实现。我正在使用我的老师编写的算法,但出于某种原因,我得到的图像与 Mandelbrot 集不同。我尝试了各种修改,但无法找到解决问题的方法。
CanvasFX.java
package mandelbrot;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class CanvasFX extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Mandelbrot Set");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package mandelbrot;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.BlendMode;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.image.WritableImage;
import javafx.scene.image.PixelWriter;
public class Controller extends Complex {
public Canvas canvas;
private GraphicsContext gc;
private double x1, y1, x2, y2;
public void initialize() {
final int size = 512;
gc = canvas.getGraphicsContext2D();
clear(gc);
Complex a = new Complex(-2.5, 2.5);
Complex b = new Complex(2.5, -2.5);
WritableImage wr = new WritableImage(size, size);
drawMandelbrot(wr, a, b,512, 512);
}
private void clear(GraphicsContext gc) {
gc.setFill(Color.WHITE);
gc.setGlobalBlendMode(BlendMode.SRC_OVER);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
private void rect(GraphicsContext gc) {
double x = x1;
double y = y1;
double width = x2 - x1;
double height = y2 - y1;
if (width < 0) {
x = x2;
width = -width;
}
if (height < 0) {
y = y2;
height = -height;
}
gc.strokeRect(x + 0.5, y + 0.5, width, height);
}
public void mouseMoves(MouseEvent mouseEvent) {
double x = mouseEvent.getX();
double y = mouseEvent.getY();
gc.setGlobalBlendMode(BlendMode.DIFFERENCE);
gc.setStroke(Color.WHITE);
rect(gc);
x2 = x;
y2 = y;
rect(gc);
}
public void mousePressed(MouseEvent mouseEvent) {
x1 = mouseEvent.getX();
y1 = mouseEvent.getY();
x2 = x1;
y2 = y1;
}
public void mouseReleased(MouseEvent mouseEvent) {
rect(gc);
System.out.format("%f %f %f %f\n", x1, y1, x2, y2);
}
private void drawMandelbrot(WritableImage wr, Complex a, Complex b, int height, int width) {
final int maxIterations = 200;
final double pixelWidth = (b.re() - a.re()) / width;
final double pixelHeight = (b.im() - a.im()) / height;
int iteration;
PixelWriter pw = wr.getPixelWriter();
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
Complex z = new Complex(); // z = 0
Complex c = new Complex(); // c = 0
c.setRe(a.re() + pixelWidth * column); // set real part of c
c.setIm(a.im() + pixelHeight * row); // set imaginary part of c
iteration = 0;
while (Complex.abs(z) <= 4 && iteration < maxIterations) {
z.mul(z); // z to the power of 2
z.add(c); // z plus c
iteration++;
}
if (iteration < maxIterations)
pw.setArgb(column, row, 0xFFFFFFFF);
else
pw.setArgb(row, column, 0xFF0000FF);
}
}
gc.setGlobalBlendMode(BlendMode.SRC_OVER);
gc.drawImage(wr, 0, 0, 512, 512);
}
}
sample.fxml
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.*?>
<GridPane fx:controller="mandelbrot.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10"
vgap="10" stylesheets="/tutorial/main.css">
<Canvas fx:id="canvas" GridPane.columnIndex="0" GridPane.rowSpan="6" width="512" height="512"
onMouseDragged="#mouseMoves" onMousePressed="#mousePressed" onMouseReleased="#mouseReleased"/>
</GridPane>
Complex.java
package mandelbrot;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class Complex implements Field<Complex> {
public double r, i; // Real and imaginary parts
public Complex() {
this.r = 0;
this.i = 0;
}
public Complex(double real) {
this.r = real;
this.i = 0;
}
public Complex(double real, double imaginary) {
this.r = real;
this.i = imaginary;
}
public Complex(Complex c) {
this.r = c.r;
this.i = c.i;
}
public Complex(String s) {
boolean isPositive = true;
if(s.charAt(0) == '-') { // Check if the first part is negative
isPositive = false;
s = s.substring(1); // Remove first minus from the string
}
if(!(s.contains("+") || s.contains("-"))) { // Check if the complex number consists only of real or imaginary part
if(s.contains("i")) { // If consists only of imaginary part
// If imaginary part is equal to 1
if(s.charAt(0) == 'i') this.i = Double.parseDouble((isPositive ? "+" : "-") + "1");
else this.i = Double.parseDouble((isPositive ? "+" : "-") +
s.substring(0, s.length() - 1));
}
else this.r = Double.parseDouble((isPositive ? "+" : "-") + s);
}
else {
boolean isImaginaryPositive = true;
if (s.contains("-")) isImaginaryPositive = false; // Check if the imaginary part if negative
String[] split = s.split("[+-]"); // Split the string to real and imaginary part
this.r = Double.parseDouble((isPositive ? "+" : "-") + split[0]);
// If imaginary part is equal to 1
if(split[1].charAt(0) == 'i') this.i = Double.parseDouble((isImaginaryPositive ? "+" : "-") + "1");
else this.i = Double.parseDouble((isImaginaryPositive ? "+" : "-") +
split[1].substring(0, split[1].length() - 1));
}
}
@Override
public Complex add(Complex b) {
this.r = this.r + b.r;
this.i = this.i + b.i;
return this;
}
@Override
public Complex sub(Complex b) {
this.r = this.r - b.r;
this.i = this.i - b.i;
return this;
}
@Override
public Complex mul(Complex b) {
double real = this.r;
double imaginary = this.i;
this.r = real * b.r - imaginary * b.i;
this.i = real * b.i + imaginary * b.r;
return this;
}
@Override
public Complex div(Complex b) throws IllegalArgumentException {
if(b.r == 0 && b.i == 0) throw new IllegalArgumentException("Can't divide by 0.");
double real = this.r;
double imaginary = this.i;
double divisor = b.r * b.r + b.i * b.i;
this.setRe((real * b.r + imaginary * b.i) / divisor);
this.setIm((imaginary * b.r - real * b.i) / divisor);
return this;
}
public double abs() {
return Math.hypot(r, i);
}
public double sqrAbs() {
return Math.sqrt(abs());
}
public double phase() {
if(this.r == 0 && this.i == 0) return -1;
double phi = 0;
if(this.r == 0) {
if(this.i > 0) {
phi = Math.PI / 2;
}
if(this.i < 0) {
phi = -(Math.PI / 2);
}
}
if(this.r > 0) {
phi = Math.atan(this.i / this.r);
}
if(this.r < 0) {
phi = Math.atan(this.i / this.r) + Math.PI;
}
return phi;
}
public double re() {
return r;
}
public double im() {
return i;
}
public static Complex add(Complex a, Complex b) {
Complex c = new Complex();
c.setRe(a.r + b.r);
c.setIm(a.i + b.i);
return c;
}
public static Complex sub(Complex a, Complex b) {
Complex c = new Complex();
c.setRe(a.r - b.r);
c.setIm(a.i - b.i);
return c;
}
public static Complex mul(Complex a, Complex b) {
Complex c = new Complex();
c.setRe(a.r * b.r - a.i * b.i);
c.setIm(a.r * b.i + a.i * b.r);
return c;
}
public static Complex div(Complex a, Complex b) throws IllegalArgumentException {
if(b.r == 0 && b.i == 0) throw new IllegalArgumentException("Can't divide by 0.");
Complex c = new Complex();
double divisor = b.r * b.r + b.i * b.i;
c.setRe((a.r * b.r + a.i * b.i) / divisor);
c.setIm((a.i * b.r - a.r * b.i) / divisor);
return c;
}
public static double abs(Complex a) {
return Math.hypot(a.r, a.i);
}
public static double sqrtabs(Complex a) {
return Math.sqrt(abs(a));
}
public static double phase(Complex a) {
if(a.r == 0 && a.i == 0) return -1;
double phi = 0;
if(a.r == 0) {
if(a.i > 0) {
phi = Math.PI / 2;
}
if(a.i < 0) {
phi = -(Math.PI / 2);
}
}
if(a.r > 0) {
phi = Math.atan(a.i / a.r);
}
if(a.r < 0) {
phi = Math.atan(a.i / a.r) + Math.PI;
}
return phi;
}
public static double re(Complex a) {
return a.r;
}
public static double im(Complex a) {
return a.i;
}
@Override
public String toString() {
NumberFormat formatter = new DecimalFormat("#0.00");
if(i == 0) return formatter.format(r);
if(r == 0) return formatter.format(i) + "i";
if(i > 0) return formatter.format(r) + "+" + formatter.format(i) + "i";
return formatter.format(r) + formatter.format(i) + "i";
}
static Complex valueOf(String s) {
Complex complex = new Complex(s);
return complex;
}
void setRe(double real) {
this.r = real;
}
void setIm(double imaginary) {
this.i = imaginary;
}
void setVal(Complex c) {
this.r = c.r;
this.i = c.i;
}
void setVal(double real, double imaginary) {
this.r = real;
this.i = imaginary;
}
}
Output
1) 在你的 Complex-class 中替换
@Override
public Complex mul(Complex b) {
double real = this.r;
double imaginary = this.i;
this.r = real * b.r - imaginary * b.i;
this.i = real * b.i + imaginary * b.r;
return this;
}
和
@Override
public Complex mul(Complex b) {
double real = this.r;
double imaginary = this.i;
double real_b = b.r;
double imaginary_b = b.i;
this.r = real * real_b - imaginary * imaginary_b;
this.i = real * imaginary_b + imaginary * real_b;
return this;
}
或者在您的 drawMandelbrot 方法中替换
z.mul(z);
与
z = Complex.mul(z, z);
这导致:
2) 如评论中所述,将 drawMandelbrot 方法替换为 maxIterations-case
pw.setArgb(row, column, 0xFF0000FF);
与
pw.setArgb(column, row, 0xFF0000FF);
这导致: