//created=17 Apr 07
//description=Describe the location and path of an orbiting object using the six Keplerian elements.
//title=Keplerian Elements
//publish=true
//titlebar=Classical Orbital Elements
//tags=space
 
 
import SGCamera.*;
import SGGUI.*;
import Tuple.*;
 
SGGUI guiControl;
VerticalLayout parameterBox;
 
 
SGCamera cam;
 
float radius = 100;
PFont font;
 
 
int numStars = 1500;
Tuple [] stars = new Tuple[numStars];
 
int SEMIMAJOR = 1;
int INCLINATION = 2;
int RIAAN = 3;
int TRUEANOM = 4;
int ARGOFPER = 5;
int ECCENTRICITY = 0;
 
int last = 0;
 
 
 
SGSlider semiMajor;
SGSlider eccentricity;
SGSlider inclination;
SGSlider RAAN;
SGSlider argOfPer;
SGSlider trueAnom;
 
SGCheckBox showLabels;
 
float b = 0;
 
 
 
void setup (){
size(550,550,P3D);
 
font = loadFont("writing.vlw");
textFont(font);
textMode(SCREEN);
//always declare the GUI before the cam or else the moue handling will be improper
guiControl = new SGGUI(this,font);
cam = new SGCamera(this,-500,-500,500,0,0,0, SGCamera.ORBIT, SGCamera.ZOOM);
 
 
for (int i = 0; i < numStars; i++){
stars[i] = new Tuple(random(-50000,50000),random(-50000,50000),random(-50000,50000));
}
parameterBox = new VerticalLayout(guiControl,10,10,140);
parameterBox.title = "Orbital Params";
semiMajor = new SGSlider( "SemiMajor Axis",300,20,2*radius,10000);
semiMajor.addGUIListener(this);
eccentricity = new SGSlider("Eccentricity",.5,.01,.01,.99);
eccentricity.addGUIListener(this);
 
inclination = new SGSlider( "Inclination",1,.1,0,PI);
inclination.addGUIListener(this);
//inclination.toDegrees = true;
RAAN = new SGSlider( "Right Angle",.5,.1,0,6.28);
RAAN.addGUIListener(this);
//RAAN.toDegrees = true;
argOfPer = new SGSlider( "Arg of Perigee",.25,.1,0,6.28);
argOfPer.addGUIListener(this);
//argOfPer.toDegrees = true;
trueAnom = new SGSlider( "True Anomaly",.6,.1,0,6.28);
trueAnom.addGUIListener(this);
//trueAnom.toDegrees = true;
showLabels = new SGCheckBox("Labels",1);
parameterBox.add(semiMajor);
parameterBox.add(eccentricity);
parameterBox.add(inclination);
parameterBox.add(RAAN);
parameterBox.add(argOfPer);
parameterBox.add(trueAnom);
parameterBox.add(showLabels);
guiControl.add(parameterBox);
}
 
void draw(){
 
 
background(0);
lights();
 
cam.feed();
 
 
stroke(100);
beginShape(POINTS);
for (int i = 0; i < numStars; i++){
vertex(stars[i].x,stars[i].y,stars[i].z);
}
endShape();
 
fill(100,100,200);
noStroke();
sphere(radius);
 
 
 
stroke(255);
 
//draw the poles
beginShape(LINES);
//north pole
vertex(0,-radius,0);
vertex(0,-radius-radius,0);
//First line of Aires
vertex(0,0,0);
vertex(0,0,3*radius);
endShape();
 
noFill();
stroke(128);
rotateX(PI/2);
//equatorial belt
 
ellipse(0,0,3*radius,3*radius);
fill(255);
if (showLabels.getVal() == true) text("North Pole",screenX(0,0,2*radius),screenY(0,0,2*radius));
fill(255);
if (showLabels.getVal() == true) text("Line of Aries",screenX(0,3*radius,0),screenY(0,3*radius,0));
 
 
//render the RAAN arc
if (last == RIAAN) fill(255,255,0,100);
else fill(255,100);
pushMatrix();
rotateX(PI);
rotateZ(-PI/2);
arc(0,0,3*radius,3*radius,0,RAAN.getVal());
popMatrix();
//rotate coordinates by RAAN
rotateZ(-RAAN.getVal());
fill(255);
 
pushMatrix();
rotateX(PI/2);
if (last == INCLINATION) fill(255,255,0,100);
else fill(255,100);
arc(0,0,3*radius,3*radius,0,inclination.getVal());
fill(255);
if (showLabels.getVal() == true) text("Inclination",screenX(1.5*radius*cos(inclination.getVal()),1.5*radius*sin(inclination.getVal()),0),screenY(1.5*radius*cos(inclination.getVal()),1.5*radius*sin(inclination.getVal()),0));
 
popMatrix();
fill(255);
if (showLabels.getVal() == true) text("Ascending Node",screenX(0,1.25*radius,0),screenY(0,1.25*radius,0));
//rotate by inclination
rotateY(-inclination.getVal());
fill(255,255,255,128);
 
//rotate by the argument of Perigee
rotateZ(-argOfPer.getVal() + PI/2);
 
//eccentricity = focal length/semimajoraxis
//we have eccentricity and semimajor, get the focal length
float focus = eccentricity.getVal()*semiMajor.getVal();
//calculate the semiminor axis
//b = a*sqrt(1-e^2);
b = semiMajor.getVal()*sqrt(1-eccentricity.getVal()*eccentricity.getVal());
 
//don't penetrate the earth with your perigee
//use iterative multiplicative for smooth scaling
if ((semiMajor.getVal() - focus) < radius) semiMajor.setVal(1.1*semiMajor.getVal());
 
//translate reference frame so that the occupied focus is the earth
translate(-focus,0,0);
 
//draw the semimajor axis
if (last == SEMIMAJOR) stroke(255,255,0,100);
else stroke(255);
line(0,0,semiMajor.getVal(),0);
fill(255);
if (showLabels.getVal() == true) text("Perigee",screenX(semiMajor.getVal(),0,0),screenY(semiMajor.getVal(),0,0));
if (showLabels.getVal() == true) text("Apogee",screenX(-semiMajor.getVal(),0,0),screenY(-semiMajor.getVal(),0,0));
stroke(255,100);
noFill();
ellipse(0,0,2*semiMajor.getVal(),2*b);
 
 
//render the argument of perigee
pushMatrix();
stroke(255);
if (last == ARGOFPER){
fill(255,255,0,100);
stroke(255,255,0,100);
}
else{
fill(0,0,255,100);
stroke(255,100);
}
 
translate(focus,0,0);
rotateZ(argOfPer.getVal());
beginShape();
vertex(0,0,0);
float tp;
for (int i = 0; i <= 30; i++){
tp = i*-argOfPer.getVal()/30;
vertex(1.5*radius*cos(tp),1.5*radius*sin(tp),0);
}
vertex(0,0,0);
 
endShape();
popMatrix();
 
 
 
//render the true anomaly
pushMatrix();
stroke(255);
if (last == TRUEANOM) fill(255,255,0,100);
else fill(255,100);
beginShape();
 
//parametric equation of an ellipse from center of earth
float tmp;
vertex(focus,0,0);
for (int i =0; i <= 30; i++){
tmp = i*-trueAnom.getVal()/30;
vertex(semiMajor.getVal()*cos(tmp),b*sin(tmp),0);
}
vertex(focus,0,0);
endShape();
popMatrix();
fill(255);
 
 
}
 
void GUIEventPerformed(GUIEvent e){
 
 
if (e.title.equals("Inclination")){
 
last = INCLINATION;
 
}
 
else if (e.title.equals("Eccentricity")){
 
last = ECCENTRICITY;
}
 
else if (e.title.equals("SemiMajor Axis")){
 
last = SEMIMAJOR;
}
 
else if (e.title.equals("Right Angle")){
 
last = RIAAN;
}
else if (e.title.equals("Arg of Perigee")){
 
last = ARGOFPER;
}
 
else if (e.title.equals("True Anomaly")){
 
last = TRUEANOM;
}
 
 
}
 
 
class Tuple{
float x,y,z;
public Tuple(float x, float y, float z){
this.x = x;
this.y = y;
this.z = z;
}
 
 
}