Using Arrays to Streamline the Solar System Implementation

In class, on Monday, Jan. 31st, after further discussing object-oriented programming, we turned to Java arrays (which by the way are very much like C arrays).

Chapters 8 and 9 (on objects and arrays) are as far as we will go in Shifmann’s Learning Processing text. We now know enough Java to turn to his Nature of Code text.

To see how sophisticated we can be with what we have covered in the first three weeks, below is an implementation of the Solar System that is especially pretty.

Do not feel troubled if you are not remotely this far along! The most advanced thing we introduced, inheritance, will still be bewildering until it has had more time to sink in. Also, I didn’t even introduce the abstract keyword when I discussed inheritance, but Carmen’s implementation uses that. You only need to understand the much more pedestrian usage of objects that we did together in class. Study Chapter I of The Nature of Code to checkpoint and consolidate what you have learned.

What Carmen’s Simulation Looks Like

Solar System Screenshot Carmen's Version

Interacting with Carmen’s Simulation

  1. Try pressing the r key, the g key, and the m key to have the rocky planets, the gas giants, or the moons magnified, respectively.
  2. Even using magnification, some false scaling has had to be done in Carmen’s implementation. The scales of the solar system are too extreme to simultaneously show the entire solar system and the objects within it.
  3. To say the previous thing slightly differently: orbital radii of planets are stupendously larger than their diameters. If you want to appreciate just how different they are, try this incredibly well-done video: To Scale: The Solar System by Wylie Overstreet. It’s only 7 minutes, and even after several watchings you can still find more to appreciate. The main point of the video is that even if the Earth is just the size of a marble, you need a good fraction of the Black Rock desert(!) to represent the whole thing.

The Source Code

/*
Assignment 2: Solar System
 ---
 Includes rotating planets, the primary satelities of earth and jupiter, 
 a static starfield, and pretty colors. I know that I could have modified
 the Planet class to let moons and planets all be Planets, but honestly copying
 and pasting the code to a new class felt faster.
 */

final float dt = 0.01;
final char rocky = 'r';
final char gas = 'g';

Planet saturn;

//and the starfield
Star[] stars = new Star[5000];
Asteroid[] asteroidBelt = new Asteroid[2000];
Asteroid[] kuiperBelt = new Asteroid[2000];
SolarSystemBody[] solarSystem = new SolarSystemBody[13];

void setup() {
  size(1000, 1000);

  // frameRate(15);
  // gives each planet its color and position. Yet, because of how the class
  // is designed their x and y are still set to 0, 0
  solarSystem[0] = new Planet(0.5, 6.0, rocky, #555555);
  solarSystem[1] = new Planet(1.2, 11, rocky, #ddff77);
  solarSystem[2] = new Planet(1.3, 15, rocky, #88ff33);
  solarSystem[3] = new Planet(0.7, 23, rocky, #ff2200);
  solarSystem[4] = new Planet(13.8, 78, gas, #ff5500);
  solarSystem[5] = new Planet(11.5, 143, gas, #ffdd00);
  saturn = (Planet)solarSystem[5]; // Saturn has rings -- it is special-cased
  solarSystem[6] = new Planet(5.1, 287, gas, #66ddff);
  solarSystem[7] = new Planet(4.9, 450, gas, #ee33ff);
  solarSystem[8] = new Moon((Planet)solarSystem[2], 1.3, 0.038, 27.3, #555555);
  solarSystem[9] = new Moon((Planet)solarSystem[4], 1.4, 0.042, 1.8, #555555);
  solarSystem[10] = new Moon((Planet)solarSystem[4], 1.3, 0.067, 3.6, #555555);
  solarSystem[11] = new Moon((Planet)solarSystem[4], 1.5, 0.107, 7.2, #555555);
  solarSystem[12] = new Moon((Planet)solarSystem[4], 1.5, 0.188, 16.7, #555555);

  //calls the constructor of each star which random chooses their position onscreen.
  loadBelts();
  loadStars();
}


void draw() {
  //draw the static elements
  background(0);
  drawSun();
  drawStars();

  for (int i = 0; i < asteroidBelt.length; i++) {
    asteroidBelt[i].display();
    asteroidBelt[i].move();
  }
  for (int i = 0; i < kuiperBelt.length; i++) {
    kuiperBelt[i].display();
    kuiperBelt[i].move();
  }
  for (int i = 0; i < solarSystem.length; i++) {
    solarSystem[i].display();
    solarSystem[i].move();
  }
  drawRings();
}

void drawSun() {
  //crosshairs
  stroke(64);
  line(width/2, height/2 + 23, width/2, height/2 - 23);
  line(width/2 - 23, height/2, width/2 + 23, height/2);

  //the sun (it looks off center but that might just be me or my monitor???)
  ellipseMode(CENTER);
  noStroke();
  fill(#ffff00);
  ellipse(width/2, height/2, 5, 5);
}

void loadBelts() {
  for (int i = 0; i < asteroidBelt.length; i++) {
    asteroidBelt[i] = new Asteroid(50, 5);
  }
  for (int i = 0; i < kuiperBelt.length; i++) {
    kuiperBelt[i] = new Asteroid(550, 10);
  }
}

void loadStars() {
  //called in setup to initialize all the stars
  for (int i = 0; i < stars.length; i++) {
    stars[i] = new Star();
  }
}

void drawStars() {
  //draws the stars to screen
  for (int i = 0; i < stars.length; i++) {
    stars[i].display();
  }
}

void drawRings() {
  ellipseMode(CENTER);
  noFill();
  stroke(#ffdd00);

  if (keyPressed && key == 'g') {
    for (int i = 40*5; i >= 30*5; i -= 2) {
      ellipse(saturn.x(), saturn.y(), i, i);
    }
  } else {
    for (int i = 40; i >= 30; i -= 2) {
      ellipse(saturn.x(), saturn.y(), i, i);
    }
  }
}


//class code

abstract class SolarSystemBody {

  abstract void display();

  abstract void move();
}

abstract class SunOrbitingBody extends SolarSystemBody {

  float angVelocity;
  float angle;

  void move() {
    angle = angle + angVelocity * dt;
  }
}

class Planet extends SunOrbitingBody {
  float diameter;
  float distFromSun;
  color c;
  char type;

  Planet(float diameter, float distFromSun, char type, color c) {
    this.distFromSun = distFromSun;
    this.diameter = diameter;
    this.angVelocity = 2 * (float)Math.PI * (float)Math.pow(15.0 / distFromSun, 1.5);
    this.angle = 0.0;
    this.type = type;
    this.c = c;
  }

  float x() {
    return width/2 + distFromSun * (float)Math.cos(angle);
  }

  float y() {
    return height/2 - distFromSun * (float)Math.sin(angle);
  }

  void display() {
    ellipseMode(RADIUS);
    stroke(64);
    noFill();
    ellipse(width/2, height/2, distFromSun, distFromSun);

    noStroke();
    fill(c);

    if (keyPressed && key == type && type == 'r') {
      ellipse(x(), y(), diameter*10, diameter*10);
    } else if (keyPressed && key == type && type == 'g') {
      ellipse(x(), y(), diameter*5, diameter*5);
    } else {
      ellipse(x(), y(), diameter, diameter);
    }
  }
}

class Moon extends SolarSystemBody {
  Planet parent;
  float diameter;
  float distFromPlan;
  float angVelocity;
  float angle;
  color c;

  Moon(Planet parent, float diameter, float distFromPlan, float period, color c) {
    this.parent = parent;
    this.distFromPlan = distFromPlan;
    this.diameter = diameter;
    this.angVelocity = (float)TWO_PI * 365.24/period;
    this.angle = 0.0;
    this.c = c;
  }

  void display() {
    float x = parent.x() + (parent.diameter + distFromPlan) * (float)Math.cos(angle);
    float y = parent.y() - (parent.diameter + distFromPlan) * (float)Math.sin(angle);

    ellipseMode(CENTER);
    noStroke();
    fill(c);

    if (key == 'm' && keyPressed) {
      ellipse(x, y, diameter*10, diameter*10);
    } else {
      ellipse(x, y, diameter, diameter);
    }
  }

  void move() {
    angle = angle + angVelocity * dt;
  }
}

class Star extends SolarSystemBody {
  float x;
  float y;

  Star() {
    x = random(0, width);
    y = random(0, height);
  }

  void move() {
  }

  void display() {
    stroke(255);
    point(x, y);
  }
}

class Asteroid extends SunOrbitingBody {
  float distFromSun;

  Asteroid(float distFromSun, float spread) {
    this.distFromSun = randomGaussian() * spread + distFromSun;
    this.angVelocity = 2 * (float)Math.PI * (float)Math.pow(15.0 / distFromSun, 1.5);
    this.angle = random(0, TWO_PI);
  }

  void display() {
    float x = width/2 + (distFromSun * (float)Math.cos(angle));
    float y = height/2 + (-1 * distFromSun * (float)Math.sin(angle));
    stroke(180);
    point(x, y);
  }
}