Home | Science | * Dark Energy |     Share This Page
The Physics of Dark Energy
An exploration of a recent discovery in cosmology.

— Copyright © 2007, P. Lutus  Message Page

Space Applet Java Listing
/***************************************************************************
 *   Copyright (C) 2012, Paul Lutus                                        *
 *                                                                         *
 *   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.             *
 ***************************************************************************/

/*
 * SpaceApplet.java
 *
 * Created on February 12, 2007, 3:14 PM
 */
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.util.*;

public class SpaceApplet extends java.applet.Applet implements Runnable {
  
  //  m_fStandAlone will be true if applet is run in a frame
  private boolean m_fStandAlone = false;
  int defaultWidth = 640;
  int defaultHeight = 480;
  
  // a support function to launch an independent applet frame
  public static void main(String args[]) {
    SpaceApplet applet = new SpaceApplet();
    GenericFrame frame = new GenericFrame(applet.appName);
    frame.setSize(frame.getInsets().left + frame.getInsets().right + applet.defaultWidth,
    frame.getInsets().top + frame.getInsets().bottom + applet.defaultHeight);
    frame.add("Center", applet);
    applet.m_fStandAlone = true;
    applet.init();
    applet.start();
    frame.setVisible(true);
  }
  public String appName = "Space Applet 1.3";
  public String copyright = "Copyright (C) 2012, Paul Lutus, released under the GPL";
  private Thread m_space = null;
  private int repaintMs = 5;
  public int cometCount = 16;
  public double darkEnergy;
  public double timeStepHours;
  public boolean darkEnergyMode = false;
  private boolean running = true;
  double toRad = Math.PI / 180;
  private SpacePanel spacePanel;
  public OrbitingData orbitData;
  
  // initialize the applet
  public void init() {
    initComponents();
    orbitData = new OrbitingData(this);
    spacePanel = new SpacePanel(this);
    add(spacePanel, java.awt.BorderLayout.CENTER);
    if (m_fStandAlone) {
      bodyControlPanel.remove(launchButton);
    }
    setup();
  }
  
  // Launch applet in frame from Web page
  private void launchInFrame() {
    if (!m_fStandAlone) {
      runStopCheckbox.setSelected(false);
      stop();
      main(new String[2]);
    }
  }
  
  // initial setup, also to reset the simulator on command
  private void setup() {
    stop();
    orbitData.setup();
    setTimeStep();
    setCometCount();
    setDarkEnergy();
    startStop();
  }
  
  // some functions to read the user interface
  private void setTimeStep() {
    timeStepHours = 1;
    try {
      timeStepHours = Double.parseDouble(timeStepTextField.getText());
      } catch (Exception e) {
    }
    timeStepHours = (timeStepHours < 1) ? 1 : timeStepHours;
  }
  
  private void setDarkEnergy() {
    darkEnergy = 0;
    darkEnergyMode = darkEnergyCheckBox.isSelected();
    try {
      darkEnergy = Double.parseDouble(darkEnergyTextField.getText());
      } catch (Exception e) {
    }
  }
  
  private void setAnaglyphMode() {
    spacePanel.anaglyphic = anaglyphCheckbox.isSelected();
  }
  
  private void setCometCount() {
    cometCount = 16;
    try {
      cometCount = Integer.parseInt(cometsTextField.getText());
      } catch (Exception e) {
    }
    cometCount = (cometCount < 1) ? 1 : cometCount;
    orbitData.readComets(cometCount);
  }
  
  private void startStop() {
    if (runStopCheckbox.isSelected()) {
      start();
      } else {
      stop();
    }
  }
  
  // applet thread control
  public void start() {
    if (m_space == null) {
      m_space = new Thread(this);
      running = true;
      m_space.start();
    }
  }
  
  public void stop() {
    if (m_space != null) {
      running = false;
      while (m_space.isAlive());
      m_space = null;
    }
  }
  
  public void run() {
    while (running) {
      try {
        if (isVisible()) {
          spacePanel.repaint();
          getToolkit().sync();
          Thread.sleep(repaintMs);
          } else {
          Thread.sleep(200);
        }
        } catch (InterruptedException e) {
        stop();
      }
    }
  }
  
  /** This method is called from within the init() method to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  private void initComponents() {//GEN-BEGIN:initComponents
    controlPanel = new javax.swing.JPanel();
    defaultPanel = new javax.swing.JPanel();
    jLabel1 = new javax.swing.JLabel();
    timeStepTextField = new javax.swing.JTextField();
    runStopCheckbox = new javax.swing.JCheckBox();
    anaglyphCheckbox = new javax.swing.JCheckBox();
    resetButton = new javax.swing.JButton();
    bodyControlPanel = new javax.swing.JPanel();
    planetCheckBox = new javax.swing.JCheckBox();
    cometCheckBox = new javax.swing.JCheckBox();
    cometsTextField = new javax.swing.JTextField();
    darkEnergyCheckBox = new javax.swing.JCheckBox();
    darkEnergyTextField = new javax.swing.JTextField();
    launchButton = new javax.swing.JButton();
    
    setLayout(new java.awt.BorderLayout());
    
    controlPanel.setLayout(new java.awt.BorderLayout());
    
    controlPanel.setBackground(new java.awt.Color(255, 255, 204));
    controlPanel.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0)));
    defaultPanel.setBackground(new java.awt.Color(255, 255, 204));
    jLabel1.setText("Time Step (hours)");
    defaultPanel.add(jLabel1);
    
    timeStepTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
    timeStepTextField.setText("64");
    timeStepTextField.setMinimumSize(new java.awt.Dimension(30, 19));
    timeStepTextField.setPreferredSize(new java.awt.Dimension(60, 19));
    timeStepTextField.addKeyListener(new java.awt.event.KeyAdapter() {
        public void keyReleased(java.awt.event.KeyEvent evt) {
          timeStepTextFieldKeyReleased(evt);
        }
    });
    
    defaultPanel.add(timeStepTextField);
    
    runStopCheckbox.setBackground(new java.awt.Color(255, 255, 204));
    runStopCheckbox.setSelected(true);
    runStopCheckbox.setText("Run/Stop");
    runStopCheckbox.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseClicked(java.awt.event.MouseEvent evt) {
          runStopCheckboxMouseClicked(evt);
        }
    });
    
    defaultPanel.add(runStopCheckbox);
    
    anaglyphCheckbox.setBackground(new java.awt.Color(255, 255, 204));
    anaglyphCheckbox.setText("Anaglyphic");
    anaglyphCheckbox.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseClicked(java.awt.event.MouseEvent evt) {
          anaglyphCheckboxMouseClicked(evt);
        }
    });
    
    defaultPanel.add(anaglyphCheckbox);
    
    resetButton.setBackground(new java.awt.Color(255, 255, 204));
    resetButton.setText("Reset");
    resetButton.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseClicked(java.awt.event.MouseEvent evt) {
          resetButtonMouseClicked(evt);
        }
    });
    
    defaultPanel.add(resetButton);
    
    controlPanel.add(defaultPanel, java.awt.BorderLayout.CENTER);
    
    bodyControlPanel.setBackground(new java.awt.Color(255, 255, 204));
    planetCheckBox.setBackground(new java.awt.Color(255, 255, 204));
    planetCheckBox.setSelected(true);
    planetCheckBox.setText("Planets");
    bodyControlPanel.add(planetCheckBox);
    
    cometCheckBox.setBackground(new java.awt.Color(255, 255, 204));
    cometCheckBox.setSelected(true);
    cometCheckBox.setText("Comets");
    bodyControlPanel.add(cometCheckBox);
    
    cometsTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
    cometsTextField.setText("32");
    cometsTextField.setPreferredSize(new java.awt.Dimension(60, 19));
    cometsTextField.addKeyListener(new java.awt.event.KeyAdapter() {
        public void keyReleased(java.awt.event.KeyEvent evt) {
          cometsTextFieldKeyReleased(evt);
        }
    });
    
    bodyControlPanel.add(cometsTextField);
    
    darkEnergyCheckBox.setBackground(new java.awt.Color(255, 255, 204));
    darkEnergyCheckBox.setText("Dark Energy");
    darkEnergyCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseClicked(java.awt.event.MouseEvent evt) {
          darkEnergyCheckBoxMouseClicked(evt);
        }
    });
    
    bodyControlPanel.add(darkEnergyCheckBox);
    
    darkEnergyTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
    darkEnergyTextField.setText(".001");
    darkEnergyTextField.setPreferredSize(new java.awt.Dimension(60, 19));
    darkEnergyTextField.addKeyListener(new java.awt.event.KeyAdapter() {
        public void keyReleased(java.awt.event.KeyEvent evt) {
          darkEnergyTextFieldKeyReleased(evt);
        }
    });
    
    bodyControlPanel.add(darkEnergyTextField);
    
    launchButton.setBackground(new java.awt.Color(255, 255, 204));
    launchButton.setText("Separate");
    launchButton.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseClicked(java.awt.event.MouseEvent evt) {
          launchButtonMouseClicked(evt);
        }
    });
    
    bodyControlPanel.add(launchButton);
    
    controlPanel.add(bodyControlPanel, java.awt.BorderLayout.SOUTH);
    
    add(controlPanel, java.awt.BorderLayout.SOUTH);
    
  }//GEN-END:initComponents
  
  private void launchButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_launchButtonMouseClicked
    launchInFrame();
  }//GEN-LAST:event_launchButtonMouseClicked
  
  private void timeStepTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_timeStepTextFieldKeyReleased
    // TODO add your handling code here:
    setTimeStep();
  }//GEN-LAST:event_timeStepTextFieldKeyReleased
  
  private void cometsTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_cometsTextFieldKeyReleased
    // TODO add your handling code here:
    setCometCount();
  }//GEN-LAST:event_cometsTextFieldKeyReleased
  
  private void darkEnergyTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_darkEnergyTextFieldKeyReleased
    // TODO add your handling code here:
    setDarkEnergy();
  }//GEN-LAST:event_darkEnergyTextFieldKeyReleased
  
  private void darkEnergyCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_darkEnergyCheckBoxMouseClicked
    setDarkEnergy();
  }//GEN-LAST:event_darkEnergyCheckBoxMouseClicked
  
  private void resetButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_resetButtonMouseClicked
    setup();
  }//GEN-LAST:event_resetButtonMouseClicked
  
  private void anaglyphCheckboxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_anaglyphCheckboxMouseClicked
    setAnaglyphMode();
  }//GEN-LAST:event_anaglyphCheckboxMouseClicked
  
  private void runStopCheckboxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_runStopCheckboxMouseClicked
    startStop();
  }//GEN-LAST:event_runStopCheckboxMouseClicked
  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JCheckBox anaglyphCheckbox;
  private javax.swing.JPanel bodyControlPanel;
  public javax.swing.JCheckBox cometCheckBox;
  private javax.swing.JTextField cometsTextField;
  private javax.swing.JPanel controlPanel;
  private javax.swing.JCheckBox darkEnergyCheckBox;
  private javax.swing.JTextField darkEnergyTextField;
  private javax.swing.JPanel defaultPanel;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JButton launchButton;
  public javax.swing.JCheckBox planetCheckBox;
  private javax.swing.JButton resetButton;
  private javax.swing.JCheckBox runStopCheckbox;
  private javax.swing.JTextField timeStepTextField;
  // End of variables declaration//GEN-END:variables
}

// the animation panel class
class SpacePanel extends java.awt.Panel {
  
  public boolean anaglyphic = false;
  private PhysicsEngine engine = null;
  private boolean initialized = false;
  double rotation;
  double anaglyphDepth = 0.1;
  double drawingScale = 1e-12;
  double sinVal, cosVal, oldAngle = 1e30;
  SpaceApplet parent;
  boolean busy = false;
  Stack undrawData;
  Image image = null;
  Dimension old_size = null;
  
  SpacePanel(SpaceApplet p) {
    parent = p;
    rotation = 20 * parent.toRad;
    engine = new PhysicsEngine(parent);
    setBackground(Color.black);
    undrawData = new Stack();
    initialized = true;
  }
  
  public void testRepaint() {
    
    if (!busy) {
      busy = true;
      repaint();
      getToolkit().sync();
      busy = false;
      } else {
      System.out.println("skipped frame");
    }
  }
  
  public void paint(Graphics g) {
    update(g);
  }
  
  public void update(Graphics g) {
    double time_step_seconds = parent.timeStepHours * 3600; // convert to seconds
    Dimension size = getSize();
    
    // create background drawing buffer
    
    if (image == null || old_size == null || !old_size.equals(size)) {
      image = createImage(size.width, size.height);
      Graphics bg = image.getGraphics();
      bg.setColor(Color.black);
      bg.fillRect(0, 0, size.width, size.height);
      old_size = size;
    }
    
    Graphics bg = image.getGraphics();
    drawObjects(time_step_seconds, bg, size);
    
    // transfer finished image to display
    
    g.drawImage(image, 0, 0, this);
  }
  
  private void drawObjects(double time_step, Graphics g, Dimension size) {
    
    double cx = size.width / 2;
    double cy = size.height / 2;
    int ovalSize = (int) (cx / 96);
    ovalSize = (ovalSize < 4) ? 4 : ovalSize;
    undrawAll(g, ovalSize);
    
    // draw "sun" oval (always)
    
    g.setColor(Color.white);
    drawOval(g, (int) cx, (int) cy, ovalSize * 2);
    if (anaglyphic) {
      g.setXORMode(Color.white);
      } else {
      g.setPaintMode();
    }
    if (parent.planetCheckBox.isSelected()) {
      
      // skip sun redraw here
      
      drawSubset(1, time_step, cx, cy, ovalSize, g, size, parent.orbitData.planet_array);
      
    }
    if (parent.cometCheckBox.isSelected()) {
      drawSubset(0, time_step, cx, cy, ovalSize, g, size, parent.orbitData.comet_array);
    }
    
  }
  
  private void drawSubset(int first, double time_step, double cx, double cy, int ovalSize, Graphics g, Dimension size, OrbitingBody[] array) {
    engine.processObjects(array, time_step);
    for (int i = first; i < array.length; i++) {
      OrbitingBody p = array[i];
      Cart3 pp = scaleOrbitingBody(p, cx, cy, rotation);
      
      // detect anaglyphic mode
      
      if (!anaglyphic) {
        g.setColor(p.color);
        drawOval(g, (int) pp.x, (int) pp.y, ovalSize);
        } else {
        
        // create two perspective views in different colors
        
        int ax = (int) (pp.x + pp.z * anaglyphDepth);
        int bx = (int) (pp.x - pp.z * anaglyphDepth);
        g.setColor(Color.cyan);
        drawOval(g, (int) ax, (int) pp.y, ovalSize);
        g.setColor(Color.red);
        drawOval(g, (int) bx, (int) pp.y, ovalSize);
      }
    }
  }
  
  private void drawOval(Graphics g, int x, int y, int ovalSize) {
    Point p = new Point(x, y);
    undrawData.push(p);
    g.fillOval(x, y, ovalSize, ovalSize);
  }
  
  // rather than slowly erase the drawing buffer
  // redraw the drawn objects in black (faster)
  private void undrawAll(Graphics g, int ovalSize) {
    g.setPaintMode();
    g.setColor(Color.black);
    while (!undrawData.empty()) {
      Point p = (Point) undrawData.pop();
      g.fillOval(p.x, p.y, ovalSize, ovalSize);
    }
  }
  
  // scale and rotate coordinates
  private Cart3 scaleOrbitingBody(OrbitingBody p, double cx, double cy, double a) {
    
    Cart3 cp = new Cart3();
    cp.x = (p.pos.x * drawingScale * cx) + cx;
    if (a != oldAngle) {
      sinVal = Math.sin(a);
      cosVal = Math.cos(a);
      oldAngle = a;
    }
    double py = p.pos.z * sinVal + p.pos.y * cosVal;
    double pz = p.pos.z * cosVal + p.pos.y * sinVal;
    cp.y = (py * drawingScale * cy) + cy;
    cp.z = (pz * drawingScale * cy);
    return cp;
  }
  
  public void toggleAnaglyphic() {
    anaglyphic = !anaglyphic;
    testRepaint();
  }
};

// the physics engine class is responsible for
// computing gravitation as well as
// dark energy accelerations
class PhysicsEngine {
  
  double G = 6.6742e-11; // universal gravitational constant
  OrbitingBody sun;
  SpaceApplet parent;
  
  PhysicsEngine(SpaceApplet p) {
    parent = p;
    this.sun = parent.orbitData.planet_array[0];
  }
  
  void UpdatePosition(OrbitingBody pa, OrbitingBody pb, double dt, double darkEnergy) {
    // don't compute self-gravitation
    if (pa != pb) {
      // this trig-free method does this:
      // 1. vel += radius * -G * mass * dt * radius.abs()^-3
      // 2. pos += vel * dt
      Cart3 radius = pa.pos.sub(pb.pos);
      pa.vel.addTo(radius.mult(darkEnergy - G * pb.mass * dt * Math.pow(radius.abs(), -3)));
      pa.pos.addTo(pa.vel.mult(dt));
    }
  }
  
  // perform all gravitational computations
  public void processObjects(OrbitingBody[] array, double dt) {
    double darkEnergy = (parent.darkEnergyMode) ? parent.darkEnergy : 0.0;
    // compute gravitation only wrt the sun, not wrt all other bodies
    for (OrbitingBody ob : array) {
      UpdatePosition(ob, sun, dt, darkEnergy);
    }
  }
};

// an orbiting body data class
class OrbitingBody {
  
  String name;
  double radius;
  Cart3 pos;
  Cart3 vel;
  double mass;
  Color color;
  
  OrbitingBody(String name, double radius, Cart3 pos, Cart3 vel, double mass, Color color) {
    this.name = name;
    this.radius = radius;
    this.pos = pos;
    this.vel = vel;
    this.mass = mass;
    this.color = color;
  }
};

// a source for orbiting objects
// including the entire solar system
// plus a random "comet" generator
class OrbitingData {
  
  SpaceApplet parent;
  public OrbitingBody[] planet_array = null;
  public OrbitingBody[] comet_array = null;
  public Color planet_colors[] = {
    Color.white, Color.yellow, Color.cyan, new Color(128, 128, 255),
    Color.red, Color.green, Color.magenta, Color.blue
  };
  String data =
  "Name,OrbitRad,BodyRad,Mass,OrbitVel\n"
  + "Sun,0,695000000,1.989E+030,0\n"
  + "Mercury,57900000000,2440000,3.33E+023,47900\n"
  + "Venus,108000000000,6050000,4.869E+024,35000\n"
  + "Earth,150000000000,6378140,5.976E+024,29800\n"
  + "Mars,227940000000,3397200,6.421E+023,24100\n"
  + "Jupiter,778330000000,71492000,1.9E+027,13100\n"
  + "Saturn,1429400000000,60268000,5.688E+026,9640\n"
  + "Uranus,2870990000000,25559000,8.686E+025,6810\n"
  + "Neptune,4504300000000,24746000,1.024E+026,5430\n"
  // I guess Pluto isn't really a planet any more
  
  + "Pluto,5913520000000,1137000,1.27E+022,4740\n";
  
  OrbitingData(SpaceApplet p) {
    parent = p;
    setup();
  }
  
  public void setup() {
    readOrbitingBodies();
    readComets(parent.cometCount);
  }
  
  private void readOrbitingBodies() {
    Vector list = new Vector();
    String planetStrings[] = data.split("\n");
    double vals[] = new double[4];
    for (int i = 1; i < planetStrings.length; i++) {
      String fields[] = planetStrings[i].split(",");
      for (int j = 1; j < fields.length; j++) {
        vals[j - 1] = Double.parseDouble(fields[j]);
      }
      Cart3 pos = new Cart3(-vals[0], 0, 0);
      Cart3 vel = new Cart3(0, 0, vals[3]);
      Color c = planet_colors[(i - 1) % planet_colors.length];
      OrbitingBody planet = new OrbitingBody(fields[0], vals[1], pos, vel, vals[2], c);
      list.add(planet);
    }
    planet_array = (OrbitingBody[]) list.toArray(new OrbitingBody[list.size()]);
  }
  
  public void readComets(int count) {
    Vector list = new Vector();
    for (int i = 0; i < count; i++) {
      String name = "comet" + i;
      double ca = Math.random() * 360; // angle in x-z plane
      double cr = (Math.random() * 100000) + 100000; // distance from sun
      cr *= 4e6;
      Cart3 pos = new Cart3(cr * Math.sin(ca * parent.toRad), 0, cr * Math.cos(ca * parent.toRad));
      // comet initial velocity
      double v = ((Math.random() * 200) + 100) * 50.0;
      v = (i % 2 == 1) ? -v : v;
      Cart3 vel = new Cart3(0, v, 0);
      Color c = planet_colors[i % planet_colors.length];
      OrbitingBody comet = new OrbitingBody(name, 1e3, pos, vel, 1e9, c);
      list.add(comet);
    }
    comet_array = (OrbitingBody[]) list.toArray(new OrbitingBody[list.size()]);
  }
};

// a convenience class for handling 3D Cartesian vectors
class Cart3 {
  
  double x = 0, y = 0, z = 0;
  
  Cart3() {
  }
  
  Cart3(double x, double y, double z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }
  
  Cart3(Cart3 o) {
    x = o.x;
    y = o.y;
    z = o.z;
  }
  
  public Cart3 sub(Cart3 a) {
    return new Cart3(x - a.x, y - a.y, z - a.z);
  }
  
  public Cart3 mult(double m) {
    return new Cart3(x * m, y * m, z * m);
  }
  
  public Cart3 addTo(Cart3 a) {
    x += a.x;
    y += a.y;
    z += a.z;
    return this;
  }
  
  public double abs() {
    return Math.sqrt(x * x + y * y + z * z);
  }
  
  public String toString() {
    return x + "," + y + "," + z;
  }
};

// a generic frame class for stand-alone operation
class GenericFrame extends Frame {
  
  public GenericFrame(String str) {
    super(str);
    addWindowListener(new WindowAdapter() {
        
        public void windowClosing(WindowEvent e) {
          dispose();
          System.exit(0);
        }
    });
  }
}
          
 

Home | Science | * Dark Energy |     Share This Page