// Kepler.java // /************************************************************************* * * * Copyright (c) 1998 Northwestern University * * * * This program is free software. You can copy, modify, or redistribute * * this software 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, provided that both * * the above copyright notice appears in all copies and that this * * permission notice appear in any supporting documentation. * * * * This software is provided "as is" in the hope that it will be useful, * * but WITHOUT any expressed or implied warranty of merchantability or * * fitness for any particular purpose. Your milage may vary. * * See the GNU General Public License for more details. * * * *************************************************************************/ // Modification History // Date Initials Change /* * @author Xiaowei Jiang, Greg Anderson * @version 0.1, June 1999 */ import java.applet.*; import java.awt.*; import java.lang.Math; import Banner; public class Kepler extends Applet{ KeplerInputPanel inputPanel; KeplerOptionPanel optionPanel; KeplerCanvas twoBody; public Kepler(){ setLayout(new BorderLayout()); inputPanel = new KeplerInputPanel(); add("North", new Banner("Kepler's Law")); add("South",inputPanel); optionPanel = new KeplerOptionPanel(); add("East",optionPanel); twoBody = new KeplerCanvas(); add("Center", twoBody); } public void init(){ } public void start(){ twoBody.init(); } public String getAppletInfo(){ return "The Northwestern University Virtual Physics Lab, Nov. 1998"; } public boolean action(Event evt, Object arg){ if(evt.target instanceof Button || evt.target instanceof TextField){ twoBody.setE(inputPanel.getE()); twoBody.setP(inputPanel.getP()); twoBody.setMassRatio(inputPanel.getRatio()); twoBody.setTimeInterval(inputPanel.getTimeInterval()); if(optionPanel.isMoving()) { twoBody.start(); } else{ twoBody.stop(); } } else if(evt.target instanceof Checkbox){ if(evt.target == optionPanel.getVelocityBox()) twoBody.setShowVelocity(optionPanel.showVelocity()); else{ if(evt.target == optionPanel.getAreaBox()){ twoBody.setShowArea(optionPanel.showArea()); } else{ twoBody.setShowOrbit(optionPanel.showOrbit()); twoBody.setPerspective(optionPanel.Perspective()); } } } return true; } } class KeplerInputPanel extends Panel{ private TextField mRatio, pOrbit, eOrbit, timeInterval; public KeplerInputPanel(){ setLayout(new GridLayout(2,4,5,5)); //nr, nc, vg, hg Color darkMagenta = new Color(150,0,150); Button btemp; btemp = new Button("m_Red/m_Blue = "); btemp.setForeground(darkMagenta); add(btemp); add(mRatio = new TextField("8", 2)); btemp = new Button("Orbit param : p = "); btemp.setForeground(darkMagenta); add(btemp); add(pOrbit = new TextField("5", 2)); btemp = new Button("Time interval: t = "); btemp.setForeground(Color.blue); add(btemp); add(timeInterval = new TextField("10", 2)); btemp = new Button("Orbit param : e = "); btemp.setForeground(darkMagenta); add(btemp); add(eOrbit = new TextField("0.4", 2)); } public double getP(){ return getValue(pOrbit); } public double getE(){ return getValue(eOrbit); } public double getRatio(){ return getValue(mRatio); } public int getTimeInterval(){ return getInteger(timeInterval); } private double getValue(TextField t){ return (Double.valueOf(t.getText().trim())).doubleValue(); } private int getInteger(TextField t){ return (Integer.valueOf(t.getText().trim())).intValue(); } } class KeplerOptionPanel extends Panel{ CheckboxGroup perspective; Checkbox redPlanet; Checkbox bluePlanet; Checkbox center; Checkbox displayArea; Label area; Checkbox displayVelocity; Checkbox displayOrbit; Button startStop; boolean isStart; public KeplerOptionPanel(){ setLayout(new GridLayout(8,1,2,5)); //nr, nc, vg, hg Color darkMagenta = new Color(150,0,150); perspective = new CheckboxGroup(); add(new Label("Perspective: ")); redPlanet = new Checkbox("red planet", perspective, false); redPlanet.setForeground(Color.red); add(redPlanet); bluePlanet = new Checkbox("blue planet", perspective, false); bluePlanet.setForeground(Color.blue); add(bluePlanet); add(center = new Checkbox("center of mass", perspective, true)); add(displayArea = new Checkbox("show area")); add(displayVelocity = new Checkbox("show velocity")); add(displayOrbit = new Checkbox("show orbit")); startStop = new Button("Start"); // startStop.setBackground(Color.gray); startStop.setForeground(Color.red); add(startStop); isStart = false; } public Checkbox getVelocityBox(){ return displayVelocity; } public Checkbox getAreaBox(){ return displayArea; } public boolean isMoving(){ return isStart; } public int Perspective(){ Checkbox cb = perspective.getCurrent(); if(cb == redPlanet) return 1; if(cb == bluePlanet) return 2; else return 3; } public boolean showOrbit(){ return displayOrbit.getState(); } public boolean showArea(){ return displayArea.getState(); } public boolean showVelocity(){ return displayVelocity.getState(); } public boolean action(Event evt, Object arg){ if(evt.target == startStop){ isStart = !isStart; if (isStart) startStop.setLabel("Stop"); else startStop.setLabel("Start"); } return false; } private void changeLabel(String s){ startStop.setLabel(s); } } class KeplerCanvas extends Canvas implements Runnable{ private KeplerCircle cRed, cBlue; private KeplerVector vecRed, vecBlue; private Thread demo; private Image offImage, orbitTrace; private Graphics og, tg; private double p, e, massRatio; //parameters for the motion private boolean showOrbit; //if the orbit is shown private boolean showArea; //if the area is shown private boolean showVelocity; //if the velocity vector is shown private int perspective; //1 stands for Red, 2 for Blue, 3 for CM; private int count; private int timeInterval; private boolean justChanged; //show if the parameters have just been changed private double r, theta; //current location of the planets private double xx, yy; public KeplerCanvas(){ showOrbit = false; showArea = false; showVelocity = false; setBackground(Color.black); cRed = new KeplerCircle(5, Color.red); cBlue = new KeplerCircle(5, Color.blue); vecRed = new KeplerVector(Color.green); vecBlue = new KeplerVector(Color.green); p = 5; e = 0; massRatio = 10; perspective = 3; count = 0; timeInterval = 10; } public void init(){ offImage = createImage(size().width, size().height); orbitTrace = createImage(size().width, size().height); og = offImage.getGraphics(); tg = orbitTrace.getGraphics(); } public void mypaint(Graphics g){ if(demo != null){ cRed.draw(g); cBlue.draw(g); if(showVelocity){ vecRed.draw(g); vecBlue.draw(g); } } } public final void update(Graphics g){ g.drawImage(offImage, 0, 0, null); } public void advance(){ compute(); prepareImage(); justChanged = false; } /**do calculations*/ private void compute(){ Dimension dim = size(); double x = xx, y = yy; r = p/(1 + e * Math.cos(theta)); xx = dim.width/20. * r * Math.cos(theta); yy = dim.height/20. * r * Math.sin(theta); advanceTheta(); double fRed, fBlue; switch (perspective){ case 1: //red ball at center fRed = 0; fBlue = 1; break; case 2: //blue ball at center fRed = -1; fBlue = 0; break; case 3: //CM at center default : fRed = -1/(1 + massRatio); fBlue = 1/(1 + 1/massRatio); } int xp = dim.width/2+round(xx*fRed); int yp = dim.height/2+round(yy*fRed); cRed.setLocation(xp, yp); vecRed.setLocation(xp, yp); xp = dim.width/2+round(xx*fBlue); yp = dim.height/2+round(yy*fBlue); cBlue.setLocation(xp, yp); vecBlue.setLocation(xp, yp); vecRed.set(fRed * (xx - x) * 5., fRed * (yy - y) * 5.); vecBlue.set(fBlue * (xx - x) * 5., fBlue * (yy - y) * 5.); } private void prepareImage(){ Color bg = getBackground(); //Erase the previous image. if(showOrbit||showArea) { if(justChanged) clear(tg); else { if(showArea){ Point center = new Point(size().width/2, size().height/2); if(count < timeInterval){ cRed.drawArea(tg, center); cBlue.drawArea(tg, center); } else{ cRed.eraseArea(tg, center, getBackground()); cBlue.eraseArea(tg, center, getBackground()); } count++; count = (count % (2*timeInterval)); } if(showOrbit){ cRed.drawOrbit(tg); cBlue.drawOrbit(tg); } } og.drawImage(orbitTrace, 0, 0, null); } else clear(og); mypaint(og); } public void start(){ if(demo == null){ demo = new Thread(this); demo.start(); } } public void stop(){ if(demo != null){ demo.stop(); demo = null; } } public void run(){ while(true){ advance(); repaint(); try{ Thread.sleep(30); } catch (InterruptedException e){ /* do nothing*/ } } } public void setP(double pIn){ p = pIn; justChanged = true; } public void setE(double eIn){ e = eIn; justChanged = true; } public void setMassRatio(double rIn){ massRatio = rIn; justChanged = true; } public void setPerspective(int perIn){ perspective = perIn; justChanged = true; } public void setShowOrbit(boolean b){ showOrbit = b; justChanged = true; } public void setShowArea(boolean b){ showArea = b; count = 0; justChanged = true; } public void setShowVelocity(boolean b){ showVelocity = b; } public void setTimeInterval(int i){ timeInterval = i; justChanged = true; } private void clear(Graphics g){ g.setColor(getBackground()); g.fillRect(0, 0, size().width, size().height); } private void advanceTheta(){ theta += 1/(r * r); } private int round(double x){ return (int) (x + .5); } } class KeplerCircle{ private int myRadius; private Color myColor; private Point myLocation; private Point lastLocation; /*Constructor*/ public KeplerCircle(int radius, Color c){ myRadius = radius; myColor = c; myLocation = new Point(0,0); lastLocation = new Point(1,1); } /*Display the shape*/ public void draw(Graphics g){ g.setColor(myColor); g.fillArc(myLocation.x - myRadius, myLocation.y - myRadius, (2*myRadius), (2*myRadius), 0, 360); } public void drawOrbit(Graphics g){ g.setColor(myColor); g.drawLine(lastLocation.x, lastLocation.y, myLocation.x, myLocation.y); } public void erase(Graphics g, Color bg){ g.setColor(bg); g.fillArc(lastLocation.x - myRadius, lastLocation.y - myRadius, (2*myRadius), (2*myRadius), 0, 360); } public void drawArea(Graphics g, Point center){ drawAreaWithColor(g, center, myColor); } public void eraseArea(Graphics g, Point center, Color bg){ drawAreaWithColor(g, center, bg); } private void drawAreaWithColor(Graphics g, Point center, Color c){ int[] xPoints = new int[3]; int[] yPoints = new int[3]; xPoints[0] = center.x; yPoints[0] = center.y; xPoints[1] = lastLocation.x; yPoints[1] = lastLocation.y; xPoints[2] = myLocation.x; yPoints[2] = myLocation.y; g.setColor(c); g.fillPolygon(xPoints, yPoints, 3); } public Point getLocation(){ return myLocation; } public void setLocation(Point pt){ lastLocation.x = myLocation.x; lastLocation.y = myLocation.y; myLocation = pt; } public void setLocation(int x, int y){ lastLocation.x = myLocation.x; lastLocation.y = myLocation.y; myLocation.x = x; myLocation.y = y; } /**Return this shape's area*/ public double getArea(){ return Math.PI * (myRadius * myRadius); } /**Return this shape's perimeter*/ public double getPerimeter(){ return 2 * Math.PI * myRadius; } public String getKind(){ return "Circle"; } } class KeplerVector{ private double r, theta; private double width; private Point location; private Color myColor; KeplerVector(Color c){ width = 1; myColor = c; location = new Point(0,0); r = theta = 0; } KeplerVector(double w, Color c){ width = w; myColor = c; location = new Point(0,0); r = theta = 0; } public void draw(Graphics g){ double xa = - width * Math.sin(theta), ya = width * Math.cos(theta); double xv = r * Math.cos(theta), yv = r * Math.sin(theta); int[] xTail = new int[4]; int[] yTail = new int[4]; xTail[0] += round(location.x + xa); yTail[0] += round(location.y + ya); xTail[1] += round(location.x - xa); yTail[1] += round(location.y - ya); xTail[2] += round(location.x + xv - xa); yTail[2] += round(location.y + yv - ya); xTail[3] += round(location.x + xv + xa); yTail[3] += round(location.y + yv + ya); g.setColor(myColor); g.fillPolygon(xTail, yTail, 4); } public void setLocation(int x, int y){ location.x = x; location.y = y; } public void set(double x, double y){ r = Math.sqrt(x * x + y * y); theta = Math.atan2(y, x); } public void setNorm(double n){ r = n; } public void setAngle(double t){ theta = t; } private int round(double x){ return (int) (x + .5); } }