8
\$\begingroup\$

I've been working on a version of Conway's Game of Life for about a week now. It has loads of features like drawing onto the screen and saving/loading images. The problem is that ever since I added mouse support, it seems to start running pretty slowly. I don't know if it is due to the mouse or if it's something else. I'm a pretty newbie coder so I'd really like some help in optimizing this.

Just noting: I didn't include any imports due to the character limit.

Main Class

public class MainGame extends JFrame{    
    static int size = 721;
    static JFrame frame = new JFrame("Game of Life");
    static Life life = new Life(size);
    Random r = new Random();
    JLabel lblCellCount = new JLabel("Cell Count");
    JLabel lblGeneration = new JLabel("Generation");
    JSlider slider = new JSlider();
    JButton play = new JButton("Pause");
    JLabel lblSpeed = new JLabel("Speed: (10) ups");
    public SaveLoad sl = new SaveLoad();
    String[] patterns = sl.loadImages(new File(".\\Patterns\\"));
    final JFileChooser fc = new JFileChooser();
    final DefaultComboBoxModel model;
    final JComboBox<?> cmboPatterns;
    Point start;

    public void runGameLoop(){
        life.running = true;
        Thread loop = new Thread() {
            public void run(){
                gameLoop();
            }
        };
        loop.start();
    }
    public void gameLoop(){
        System.out.println("Started");
        final int MAX_UPDATES_BEFORE_RENDER = 5;
        double lastUpdateTime = System.nanoTime();
        double lastRenderTime = System.nanoTime();

        int lastSecondTime = (int) (lastUpdateTime / 1000000000);
        while(life.running){
            final double GAME_HERTZ = (double) slider.getValue();
            final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
            final double TARGET_FPS = (int) GAME_HERTZ;
            final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
            double now = System.nanoTime();
            int updateCount = 0;
            if (!life.paused){
                while(now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER){
                    // update activeCells here
                    changeText(life.countCells());
                    changeText(life.generation, true);
                    life.checkCells();
                    lastUpdateTime += TIME_BETWEEN_UPDATES;
                    updateCount++;
                }
                if (now - lastUpdateTime > TIME_BETWEEN_UPDATES){
                    lastUpdateTime = now - TIME_BETWEEN_UPDATES;
                }

                // render to screen here
                life.fillCells(life.activeCells);
                lastRenderTime = now;

                int thisSecond = (int) (lastUpdateTime / 1000000000);
                if (thisSecond > lastSecondTime){
                    lastSecondTime = thisSecond;
                }

                while(now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES){
                    Thread.yield();
                    try{
                        Thread.sleep(1);
                    } catch(Exception e){
                    }
                    now = System.nanoTime();
                }
            }
        }
        // System.out.println("Generations " + generation);
        //Thread.yield();
    }
    public static void main(String[] args){
        try{
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch(Throwable e){
            e.printStackTrace();
        }
        EventQueue.invokeLater(new Runnable() {
            public void run(){
                try{
                    MainGame frame = new MainGame();
                    frame.setVisible(true);
                    frame.setResizable(false);
                } catch(Exception e){
                    e.printStackTrace();
                }
            }
        });
    }

    public void changeText(int cells){
        lblCellCount.setText("Live Cells: " + String.valueOf(cells));
    }

    public void changeText(int generation, boolean gen){
        lblGeneration.setText("Generation: " + String.valueOf(generation));
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public MainGame(){
        pack();
        setTitle("Game Of Life");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().setLayout(null);
        addMouseMotionListener(new MouseMotionListener() {
            @Override
            public void mouseDragged(MouseEvent e){
                int xCell = (e.getX() - getInsets().left) / life.CELL_SIZE;
                int yCell = (e.getY() - getInsets().top) / life.CELL_SIZE;

                if (life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && xCell >= 0 && yCell >= 0 && !life.activeCells[xCell][yCell] && !SwingUtilities.isRightMouseButton(e)){
                    //life.fillCell(xCell, yCell, 0 - r.nextInt(16777215));
                    life.fillLine((int) (start.getX() - getInsets().left) / life.CELL_SIZE, (int) (start.getY() - getInsets().top) / life.CELL_SIZE, (int) (e.getPoint().getX() - getInsets().left) / life.CELL_SIZE, (int) (e.getPoint().getY() - getInsets().top) / life.CELL_SIZE, 0 - r.nextInt(16777215));
                    start = e.getPoint();
                    changeText(life.countCells());
                } else if (!life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && xCell >= 0 && yCell >= 0 && !life.activeCells[xCell][yCell] && !SwingUtilities.isRightMouseButton(e)){
                    //life.fillCell(xCell, yCell, -16777215);
                    life.fillLine((int) (start.getX() - getInsets().left) / life.CELL_SIZE, (int) (start.getY() - getInsets().top) / life.CELL_SIZE, (int) (e.getPoint().getX() - getInsets().left) / life.CELL_SIZE, (int) (e.getPoint().getY() - getInsets().top) / life.CELL_SIZE, -16777215);
                    start = e.getPoint();
                    //System.out.println("main x " + (e.getPoint().getX() - getInsets().left) / life.CELL_SIZE + " main y " + (e.getPoint().getY() - getInsets().top) / life.CELL_SIZE);
                    changeText(life.countCells());
                } else if (life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && SwingUtilities.isRightMouseButton(e)){
                    //life.emptyCell(xCell, yCell);
                    life.emptyLine((int) (start.getX() - getInsets().left) / life.CELL_SIZE, (int) (start.getY() - getInsets().top) / life.CELL_SIZE, (int) (e.getPoint().getX() - getInsets().left) / life.CELL_SIZE, (int) (e.getPoint().getY() - getInsets().top) / life.CELL_SIZE);
                    start = e.getPoint();
                    changeText(life.countCells());
                } else if (!life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && SwingUtilities.isRightMouseButton(e)){
                    //life.emptyCell(xCell, yCell);
                    life.emptyLine((int) (start.getX() - getInsets().left) / life.CELL_SIZE, (int) (start.getY() - getInsets().top) / life.CELL_SIZE, (int) (e.getPoint().getX() - getInsets().left) / life.CELL_SIZE, (int) (e.getPoint().getY() - getInsets().top) / life.CELL_SIZE);
                    start = e.getPoint();
                    changeText(life.countCells());
                }
            }

            @Override
            public void mouseMoved(MouseEvent e){
            }
        });
        addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e){
            }

            @Override
            public void mouseEntered(MouseEvent e){
            }

            @Override
            public void mouseExited(MouseEvent e){
            }

            @Override
            public void mousePressed(MouseEvent e){
                int xCell = (e.getX() - getInsets().left) / life.CELL_SIZE;
                int yCell = (e.getY() - getInsets().top) / life.CELL_SIZE;
                if (life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && !SwingUtilities.isRightMouseButton(e)){
                    life.fillCell(xCell, yCell, 0 - r.nextInt(16777215));
                    start = e.getPoint();
                    changeText(life.countCells());
                } else if (!life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && !SwingUtilities.isRightMouseButton(e)){
                    life.fillCell(xCell, yCell, -16777215);
                    start = e.getPoint();
                    changeText(life.countCells());
                } else if (life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && SwingUtilities.isRightMouseButton(e)){
                    life.emptyCell(xCell, yCell);
                    start = e.getPoint();
                    changeText(life.countCells());
                } else if (!life.colour && xCell < life.activeCells.length && yCell < life.activeCells.length && SwingUtilities.isRightMouseButton(e)){
                    life.emptyCell(xCell, yCell);
                    start = e.getPoint();
                    changeText(life.countCells());
                }
            }

            @Override
            public void mouseReleased(MouseEvent e){
            }
        });

        life.setBounds(0, 0, size, size);
        getContentPane().add(life);

        JPanel customisePanel = new JPanel();
        customisePanel.setBounds(0, size, size, 35);
        getContentPane().add(customisePanel);
        customisePanel.setLayout(null);

        final JButton stop = new JButton("Restart");
        stop.setBounds(8, 8, 75, 23);
        stop.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e){
                life.reset();
                //life.generation = 0;
                changeText(life.countCells());
                changeText(0, true);
            }
        });
        customisePanel.add(stop);

        play.setBounds(90, 8, 75, 23);
        play.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0){
                if (life.running){
                    life.running = false;
                    play.setText("Play");
                    changeText(life.countCells());
                } else{
                    life.running = true;
                    runGameLoop();
                    play.setText("Pause");
                }
            }
        });
        customisePanel.add(play);

        JButton clear = new JButton("Clear");
        clear.setBounds(172, 8, 75, 23);
        clear.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e){
                life.running = false;
                life.generation = 0;
                changeText(0, true);
                play.setText("Play");
                life.cls();
                life.cls();
                changeText(life.countCells());
            }
        });
        customisePanel.add(clear);

        JButton save = new JButton("Save...");
        save.setBounds(254, 8, 75, 23);
        save.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e){
                fc.setCurrentDirectory(new File(".\\Patterns\\"));
                life.running = false;
                changeText(0, true);
                play.setText("Play");
                changeText(life.countCells());
                int returnVal = fc.showSaveDialog(MainGame.this);
                if (returnVal == JFileChooser.APPROVE_OPTION){
                    File file = fc.getSelectedFile();
                    fc.setCurrentDirectory(file);
                    sl.saveImage(life.activeCells, size / life.CELL_SIZE, file);
                }
                cmboPatterns.removeAllItems();
                patterns = sl.loadImages(new File(".\\Patterns\\"));
                orderStrings();
                for (int i = 0; i < patterns.length; i++){
                    model.addElement(patterns[i]);
                }
            }
        });
        customisePanel.add(save);

        // add the colour checkbox
        JCheckBox colour = new JCheckBox("Colourful");
        colour.setBounds(size - 74, 8, 74, 23);
        colour.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e){
                if (e.getStateChange() == ItemEvent.SELECTED){
                    life.colourCells();
                } else{
                    life.deColourCells();
                }
            }
        });
        customisePanel.add(colour);

        orderStrings();

        model = new DefaultComboBoxModel(patterns);

        cmboPatterns = new JComboBox<Object>(model);
        cmboPatterns.setBounds(size - colour.getWidth() - 210, 9, 120, 22);
        cmboPatterns.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e){
                for (int i = 0; i < patterns.length; i++){
                    if (i == cmboPatterns.getSelectedIndex()){
                        life.cls();
                        try{
                            Thread.sleep(1);
                        } catch(InterruptedException e1){
                            e1.printStackTrace();
                        }
                        life.cls();
                        life.generation = 0;
                        changeText(life.generation, true);
                        life.fillCells(sl.loadImage(patterns[i], life.activeCells, size / life.CELL_SIZE));
                    }
                }
            }
        });
        customisePanel.add(cmboPatterns);

        lblCellCount.setBounds(size - colour.getWidth() - 80, 12, 100, 14);
        customisePanel.add(lblCellCount);

        lblGeneration.setBounds(336, 12, 100, 14);
        lblGeneration.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent arg0){
            }

            @Override
            public void mouseEntered(MouseEvent arg0){
            }

            @Override
            public void mouseExited(MouseEvent arg0){
            }

            @Override
            public void mousePressed(MouseEvent arg0){
                life.generation = 0;
                changeText(0, true);
            }

            @Override
            public void mouseReleased(MouseEvent arg0){
            }
        });
        customisePanel.add(lblGeneration);

        JPanel panel = new JPanel();
        panel.setLayout(null);
        panel.setBounds(0, size + customisePanel.getHeight(), size, 46);
        getContentPane().add(panel);
        slider.setSnapToTicks(true);
        slider.setPaintTicks(true);

        slider.setValue(10);
        slider.setMinimum(1);
        slider.setMaximum(50);
        slider.setBounds(0, 0, size, 28);
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent arg0){
                lblSpeed.setText("Speed: (" + String.valueOf(((JSlider) arg0.getSource()).getValue()) + ") ups");
                slider.repaint();
            }
        });
        panel.add(slider);

        lblSpeed.setHorizontalAlignment(SwingConstants.CENTER);
        lblSpeed.setBounds(size / 2 - (80 / 2), 26, 80, 14);
        panel.add(lblSpeed);
        setBounds(0, 0, size + getInsets().left * 2 - 2, size + panel.getHeight() + customisePanel.getHeight() + 30);
        // start the game
        runGameLoop();
    }

    /*
     * File.toString() returns a weird filepath so this changes that
     * to something i can easily understand and manipulate.
     */
    public void orderStrings(){
        for (int i = 0; i < patterns.length; i++){
            if (patterns[i].length() > 2){
                if (patterns[i].substring(patterns[i].length() - 4).equals(".png")){
                    patterns[i] = patterns[i].substring(0, patterns[i].length() - 4);
                }
                if (patterns[i].substring(0, 11).equals(".\\Patterns\\")) patterns[i] = patterns[i].substring(11);
            }
        }
    }
}

Class that handles the actual game panel

public class Life extends JPanel{
    //idk why this is here, eclipse puts it in automatically
    private static final long serialVersionUID = 1L;

    private BufferedImage canvas;
    private Random r = new Random();
    private boolean[][] tempArray;
    boolean[][] activeCells;
    public final int CELL_SIZE = 8;
    public int generation = 0;
    public int cellCount = 0;
    public boolean colour = false;
    public boolean running = false;
    public boolean paused = false;

    /*
     * Life Constructor, creates the main image that gets put onto the JPanel
     */
    public Life(int size){
        canvas = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
        fillCanvas(Color.WHITE);
        activeCells = new boolean[canvas.getWidth() / CELL_SIZE][canvas.getHeight() / CELL_SIZE];
        tempArray = new boolean[canvas.getWidth() / CELL_SIZE][canvas.getHeight() / CELL_SIZE];
        activeCells = randArray(activeCells);
        //drawGrid(CELL_SIZE, CELL_SIZE);
        fillCells(activeCells);
        //printArray(activeCells);
    }

    /*
     * Resets the game with a full set of random data
     */
    public void reset(){
        //fillCanvas(Color.WHITE);
        activeCells = randArray(activeCells);
        generation = 0;
        //drawGrid(CELL_SIZE, CELL_SIZE);
        fillCells(activeCells);
    }

    /*
     * Uses a temporary version of the activeCells array (tempCells)
     * and updates it by checking through the neighbours method.
     */
    public void checkCells(){
        int size = activeCells.length;
        boolean[][] tempCells = new boolean[size][size];

        for (int i = 0; i < size; i++){
            for (int j = 0; j < size; j++){
                tempCells[i][j] = activeCells[i][j];
            }
        }
        for (int row = 0; row < activeCells.length; row++){
            for (int col = 0; col < activeCells[row].length; col++){
                int n = neighbours(row, col);

                if (n > 3 || n < 2) tempCells[row][col] = false;
                else if (n == 3) tempCells[row][col] = true;
                else tempCells[row][col] = activeCells[row][col];
            }
        }
        activeCells = tempCells;
        generation++;
    }

    /*
     * Loops through a 3x3 grid around the selected cell (row, col)
     * and returns the amount of true cells (no. of "neighbours")
     */
    public int neighbours(int row, int col){
        int count = 0;
        for (int i = row - 1; i <= row + 1; i++){
            for (int j = col - 1; j <= col + 1; j++){
                try{
                    if (activeCells[i][j] == true && (i != row || j != col)){
                        count++;
                    }
                } catch(ArrayIndexOutOfBoundsException f){
                    continue;
                }
            }
        }
        return count;
    }

    /*
     * Returns the number of true values in activeCells
     */
    public int countCells(){
        int count = 0;
        for (int i = 0; i < activeCells.length; i++){
            for (int j = 0; j < activeCells[i].length; j++){
                if (activeCells[i][j]) count++;
            }
        }
        cellCount = count;
        return count;
    }

    /*
     * Loops through the given array and returns it filled with random data
     */
    public boolean[][] randArray(boolean[][] array){
        for (int i = 0; i < array.length; i++){
            for (int j = 0; j < array[i].length; j++){
                if (r.nextInt(5) == 1) array[i][j] = true;
                else array[i][j] = false;
            }
        }
        return array;
    }

    /*
     * Fills all cells on screen with data from array. Also checks whether to
     * colour each cell or not.
     */
    public void fillCells(boolean[][] array){
        for (int i = 0; i < array.length; i++){
            for (int j = 0; j < array[i].length; j++){
                if (colour && !tempArray[i][j] && array[i][j]) fillCell(i, j, 0 - r.nextInt(16777215));
                else if (array[i][j] && !colour) fillCell(i, j, Color.BLACK.getRGB());
                else if (!array[i][j]) emptyCell(i, j);
                tempArray[i][j] = array[i][j];
            }
        }
    }

    //Interpolates between two points because just drawing onto the screen didn't update fast enough
    public void fillLine(int x1, int y1, int x2, int y2, int colour){
        double m;
        if (x2 - x1 == 0){
            if (y1 < y2){
                for (int i = y1; i < y2; i++){
                    fillCell(x2, i, colour);
                }
            } else{
                for (int i = y2; i < y1; i++){
                    fillCell(x2, i, colour);
                    //System.out.println("xFilling point (" + x2 + ", " + i + ")");
                }
            }
        } else{
            double dx = x1 - x2;
            double dy = y1 - y2;
            m = dy / dx;
            double c = y1 - (m * x1);
            if (x1 < x2 && m >= 1){
                for (int i = y1; i < y2; i++){
                    double x = (i - c) / m;
                    fillCell((int) x, i, colour);
                    //System.out.println("Filling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                }
            } else if (x1 < x2 && m <= 1 && m >=-1){
                for (int i = x1; i < x2; i++){
                    double y = (i * m) + c;
                    fillCell(i, (int) y, colour);
                    //System.out.println("Filling point (" + (i) + ", " + (int) y + "). gradient: " + m);
                }
            } else{
                for (int i = y2; i < y1; i++){
                    double x = (i - c) / m;
                    fillCell((int) x, i, colour);
                    //System.out.println("yFilling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                }
            }

                if (y1 < y2 && m <= -1){
                    for (int i = y1; i < y2; i++){
                        double x = (i - c) / m;
                        fillCell((int) x, i, colour);
                        //System.out.println("yFilling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                    }
                } else if (y2 < y1 && m >= 1){
                    for (int i = y2; i < y1; i++){
                        double x = (i - c) / m;
                        fillCell((int) x, i, colour);
                        //System.out.println("yFilling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                    }
                } else{
                    for (int i = x2; i < x1; i++){
                        double y = (i * m) + c;
                        fillCell(i, (int) y, colour);
                        //System.out.println("pFilling point (" + (i) + ", " + (int) y + "). gradient: " + m);
                    }
                }
            }
        }


    /*
     * Colours all current active cells
     */
    public void colourCells(){
        for (int i = 0; i < activeCells.length; i++){
            for (int j = 0; j < activeCells[i].length; j++){
                if (activeCells[i][j]) fillCell(i, j, 0 - r.nextInt(16777215));
            }
        }
        colour = true;
    }

    /*
     * Turns all current active cells black
     * 
     * Could have put both colourCells and deColourCells together in one with a parameter
     * but it would have ended up being the same if not more code anyway.
     */
    public void deColourCells(){
        for (int i = 0; i < activeCells.length; i++){
            for (int j = 0; j < activeCells[i].length; j++){
                if (activeCells[i][j]) fillCell(i, j, -16777215);
            }
        }
        colour = false;
    }

    /*
     * Colours the cell at (x, y) white and makes the correspoding activeCells = false
     */
    public void emptyCell(int x, int y){
        int colour = Color.WHITE.getRGB();
        for (int i = 0; i < CELL_SIZE; i++){ //CELL_SIZE-1 for grid
            for (int j = 0; j < CELL_SIZE; j++){ //same here
                canvas.setRGB(x * CELL_SIZE + 1 + i, y * CELL_SIZE + 1 + j, colour);
            }
        }
        activeCells[x][y] = false;
        repaint();
    }

    /*
     * Clears the screen by calling emtpyCell and making activeCells false;
     * could probably remove the emptyCell part and just have it paint the
     * whole screen white. This was for when i had a grid in aswell.
     * 
     * edit: done
     */
    public void cls(){
        for (int i = 0; i < activeCells.length; i++){
            for (int j = 0; j < activeCells[i].length; j++){
                activeCells[i][j] = false;
                //emptyCell(i, j);
            }
        }
        fillCanvas(Color.WHITE);
    }

    /*
     * Fills the set cell with the set colour
     */
    public void fillCell(int x, int y, int rgb){
        //System.out.println(Color.BLACK.getRGB());
        //int colour = Color.BLACK.getRGB();
        for (int i = 0; i < CELL_SIZE; i++){ //CELL_SIZE-1 for grid
            for (int j = 0; j < CELL_SIZE; j++){ //same here
                canvas.setRGB(x * CELL_SIZE + 1 + i, y * CELL_SIZE + 1 + j, rgb);
            }
        }
        activeCells[x][y] = true;
        repaint();
    }

    /*
     * Draws a grid onto the screen with the given cell width and height
     */
    public void drawGrid(int height, int width){
        int colour = Color.BLACK.getRGB();
        for (int i = 0; i < canvas.getWidth(); i++){
            for (int j = 0; j < canvas.getHeight(); j++){
                if (j % width == 0) canvas.setRGB(i, j, colour);
                if (i % height == 0) canvas.setRGB(i, j, colour);
            }
        }
        repaint();
    }

    /*
     * Sets the size of the JPanel
     */
    public Dimension getPreferredSize(){
        return new Dimension(canvas.getWidth(), canvas.getHeight());
    }

    /*
     * Handles painting to the canvas
     */
    public void paintComponent(Graphics g){
        super.paintComponents(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.drawImage(canvas, null, null);
    }

    /*
     * Fills the entire canvas with the given colour
     */
    public void fillCanvas(Color c){
        int colour = c.getRGB();
        for (int x = 0; x < canvas.getWidth(); x++){
            for (int y = 0; y < canvas.getHeight(); y++){
                canvas.setRGB(x, y, colour);
            }
        }
        repaint();
    }

    /*
     * Prints the array to the console, for debugging.
     */
    public void printArray(boolean[][] array){
        for (int i = 0; i < array.length; i++){
            for (int j = 0; j < array[i].length; j++){
                if (array[i][j] == true) System.out.print("1 ");
                if (array[i][j] == false) System.out.print("0 ");
            }
            System.out.println();
        }
    }

    public void emptyLine(int x1, int y1, int x2, int y2){
        double m;
        if (x2 - x1 == 0){
            if (y1 < y2){
                for (int i = y1; i < y2; i++){
                    emptyCell(x2, i);
                }
            } else{
                for (int i = y2; i < y1; i++){
                    emptyCell(x2, i);
                    //System.out.println("Filling point (" + x2 + ", " + i + ")");
                }
            }
        } else{
            double dx = x1 - x2;
            double dy = y1 - y2;
            m = dy / dx;
            double c = y1 - (m * x1);
            if (x1 < x2 && m >= 1){
                for (int i = y1; i < y2; i++){
                    double x = (i - c) / m;
                    emptyCell((int) x, i);
                    //System.out.println("yFilling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                }
            } else if (x1 < x2 && m <= 1 && m >=-1){
                for (int i = x1; i < x2; i++){
                    double y = (i * m) + c;
                    emptyCell(i, (int) y);
                    //System.out.println("Filling point (" + (i) + ", " + (int) y + "). gradient: " + m);
                }
            } else{
                for (int i = y2; i < y1; i++){
                    double x = (i - c) / m;
                    emptyCell((int) x, i);
                    //System.out.println("yFilling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                }
            }

                if (y1 < y2 && m <= -1){
                    for (int i = y1; i < y2; i++){
                        double x = (i - c) / m;
                        emptyCell((int) x, i);
                        //System.out.println("yFilling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                    }
                } else if (y2 < y1 && m >= 1){
                    for (int i = y2; i < y1; i++){
                        double x = (i - c) / m;
                        emptyCell((int) x, i);
                        //System.out.println("yFilling point (" + (i) + ", " + (int) x + "). gradient: " + m);
                    }
                } else{
                    for (int i = x2; i < x1; i++){
                        double y = (i * m) + c;
                        emptyCell(i, (int) y);
                        //System.out.println("pFilling point (" + (i) + ", " + (int) y + "). gradient: " + m);
                    }
                }
            }
    }
}

Class for handling Saving and loading

public class SaveLoad{
    private BufferedImage canvas;
    private File f;

    /*
     * Saves the current activeCells array to a png by first
     * creating a BufferedImage of the data and then writing
     * it to file.
     */
    public void saveImage(boolean[][] array, int size, File file){
        canvas = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
        for (int i = 0; i < size; i++){
            for (int j = 0; j < size; j++){
                if (array[i][j]) canvas.setRGB(i, j, Color.BLACK.getRGB());
                else canvas.setRGB(i, j, Color.WHITE.getRGB());
            }
        }
        try{
            File outputFile = new File(file.toString() + ".png");
            ImageIO.write(canvas, "png", outputFile);
            System.out.println(file.toString() + ".png Save Successfully!");
        } catch(IOException e){
            System.out.println("Something went wrong...");
        }
    }

    /*
     * Returns all pngs found in the current directory in the
     * format of an array of Strings
     */
    public String[] loadImages(File dir){
        String[] files;
        File[] all = dir.listFiles();
        int pngs = 0;
        for (int i = 0; i < all.length; i++){
            if (all[i].toString().indexOf(".png") != -1){
                pngs++;
            }
        }
        files = new String[pngs];
        pngs = 0;
        for (int i = 0; i < all.length; i++){
            if (all[i].toString().indexOf(".png") != -1){
                files[pngs] = all[i].toString();
                pngs++;
            }
        }
        return files;
    }

    /*
     * Converts a png into a 2d array of booleans which can then
     * be used to fill cells on the screen.
     */
    public boolean[][] loadImage(String file, boolean[][] array, int size){
        f = new File(".\\Patterns\\" + file + ".png");
        try{
            canvas = ImageIO.read(f);
        } catch(IOException e){
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        for (int i = 0; i < size; i++){
            for (int j = 0; j < size; j++){
                if (canvas.getRGB(i, j) == Color.BLACK.getRGB()) array[i][j] = true;
            }
        }
        return array;
    }
}

Sorry for including all of my code, but it's because I don't really know where the problem lies, and it could be all over.

\$\endgroup\$
  • \$\begingroup\$ If you comment-out the mouse-support code, does it run quickly again? \$\endgroup\$ – ChrisW Jan 24 '14 at 23:40
  • 4
    \$\begingroup\$ MouseMotionListener is likely eating a lot of CPU cycles. Would a simple click listener be good enough? \$\endgroup\$ – Teresa Carrigan Jan 25 '14 at 0:46
  • \$\begingroup\$ @TeresaCarrigan Expand a little on that and you have a nice answer to this question. \$\endgroup\$ – Simon Forsberg Jan 25 '14 at 11:02
  • \$\begingroup\$ And to anyone and everyone: It wouldn't hurt to review the rest of the code here as well, there are probably a lot of things that can be cleaned up... \$\endgroup\$ – Simon Forsberg Jan 25 '14 at 11:03
  • \$\begingroup\$ Commenting out the mouse code doesn't seem to affect the speed of the program. @TeresaCarrigan I don't know how i would put the complicated mouse support code into another method because it requires the use of the MouseEvent e, also I like the fact that I can draw on the screen while it is running so pausing, drawing, then running again isn't really what i want (which you can do at the moment, but you can also do it live). \$\endgroup\$ – SullyJHF Jan 25 '14 at 23:40
4
\$\begingroup\$

As @ChrisW mentioned: if you comment out the mouse support code, does it run quickly again? If this is the case, then changing from using a MouseMotionListener to a simple MouseListener, and check for a click instead of a drag.

Another thing that might help is if you move complicated code out of the body of the listener method. Just making a separate method to do the task and having a call to this method will make the code easier to read, but if you are still seeing problems with the code running slowly you may want to have the listener method spawn a thread to do the updating.

Another possibility is to have two modes of running the simulation. Add a pause button which stops the generations and enables the mouse listener. After the user makes changes, she can press a resume button which disables the mouse listener. This makes the logic easier as well, since you don't have to worry about cells changing state while you are trying to calculate the next generation.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.