package solarsystem;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
*
* @author NDUNN
* @date Jan 29, 2010
*/
// Make the sun this proportion of screen DEFAULT_WIDTH
private static final double SUN_RADIUS_PROPORTION = 0.2;
private static final double EARTH_RADIUS_PROPORTION = .3 * SUN_RADIUS_PROPORTION;
private static final double MOON_RADIUS_PROPORTION = .2 * EARTH_RADIUS_PROPORTION;
private static final double EARTH_DISTANCE_PROPORTION_SCREEN = 0.4;
private static final double MOON_DISTANCE_PROPORTION_SCREEN = 0.1;
private static final int DEFAULT_WIDTH = 640;
private static final int DEFAULT_HEIGHT = 640;
private static final int NUM_STARS = 100;
private static final int MAX_STAR_RADIUS = 3;
private static final double TWO_PI
= 2.0
* Math.
PI;
private SolarSystemModel model;
private int[] starX;
private int[] starY;
private int[] starRadius;
public SolarSystemView(SolarSystemModel model) {
super();
this.model = model;
setPreferredSize
(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT
));
createStarField(DEFAULT_WIDTH, DEFAULT_HEIGHT, MAX_STAR_RADIUS);
}
/**
* Creates and populates our arrays of star x values, star y values, and
* star radii
* @param width what is max x value we should consider for star
* @param height what is max y value we should consider for star
*/
private void createStarField(int width, int height, int maxRadius) {
// Create the arrays
starX = new int[NUM_STARS];
starY = new int[NUM_STARS];
starRadius = new int[NUM_STARS];
// Fill them in with random values
for (int i = 0; i < NUM_STARS; i++) {
starX[i] = random.nextInt(width);
starY[i] = random.nextInt(height);
starRadius[i] = random.nextInt(maxRadius);
}
}
/**
* Draws a black backdrop with star field
* @param g2
*/
// Draw background as black
g2.
setColor(Color.
BLACK);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.
setColor(Color.
WHITE);
for (int i = 0; i < NUM_STARS; i++) {
g2.fillOval(starX[i], starY[i], starRadius[i], starRadius[i]);
}
}
/**
*
* @param g2 graphics context with (0,0) in center of screen (where sun will
* be centered)
*/
int sunRadius = (int) (SUN_RADIUS_PROPORTION * getWidth());
g2.setPaint(sunColor);
g2.fillOval(-sunRadius/2, -sunRadius/2, sunRadius, sunRadius);
}
/**
* Draws the earth to the screen, whose position is dependent upon the
* day of the year
* @param g2 the graphics context with its origin in the center of the sun
*/
// Draw the earth
// Calculate what portion along its orbit the earth is, and thus how
// far to rotate about our centerpoint
double earthTheta = map(model.getDay(), 0, SolarSystemModel.DAYS_PER_EARTH_REVOLUTION_AROUND_SUN, 0, TWO_PI);
// Rotate our coordinate system by that much
g2.rotate(earthTheta);
// Translate the earth
int distanceFromEarthToSun = (int) (EARTH_DISTANCE_PROPORTION_SCREEN * getWidth());
g2.translate(distanceFromEarthToSun, 0);
int earthRadius = (int) (EARTH_RADIUS_PROPORTION * getWidth());
g2.setPaint(earthColor);
g2.fillOval(-earthRadius/2, -earthRadius/2, earthRadius, earthRadius);
}
/**
* Draw the moon to the screen, whose position is dependent upon that of
* the earth and the day of the year, which dictates its position along
* its orbit around earth
* @param g2 the graphics context with its origin in the center of the earth
*/
double moonTheta = map(model.getDay(), 0, SolarSystemModel.DAYS_PER_MOON_ORBIT_AROUND_EARTH, 0, TWO_PI);
int moonRadius = (int) (MOON_RADIUS_PROPORTION * getWidth());
g2.
setColor(Color.
WHITE);
g2.rotate(moonTheta);
int distanceFromEarthToMoon = (int) (MOON_DISTANCE_PROPORTION_SCREEN * getWidth());
// Translate the earth
g2.translate(distanceFromEarthToMoon, 0);
g2.fillOval(-moonRadius/2, -moonRadius/2, moonRadius, moonRadius);
}
@Override
public void paintComponent
(Graphics g
) {
drawSpaceBackdrop(g2);
// Set the origin to be in the center of the screen
g2.translate(getWidth()/2, getHeight()/2);
// Order matters, since the earth placement is dependent upon the sun
// placement, and the moon placement is dependent upon the earth placement
drawSun(g2);
drawEarth(g2);
drawMoon(g2);
}
public static void main
(String[] args
) {
final SolarSystemModel model = new SolarSystemModel();
final SolarSystemView view = new SolarSystemView(model);
model.addObserver(view);
panel.add(view);
final JSlider daySlider
= new JSlider(0,SolarSystemModel.
DAYS_PER_EARTH_REVOLUTION_AROUND_SUN);
daySlider.setPaintLabels(true);
daySlider.setPaintTicks(true);
daySlider.setMajorTickSpacing(100);
panel.add(daySlider);
model.setDay(daySlider.getValue());
}
});
frame.add(panel);
frame.
setDefaultCloseOperation(JFrame.
EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
repaint();
}
/**
* @param value The incoming value to be converted
* @param low1 Lower bound of the value's current range
* @param high1 Upper bound of the value's current range
* @param low2 Lower bound of the value's target range
* @param high2 Upper bound of the value's target range
*/
public static final double map(double value, double low1, double high1, double low2, double high2) {
double diff = value - low1;
double proportion = diff / (high1 - low1);
return lerp(low2, high2, proportion);
}
// Linearly interpolate between two values
public static final double lerp(double value1, double value2, double amt) {
return ((value2 - value1) * amt) + value1;
}
}