package efruchter.particles.sample.fallsim.gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import efruchter.particles.datatypes.Particle;
import efruchter.particles.integrators.NewtonianIntegrators;
import efruchter.particles.integrators.NewtonianIntegrators.AccelerationFunction;
import efruchter.particles.integrators.NewtonianIntegrators.RKState;
import efruchter.vectorutils.Vector3;
@SuppressWarnings("serial")
public class SimulationPanel extends JPanel {
/**
* Time increment
*/
private final float increment = .01f;
/**
* Standard gravity
*/
static Vector3 gravity = new Vector3(0, 0, -9.81f);
/*
* The three particles
*/
public Particle pAnalytical = new Particle(), pExplicit = new Particle(), pBetter = new Particle();
public Particle[] system = new Particle[] { pAnalytical, pExplicit, pBetter };
/*
* Boilerplate.
*/
private DecimalFormat formatter = new DecimalFormat("0.00000");
private JLabel timeLabel = new JLabel();
private JLabel aLabel = new JLabel();
private JLabel bLabel = new JLabel();
private JLabel resultLabel = new JLabel("-");
private JButton runSimButton = new JButton("Start Sim");
private JButton pauseSimButton = new JButton("Pause Sim");
private JFrame frame = new JFrame("Simulation Control");
private float time = 0;
private boolean active = false;
final private Vector3 anaStart = new Vector3(-50 / 2, 0, 80);
final AccelerationFunction acc = new AccelerationFunction(){
@Override
public Vector3 getAcceleration(RKState state, float time) {
return SimulationPanel.gravity;
}
};
public SimulationPanel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (final Exception e) {
System.err.println("Failed to grab a proper look-and-feel from the OS.");
}
setBorder(new EmptyBorder(10, 10, 10, 10));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
/**
* Add the GUI components.
*/
runSimButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
runSim();
}
});
pauseSimButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
pauseSim();
}
});
add(timeLabel);
add(Box.createRigidArea(new Dimension(10, 5)));
add(runSimButton);
add(Box.createRigidArea(new Dimension(10, 5)));
add(pauseSimButton);
add(Box.createRigidArea(new Dimension(10, 5)));
add(new JLabel("Positional Error of Particles:"));
add(Box.createRigidArea(new Dimension(10, 2)));
add(aLabel);
add(Box.createRigidArea(new Dimension(10, 2)));
add(bLabel);
add(Box.createRigidArea(new Dimension(10, 5)));
add(resultLabel);
resultLabel.setForeground(Color.RED);
resultLabel.setVisible(false);
aLabel.setForeground(Color.BLUE);
bLabel.setForeground(Color.BLUE);
updateUILabels();
setPreferredSize(new Dimension(300, 200));
frame.pack();
frame.setVisible(true);
}
private void runSim() {
time = 0;
active = true;
pAnalytical.x = anaStart;
pExplicit.x = new Vector3(0, 0, 80);
pBetter.x = new Vector3(50 / 2, 0, 80);
for (Particle p : system) {
p.reset();
}
runSimButton.setText("Restart Sim");
resultLabel.setVisible(true);
}
private void pauseSim() {
active = !active && pAnalytical.x.z > 0;
}
public float getSimTime() {
return time;
}
public void updateSimTime() {
if (active) {
time += increment;
// Clear force accumulator
for (Particle p : system)
p.clearForces();
// Accumulate forces
for (Particle p : system)
p.addForce(gravity);
// Integrate
if (pAnalytical.x.z > 0) {
pAnalytical.x = posAnalyticalConstant(anaStart, Vector3.ZERO, gravity, time);
NewtonianIntegrators.rungeKutta4(pExplicit, increment, acc);
NewtonianIntegrators.trapazoidal(pBetter, increment);
System.out.println("RK4: " + pExplicit.v);
} else {
pauseSim();
}
}
updateUILabels();
}
private void updateUILabels() {
timeLabel.setText("Time: " + time);
aLabel.setText("RK4 : " + formatter.format(Math.abs(pAnalytical.x.z - pExplicit.x.z)) + " units");
bLabel.setText("Trapazoidal : " + formatter.format(Math.abs(pAnalytical.x.z - pBetter.x.z)) + " units");
resultLabel.setText((pAnalytical.x.z > 0) ? "---" : "Analytical particle has hit ground!");
if (pAnalytical.x.z > 0) {
pauseSimButton.setText((active) ? "Pause Sim" : "Unpause Sim");
pauseSimButton.setEnabled(true);
} else {
pauseSimButton.setEnabled(false);
}
}
/**
* Analytically find position at time for given inputs
*
* @param x0
* initial position
* @param v0
* initial velocity
* @param a
* accleration
* @param t
* time
* @return the position at time t
*/
private Vector3 posAnalyticalConstant(final Vector3 x0, final Vector3 v0, final Vector3 a, final float t) {
return x0.add(v0.scale(t)).add(a.scale(t * t * .5f));
}
}