1

I have coded up a simple simulation where 2 balls moving at different speeds try to move to the middle of the frame and then move back to their starting positions. At the end a specified, totalTime, I end the simulation and I record this data.

My question is, is it possible to loop through and run multiple simulations automatically? Currently, when my totalTime is up, the animation just freezes but the window doesn't close. Ideally I guess, I'd like to see the old window close and a new window pop up with new speeds for the different balls.

So my code looks something like this:

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

        @Override
        public void run() {
            double rSpeed = 0;
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            frame.setSize(500, 500);
            Rectangle r = frame.getBounds();
            frame.add(new MoveAgents(r.getWidth(), rSpeed));
        }            
    });
}

public MoveAgents(double w, double rFrameSpeed) {
     //initialize my 2 balls and their speeds and starting locations

    Timer timer = new Timer(15, null);
    timer.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            totalTime++;

            if (totalTime == 1000) {
                timer.stop();

                try {
                    generateCsvFile(OUTPUT_FILE);
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
          //otherwise, do things 
    }
 }
Kevin
  • 3,209
  • 9
  • 39
  • 53

1 Answers1

2

Is it possible to loop through and run multiple simulations automatically?

Yes. When the simulation ends, don't try to replace the view. Instead,

  • Invoke stop() on the Timer.

  • Invoke generateCsvFile() to save the results.

  • Initialize the simulation's model exactly as done the first time.

  • Invoke restart() on the Timer.

In the example below, the model is an int named data, which the simulation simply renders and increments to 1000. A count of the number of times to repeat the simulation is passed as a parameter to the Simulation constructor. Instead of frame.setSize(), override the drawing panel's getPreferredSize() as discussed here.

image

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * @see https://stackoverflow.com/a/37293513/230513
 */
public class SimTest {

    private void display() {
        JFrame f = new JFrame("SimTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new Simulation(8));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class Simulation extends JPanel {

        private final Font font = this.getFont().deriveFont(36f);
        private int count;
        private int data;
        Timer t = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                repaint();
                if ((data += 100) == 1000) {
                    t.stop();
                    System.out.println("Saving results…");//generateCsvFile();
                    if (--count == 0) {
                        System.out.println("Fin!");
                        System.exit(0);
                    } else {
                        init();
                        t.restart();
                    }
                }
            }
        });

        public Simulation(int count) {
            this.count = count;
            init();
            t.start();
        }

        private void init() {
            data = 0;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setFont(font);
            g.drawString(String.valueOf(data), 8, g.getFontMetrics().getHeight());
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(256, 128);
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new SimTest()::display);
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Ah thanks amazing! This is infinitely better than all the problems I ran into trying to do the loop in my main method. – Kevin May 18 '16 at 16:52
  • @Kevin: I found it helpful to think of `javax.swing.Timer` as providing a loop that can be controlled externally and doesn't block the [EDT](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod May 19 '16 at 00:39