2016-04-05 17 views
0

Ich schreibe ein Java-Programm, um das Mandelbrot-Set für meine einleitende Programmierklasse anzuzeigen. Ich glaube, ich habe die ganze Mathematik richtig eingerichtet, aber wenn ich versuche, das Fraktal zu zeichnen, bekomme ich nur eine feste Farbe. Ich habe die Mathematik getestet und es scheint, dass es funktionieren sollte. Ich habe über eine Stunde gesucht, aber ich habe nichts gefunden, was geholfen hat. Hier sind meine Klassen für komplexe Zahlen und tatsächlich die Mandelbrot-Menge zu erstellen: Komplexe ZahlenVerwenden eines BufferedImage zum Zeichnen der Mandelbrot-Set, nur eine durchgehende Farbe

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

Mandelbrots

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

Ich habe einige JUnit Tests durchgeführt und beide der oben genannten Klassen zu funktionieren scheint. Es könnte einen Fehler in meinen Tests geben, der zu einem Versehen geführt hat, aber nichts, was ich unterscheiden könnte. Es scheint mir, dass mein Problem mit meiner eigentlichen Erstellung des Bildes ist, das ist die Klasse unter:

Visuelle (Ich versuche zuerst mit nur zwei Farben diese Arbeit zu bekommen)

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

Hier meine Hauptmethode auch:

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

Ich bin wirklich ratlos. Was scheint zu geschehen ist, dass, wenn ich calcitMandelbrot() aufrufen, die Rückkehr immer gleich ist. Bei meinen Tests habe ich jedoch festgestellt, dass dies nicht der Fall ist. Ich habe nicht viel Erfahrung mit BufferedImages, also gibt es vielleicht einen Fehler darin, wie ich das benutze?

Da ich es so oft erwähnt habe, hier ist der Testcode, den ich auch verwendete. Ich weiß, dass dies wirklich nicht die richtige Form ist, oder zumindest nicht die Form, die mein Professor lehrte, aber ich war wirklich nur darauf konzentriert, das Problem zu finden.

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

Ich hoffe, das war nicht zu viel Code, um hier auf einmal zu kotzen. Vielen Dank für die Hilfe!

+0

Es gibt nur eine Lösung, die ich für dieses aktuelle Problem kenne: und das ist zu lernen, einen Debugger zu verwenden. –

+0

Beachten Sie, dass Sie einen eklatanten offensichtlichen Fehler haben. Ich bin mir nicht sicher, ob es Ihre Probleme verursacht, aber unabhängig davon muss es behoben werden: Sie machen wichtige Berechnungen innerhalb einer paintComponent-Methode, etwas, das Sie ** nie ** tun sollten. Mache diese in einem Hintergrundthread und zeige sie dann ** in paintComponent an. –

+0

[Zum Beispiel] (http://stackoverflow.com/questions/33859923/change-contents-of-bufferedimage-thenupdate-jframe-to-reflect-it/33860388#33860388). –

Antwort

1

ich diese Arbeit bekommen verwaltet. Als Hovercraft voller Aale vorgeschlagen, entfernte ich die Berechnungen von meiner paintComponent() -Methode. Stattdessen habe ich sie in der Hauptmethode gemacht, die Werte in einem 2D-Array gespeichert, wie Weather Vane vorgeschlagen, und meine VisualComponent-Klasse modifiziert, um das Array als Parameter beim Aufrufen des Konstruktors aufzunehmen. Die Berechnungen selbst waren auch fehlerhaft, mein anfängliches Vertrauen in sie ist fehl am Platz. Ich hatte ein Missverständnis, wie ich die Escape-Bedingung strukturieren sollte, da ich nicht erkannte, dass ich die Real- und Imaginärteile quadrieren sollte, dann addiere ich sie und vergleiche mit 4. Ich brauchte auch nicht den absoluten Wert von ihnen zu nehmen alle (seit dem Quadrieren des Wertes wird sichergestellt, dass es positiv ist). Schließlich initialisierte ich nicht jedes Mal, wenn die Methode aufgerufen wurde, die komplexen Zahlen, was ein Fehler war, auf den gpasch hinwies. Es gab einen Grund dafür, aber im Nachhinein war ich völlig dumm und dachte, dass es mehrere MandelbrotGenerator-Objekte geben würde und jeder die Methode nur einmal aufrufen würde. Ja, ich war super verwirrt.Meine Arbeits Code ist wie folgt:

Mandelbrot (I vollständig das Verfahren neu strukturiert, die die Berechnungen der Fall ist):

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

Haupt

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

Visuelle (meine aktuelle Farbauswahl sind willkürlich und waren nur mein eigenes Experiment)

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

Es gab keine Veränderung Es wurde zu meiner komplexen Zahlenklasse gemacht. Offensichtlich muss ich das Programm noch dazu bringen, etwas anderes zu tun, als nur das Basis-Image zu generieren, aber jetzt, wo ich alles klar gemacht habe, denke ich, dass ich das herausfinden kann. Danke nochmal an Hovercraft Full of Aels und Weather Vane für ihre hilfreichen Kommentare!

EDIT: Ich merke in meinem Code oben, dass es einige Instanzen gibt, wo ich 400 anstelle einer Variablen verwende, die die Größe des Rahmens enthält. Ich habe das bereits behoben, wollte nur sichergehen, dass es klar war, dass ich das übersehen habe. Here's an image of my result

2

Ihr Problem ist, dass Sie die z Punkte neu initialisieren nicht so die Berechnung, wie Sie sagen, stapfte ist:

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; 

} 
+0

Hmm, tut mir leid, aber ich verstehe das nicht ganz. Die Mandelbrot-Berechnungen erfordern, dass das Ergebnis der vorherigen Iteration in der nächsten Berechnung verwendet wird, was ich versuchte, indem ich z = currentCalc setzte; vor der Verwendung, um die nächste Berechnung zu berechnen.Gibt es etwas Spezifischeres, das ich vermisse? – ZMartin

+0

Ich komme Wochen später wieder und merke, dass du Recht hast, aber ich habe deine Formulierung einfach nicht verstanden. So leid! Ich werde den Antwort-Post mit Kredit für Sie bearbeiten! – ZMartin