1

I have created a window that has a large JPanel occupying the greater part of the area that will be used to animate a set of fireworks. I have a private inner class AnimationPanel which extends JPanel. I have overridden paintComponent() in order to use a buffered image as a background. Upon pressing the start button, a simulation will run which will animate the set of fireworks. Right now, this simulation outputs the positions to the console, and generates a collection called snapshot containing all of the fireworks at a particular point in time.

At the moment, I have successfully drawn the background and a small rectangle to represent the launch tube, but when I try to paint the fireworks from the snapshot, nothing shows up. At this point I am not concerned about animation (still learning how to do this using the Timer class), but I need to know that the draw will work, so I am just trying to draw the snapshot. I am repainting animationPanel at the end of the simulation after getting the snapshot.

The private inner class AnimationPanel

private class AnimationPanel extends JPanel {

    private BufferedImage background;

    public AnimationPanel() {
        super();
        try {
            background = ImageIO.read(new File("background.jpg"));
        } catch(IOException error) {
            error.printStackTrace();
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(background,0,0,getWidth(),getHeight(),this); //this shows up
        g.setColor(Color.RED);
        g.fillRect(getWidth()/2, getHeight()-30, 10, 30); //this shows up
        paintFireWorks(g); //this doesn't show up
    }

    private void paintFireWorks(Graphics g) {
        if(snapshot != null) {
            for(Firework item: snapshot) {
                double[] position = item.getPosition();
                if(item instanceof Star){
                    int x = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int y = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int r = ((Star) item).getRenderSize();
                    g.fillOval(x,y,r,r);
                } else if(item instanceof Spark){
                    int x1 = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int x2 = (int)Math.round(position[0])-((Spark)item).getRenderSize();
                    int y1 = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int y2 = animationPanel.getHeight()-(int)Math.round(position[1])+((Spark)item).getRenderSize();
                    g.drawLine(x1,y1,x2,y2);
                }
            }
        }
    }

}

The entire FireWorksWindow class:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.*;

@SuppressWarnings("serial")
public class FireWorksWindow extends JFrame {

//DIMENSIONS AND POSITIONS
private final int STARTING_POS_X = 1600;
private final int STARTING_POS_Y = 100;

//CONTAINERS AND COMPONENTS
private AnimationPanel animationPanel;
private JPanel controlPanel, sliderPanel;
private Box controlBox;
private JLabel windSliderLabel, angleSliderLabel;
private JSlider windSpeed, launchAngle;
private JButton startButton, pauseButton, exitButton;

//TIMER
private Timer animationTimer;

//COLLECTIONS
ArrayList<Particle> fireworks;
ArrayList<Particle> snapshot;

//CONSTRUCTOR
public FireWorksWindow() {
    setTitle("Roman Candle Animation");
    setLocation(STARTING_POS_X,STARTING_POS_Y);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    sliderPanel = new JPanel();
    GridBagLayout sliderGrid = new GridBagLayout();
    sliderPanel.setLayout(sliderGrid);
    initSliders();
    initLabels();
    addComponent(sliderGrid, windSliderLabel,       1,1,new Insets(5,10,5,10));
    addComponent(sliderGrid, angleSliderLabel,      1,2,new Insets(5,10,5,10));
    addComponent(sliderGrid, windSpeed,             2,1,new Insets(5,10,5,10));
    addComponent(sliderGrid, launchAngle,           2,2,new Insets(5,10,5,10));

    controlPanel = new JPanel();
    controlPanel.setLayout(new BoxLayout(controlPanel,BoxLayout.X_AXIS));
    initButtons();
    initControlBox();
    controlPanel.add(controlBox);
    controlPanel.setBorder(BorderFactory.createLineBorder(Color.MAGENTA , 3 ));

    animationPanel = new AnimationPanel();
    animationPanel.setSize(new Dimension(750,500));
    animationPanel.setMinimumSize(new Dimension(animationPanel.getWidth(),animationPanel.getHeight()));
    animationPanel.setPreferredSize(new Dimension(animationPanel.getWidth(),animationPanel.getHeight()));

    add(animationPanel,BorderLayout.CENTER);
    add(controlPanel,BorderLayout.SOUTH);
    pack();
    setMinimumSize(new Dimension(getWidth(),getHeight()));
    sliderPanel.setMaximumSize(new Dimension(sliderPanel.getWidth(), sliderPanel.getHeight()));
}

//CONVENIENCE METHODS
private void addComponent(GridBagLayout layout, Component component, int row, int column, Insets padding) {
    GridBagConstraints constraints = new GridBagConstraints();
    constraints.gridx = column;
    constraints.gridy = row;
    constraints.insets = padding;
    layout.setConstraints(component, constraints);
    sliderPanel.add(component);
}

private void initLabels() {
    windSliderLabel = new JLabel("Wind Speed");
    angleSliderLabel = new JLabel("Launch Angle");
}

private void initSliders() {
    windSpeed = new JSlider(-20,20);
        windSpeed.setMajorTickSpacing(10);
        windSpeed.setMinorTickSpacing(5);
        windSpeed.setPaintTicks(true);
        windSpeed.setPaintLabels(true);
    launchAngle = new JSlider(-15,15);
        launchAngle.setMajorTickSpacing(5);
        launchAngle.setMinorTickSpacing(1);
        launchAngle.setPaintTicks(true);
        launchAngle.setPaintLabels(true);
}

private void initButtons() {
    startButton = new JButton("Start");
    startButton.addActionListener(new StartHandler());
    pauseButton = new JButton("Pause");
    pauseButton.addActionListener(new PauseHandler());
    exitButton  = new JButton("Exit" );
    exitButton.addActionListener(new ExitHandler());
}

private void initControlBox() {
    controlBox = Box.createHorizontalBox();
    controlBox.add(Box.createHorizontalStrut(10));
    controlBox.add(sliderPanel);
    controlBox.add(Box.createHorizontalStrut(30));
    controlBox.add(startButton);
    controlBox.add(Box.createHorizontalStrut(10));
    controlBox.add(pauseButton);
    controlBox.add(Box.createHorizontalGlue());
    controlBox.add(Box.createHorizontalStrut(10));
    controlBox.add(Box.createHorizontalGlue());
    controlBox.add(exitButton);
    controlBox.add(Box.createHorizontalStrut(10));
}

//ACTION LISTENERS
public class WindAdjustListener implements ActionListener{
    public void actionPerformed(ActionEvent e) {

    }

}

public class AngleAdjustListener implements ActionListener{
    public void actionPerformed(ActionEvent e) {

    }

}

public class StartHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        System.out.println("START Pressed");
        startButton.setText("Reset");
        repaint();
        runAnimation();
        startButton.setText("Start");
    }
}

public class PauseHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        System.out.println("PAUSE Pressed");
    }
}

public class ExitHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        if(animationTimer != null)
            if(animationTimer.isRunning())
                animationTimer.stop();
        System.exit(0);
    }
}

public class AnimationClock implements ActionListener {
    public void actionPerformed(ActionEvent arg0) {

    }   
}

//ACCESSORS
public double[] getSliderValues() {
    double[] sliders = new double[2];
    sliders[0] = windSpeed.getValue();
    sliders[1] = launchAngle.getValue();
    return sliders;
}

//OTHER METHODS
public void runAnimation() {
    double[] inputs = getSliderValues();
    double wind = inputs[0];
    double launchAngle = inputs[1];
    double timeInterval = 0.05;     // seconds
    ParticleManager manager = null;
    try {
        manager = new ParticleManager(wind, launchAngle);
    } catch (EnvironmentException except) {
        System.out.println(except.getMessage());
        return;
    } catch (EmitterException except) {
        System.out.println(except.getMessage());            
        return;
    }
    System.out.println("Counts:");
    System.out.println("time\tStars\tSparks\tLaunchSparks");
    double time = 0;
    manager.start(time);
    fireworks = manager.getFireworks(time);
    do {
        //if (time % 0.5 == 0)
            showTypesCount(fireworks, time);
        if (Math.abs(time - 2.0) < timeInterval)
            snapshot = fireworks;
        time += timeInterval;
        fireworks = manager.getFireworks(time);
    } while (fireworks.size() > 0);
    showFireworks(snapshot, 2.0);
    animationPanel.repaint();
}

public void showFireworks(ArrayList<Particle> fireworks, double time) {
    if (fireworks == null)
        return;
    System.out.printf("\nAt time%5.2f seconds:\n", time);
    System.out.println("Type\t\tPosition (metres)");
    for (Particle firework : fireworks)
        System.out.println(firework);
}

public void showTypesCount(ArrayList<Particle> fireworks, double time) {
    int starCount = 0;
    int sparkCount = 0;
    int launchSparkCount = 0;
    for (Particle firework : fireworks) {
        if (firework instanceof Star)
            starCount++;
        else if (firework instanceof LaunchSpark)
            launchSparkCount++;
        else
            sparkCount++;
    }
    System.out.printf("%5.2f\t", time);
    System.out.println(starCount + "\t" + sparkCount + "\t" + launchSparkCount);
}

private class AnimationPanel extends JPanel {

    private BufferedImage background;

    public AnimationPanel() {
        super();
        try {
            background = ImageIO.read(new File("background.jpg"));
        } catch(IOException error) {
            error.printStackTrace();
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(background,0,0,getWidth(),getHeight(),this); //this shows up
        g.setColor(Color.RED);
        g.fillRect(getWidth()/2, getHeight()-30, 10, 30); //this shows up
        paintFireWorks(g); //this doesn't show up
    }

    private void paintFireWorks(Graphics g) {
        if(snapshot != null) {
            for(Firework item: snapshot) {
                double[] position = item.getPosition();
                if(item instanceof Star){
                    int x = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int y = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int r = ((Star) item).getRenderSize();
                    g.fillOval(x,y,r,r);
                } else if(item instanceof Spark){
                    int x1 = (int)Math.round(position[0])+animationPanel.getWidth()/2;
                    int x2 = (int)Math.round(position[0])-((Spark)item).getRenderSize();
                    int y1 = animationPanel.getHeight()-(int)Math.round(position[1]);
                    int y2 = animationPanel.getHeight()-(int)Math.round(position[1])+((Spark)item).getRenderSize();
                    g.drawLine(x1,y1,x2,y2);
                }
            }
        }
    }

}

}

Any ideas as to why the fireworks drawings are not showing up? I have tried to adapt to the methods shown in this answer (Dynamic Graphics Object Painting) and this one (panel with image background and mouse draw), but have had no success. Still stuck with a background and a rectangle.

Community
  • 1
  • 1
Yak Attack
  • 105
  • 2
  • 11
  • 1) For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete and Verifiable Example). 2) One way to get image(s) for an example is to hot-link to the images seen in [this answer](http://stackoverflow.com/a/19209651/418556). – Andrew Thompson Mar 31 '14 at 06:07
  • @Andrew Thompson Absolutely will do so from now on - thanks for the links on the subject – Yak Attack Apr 01 '14 at 02:00

1 Answers1

2

Based on this...

public class StartHandler implements ActionListener {
    public void actionPerformed(ActionEvent event) {
        System.out.println("START Pressed");
        startButton.setText("Reset");
        repaint();
        runAnimation();
        startButton.setText("Start");
    }
}

You are calling runAnimation from within the context of the Event Dispatching Thread

In you runAnimation method you then do this...

do {
   //...
} while (fireworks.size() > 0);

Which is block the EDT and preventing from processing new events, including repaint requests

Take a look at Concurrency in Swing

A simple solution would be to use a Swing Timer which would allow you offload a small wait into the background but which is triggered within the context of the EDT...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for the fast answer. I was more interested in getting the drawing of a particular instant than animating at this point though. As it turns out, the reason the drawing wasn't showing up was because I wasn't converting the heights properly, which I discovered shortly after this posting. I can now see my drawings. Sorry for the time wasted. – Yak Attack Mar 31 '14 at 01:35