2015-11-22 3 views
1

Я пытаюсь сделать рендерер Mandelbrot Set с графическим интерфейсом, в котором вы можете щелкнуть и перетащить, чтобы увеличить масштаб в определенной области. При запуске он будет выполнять начальные вычисления и рендеринг, но когда вы пытаетесь щелкнуть и перетащить, чтобы увеличить масштаб, консоль говорит, что он выполняет вычисления, но содержимое JFrame не обновляется.Изменить содержимое BufferedImage, а затем обновить JFrame, чтобы отразить его

Однако я даже не уверен, что он пересчитывается, потому что начальный расчет занимает около 8 секунд, но когда вы нажимаете/перетаскиваете для увеличения, это занимает около 6 мс.

Я разместил свой код ниже.

комплексных чисел класса

public class Complex { 
    private double real, imag; 

    // Constructors 
    public Complex(){ 
     real=0.0; 
     imag=0.0; 
    } 

    public Complex(double real, double imag) { 
     this.real=real; 
     this.imag=imag; 
    } 

    // add given complex number to this one, returning the Complex result 
    public Complex add(Complex other) { 
     return new Complex(this.real+other.real, this.imag+other.imag); 
    } 

    // multiply given complex number by this one, returning the Complex result 
    public Complex multiply(Complex other) { 
     return new Complex((this.real*other.real)-(this.imag*other.imag), (this.imag*other.real)+(this.real*other.imag)); 
    } 

    // get the magnitude of this complex number 
    public double getMagnitude() { 
     return Math.sqrt((real*real)+(imag*imag)); 
    } 
} 

Runnable MandelbrotTask Класс

public class MandelbrotTask implements Runnable { 
    private double x1, y1, x2, y2; 
    private int startCol, endCol, startRow, endRow, maxIters; 
    private int[][] iterCounts; 

     public MandelbrotTask(int maxIters, double x1, double y1, double x2, double y2, int startCol, int endCol, int startRow, int endRow, int[][] iterCounts) { 
      this.x1 = x1; 
      this.y1 = y1; 
      this.x2 = x2; 
      this.y2 = y2; 
      this.startCol = startCol; 
      this.endCol = endCol; 
      this.startRow = startRow; 
      this.endRow = endRow; 
      this.iterCounts = iterCounts; 
      this.maxIters=maxIters;   
} 

    @Override 
    public void run() { 
     for (int i = startRow; i < endRow; i++) { 
      for (int j = startCol; j < endCol; j++) { 
       Complex c = getComplex(i, j); 
       int iterCount = countIters(c); 
       iterCounts[i][j] = iterCount; 
      } 
     } 
    } 

    public Complex getComplex(int i, int j){ 
     //output image is 600 X 600 pixels 
     double incrementX; 
     double incrementY; 
     if(x2!=x1){ 
      incrementX=(Math.abs(x2-x1)/600); 
     } 
     else{ 
      throw new ArithmeticException("Error: area=0"); 
     } 
     if(y2!=y1){ 
      incrementY=(Math.abs(y2-y1)/600); 
     } 
     else{ 
      throw new ArithmeticException("Error: area=0"); 
     } 

     return new Complex(x1+((double)i*incrementX), y1+((double)j*incrementY)); 
    } 

    public int countIters(Complex c){ 
     Complex z=new Complex(0, 0); 
     int iters=0; 
     while(z.getMagnitude()<2 && iters<=maxIters){ 
      z=z.multiply(z).add(c); 
      iters++; 
     } 
     return iters; 
    } 
} 

Главная Мандельброт Класс

import java.awt.BasicStroke; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Point; 
import java.awt.Rectangle; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 
import java.io.BufferedOutputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.util.Scanner; 
import javax.imageio.ImageIO; 
import javax.swing.ImageIcon; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.SwingUtilities; 

public class Mandelbrot { 
    private static final int HEIGHT = 600; 
    private static final int WIDTH = 600; 
    private static final int maxIters=50000; 

    private static Rectangle zoomBox; 
    private static Point initialClick; 
    private static JLabel content; //bufferedImage will be put into this JLabel 
    private static int[][] iterCounts; 
    private static BufferedImage bufferedImage; //rendering will be written to this bufferedImage 
    private static JFrame frame; 

    public static void main(String[] args) throws IOException { 
     zoomBox=null; 
     Scanner keyboard = new Scanner(System.in); 

     double x1 = -2; 
     double y1 = -2; 
     double x2 = 2; 
     double y2 = 2; 
     /*System.out.print("Max iterations (16,581,375 supported): "); 
     int maxIters=50000; 
     if(maxIters>16581375){ 
      throw new UnsupportedOperationException("Error: Max Iterations: Overflow."); 
     } 
     System.out.print("Output filename: "); 
     String fileName = keyboard.next(); 
     if(!fileName.endsWith(".png") && !fileName.endsWith(".PNG")){ 
      fileName=fileName + ".png"; 
     }*/ 

     // TODO: create the rendering, save it to a file 
     iterCounts=new int[WIDTH][HEIGHT]; 
     recalculate(x1, y1, x2, y2, iterCounts); 

     bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB); 
     MouseAdapter listener = new MouseAdapter() { 
      @Override 
      public void mousePressed(MouseEvent e) { 
       handleMousePressed(e); 
      } 

      @Override 
      public void mouseDragged(MouseEvent e) { 
       handleMouseDragged(e); 
      } 

      @Override 
      public void mouseReleased(MouseEvent e) { 
       handleMouseReleased(e); 
      } 
     }; 
     content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, true))); 
     content.addMouseListener(listener); 
     content.addMouseMotionListener(listener); 

     /*OutputStream os = new BufferedOutputStream(new FileOutputStream(fileName)); 
     try { 
      ImageIO.write(bufferedImage, "PNG", os); 
     } finally { 
      os.close(); 
     }*/ 


     frame = new JFrame("Mandelbrot Viewer"); 
     frame.getContentPane().add(content); 
     frame.pack(); 
     frame.setSize(new Dimension(WIDTH, HEIGHT)); 
     frame.setResizable(false); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
    } 

    public static BufferedImage render(int[][] iterCounts, BufferedImage bufferedImage, Rectangle zoomBox, boolean updated){ 
     bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB); 
     Graphics g = bufferedImage.getGraphics(); 
     Graphics2D g2=(Graphics2D) g; 
     if(updated){ 
      for(int i=0; i<WIDTH; i++){ 
       for(int j=0; j<HEIGHT; j++){ 
        if(iterCounts[i][j]<maxIters){ 
         String hexCode= String.format("#%06x", (0xFFFFFF & (32*iterCounts[i][j]))); 
         g.setColor(Color.decode(hexCode)); 
        } 
        else{ 
         g.setColor(Color.CYAN); 
        } 
        g.drawLine(i, j, i, j); 
       } 
      } 
     } 
     else{ 
      if(zoomBox!=null){ 
       g2.setStroke(new BasicStroke(7)); 
       g2.draw(zoomBox); 
      } 
     } 

     return bufferedImage; 
    } 

    public static int[][] recalculate(double x1, double y1, double x2, double y2, int[][] iterCounts){ 
     MandelbrotTask[] tasks=new MandelbrotTask[4]; 
     tasks[0]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 0, HEIGHT/4, iterCounts); 
     tasks[1]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, HEIGHT/4, 2*(HEIGHT/4), iterCounts); 
     tasks[2]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 2*(HEIGHT/4), 3*(HEIGHT/4), iterCounts); 
     tasks[3]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 3*(HEIGHT/4), 4*(HEIGHT/4), iterCounts); 
     //parallelize computation 
     Thread[] threads=new Thread[4]; 
     for(int i=0; i<4; i++){ 
      threads[i]=new Thread(tasks[i]); 
     } 

     System.out.println("Working..."); 
     //start timer, start computation 
     long start=System.currentTimeMillis(); 
     for(int i=0; i<4; i++){ 
      threads[i].start(); 
     } 

     for(int i=0; i<4; i++){ 
      try { 
       threads[i].join(); 
      } catch (InterruptedException e) { 
       System.err.println("A thread was interrupted."); 
      } 
     } 
     //end timer 
     long end=System.currentTimeMillis(); 
     long elapsed=end-start; 
     System.out.println("Done."); 
     System.out.println("Took " + elapsed + " ms."); 

     return iterCounts; 
    } 

    protected static void handleMousePressed(MouseEvent e) { 
     initialClick=e.getPoint(); 
    } 

    protected static void handleMouseDragged(MouseEvent e) { 
     if(e.getX()>e.getY()){ 
      zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getX())); 
     } 
     else if(e.getY()>e.getX()){ 
      zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getY()), (int)(e.getY()-initialClick.getY())); 
     } 
     else{ 
      zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getY())); 
     } 
     content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false))); 
     content.repaint(); 
    } 

    protected static void handleMouseReleased(MouseEvent e) { 
     recalculate(initialClick.getX(), initialClick.getY(), e.getX(), e.getY(), iterCounts); 
     SwingUtilities.invokeLater(new Runnable(){ 
      @Override 
      public void run() { 
       zoomBox=null; 
       content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false))); 
       content.repaint(); 
      } 
     }); 
    } 
} 
+1

Плохая нить - вы вызываете 'join' в своих потоках из потока событий Swing, что почти наверняка заморозит ваш графический интерфейс. Используйте SwingWorker, а затем после получения уведомления от работника, когда это будет сделано, используйте его данные. Кроме того, вы сильно используете статические переменные в графическом интерфейсе. Создайте поля экземпляра компонентов GUI, а не статические поля. –

+0

@HovercraftFullOfEels, как вы используете SwingWorker? Я не знаком с ними. –

+0

Google на помощь: [Урок: параллелизм в качелях] (https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) и поиск этого сайта: [Поиск Google в StackOverflow] (https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=how+to+use+ a + SwingWorker + site: http:% 2F% 2Fstackoverflow.com% 2F) –

ответ

3

Во-первых, вы создаете новый JLabel с каждой повторной итерацией, и этот JLabel добавляется ни к чему.

Вместо этого используйте один и тот же JLabel, а скорее создайте новый ImageIcon и установите просматриваемый значок JLabel.

ImageIcon icon = new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false)); 
content.setIcon(icon); 

Вы также ничего не делаете с массивами int, возвращаемыми из пересчета.

Если не ваш handleMouseReleased метода есть:

iterCounts = recalculate(initialClick.getX(), initialClick.getY(), 
    e.getX(), e.getY(), iterCounts); 

Кроме того, вы до сих пор плохо нарезание резьбы - вы звоните присоединиться вашими нити в потоке событий Качелей, что-то почти наверняка заморозить GUI , Используйте SwingWorker, а затем после получения уведомления от работника, когда это будет сделано, используйте его данные. Кроме того, вы сильно используете статические переменные в графическом интерфейсе. Создайте поля экземпляра компонентов GUI, а не статические поля.

Есть несколько логических ошибок, которые я до сих пор найти Я боюсь ...


Вторая итерация программы - С расчетов Мандельброта:

enter image description here

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Point; 
import java.awt.Rectangle; 
import java.awt.Window; 
import java.awt.Dialog.ModalityType; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.util.concurrent.ExecutionException; 

import javax.swing.*; 

@SuppressWarnings("serial") 
public class Mandel2 extends JPanel { 
    private static final int GUI_HEIGHT = 600; 
    private static final int GUI_WIDTH = 600; 
    private static final int MAX_ITERS = 50000; 
    private BufferedImage image = new BufferedImage(GUI_WIDTH, GUI_HEIGHT, 
      BufferedImage.TYPE_INT_ARGB); 
    private Rectangle zoomRect; 
    private double myX0 = -2.5; 
    private double myY0 = -2.0; 
    private double myX1 = 1.5; 
    private double myY1 = 2.0; 
    private JDialog waitDialog; 

    public Mandel2() { 
     final MyMouse myMouse = new MyMouse(); 

     int delayStartingCalc = 2 * 1000; // 2 second delay 
     Timer timer = new Timer(delayStartingCalc, new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       addMouseListener(myMouse); 
       addMouseMotionListener(myMouse); 

       Rectangle myRect = new Rectangle(0, 0, GUI_WIDTH, GUI_HEIGHT); 
       createMandel(myRect); 
      } 
     }); 
     timer.setRepeats(false); 
     timer.start(); 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     if (isPreferredSizeSet()) { 
      return super.getPreferredSize(); 
     } 
     return new Dimension(GUI_WIDTH, GUI_HEIGHT); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     if (image != null) { 
      g.drawImage(image, 0, 0, this); 
     } 
     Graphics2D g2 = (Graphics2D) g; 
     if (zoomRect == null) { 
      return; 
     } 
     g2.setXORMode(Color.gray); 
     g2.draw(zoomRect); 
    } 

    private double screenToLogicalX(double screenX) { 
     return myX0 + (screenX * (myX1 - myX0))/GUI_WIDTH; 
    } 

    private double screenToLogicalY(double screenY) { 
     return myY0 + ((GUI_HEIGHT - screenY) * (myY1 - myY0))/GUI_HEIGHT; 
    } 

    private void createMandel(Rectangle myRect) { 
     double x0 = screenToLogicalX(myRect.x); 
     double y0 = screenToLogicalY(myRect.y + myRect.height); 
     double x1 = screenToLogicalX(myRect.x + myRect.width); 
     double y1 = screenToLogicalY(myRect.y); 

     myX0 = x0; 
     myY0 = y0; 
     myX1 = x1; 
     myY1 = y1; 

     MandelWorker mandelWorker = new MandelWorker(MAX_ITERS, x0, y0, x1, y1); 
     mandelWorker.addPropertyChangeListener(new MandelWorkerListener()); 
     mandelWorker.execute(); 
     if (waitDialog == null) { 
      Window win = SwingUtilities.getWindowAncestor(Mandel2.this); 
      JProgressBar jProgressBar = new JProgressBar(); 
      jProgressBar.setIndeterminate(true); 
      waitDialog = new JDialog(win, "Please Wait", ModalityType.APPLICATION_MODAL); 
      waitDialog.add(jProgressBar); 
      waitDialog.pack(); 
      waitDialog.setLocationRelativeTo(win); 
     } 
     waitDialog.setVisible(true); 
    } 

    private class MyMouse extends MouseAdapter { 
     private Point p; 

     @Override 
     public void mousePressed(MouseEvent e) { 
      p = e.getPoint(); 
     } 

     public void mouseDragged(MouseEvent e) { 
      zoomRect = createRect(e); 
      repaint(); 
     }; 

     @Override 
     public void mouseReleased(MouseEvent e) { 
      zoomRect = createRect(e); 
      repaint(); 
      createMandel(zoomRect); 
     } 

     private Rectangle createRect(MouseEvent e) { 
      int x = Math.min(p.x, e.getX()); 
      int y = Math.min(p.y, e.getY()); 
      int width = Math.abs(p.x - e.getX()); 
      int height = Math.abs(p.y - e.getY()); 
      return new Rectangle(x, y, width, height); 
     } 
    } 

    private class MandelWorkerListener implements PropertyChangeListener { 
     @Override 
     public void propertyChange(PropertyChangeEvent evt) { 
      if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
       waitDialog.setVisible(false); 
       waitDialog.dispose(); 
       MandelWorker worker = (MandelWorker) evt.getSource(); 
       try { 
        image = worker.get(); 
        zoomRect = null; 
        repaint(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } catch (ExecutionException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    private class MandelWorker extends SwingWorker<BufferedImage, Void> { 
     private int maxIters; 
     private double x1; 
     private double y1; 
     private double x2; 
     private double y2; 

     public MandelWorker(int maxIters, double x1, double y1, double x2, double y2) { 
      this.maxIters = maxIters; 
      this.x1 = x1; 
      this.y1 = y1; 
      this.x2 = x2; 
      this.y2 = y2; 
     } 

     @Override 
     protected BufferedImage doInBackground() throws Exception { 
      int[][] iterGrid = new int[GUI_HEIGHT][GUI_WIDTH]; 
      for (int i = 0; i < GUI_HEIGHT; i++) { 
       double y = y1 + i * (y2 - y1)/GUI_HEIGHT; 
       for (int j = 0; j < GUI_WIDTH; j++) { 
        double x = x1 + j * (x2 - x1)/GUI_WIDTH; 
        int iIndex = GUI_HEIGHT - i - 1; 
        iterGrid[iIndex][j] = calcMandel(x, y); 
       } 
      } 

      return render(iterGrid); 
     } 

     private BufferedImage render(int[][] iterGrid) { 
      int w = GUI_WIDTH; 
      int h = GUI_HEIGHT; 
      BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 
      Graphics2D g2 = img.createGraphics(); 
      for (int i = 0; i < w; i++) { 
       for (int j = 0; j < h; j++) { 
        if (iterGrid[i][j] < maxIters) { 
         String hexCode = String.format("#%06x", (0xFFFFFF & (32 * iterGrid[i][j]))); 
         g2.setColor(Color.decode(hexCode)); 
        } else { 
         g2.setColor(Color.CYAN); 
        } 
        g2.drawLine(j, i, j, i); 
       } 
      } 
      g2.dispose(); 
      return img; 
     } 

     private int calcMandel(double x, double y) { 
      Complex c = new Complex(x, y); 
      Complex z = new Complex(); 
      int iters = 0; 

      while (z.getMagnitude() < 2 && iters <= maxIters) { 
       z = z.multiply(z).add(c); 
       iters++; 
      } 
      return iters; 
     } 
    } 

    private class Complex { 
     private double real, imag; 

     // Constructors 
     public Complex() { 
      real = 0.0; 
      imag = 0.0; 
     } 

     public Complex(double real, double imag) { 
      this.real = real; 
      this.imag = imag; 
     } 

     // add given complex number to this one, returning the Complex result 
     public Complex add(Complex other) { 
      return new Complex(this.real + other.real, this.imag + other.imag); 
     } 

     // multiply given complex number by this one, returning the Complex 
     // result 
     public Complex multiply(Complex other) { 
      return new Complex((this.real * other.real) - (this.imag * other.imag), 
        (this.imag * other.real) + (this.real * other.imag)); 
     } 

     // get the magnitude of this complex number 
     public double getMagnitude() { 
      return Math.sqrt((real * real) + (imag * imag)); 
     } 
    } 

    private static void createAndShowGui() { 
     Mandel2 mainPanel = new Mandel2(); 

     JFrame frame = new JFrame("Mandel2"); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     frame.getContentPane().add(mainPanel); 
     frame.setResizable(false); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       createAndShowGui(); 
      } 
     }); 
    } 
} 

Вторая итерация - она ​​выполняет вычисления, но не очень эффективна.

+0

Вы имеете в виду использование SwingWorker вместо моего класса MandelbrotTask? –

+0

@ mjones.udri: да, я бы сделал это, но не добрался до этой части. Ваш код плотный, поэтому мне трудно найти проблемы. –

+0

Другой вопрос: можете ли вы распараллелить SwingWorkers? Этот проект предназначен для присвоения класса и требует распараллеливания. –

 Смежные вопросы

  • Нет связанных вопросов^_^