/* File: simple.java A simple version of a physics simulation This is a text-only version of the simple spring simulation. Part of the www.MyPhysicsLab.com physics simulation applet. Copyright (c) 2001 Erik Neumann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact Erik Neumann at erikn@MyPhysicsLab.com or 610 N. 65th St. Seattle WA 98103 */ /* Instructions for use: NOTE: I recommend you delete all *.class files before beginning to be sure you are not using any old classes. -------- AS STANDALONE JAVA APP --------------- in your command line (eg. DOS prompt in Windows): 1. compile with the command javac simple.java -target 1.1 2. run with the command java simple This will start the app, you can click "stop" and "start" to pause. -------- AS APPLET IN WEB BROWSER ------------- 1. compile with: javac simple.java -target 1.1 2. open a web page that displays the applet... here is some sample html code: Simple Simulation

Simple Simulation

-------- TO DEBUG WITH PRINT STATEMENTS -------- Put statments into your code such as: System.out.println("value = "+value); where 'value' is a variable you want to see. Note that if running in a browser you must 'show console' to see the print statements. -------- TO DEBUG WITH JDB ----------------- 1. compile with the debug option: javac -g simple.java 2. start running with the jdb debugger jdb simple 3. set a breakpoint by: stop at class:line where 'class' is replaced by a class and 'line' by a line number here's an example: stop at simple:138 4. type: run the window for the app will open and start running. the debugger will stop execution at the line you specified. 5. use the jdb commands such as: stop at, step, next, cont, print, where, locals type: help to see a list of available commands (type these into your DOS prompt window, not into the application!) */ import java.awt.*; import java.awt.event.*; import java.applet.*; import java.text.DecimalFormat; import java.text.NumberFormat; import java.net.URL; import java.util.Enumeration; import java.io.*; import java.util.Iterator; /* Single spring & single mass, in one dimension (moving horizontally) vars[0] = position (x) with origin as above vars[1] = velocity (v=x') R = rest length len = current length of spring = x - origin.x L = how much spring is stretched from rest length L = len - R = x - origin.x - R k = spring constant b = damping constant F = m a (force = mass * acceleration) leads to: F = -kL -bv = -k(x - origin.x - R) -bv = m v' so diffeq's are: x' = v v' = -(k/m)(x - origin.x - R) -(b/m)v */ class S_Spring { double m_SpringConst = 3; double m_RestLength = 2.5; double m_X1 = 0; // left end of spring } class S_Mass { double m_Mass = 0.5; double m_Damping = 0.2; } public class simple extends Applet implements ActionListener { S_Spring m_Spring; S_Mass m_Mass; public int numVars = 2; // number of variables in vars[] public static final int MAX_VARS = 40; private S_LabTimer timer = null; TextArea textarea1; Button button_stop; Button button_start; double m_time = -9999; double sim_time = 0; boolean m_Animating = true; public double[] vars; // array of variables public void outputObjects() // report state of objects { DecimalFormat df = new DecimalFormat(" 0.0000;-0.0000"); String msg = "t = "+df.format(sim_time)+ " x = "+df.format(vars[0])+ " v = "+df.format(vars[1])+"\n"; textarea1.append(msg); } /* Explanation of how to code up differential equations: The variables are stored in the vars[] array. Let y = var[0], w = var[1], z = var[2], etc. The differential equations must all be first order, in the form: y' = f(t, y, w, z, ...) w' = g(t, y, w, z, ...) z' = h(t, y, w, z, ...) ... You will have as many equations as there are variables diffeq0 returns the right hand side of the first equation y' = f(t, y, w, z, ...) diffeq1 returns the right hand side of the second equation w' = g(t, y, w, z, ...) */ public double diffeq0(double t, double[] x) // t = time, x = array of variables { return x[1]; // x' = v } public double diffeq1(double t, double[] x) // t = time, x = array of variables { // v' = -(k/m)(x - R) - (b/m) v double r = -m_Spring.m_SpringConst*(x[0] - m_Spring.m_X1 - m_Spring.m_RestLength) - m_Mass.m_Damping*x[1]; return r/m_Mass.m_Mass; } public double diffeq2(double t, double[] x) { return 0; } public double diffeq3(double t, double[] x) { return 0; } public double diffeq4(double t, double[] x) { return 0; } public double diffeq5(double t, double[] x) { return 0; } public double diffeq6(double t, double[] x) { return 0; } public double diffeq7(double t, double[] x) { return 0; } // executes the i-th diffeq // i = which diffeq, t=time, x= array of variables public double evaluate(int i, double t, double[] x) { switch (i) { case 0: return diffeq0(t, x); case 1: return diffeq1(t, x); case 2: return diffeq2(t, x); case 3: return diffeq3(t, x); case 4: return diffeq4(t, x); case 5: return diffeq5(t, x); case 6: return diffeq6(t, x); case 7: return diffeq7(t, x); default: System.out.println("throw? problem in evaluate"); return 0; } } // A version of Runge-Kutta method using arrays // Calculates the values of the variables at time t+h // t = last time value // h = time increment // vars = array of variables // N = number of variables in x array public void solve(double t, double h) { int N = numVars; int i; double[] inp = new double[N]; double[] k1 = new double[N]; double[] k2 = new double[N]; double[] k3 = new double[N]; double[] k4 = new double[N]; for (i=0; i 0.25) h = 0.25; } if ((h<0) || (h>3)) { System.out.println("*** trouble with time step h = "+h+" ***"); h = 0.1; // pick a more reasonable number } // record time of this simulation step m_time = now; solve(sim_time, h); sim_time += h; } } public void init() { m_Spring = new S_Spring(); m_Mass = new S_Mass(); vars = new double[simple.MAX_VARS]; textarea1 = new TextArea("", 30, 60); add(textarea1); button_stop = new Button("stop"); add(button_stop); button_stop.addActionListener(this); button_start = new Button("start"); add(button_start); button_start.addActionListener(this); } public void actionPerformed (ActionEvent e) { if(e.getSource() == button_stop) { m_Animating = false; } else if (e.getSource() == button_start) { m_Animating = true; } } // called when user returns to browser page containing applet public void start() { if (timer == null) { timer = new S_LabTimer(this); timer.start(); } } // called when user leaves browser page containing applet public void stop() { if (timer != null) { timer.interrupt(); timer = null; // destroys the thread } } public static void main(String[] args) { Frame frame = new S_AppletFrame(); frame.show(); } } ///////////////////////////////////////////////////////////////////////////// class S_LabTimer extends Thread { private simple myApplet; private long delay = 33; S_LabTimer(simple myApp) { super("S_LabTimer Thread"); myApplet = myApp; } public void run() { try { while (!interrupted()) // loop until interrupted { myApplet.modifyObjects(); sleep(delay); // milliseconds } } catch(InterruptedException e) { System.out.println("S_LabTimer thread interrupted."); } } } ///////////////////////////////////////////////////////////////////////////// class S_LabWindowAdapter extends WindowAdapter { simple myLab = null; public S_LabWindowAdapter(simple thisLab) { myLab = thisLab; } public void windowIconified(WindowEvent e) { System.out.println("windowIconified()"); myLab.stop(); } public void windowDeiconified(WindowEvent e) { System.out.println("windowDeiconified()"); myLab.start(); } public void windowDeactivated(WindowEvent e) { System.out.println("windowDeactivated()"); //myLab.stop(); // disable this line when using jdb } public void windowActivated(WindowEvent e) { System.out.println("windowActivated()"); myLab.start(); } public void windowClosing(WindowEvent e) { myLab.stop(); System.exit(0); } } ///////////////////////////////////////////////////////////////////////////// class S_AppletFrame extends Frame implements AppletStub, AppletContext { public S_AppletFrame() // constructor { setTitle("Simple Simulation"); Toolkit tk = Toolkit.getDefaultToolkit(); Dimension d = tk.getScreenSize(); setSize(640, 480); setLocation(d.width/10, d.height/10); simple applet = new simple(); applet.setStub(this); addWindowListener(new S_LabWindowAdapter(applet)); add(applet); applet.init(); applet.start(); //addComponentListener(applet); // applet listens for resize events } // AppletStub methods public boolean isActive() { return true; } public URL getDocumentBase() { return null; } public URL getCodeBase() { return null; } public String getParameter(String name) { return ""; } public AppletContext getAppletContext() { return this; } public void appletResize(int width, int height) {} // AppletContext methods public AudioClip getAudioClip(URL url) { return null; } public Image getImage(URL url) { return null; } public Applet getApplet(String name) { return null; } public Enumeration getApplets() { return null; } public void showDocument(URL url) {} public void showDocument(URL url, String target) {} public void showStatus(String status) {} // setStream, getStream, getStreamKeys are for Java 1.4 compatibility // comment out these methods to compile with Java 1.3 public void setStream(String key, InputStream stream) throws IOException {} public InputStream getStream(String key) { return null; } public Iterator getStreamKeys() { return null; } }