当前位置: 首页 > 知识库问答 >
问题:

使用BuffereImage绘制Mandelbrot集,仅获得纯色

薛保臣
2023-03-14

我正在编写一个Java程序来显示我的入门编程类的Mandelbrot集。我相信我已经正确地设置了所有的数学,但是当我尝试绘制分形时,我得到的只是一种纯色。我已经测试了数学,它似乎应该是有效的。我搜索了一个多小时,但没有找到任何有用的东西。下面是我的复数类,并实际创建了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测试,上面的两个类似乎都可以工作。我的测试中可能有一个缺陷导致了疏忽,但我无法区分。在我看来,我的问题是我实际创建的图像,这是下面的类:

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);
}
}

我真的被难住了。似乎发生的事情是,当我调用calculateNameDelbrot()时,返回值总是相同的。但在我的测试中,我发现情况并非如此。我没有太多使用BuffereImage的经验,所以我使用BuffereImage的方式可能有缺陷?

因为我已经提到了很多,下面是我正在使用的测试代码。我知道这不是正确的形式,或者至少不是我教授教的形式,但我只是专注于寻找问题。

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);
}
}

我希望这不是太多的代码,在这里一次抛出。非常感谢你的帮助!

共有2个答案

尤夕
2023-03-14

我设法让这个工作。正如充满鳗鱼的气垫船所建议的,我从我的油漆组件()方法中删除了计算。相反,我在main方法中完成了它们,按照Weather Vane的建议将值存储到2D数组中,并修改了我的VisualComponent类,以便在调用构造函数时将数组作为参数。计算本身也有缺陷,我最初对它们的信心是错误的。我对如何构造转义条件有一个误解,因为我没有意识到我应该对真实和想象的部分进行平方,然后添加它们并比较到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);
}

没有对我的复数类进行任何更改。显然,除了生成基本图像之外,我还需要让程序做一些事情,但是现在我已经把这一切都弄清楚了,我想我能弄明白。再次感谢装满鳗鱼的气垫船和风向标,感谢他们的有益评论!

编辑:我在上面发布的代码中意识到,在某些情况下,我使用400而不是保存框架大小的变量。我已经解决了这个问题,只是想确保我清楚地意识到我的疏忽。这是我结果的图片

吕承福
2023-03-14

您的问题是,您没有重新初始化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;

}
 类似资料:
  • 我试图画曼德布罗特集,其中的点是黑色的,其他的都是白色的。在这个初始版本中,我不希望能够放大,而只是创建一个静态图像。 我创建了一个ComplexNumber类,如下所示,用于处理平方运算和将复数相加。 这是我渲染GUI并实际计算Mandelbrot Set中的点的代码。 运行完这段代码后,我得到了下图。看起来曼德尔布罗特的布景有一点模糊,但随后被一吨黑色遮住了。我做错了什么? 更新的解决方案如下

  • 因此,在学习C的一个步骤中,我决定尝试使用CImg实现mandelbrot集。这是我的程序: 我设法画出经典的曼德尔布洛特形状,但后来我试图实现平滑,以摆脱丑陋的带状。但我不能让它真正工作。 说真的,我不知道我在做什么,因为数学很难。 当保存图像时,我也会收到这个警告 所以很明显我有一个溢出的地方,但我真的不知道在哪里。 如果有人能帮助我,解释我在做什么,并指出我所做的所有错误的事情,但用一种简单

  • 我尝试使用PDFBox将BuffereImage中的图像绘制到PDF中,但失败了,我得到了黑色图像,Acrobat Reader发出警告,其中有“内存不足”之类的错误(但PDF是显示的)。 我使用BuffereImage是因为我需要将JavaFX图像对象(来自对Functiones.crearImagenDesdeTexto()的调用,是一个将文本转换为图像的函数)绘制成PDF。其余图像在不使用B

  • 我已经尝试了很多算法来渲染Mandelbrot集,包括简单的逃逸时间算法,以及优化的逃逸时间算法。但是,有没有更快的算法可以像我们在YouTube上看到的那样有效地产生真正深的缩放。此外,我很想得到一些想法,如何提高我的精度超过C/C

  • 我试图编写一个SWT组件,它能够采取和绘制的实例。我的问题是SWT的和AWT的不兼容:SWT组件不能绘制,AWT/Swing组件不能绘制。 有几种方法试图以其他方式解决这个问题(也可能有一些变化,但基本上有以下两种): 在SWT图像和AWT BuffereImage之间转换 他们都有缺点,没有满足我的期望: 第一种方法是将SWT转换为,由于为每个像素创建了一个新的实例,因此对大图像的性能较差 第二

  • 我正在制作一个简单的2D游戏。每次滴答,我都想检查一个效果队列,该队列将启动一个线程以获得特定效果(淡入淡出过渡、音频淡入淡出等)。例如,在菜单屏幕上按“播放”将向该队列添加“淡出”消息,该消息将被处理并启动一个线程,以在我的游戏面板上绘制一个黑色矩形,并增加阿尔法值。 我重写了绘制组件(),并将我的图形对象发送到我的游戏状态管理器,它将图形对象传递到当前状态的绘制()。我目前没有一个效果状态(也