使用 BufferedImage 绘制 Mandelbrot 集,只得到纯色
Using a BufferedImage to draw the Mandelbrot set, only getting a solid color
我正在编写一个 Java 程序来显示我的入门编程 class 的 Mandelbrot 集。我相信我已经正确设置了所有数学,但是当我尝试绘制分形时,我最终得到的只是一种纯色。我已经测试了数学,它似乎应该有效。我已经搜索了一个多小时,但没有找到任何有用的信息。下面是我的 classes 复数和实际创建 Mandelbrot 集:
复数
public class ComplexNum {
//Instance Fields
private double realNum; //the real number portion of the complex number
private double imgNum; //the imaginary number portion of the complex number
//NOTE TO SELF: i = sqrt(-1); i^2 = -1; i^3 = -i; i^4 = 1; then the cycle starts over.
//Constructor
/**Creates a complex number of form x+yi, where x and y are both of type double; x represents the real number piece of the
* complex number, while y represents the imaginary piece.
* @param realPart -- the double value which is the real piece of the complex number
* (Precondition: realPart is a real number of type double)
* @param imaginaryPart -- the double value which represents the imaginary piece of the complex number
* (Precondition: imaginaryPart is a real number of type double)
*/
public ComplexNum(double realPart, double imaginaryPart){
realNum = realPart;
imgNum = imaginaryPart;
}
/**Add two complex numbers by taking the sum of their real and imaginary pieces.
* (Postcondition: returns the sum of two complex numbers)
* @param comNum -- the complex number that is to be added together with this one
* (Precondition: both the complex number you are calling this method on and comNum must have been initialized)
* @return the sum of two complex numbers
*/
public ComplexNum add(ComplexNum comNum){
return new ComplexNum(realNum+comNum.getRealPart(), imgNum+comNum.getImgPart());
}
/**Square the complex number and returns the result.
* (Precondition: the complex number must have been initialized)
* @return the squared value of the complex number
*/
public ComplexNum squareComplex(){
double realPiece = realNum*realNum; //this is a normal number
double imaginaryPiece = (realNum*imgNum)+(imgNum*realNum); //each section in parenthesis has an i attached to it, allowing both sections to be added together
double iSquaredPiece = imgNum*imgNum; //this now has an i^2
//The form that the complex number currently: a + b(i) + c(i^2), where b is actually x(i)+y(i) simplified.
//since i^2 is -1, the iSquaredPiece is actually a real number. Multiply the value by -1, then add it to a,
//and the true real number piece of the complex number is created.
realPiece = realPiece + (iSquaredPiece*-1);
return new ComplexNum(realPiece, imaginaryPiece);
}
/**Allows the real piece of a complex number to be extracted.
* (Precondition: the complex number must have been initialized)
* @return the value of the real number piece of the complex number
*/
public double getRealPart(){
return realNum;
}
/**Allows the imaginary piece of a complex number to be extracted.
* (Precondition: the complex number must have been initialized)
* @return the value of the imaginary number piece of the complex number
*/
public double getImgPart(){
return imgNum;
}
曼德尔布罗特
public class MandelbrotGenerator {
//Constants
/**The maximum number of times the Mandelbrot calculations will be run on a specific point. If the real and imaginary pieces
* from each calculation don't exceed 2 within the maximum number of iterations, they are part of the Mandelbrot set.
*/
public static final int MAX_ITERATIONS = 30; //The maximum number of times the calculations will be run on a specific point.
private final double MIN_X = -2.0; //The minimum value of x when graphing the Mandelbrot set
private final double MAX_Y = 2.0; //The maximum value of y when graphing the Mandelbrot set
private final double MANDEL_X_RANGE = 4.0; //The range of x values from -2 to 2 when graphing the Mandelbrot set
private final double MANDEL_Y_RANGE = 4.0; //The range of y values from -2 to 2 when graphing the Mandelbrot set
//Instance Fields
private ComplexNum z; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_n^2
private ComplexNum c; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of C
private ComplexNum currentCalc; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_(n+1)
private int numIterations; //The current number of iterations
//Constructor
/**Create a MandelbrotGenerator object.
*/
public MandelbrotGenerator(){
z = new ComplexNum(0,0);
c = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
numIterations = 0;
}
//Methods
/**Carry out the Mandelbrot calculation on the point at the (x,y) coordinates specified by the parameters. The return value specifies
* whether or not this point is within the Mandelbrot set, which is determined by whether or not the values of the real and imaginary
* pieces of currentCalc, or Z_(n+1) from the Mandelbrot equation, both reach or exceed the value of 2 within a number of iterations
* less than or equal to MAX_ITERATIONS.
* (Postcondition: the program will return an int value which can be used to determine whether the input point is within the Mandelbrot set)
* @param xVal -- the double value of the desired x coordinate
* (Precondition: xVal is a real number)
* @param yVal -- the double value of the desired y coordinate
* (Precondition: yVal is a real number)
* @return returns the number of iterations needed to meet or exceed the 2 threshold, or the value of MAX_ITERATIONS if the threshold is never met
*/
public int calculateMandelbrot(double xVal, double yVal, double panelWidth, double panelHeight){
double xCord = convertToMandelX(xVal, panelWidth);
double yCord = convertToMandelY(yVal, panelHeight);
c = new ComplexNum(xCord,-yCord);
for(int iterations = 0; iterations <= MAX_ITERATIONS && Math.abs(currentCalc.getRealPart())+Math.abs(currentCalc.getImgPart())<=4.0; iterations ++){
numIterations = iterations;
z = currentCalc;
currentCalc = z.squareComplex().add(c);
}
return numIterations;
}
//I haven't properly commented the two methods below yet, but these
//are used to convert the coordinates of the pixel I'm testing into
//a point on the coordinate plane with x from -2 to 2 and y from
//-2i to 2i, which the Mandelbrot set is within.
//xPixLoc and yPixLoc are the (x,y) coordinates of the pixels from the
//frame, and maxXVal and maxYVal are the (x,y) dimensions of the frame,
//400 in my case.
public double convertToMandelX(double xPixLoc, double maxXVal){
double xCoordinate = MIN_X + ((xPixLoc/maxXVal)*MANDEL_X_RANGE);
return xCoordinate;
}
public double convertToMandelY(double yPixLoc, double maxYVal){
double yCoordinate = MAX_Y -((yPixLoc/maxYVal)*MANDEL_Y_RANGE);
return yCoordinate;
}
我已经完成了一些 JUnit 测试,上面的两个 classes 似乎都有效。我的测试中可能存在导致疏忽的缺陷,但我无法区分。在我看来,我的问题出在我实际创建的图像上,也就是下面的 class:
VisualComponent(我试图让这个首先只使用两种颜色)
public class VisualComponent extends JComponent{
private static final long serialVersionUID = 1L;
//Constants
public static final int DEFAULT_ZOOM_CHANGE = 10;
//Instance Fields
int pnlWidth, pnlHeight; //The width and height of the panel the image will be painted into
BufferedImage fractalImg;
boolean updateImage;
//Constructor
public VisualComponent(int panelWidth, int panelHeight){
pnlWidth=panelWidth;
pnlHeight=panelHeight;
fractalImg = new BufferedImage(panelWidth, panelHeight, BufferedImage.TYPE_INT_RGB);
updateImage = true;
//also initialize a default color pallet
}
//Methods
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if(updateImage){
generateMandelbrot();
updateImage=false;
}
g2.drawImage(fractalImg,0,0,this);
}
public void generateMandelbrot(){
MandelbrotGenerator genImg = new MandelbrotGenerator();
int iterations=0;
for(int x=0; x<pnlWidth;x++){
for(int y=0; y<pnlHeight;y++){
iterations = genImg.calculateMandelbrot((double)x, (double)y, pnlWidth, pnlHeight);
System.out.print(iterations);
if(iterations == MandelbrotGenerator.MAX_ITERATIONS){
fractalImg.setRGB(x, y, Color.BLACK.getRGB());
} else {
fractalImg.setRGB(x, y, Color.WHITE.getRGB());
}
}
}
}
这也是我的主要方法:
public class MainTester {
public static void main(String[] args){
JFrame frame=new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.setResizable(false);
VisualComponent comp = new VisualComponent(400,400);
frame.add(comp);
frame.setVisible(true);
}
}
我真的很难过。似乎正在发生的事情是,当我调用 calculateMandelbrot() 时,return 始终相同。然而,在我的测试中,我发现情况并非如此。我没有太多使用 BufferedImages 的经验,所以我的使用方式可能存在缺陷?
既然我提到了这么多,这里也是我使用的测试代码。我知道这确实不是正确的形式,或者至少不是我教授教的形式,但我真的只是专注于寻找问题。
public class ComplexNumTest {
@Test
public void testToString() {
ComplexNum num = new ComplexNum(5,7);
String res = num.toString();
assertEquals("failed toString()", "5.0+7.0i", res);
}
@Test
public void testAdd(){
ComplexNum num = new ComplexNum(5,7);
ComplexNum num2 = new ComplexNum(5,3);
ComplexNum num3 = num.add(num2);
String res = num3.toString();
assertEquals("failed add()", "10.0+10.0i", res);
ComplexNum num4 = new ComplexNum(5,-7);
ComplexNum num5 = new ComplexNum(-3,4);
ComplexNum num6 = num4.add(num5);
String res2 = num6.toString();
assertEquals("failed add()", "2.0+-3.0i", res2);
}
@Test
public void testSquareComplex(){
ComplexNum num = new ComplexNum(2,2);
ComplexNum num2 = num.squareComplex();
String res = num2.toString();
assertEquals("failed squareComplex()", "0.0+8.0i", res);
ComplexNum num3 = new ComplexNum(2,-2);
ComplexNum num4 = num3.squareComplex();
String res2 = num4.toString();
assertEquals("failed squareComplex()", "0.0+-8.0i", res2);
ComplexNum num5 = new ComplexNum(-1,0.5);
ComplexNum num6 = num5.squareComplex();
String res3 = num6.toString();
assertEquals("failed squareComplex()", "0.75+-1.0i", res3);
}
@Test
public void testCalculations(){
ComplexNum z = new ComplexNum(0,0);
ComplexNum y = new ComplexNum(-1,0.5);
ComplexNum a = z.squareComplex().add(y);
String res = a.toString();
assertEquals("failed calculations", "-1.0+0.5i", res);
z = a;
a = z.squareComplex().add(y);
res = a.toString();
assertEquals("failed squareComplex()", "-0.25+-0.5i", res);
}
@Test
public void getNums(){
ComplexNum z = new ComplexNum(1,3);
ComplexNum a = new ComplexNum(2,4);
double y = z.getRealPart()+a.getRealPart();
String num=y+"";
assertEquals("failed getRealPart()", "3.0", num);
y = z.getImgPart()+a.getImgPart();
num=y+"";
assertEquals("failed getRealPart()", "7.0", num);
}
@Test
public void testConvertToMandel(){
MandelbrotGenerator a = new MandelbrotGenerator();
double check = a.convertToMandelX(200, 400);
String res = check+"";
assertEquals("fail", "0.0", res);
check = a.calculateMandelbrot(200, 200, 400, 400);
res=check+"";
assertEquals("fail", "30.0", res);
boolean working=false;
if(check==MandelbrotGenerator.MAX_ITERATIONS){
working=true;
}
assertEquals("fail",true,working);
}
}
我希望一下子放在这里的代码不会太多。非常感谢您的帮助!
你的问题是你没有重新初始化 z 点,所以计算像你说的那样被难住了:
public int calculateMandelbrot(double xVal, double yVal, double panelWidth, double panelHeight){
z = new ComplexNum(0,0);
c = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
double xCord = convertToMandelX(xVal, panelWidth);
double yCord = convertToMandelY(yVal, panelHeight);
c = new ComplexNum(xCord,-yCord);
for(int iterations = 0; iterations <= MAX_ITERATIONS && Math.abs(currentCalc.getRealPart())+ Math.abs(currentCalc.getImgPart())<=4; iterations ++){
numIterations = iterations;
z = currentCalc;
currentCalc = z.squareComplex().add(c);
// System.out.println(currentCalc.getRealPart()+" "+currentCalc.getImgPart());
}
return numIterations;
}
我设法让它工作了。正如 Hovercraft Full of Eels 所建议的那样,我从我的 paintComponent() 方法中删除了计算。相反,我在 main 方法中完成了它们,按照 Weather Vane 的建议将值存储到二维数组中,并修改了我的 VisualComponent class 以在调用构造函数时将数组作为参数接收。计算本身也有缺陷,我最初对它们的信心是错误的。我对如何构造逃逸条件有误解,因为我没有意识到我应该对实部和虚部进行平方,然后将它们相加并与 4 进行比较。我也不需要在全部(因为对值进行平方将确保它是正的)。最后,我没有在每次调用方法时都初始化复数,这是gpasch指出的错误。这背后是有原因的,但事后看来,我完全是愚蠢的,我认为会有多个 MandelbrotGenerator 对象,每个对象只会调用该方法一次。是的,我非常困惑。我的工作代码如下:
Mandelbrot(我完全重构了计算的方法):
//Constants
/**The maximum number of times the Mandelbrot calculations will be run on a specific point. If the real and imaginary pieces
* from each calculation don't exceed 2 within the maximum number of iterations, they are part of the Mandelbrot set.
*/
public static final int MAX_ITERATIONS = 30; //The maximum number of times the calculations will be run on a specific point.
private final double MIN_X = -2.0; //The minimum value of x when graphing the Mandelbrot set
private final double MAX_Y = 2.0; //The maximum value of y when graphing the Mandelbrot set
private final double MANDEL_X_RANGE = 4.0; //The range of x values from -2 to 2 when graphing the Mandelbrot set
private final double MANDEL_Y_RANGE = 4.0; //The range of y values from -2 to 2 when graphing the Mandelbrot set
//Instance Fields
private ComplexNum z; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_n^2
private ComplexNum c; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of C
private ComplexNum currentCalc; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_(n+1)
private int numIterations; //The current number of iterations
//Constructor
/**Create a MandelbrotGenerator object.
*/
public MandelbrotGenerator(){
z = new ComplexNum(0,0);
c = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
numIterations = 0;
}
//Methods
/**Carry out the Mandelbrot calculation on the point at the (x,y) coordinates specified by the parameters. The return value specifies
* whether or not this point is within the Mandelbrot set, which is determined by whether or not the values of the real and imaginary
* pieces of currentCalc, or Z_(n+1) from the Mandelbrot equation, both reach or exceed the value of 2 within a number of iterations
* less than or equal to MAX_ITERATIONS.
* (Postcondition: the program will return an int value which can be used to determine whether the input point is within the Mandelbrot set)
* @param xVal -- the double value of the desired x coordinate
* (Precondition: xVal is a real number)
* @param yVal -- the double value of the desired y coordinate
* (Precondition: yVal is a real number)
* @return returns the number of iterations needed to meet or exceed the 2 threshold, or the value of MAX_ITERATIONS if the threshold is never met
*/
public int calculateMandelbrot(double xVal, double yVal, double panelWidth, double panelHeight){
double xCord = convertToMandelX(xVal, panelWidth);
double yCord = convertToMandelY(yVal, panelHeight);
c = new ComplexNum(xCord,-yCord);
z = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
numIterations=0;
while(numIterations<=MAX_ITERATIONS && Math.pow(currentCalc.getRealPart(),2)+Math.pow(currentCalc.getImgPart(),2)<=4.0){
numIterations++;
z = currentCalc;
currentCalc = z.squareComplex();
currentCalc = currentCalc.add(c);
}
return numIterations;
}
public double convertToMandelX(double xPixLoc, double maxXVal){
double xCoordinate = MIN_X + ((xPixLoc/maxXVal)*MANDEL_X_RANGE);
return xCoordinate;
}
public double convertToMandelY(double yPixLoc, double maxYVal){
double yCoordinate = MAX_Y -((yPixLoc/maxYVal)*MANDEL_Y_RANGE);
return yCoordinate;
}
主要
public class MainTester {
public static void main(String[] args){
JFrame frame=new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.setResizable(false);
MandelbrotGenerator genImg = new MandelbrotGenerator();
int[][] list = new int[400][400];
int iterations=0;
for(int x=0; x<400;x++){
for(int y=0; y<400;y++){
iterations = genImg.calculateMandelbrot((double)x, (double)y, 400, 400);
list[x][y]=iterations;
//System.out.println(list[x][y]);
}
}
VisualComponent comp = new VisualComponent(400,400, list);
frame.add(comp);
frame.setVisible(true);
}
}
VisualComponent(我当前的颜色选择是任意的,只是我自己的实验)
public class VisualComponent extends JComponent{
private static final long serialVersionUID = 1L;
//Constants
public static final int DEFAULT_ZOOM_CHANGE = 10;
//Instance Fields
int pnlWidth, pnlHeight; //The width and height of the panel the image will be painted into
BufferedImage fractalImg;
boolean updateImage;
int[][] fList;
//Constructor
public VisualComponent(int panelWidth, int panelHeight, int[][] list){
pnlWidth=panelWidth;
pnlHeight=panelHeight;
fractalImg = new BufferedImage(panelWidth, panelHeight, BufferedImage.TYPE_INT_ARGB);
updateImage = true;
fList=list;
//also initialize a default color pallet
}
//Methods
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Color pixColor;
for(int x = 0; x<400; x++){
for(int y=0; y<400; y++){
if(fList[x][y] >= MandelbrotGenerator.MAX_ITERATIONS){
fractalImg.setRGB(x, y, Color.BLACK.getRGB());
} else {
if(fList[x][y]<=2){
pixColor= new Color((42+fList[x][y])%255,0,(80+fList[x][y])%255);
}else if(fList[x][y]<=3){
pixColor= new Color((48+fList[x][y])%255,0,(90+fList[x][y])%255);
}else {
pixColor=new Color((50+fList[x][y])%255,0,(100+fList[x][y])%255);
}
fractalImg.setRGB(x, y, pixColor.getRGB());
}
}
}
g2.drawImage(fractalImg,0,0,this);
}
我的复数 class 没有任何变化。显然我仍然需要让程序做一些除了生成基本图像之外的事情,但现在我已经把这一切理顺了,我想我能弄清楚。再次感谢 Hovercraft Full of Eels 和 Weather Vane 的宝贵意见!
编辑:我在上面发布的代码中意识到,在某些情况下我使用 400 而不是保存框架大小的变量。我已经解决了这个问题,只是想确保我清楚地意识到了疏忽。
Here's an image of my result
我正在编写一个 Java 程序来显示我的入门编程 class 的 Mandelbrot 集。我相信我已经正确设置了所有数学,但是当我尝试绘制分形时,我最终得到的只是一种纯色。我已经测试了数学,它似乎应该有效。我已经搜索了一个多小时,但没有找到任何有用的信息。下面是我的 classes 复数和实际创建 Mandelbrot 集: 复数
public class ComplexNum {
//Instance Fields
private double realNum; //the real number portion of the complex number
private double imgNum; //the imaginary number portion of the complex number
//NOTE TO SELF: i = sqrt(-1); i^2 = -1; i^3 = -i; i^4 = 1; then the cycle starts over.
//Constructor
/**Creates a complex number of form x+yi, where x and y are both of type double; x represents the real number piece of the
* complex number, while y represents the imaginary piece.
* @param realPart -- the double value which is the real piece of the complex number
* (Precondition: realPart is a real number of type double)
* @param imaginaryPart -- the double value which represents the imaginary piece of the complex number
* (Precondition: imaginaryPart is a real number of type double)
*/
public ComplexNum(double realPart, double imaginaryPart){
realNum = realPart;
imgNum = imaginaryPart;
}
/**Add two complex numbers by taking the sum of their real and imaginary pieces.
* (Postcondition: returns the sum of two complex numbers)
* @param comNum -- the complex number that is to be added together with this one
* (Precondition: both the complex number you are calling this method on and comNum must have been initialized)
* @return the sum of two complex numbers
*/
public ComplexNum add(ComplexNum comNum){
return new ComplexNum(realNum+comNum.getRealPart(), imgNum+comNum.getImgPart());
}
/**Square the complex number and returns the result.
* (Precondition: the complex number must have been initialized)
* @return the squared value of the complex number
*/
public ComplexNum squareComplex(){
double realPiece = realNum*realNum; //this is a normal number
double imaginaryPiece = (realNum*imgNum)+(imgNum*realNum); //each section in parenthesis has an i attached to it, allowing both sections to be added together
double iSquaredPiece = imgNum*imgNum; //this now has an i^2
//The form that the complex number currently: a + b(i) + c(i^2), where b is actually x(i)+y(i) simplified.
//since i^2 is -1, the iSquaredPiece is actually a real number. Multiply the value by -1, then add it to a,
//and the true real number piece of the complex number is created.
realPiece = realPiece + (iSquaredPiece*-1);
return new ComplexNum(realPiece, imaginaryPiece);
}
/**Allows the real piece of a complex number to be extracted.
* (Precondition: the complex number must have been initialized)
* @return the value of the real number piece of the complex number
*/
public double getRealPart(){
return realNum;
}
/**Allows the imaginary piece of a complex number to be extracted.
* (Precondition: the complex number must have been initialized)
* @return the value of the imaginary number piece of the complex number
*/
public double getImgPart(){
return imgNum;
}
曼德尔布罗特
public class MandelbrotGenerator {
//Constants
/**The maximum number of times the Mandelbrot calculations will be run on a specific point. If the real and imaginary pieces
* from each calculation don't exceed 2 within the maximum number of iterations, they are part of the Mandelbrot set.
*/
public static final int MAX_ITERATIONS = 30; //The maximum number of times the calculations will be run on a specific point.
private final double MIN_X = -2.0; //The minimum value of x when graphing the Mandelbrot set
private final double MAX_Y = 2.0; //The maximum value of y when graphing the Mandelbrot set
private final double MANDEL_X_RANGE = 4.0; //The range of x values from -2 to 2 when graphing the Mandelbrot set
private final double MANDEL_Y_RANGE = 4.0; //The range of y values from -2 to 2 when graphing the Mandelbrot set
//Instance Fields
private ComplexNum z; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_n^2
private ComplexNum c; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of C
private ComplexNum currentCalc; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_(n+1)
private int numIterations; //The current number of iterations
//Constructor
/**Create a MandelbrotGenerator object.
*/
public MandelbrotGenerator(){
z = new ComplexNum(0,0);
c = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
numIterations = 0;
}
//Methods
/**Carry out the Mandelbrot calculation on the point at the (x,y) coordinates specified by the parameters. The return value specifies
* whether or not this point is within the Mandelbrot set, which is determined by whether or not the values of the real and imaginary
* pieces of currentCalc, or Z_(n+1) from the Mandelbrot equation, both reach or exceed the value of 2 within a number of iterations
* less than or equal to MAX_ITERATIONS.
* (Postcondition: the program will return an int value which can be used to determine whether the input point is within the Mandelbrot set)
* @param xVal -- the double value of the desired x coordinate
* (Precondition: xVal is a real number)
* @param yVal -- the double value of the desired y coordinate
* (Precondition: yVal is a real number)
* @return returns the number of iterations needed to meet or exceed the 2 threshold, or the value of MAX_ITERATIONS if the threshold is never met
*/
public int calculateMandelbrot(double xVal, double yVal, double panelWidth, double panelHeight){
double xCord = convertToMandelX(xVal, panelWidth);
double yCord = convertToMandelY(yVal, panelHeight);
c = new ComplexNum(xCord,-yCord);
for(int iterations = 0; iterations <= MAX_ITERATIONS && Math.abs(currentCalc.getRealPart())+Math.abs(currentCalc.getImgPart())<=4.0; iterations ++){
numIterations = iterations;
z = currentCalc;
currentCalc = z.squareComplex().add(c);
}
return numIterations;
}
//I haven't properly commented the two methods below yet, but these
//are used to convert the coordinates of the pixel I'm testing into
//a point on the coordinate plane with x from -2 to 2 and y from
//-2i to 2i, which the Mandelbrot set is within.
//xPixLoc and yPixLoc are the (x,y) coordinates of the pixels from the
//frame, and maxXVal and maxYVal are the (x,y) dimensions of the frame,
//400 in my case.
public double convertToMandelX(double xPixLoc, double maxXVal){
double xCoordinate = MIN_X + ((xPixLoc/maxXVal)*MANDEL_X_RANGE);
return xCoordinate;
}
public double convertToMandelY(double yPixLoc, double maxYVal){
double yCoordinate = MAX_Y -((yPixLoc/maxYVal)*MANDEL_Y_RANGE);
return yCoordinate;
}
我已经完成了一些 JUnit 测试,上面的两个 classes 似乎都有效。我的测试中可能存在导致疏忽的缺陷,但我无法区分。在我看来,我的问题出在我实际创建的图像上,也就是下面的 class:
VisualComponent(我试图让这个首先只使用两种颜色)
public class VisualComponent extends JComponent{
private static final long serialVersionUID = 1L;
//Constants
public static final int DEFAULT_ZOOM_CHANGE = 10;
//Instance Fields
int pnlWidth, pnlHeight; //The width and height of the panel the image will be painted into
BufferedImage fractalImg;
boolean updateImage;
//Constructor
public VisualComponent(int panelWidth, int panelHeight){
pnlWidth=panelWidth;
pnlHeight=panelHeight;
fractalImg = new BufferedImage(panelWidth, panelHeight, BufferedImage.TYPE_INT_RGB);
updateImage = true;
//also initialize a default color pallet
}
//Methods
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if(updateImage){
generateMandelbrot();
updateImage=false;
}
g2.drawImage(fractalImg,0,0,this);
}
public void generateMandelbrot(){
MandelbrotGenerator genImg = new MandelbrotGenerator();
int iterations=0;
for(int x=0; x<pnlWidth;x++){
for(int y=0; y<pnlHeight;y++){
iterations = genImg.calculateMandelbrot((double)x, (double)y, pnlWidth, pnlHeight);
System.out.print(iterations);
if(iterations == MandelbrotGenerator.MAX_ITERATIONS){
fractalImg.setRGB(x, y, Color.BLACK.getRGB());
} else {
fractalImg.setRGB(x, y, Color.WHITE.getRGB());
}
}
}
}
这也是我的主要方法:
public class MainTester {
public static void main(String[] args){
JFrame frame=new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.setResizable(false);
VisualComponent comp = new VisualComponent(400,400);
frame.add(comp);
frame.setVisible(true);
}
}
我真的很难过。似乎正在发生的事情是,当我调用 calculateMandelbrot() 时,return 始终相同。然而,在我的测试中,我发现情况并非如此。我没有太多使用 BufferedImages 的经验,所以我的使用方式可能存在缺陷?
既然我提到了这么多,这里也是我使用的测试代码。我知道这确实不是正确的形式,或者至少不是我教授教的形式,但我真的只是专注于寻找问题。
public class ComplexNumTest {
@Test
public void testToString() {
ComplexNum num = new ComplexNum(5,7);
String res = num.toString();
assertEquals("failed toString()", "5.0+7.0i", res);
}
@Test
public void testAdd(){
ComplexNum num = new ComplexNum(5,7);
ComplexNum num2 = new ComplexNum(5,3);
ComplexNum num3 = num.add(num2);
String res = num3.toString();
assertEquals("failed add()", "10.0+10.0i", res);
ComplexNum num4 = new ComplexNum(5,-7);
ComplexNum num5 = new ComplexNum(-3,4);
ComplexNum num6 = num4.add(num5);
String res2 = num6.toString();
assertEquals("failed add()", "2.0+-3.0i", res2);
}
@Test
public void testSquareComplex(){
ComplexNum num = new ComplexNum(2,2);
ComplexNum num2 = num.squareComplex();
String res = num2.toString();
assertEquals("failed squareComplex()", "0.0+8.0i", res);
ComplexNum num3 = new ComplexNum(2,-2);
ComplexNum num4 = num3.squareComplex();
String res2 = num4.toString();
assertEquals("failed squareComplex()", "0.0+-8.0i", res2);
ComplexNum num5 = new ComplexNum(-1,0.5);
ComplexNum num6 = num5.squareComplex();
String res3 = num6.toString();
assertEquals("failed squareComplex()", "0.75+-1.0i", res3);
}
@Test
public void testCalculations(){
ComplexNum z = new ComplexNum(0,0);
ComplexNum y = new ComplexNum(-1,0.5);
ComplexNum a = z.squareComplex().add(y);
String res = a.toString();
assertEquals("failed calculations", "-1.0+0.5i", res);
z = a;
a = z.squareComplex().add(y);
res = a.toString();
assertEquals("failed squareComplex()", "-0.25+-0.5i", res);
}
@Test
public void getNums(){
ComplexNum z = new ComplexNum(1,3);
ComplexNum a = new ComplexNum(2,4);
double y = z.getRealPart()+a.getRealPart();
String num=y+"";
assertEquals("failed getRealPart()", "3.0", num);
y = z.getImgPart()+a.getImgPart();
num=y+"";
assertEquals("failed getRealPart()", "7.0", num);
}
@Test
public void testConvertToMandel(){
MandelbrotGenerator a = new MandelbrotGenerator();
double check = a.convertToMandelX(200, 400);
String res = check+"";
assertEquals("fail", "0.0", res);
check = a.calculateMandelbrot(200, 200, 400, 400);
res=check+"";
assertEquals("fail", "30.0", res);
boolean working=false;
if(check==MandelbrotGenerator.MAX_ITERATIONS){
working=true;
}
assertEquals("fail",true,working);
}
}
我希望一下子放在这里的代码不会太多。非常感谢您的帮助!
你的问题是你没有重新初始化 z 点,所以计算像你说的那样被难住了:
public int calculateMandelbrot(double xVal, double yVal, double panelWidth, double panelHeight){
z = new ComplexNum(0,0);
c = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
double xCord = convertToMandelX(xVal, panelWidth);
double yCord = convertToMandelY(yVal, panelHeight);
c = new ComplexNum(xCord,-yCord);
for(int iterations = 0; iterations <= MAX_ITERATIONS && Math.abs(currentCalc.getRealPart())+ Math.abs(currentCalc.getImgPart())<=4; iterations ++){
numIterations = iterations;
z = currentCalc;
currentCalc = z.squareComplex().add(c);
// System.out.println(currentCalc.getRealPart()+" "+currentCalc.getImgPart());
}
return numIterations;
}
我设法让它工作了。正如 Hovercraft Full of Eels 所建议的那样,我从我的 paintComponent() 方法中删除了计算。相反,我在 main 方法中完成了它们,按照 Weather Vane 的建议将值存储到二维数组中,并修改了我的 VisualComponent class 以在调用构造函数时将数组作为参数接收。计算本身也有缺陷,我最初对它们的信心是错误的。我对如何构造逃逸条件有误解,因为我没有意识到我应该对实部和虚部进行平方,然后将它们相加并与 4 进行比较。我也不需要在全部(因为对值进行平方将确保它是正的)。最后,我没有在每次调用方法时都初始化复数,这是gpasch指出的错误。这背后是有原因的,但事后看来,我完全是愚蠢的,我认为会有多个 MandelbrotGenerator 对象,每个对象只会调用该方法一次。是的,我非常困惑。我的工作代码如下:
Mandelbrot(我完全重构了计算的方法):
//Constants
/**The maximum number of times the Mandelbrot calculations will be run on a specific point. If the real and imaginary pieces
* from each calculation don't exceed 2 within the maximum number of iterations, they are part of the Mandelbrot set.
*/
public static final int MAX_ITERATIONS = 30; //The maximum number of times the calculations will be run on a specific point.
private final double MIN_X = -2.0; //The minimum value of x when graphing the Mandelbrot set
private final double MAX_Y = 2.0; //The maximum value of y when graphing the Mandelbrot set
private final double MANDEL_X_RANGE = 4.0; //The range of x values from -2 to 2 when graphing the Mandelbrot set
private final double MANDEL_Y_RANGE = 4.0; //The range of y values from -2 to 2 when graphing the Mandelbrot set
//Instance Fields
private ComplexNum z; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_n^2
private ComplexNum c; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of C
private ComplexNum currentCalc; //In the Mandelbrot equation of Z_(n+1)=Z_n^2+C, this is the value of Z_(n+1)
private int numIterations; //The current number of iterations
//Constructor
/**Create a MandelbrotGenerator object.
*/
public MandelbrotGenerator(){
z = new ComplexNum(0,0);
c = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
numIterations = 0;
}
//Methods
/**Carry out the Mandelbrot calculation on the point at the (x,y) coordinates specified by the parameters. The return value specifies
* whether or not this point is within the Mandelbrot set, which is determined by whether or not the values of the real and imaginary
* pieces of currentCalc, or Z_(n+1) from the Mandelbrot equation, both reach or exceed the value of 2 within a number of iterations
* less than or equal to MAX_ITERATIONS.
* (Postcondition: the program will return an int value which can be used to determine whether the input point is within the Mandelbrot set)
* @param xVal -- the double value of the desired x coordinate
* (Precondition: xVal is a real number)
* @param yVal -- the double value of the desired y coordinate
* (Precondition: yVal is a real number)
* @return returns the number of iterations needed to meet or exceed the 2 threshold, or the value of MAX_ITERATIONS if the threshold is never met
*/
public int calculateMandelbrot(double xVal, double yVal, double panelWidth, double panelHeight){
double xCord = convertToMandelX(xVal, panelWidth);
double yCord = convertToMandelY(yVal, panelHeight);
c = new ComplexNum(xCord,-yCord);
z = new ComplexNum(0,0);
currentCalc = new ComplexNum(0,0);
numIterations=0;
while(numIterations<=MAX_ITERATIONS && Math.pow(currentCalc.getRealPart(),2)+Math.pow(currentCalc.getImgPart(),2)<=4.0){
numIterations++;
z = currentCalc;
currentCalc = z.squareComplex();
currentCalc = currentCalc.add(c);
}
return numIterations;
}
public double convertToMandelX(double xPixLoc, double maxXVal){
double xCoordinate = MIN_X + ((xPixLoc/maxXVal)*MANDEL_X_RANGE);
return xCoordinate;
}
public double convertToMandelY(double yPixLoc, double maxYVal){
double yCoordinate = MAX_Y -((yPixLoc/maxYVal)*MANDEL_Y_RANGE);
return yCoordinate;
}
主要
public class MainTester {
public static void main(String[] args){
JFrame frame=new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.setResizable(false);
MandelbrotGenerator genImg = new MandelbrotGenerator();
int[][] list = new int[400][400];
int iterations=0;
for(int x=0; x<400;x++){
for(int y=0; y<400;y++){
iterations = genImg.calculateMandelbrot((double)x, (double)y, 400, 400);
list[x][y]=iterations;
//System.out.println(list[x][y]);
}
}
VisualComponent comp = new VisualComponent(400,400, list);
frame.add(comp);
frame.setVisible(true);
}
}
VisualComponent(我当前的颜色选择是任意的,只是我自己的实验)
public class VisualComponent extends JComponent{
private static final long serialVersionUID = 1L;
//Constants
public static final int DEFAULT_ZOOM_CHANGE = 10;
//Instance Fields
int pnlWidth, pnlHeight; //The width and height of the panel the image will be painted into
BufferedImage fractalImg;
boolean updateImage;
int[][] fList;
//Constructor
public VisualComponent(int panelWidth, int panelHeight, int[][] list){
pnlWidth=panelWidth;
pnlHeight=panelHeight;
fractalImg = new BufferedImage(panelWidth, panelHeight, BufferedImage.TYPE_INT_ARGB);
updateImage = true;
fList=list;
//also initialize a default color pallet
}
//Methods
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Color pixColor;
for(int x = 0; x<400; x++){
for(int y=0; y<400; y++){
if(fList[x][y] >= MandelbrotGenerator.MAX_ITERATIONS){
fractalImg.setRGB(x, y, Color.BLACK.getRGB());
} else {
if(fList[x][y]<=2){
pixColor= new Color((42+fList[x][y])%255,0,(80+fList[x][y])%255);
}else if(fList[x][y]<=3){
pixColor= new Color((48+fList[x][y])%255,0,(90+fList[x][y])%255);
}else {
pixColor=new Color((50+fList[x][y])%255,0,(100+fList[x][y])%255);
}
fractalImg.setRGB(x, y, pixColor.getRGB());
}
}
}
g2.drawImage(fractalImg,0,0,this);
}
我的复数 class 没有任何变化。显然我仍然需要让程序做一些除了生成基本图像之外的事情,但现在我已经把这一切理顺了,我想我能弄清楚。再次感谢 Hovercraft Full of Eels 和 Weather Vane 的宝贵意见!
编辑:我在上面发布的代码中意识到,在某些情况下我使用 400 而不是保存框架大小的变量。我已经解决了这个问题,只是想确保我清楚地意识到了疏忽。 Here's an image of my result