<fieldset class=right><legend>camera</legend><select id=select_camera class="planet-select"></select>
  </fieldset>
<div id="debug">
  <fieldset>
    <legend>fps</legend>
    <output id=fps></output>
  </fieldset>
  <fieldset>
    <legend>
        info
    </legend>
    <label>
      from <select id="select_vel_from" class="planet-select"></select>
    </label>
    <label>
      to <select id="select_vel_to" class="planet-select"></select>
    </label><br>
    vel:  
    <span id=output_vel></span> km/s
    |
    dist:
    <span id=output_dist></span> km
  </fieldset>
  
  <fieldset>
    <legend>time</legend>
    warp: <span id=output_warp></span>&times;
    
    <!-- zoom: <span id=output_zoom></span> -->
  </fieldset>
</div>

<div id="controls">
    <div>
    warp
  <button class=small data-action="warp_slower">&laquo;</button>
    <button class=small data-action="warp_faster">&raquo;</button>
</div>
  <div>
  
  <button data-action="zoom_out">&minus;</button>
  <button data-action="thrust_forward">↑</button>
  <button data-action="zoom_in">&plus;</button>
  
  <button data-action="turn_left">←</button>
  <button data-action="thrust_backward">↓</button>
  <button data-action="turn_right">→</button>
  </div>
    
</div>
body {
    margin:0;
    overflow: hidden;
    background:black;
    color:white;
    font:.9em Verdana,sans-serif
}
input,select,textarea,button {
  font:inherit;
}
#controls {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 6em;
 font-size:2em;
  div {clear: both;}
  button {
    font:inherit;
    font-weight: bold;
    border:0;
    width: 2em;
    height: 2em;
    line-height: 2em;
    padding: 0;
    float: left;
    background:rgba(#000,.5);
    color:white;
    border:1px solid currentColor;
    &.small {
      height: 1em;
      width: 1em;
      line-height: 1em;
    }
    &.active {
      background:rgba(#666,.5);
    }
  }
}

fieldset {
  float: left;
  //position: absolute;
  &.right {
    float: right;
  }
}
[id=debug] {
  position: absolute;
}
[id=help] {
  position: absolute;
  left: 0;
  padding: 1em;
  background:rgba(0,0,0,.5);
  width: 25%;
  line-height: 1;
  transition:opacity .5s ease;
  opacity:1;
}
.hidden {
  opacity:0;
  pointer-events:none;
}
// canvas {cursor:none}
h1{
  margin: 0;
  font-size:1.2em;
  text-transform: uppercase;
  letter-spacing:.025em;
}
canvas{
  position: absolute;
  z-index: -1;
}
var planets = [];
var PLANET_LIMIT=128;

var G = 6.67408e-11;
var GRAVITY_POW = 2; // Math.pow ( distance, GRAVITY_POW )

var enableTrails = false;
var space_layers = []

var TIME_FACTOR = 1;
var SHIP_THRUST = 1e6;
var SHIP_ROTATE = .1;

var draw_pow = .33;
var draw_color; 
var zoom = .000003;
var $planetSelect;
var $cameraSelect;
var $velFromSelect;
var vel_from_value = -1;
var $velToSelect;
var vel_to_value = 3;

var myCameraObject;
// var delta time 
var output_vel = document.getElementById("output_vel");

var output_dist = document.getElementById("output_dist");

var output_warp = document.getElementById("output_warp");

var output_fps = document.getElementById("fps");

var BG_COLOR;
//var output_zoom = document.getElementById("output_zoom");

function mouseWheel(e) {
  var d = e.delta > 0 ? 1/1.1 : 1.1;
  zoom *= d
  resetTrails();
}
function resetTrails() {
  for (var x = 0; x < planets.length; x++) {
    planets[x].locHistory = [];
  }
  myShip.locHistory = [];
  background(0);
}

var maskImage;


function preload() {
  preload_planet_images(start_planets);
  
  maskImage = loadImage('https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/mask.png');
}

function preload_planet_images(obj) {
  for(var x=0;x<obj.length; x++) {
    if (obj.hasOwnProperty(x)) {
      
      var p = obj[x];
      if (p.image) {
        p.img = loadImage(p.image);
      }
      if (p.children) {
        preload_planet_images(p.children);
      }
    }
  }
}

var start_planets = [
  {name:'sun',
   mass:1.9884e32,
   dist:0,
   density:1.408,
   vel:0,
   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/sun.jpg',
   color:[255,255,0],
   children:[
  {
    name:'mercury',
    mass:3.301e25,
    dist:5.7909e10,
    density:5.427,
    vel:47.36,
    color:[200,100,0],
    image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/mercury.jpg'
  },
  {
    name:'venus',
    mass: 4.869e26, 
    dist:1.0816e11,
    density:5.243,
    vel:35.02,
    color:[100,0,200],
    image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/venus.jpg'
  },
  {
    name:'earth',
    mass: 5.974e26,
    dist:1.496e11,
    density:5.515,
    vel:29.78,
    color:[0,0,255],
    image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/earth.jpg',
    children: [{
      name:'moon',
      mass:7.349e24, 
      density:3.341,
      color:[200,200,100],
      dist:-3.8440000e8,
      vel:1.023,
      image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/moon.jpg'
    }]
  },
  {
    name:'mars',
    mass:6.419e25,
    dist:2.2799e11,
    vel:24.13,
    density:3.933,
    color:[255,0,0],
    image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/mars.jpg',
    children:[{
      name:'phobos',
      mass:1.072e18,
      vel:2.139,
      dist:-9378000,
      density:1.471,
    },
    {
      name:'deimos',
      mass:1.8e17,
      vel:1.351,
      dist:-23459000,
      density:1.471
    }]
  },
  {
    name:'jupiter',
    mass:1.899e29 ,
    dist:7.7836e11,
    vel:13.07,
    density:1.326,
    color:[200,100,50],
    image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/jupiter.jpg',
    children: [{
      name:'io',
      mass:8.9e24,
      density:3.56,
      dist:4.218e8,
      vel:17.3,
      color:[100,200,0],
      image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/io.jpg'
    },{
      name:'europa',
      mass:4.8e24,
      density:3.01,
      dist:6.71100e8,
      vel:13.74,
      color:[200,100,0],
      image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/europa.jpg',
    },
    {
      name:'ganymed',
      mass:1.482e25,
      density:1.940,
      vel:10.88,
      dist:1.0704e9,
      color:[170,160,150],
      image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/ganymed.jpg',
    },
    {
      name:'callisto',
      mass:1.1e25,
      density:1.83,
      dist:1.8827e9,
      vel:8.2,
      color:[150,100,100],
      image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/callisto.jpg'
      
    },
    {
      name:'amalthea',
      mass:2.1e20,
      density:0.85,
      dist:1.81400e8,
      vel:26.49,
      image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/amalthea.jpg'
    },
    {
      name:'himalia',
      mass:6.7e20,
      density:2.6,
      dist:1.1461e10,
      vel:3.3,
    },
    {
      name:'elara',
      mass:8.66e19,
      density:2.6,
      dist:1.1741e10,
      vel:3.25,
    }
    ]
  } ,
     {
       name:'saturn',
       mass:5.685e28,
       density:0.687,
       dist:1.4335e12,
       vel:9.69,
       color:[220,180,150],
       image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/saturn.png',
       imageScale:2.35,
       children:[{
         name:'mimas',
         mass:3.75e21,
         dist:1.856e8,
         vel:14.322,
         density:1.1479,
         color:[100,100,100],
         image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/mimas.jpg'
       },
       {
         name:'enceladus',
         mass:1.08e22,
         dist:2.381e8,
         vel:12.6353,
         color:[150,150,220],
         density:1.608,
         image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/enceladus.jpg'
       },
                 {
                   name:'tethys',
                   mass:6.18e22,
                   dist:2.947e8,
                   density:0.984,
                   vel:11.35,
                   color:[160,160,160],
                   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/tethys.jpg',
                 },
                 {
                   name:'dione',
                   mass:1.096e23,
                   dist:3.7742e8,
                   vel:10.03,
                   density:1.476,
                   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/dione.jpg',
                 },
                 {
                   name:'rhea',
                   mass:2.3166e23,
                   dist:5.2704e8,
                   vel:8.48,
                   color:[180,180,200],
                   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/rhea.jpg',
                 },
                 {
                   name:'titan',
                   mass:1.345e25,
                   density:1.88,
                   dist:1.22183e9,
                   vel:5.57,
                   color:[250,230,120],
                   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/titan.jpg',
                 },
                 {
                   name:'hyperion',
                   mass:5.6199e20,
                   dist:1.4641e9,
                   vel:5,
                   color:[220,180,140],
                 },
                 {
                   name:'iapetus',
                   mass:1.6e23,
                   color:[160,220,170],
                   dist:3.5613e9,
                   vel:3.26,
                   density:1.27,
                   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/iapetus.jpg',
                 },
                 {
                   name:'phoebe',
                   mass:8.3e20,
                   vel:1.7,
                   dist:1.2952e10
                 },
                 
       ],
       
     },
     {
       name:'uranus',
       mass:8.683e27,
       dist:2.8724e12,
       vel:6.81,
       color:[50,200,200],
       image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/uranus.jpg',
       children:[{
         name:'ariel',
         mass:1.353e23,
         vel:5.51,
         dist:1.909e8,
         density:1.66,
         image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/ariel.jpg',
         
       },
                 {
                   name:'umbriel',
                   mass:1.72e23,
                   dist:2.663e8,
                   vel:4.67,
                   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/umbriel.jpg',
                 },
                 
       {
         name:'titania',
         mass:3.527e23,
         dist:4.363e8,
         vel:3.87,
         color:[230,190,140],  
         density:1.711,
         image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/titania.jpg'
       },
                 {
                   name:'oberon',
                   mass:3.014e23,
                   dist:5.83519e8,
                   vel:3.15,
                   color:[220,160,200],
                   image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/oberon.jpg',
                 },
                 {
                   name:'miranda',
                   mass:6.59e21,
                   dist:1.29872e8,
                   vel:6.68,
                   color:[240,240,240],
                   
                 }
       ]
     },
     {
       name:'neptun',
       mass:1.0243e28,
       dist:4.4984e12,
       vel:5.43,
       color:[50,0,255],
       image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/neptun.jpg',
       children:[{
         name:'triton',
         mass:2.1e24,
         dist:3.54759e8,
         vel:4.39,
         color:[240,230,150],
         image:'https://s3-us-west-2.amazonaws.com/s.cdpn.io/36559/triton.jpg'
         
       },
                 {
                   name:'proteus',
                   mass:5e21,
                   vel:7.623,
                   dist:1.17647e8,
                   
                 },
                 {
                   name:'nereid',
                   mass:3.1e21,
                   vel:.934,
                   dist:5.513787e9
                 }
                 ]
     }
   ]
  },
  
]

function createAllChildren(p,pl) {
  for (var x = 0; x < p.children.length; x++) {
    var cp = p.children[x]; 
    if (cp.dist && cp.mass) {
    var cpl = new Planet(cp.name, cp.dist+pl.loc.x,pl.loc.y,cp.mass,cp.density?cp.density:1, cp.color?color(cp.color):undefined, cp.img?cp.img:undefined,pl.vel.x, -cp.vel*1e4 + pl.vel.y, pl, cp.imageScale)
    planets.push(cpl); 
   //console.log("created " + cp.name);
    if (cp.children) {
      createAllChildren(cp,cpl);
    }
    }

  }
}

function setup() {
  
 for ( var x = 0; x < start_planets.length; x++) {
   var p = start_planets[x];
   var pl = new Planet(p.name, p.dist,0,p.mass, p.density?p.density:1, p.color?color(p.color):undefined,p.img?p.img:undefined,0,-p.vel*1e4,undefined,p.imageScale);
   planets.push(pl);
   
   //console.log("created " + p.name);
   if (p.children) {
     
   createAllChildren(p,pl);
   }
   
 } 
  
  var earth = start_planets[0].children[2];
  
  var ship_distance =  6.317e6 + 400000;
  var ship_vel = 7.777*1e5;
  
  myShip = new Ship(earth.dist + ship_distance,0,0,(-earth.vel*1e4-ship_vel));
  createCanvas(window.innerWidth, window.innerHeight);
  
  noStroke();
  $planetSelect = $('.planet-select');
  $planetSelect.append("<option value=-1>ship</option>");
  
  var $actionButtons = $("button[data-action]")
  $actionButtons.on("mousedown touchstart", function(e) {
    var $this = $(this);
    var action = $this.data("action");
    $this.addClass("active");
    if (action == "zoom_in") {
      zoom*=1.1;
    }
    if (action == "zoom_out") {
      zoom/=1.1;
    }
    if (action == "thrust_forward") {
      THRUST_FORWARDS = true;
    }
    if (action == "thrust_backward") {
      THRUST_BACKWARDS = true;
    }
    if (action == "turn_left") {
      ROTATE_LEFT = true;
    }
    if (action == "turn_right") {
      ROTATE_RIGHT = true;
    }
    
    if(action=="warp_faster") {
      TIME_FACTOR *= 1.1;
      if (TIME_FACTOR > 50000) TIME_FACTOR = 50000;
    }
    if(action=="warp_slower") {
      TIME_FACTOR /= 1.1;
      if (TIME_FACTOR < 1) TIME_FACTOR = 1;
    }
    //e.preventDefault();
    //return false;
  });
  $actionButtons.on("touchmove", function() {
    return false;
  });
  
  $actionButtons.on("touchend touchleave mouseup mouseleave", function() {
    var $this = $(this);
    var action = $this.data("action");
    $this.removeClass("active");
    
    if (action == "thrust_forward") {
      THRUST_FORWARDS = false;
    }
    if (action == "thrust_backward") {
      THRUST_BACKWARDS = false;
    }
    if(action == "turn_left") {
      ROTATE_LEFT = false;
    }
    if(action=="turn_right") {
      ROTATE_RIGHT = false;
    }
    return false;
  })
  
  function getParentIndent(p) {
    var returnStr = "";
    
    if (p.parent) {
      returnStr = "┖&nbsp;";
      while(p.parent) {
        returnStr = "&nbsp;&nbsp;" + returnStr;

        p = p.parent
      }
    }
    
    return returnStr;
  }
  
  for (var x = 0; x < planets.length; x++) {
    
    
    $planetSelect.append("<option value='"+x+"'>" + getParentIndent(planets[x]) + planets[x].name + "</option>");
  }
  $cameraSelect = $("#select_camera");
  $cameraSelect.change(function() {
    if (this.value > -1) {
      myCameraObject = planets[this.value];
    } else {
      myCameraObject = myShip;
    }
    
    resetTrails();
  })
  
  
  $velFromSelect = $("#select_vel_from");
  $velFromSelect.val(vel_from_value );
  $velFromSelect.change(function() {
    vel_from_value = this.value;
  })
  $velToSelect = $("#select_vel_to");
  $velToSelect.val(vel_to_value);
  $velToSelect.change(function() {
    vel_to_value = this.value;
  })
   
  //setup_space_layers();
  
  myCameraObject = myShip; 
  BG_COLOR = color(30,0,30);
  background(BG_COLOR);
  frameRate(60);
}
function windowResized() {
  resizeCanvas(window.innerWidth, window.innerHeight);
  setup_space_layers();
}



// add new planets by "throwing"
var start_x=0, start_y=0,start_frame=0, mouse_dragging=false;



var THRUST_FORWARDS = false, THRUST_BACKWARDS = false, ROTATE_LEFT = false, ROTATE_RIGHT = false;
var prev_millis = 0;
var delta_millis = 0;
var myShip;
var prev_fps=60;

var zoom_translate_x = 0, zoom_translate_y = 0; 
function draw() {
  
  var milli = millis();
  delta_millis = 1;
  
  
  //document.getElementById("debug").innerHTML = "ZOOM:" + zoom + " | " + 
    //                    "TIME: " + TIME_FACTOR.toFixed(0) + " x | " + 
    //                    "VEL: " + myShip.vel.mag().toFixed(0) + "m/s"  ;
  var i = getRelativeInfo();   
  
  output_vel.innerHTML = i.vel;
  output_dist.innerHTML = i.dist;
  
  var fps = 1000/(milli - prev_millis);
  prev_fps = Math.floor(lerp(prev_fps,fps,.1));
  output_fps.innerHTML = prev_fps;
  
  //output_zoom.innerHTML = zoom;
  output_warp.innerHTML = TIME_FACTOR.toFixed(1);
  
  var delta_t = delta_millis * TIME_FACTOR / 100;
  
  translate(width/2, height/2);
  zoom_translate_x = -myCameraObject.loc.x * zoom ; 
  zoom_translate_y = -myCameraObject.loc.y * zoom ;
  translate(-myCameraObject.loc.x, -myCameraObject.loc_y);
  //rotate(-myShip.rotation);
   background(BG_COLOR);
  //draw_space_layers(myCameraObject.loc.x,myCameraObject.loc.y);
 
  //translate(width/2,height/2);
  noStroke();
  for(var x=0; x < planets.length-1; x++) {
    var p = planets[x];
    if (p.r)  {
      if (myShip.loc.dist(p.loc) * 2 > p.r*.98 ) {
        myShip.attract(p);
      } else { 
        myShip.bounceOff(p);
      }
    }
    // n body
    for (var ox=0; ox < planets.length; ox++) {
      if (x !== ox) {
        var op = planets[ox];
        p.attract(op);
        
      }
      
    }
    
    // 2 body
    //if (p.parent !== null) {
     //p.attract(p.parent, delta_millis); 
    //}
  }
  for(var x=0; x < planets.length; x++) {
   var p = planets[x];
   
      p.draw();
      p.update(delta_t);
    
  } 
  if (THRUST_FORWARDS) {
    var v = new p5.Vector(0, -SHIP_THRUST);
    v.rotate(myShip.rotation);
    myShip.applyForce(v);
  }
  if (THRUST_BACKWARDS) {
    var v = new p5.Vector(0, SHIP_THRUST);
    v.rotate(myShip.rotation);
    myShip.applyForce(v);
  }
  if (ROTATE_LEFT) {
    myShip.applyRadialForce(-SHIP_ROTATE/TIME_FACTOR);
  }
  if (ROTATE_RIGHT) {
    myShip.applyRadialForce(SHIP_ROTATE/TIME_FACTOR);
  }
      
      
  myShip.draw(); 
  myShip.update(delta_t); 
  //rotate(myShip.rotation);
  
  prev_millis = milli;
}
function getRelativeInfo() {
  if (vel_from_value == vel_to_value) return 0;
  var v_from,v_to;
  var d_from,d_to;
  
  if (vel_from_value == -1) {
    v_from = myShip.vel;
    d_from = myShip.loc;
  } else {
    var p = planets[vel_from_value];  
   v_from = p.vel;  
    d_from = p.loc;
  }
   
  if (vel_to_value == -1) {
    v_to = myShip.vel;
    d_to = myShip.loc;
  } else {
    var p = planets[vel_to_value];
    v_to = p.vel;
    d_to = p.loc;
  }
  var v_delta = p5.Vector.sub(v_from,v_to);
  var d = p5.Vector.sub(d_from,d_to);
  return {
    vel:(v_delta.mag() / 10000).toFixed(1),
    dist:(d.mag()/1000).toFixed(1)
  }
}

function keyPressed(e) {
  //console.log (e)
  if (e.key=="c") {
    draw_color = getRandomColor();
    return false;
  } else if (e.key == "e") {
    planets = [];
  } else if (e.key == '+') {
    TIME_FACTOR *= 1.1;
    if (TIME_FACTOR > 50000) TIME_FACTOR = 50000;
  } else if (e.key == '-') {
    TIME_FACTOR *= .9;
    if (TIME_FACTOR < 1) TIME_FACTOR = 1;
  } else if (e.key == "h") {
    document.getElementById("help").classList.toggle("hidden");
  } else if (e.keyCode == LEFT_ARROW ) {
    ROTATE_LEFT = true;
    
  } else if (e.keyCode == RIGHT_ARROW) {
    
    ROTATE_RIGHT = true;
  } else if (e.keyCode == UP_ARROW) {
    THRUST_FORWARDS = true;
    
  } else if (e.keyCode == DOWN_ARROW) {
    THRUST_BACKWARDS = true;
  }
    
}
function keyReleased(e) {
  if (e.keyCode == UP_ARROW) {
    THRUST_FORWARDS = false;
  } else if (e.keyCode == DOWN_ARROW) {
    THRUST_BACKWARDS = false;
  } else if (e.keyCode == LEFT_ARROW) {
    ROTATE_LEFT = false;
  } else if (e.keyCode == RIGHT_ARROW) {
    ROTATE_RIGHT = false;
  }
  
  
} 


function Planet(name, x,y,mass,density,c,img,vx, vy, parent_planet, imgScale) {
  this.loc = new p5.Vector(x,y);
  this.name = name;
  this.locHistory = [];
  
  vx = vx || 0;
  vy = vy || 0;
  this.vel = new p5.Vector(vx,vy);
  this.acc = new p5.Vector();
  
  if (typeof parent_planet == "undefined") parent_planet = null;
  this.parent = parent_planet;
  if (typeof img !== "undefined")  {
    this.image = img;
    this.image.mask(maskImage);
  }
  if (typeof imgScale !== "undefined") {
    this.imageScale = imgScale;
  }
  if (typeof c == "undefined") c = color(128);
  this.color = c;
  this.density = density;
  if (typeof mass == "undefined") mass = 100;
  this.mass = mass;
  this.r = getPlanetSize(this.mass, this.density);
  
  this.is_alive = true;
}
Planet.prototype.applyForce = function(v) {
  if (this.is_alive) {
    v.div(this.mass);
    this.acc.add(v);
  }
}
Planet.prototype.attract = function(other_planet) {
  var force = p5.Vector.sub(this.loc,other_planet.loc); // find the vector of the force
  var d = force.mag();              // and the distance
  force.normalize();   
  var strength =  -(G * this.mass * other_planet.mass) / Math.pow(d, GRAVITY_POW);  
  force.mult(strength);   
  this.applyForce(force);
}
Planet.prototype.update = function(delta_t) {
  if (typeof delta_t === "undefined") delta_t = 1;
  
  if (this.is_alive) {
    this.vel.add(p5.Vector.mult(this.acc, delta_t)); 
    this.loc.add(p5.Vector.mult(this.vel, delta_t ));
    this.acc.mult(0);
  }
}
Planet.prototype.draw = function() {
  if (this.is_alive) {
    
    
    var x_pos = zoom_translate_x + this.loc.x*zoom;
    var y_pos = zoom_translate_y + this.loc.y*zoom;
    var d = this.r * zoom; // /2*zoom+Math.pow(2 * (1-zoom) * this.mass,.1)/100;
    
    // draw trail:
    if (enableTrails && this !== myCameraObject) {   
      noFill();
      stroke(this.color);
      strokeWeight(2);
      beginShape();
      for (var x = 0; x < this.locHistory.length; x++) {curveVertex(this.locHistory[x].x,this.locHistory[x].y);
                                                       }
      endShape();
      if (this.locHistory.length > 1000) {
        this.locHistory.splice(0,1);
      }
      this.locHistory.push({
        x:x_pos,y:y_pos
      });
        noStroke();
    }
    
    if (this.name == "earth") {
      //console.log(this.name + ": " + x_pos + " : " + y_pos);
    }
    if ((x_pos + this.r*zoom) > (-width/2) && 
        (x_pos - this.r*zoom) < (width/2) && 
        (y_pos + this.r*zoom) > (-height/2) && 
        (y_pos - this.r*zoom) < (height/2) )  {
      if(this.image) {
        if (d>2) {
          var dd = d;
          if (this.imageScale) {
            dd = dd * this.imageScale;
          }
          
          
        image(this.image,x_pos-dd/2,y_pos-dd/2,dd,dd);
          noFill();
          stroke(this.color)
        } else {
          d=2;
          fill(this.color);
          noStroke();
        }
        
        
        ellipse(x_pos,y_pos,d*.98);
        
        noFill();
      } else {
        noStroke();
        if (d < 2) d=2;
        fill(this.color);
        ellipse(x_pos,y_pos,d);
      }
    }
  }
}
Planet.prototype.eats = function(other_planet) {
  if (this.is_alive && other_planet.is_alive) {
    var totalMass = (this.mass+other_planet.mass);
    // weight location accordion to mass
    
    this.color = lerpColor(other_planet.color, this.color, this.mass/totalMass);
    
    var loc = p5.Vector.lerp(other_planet.loc,this.loc,this.mass / totalMass )
    
    var vel = p5.Vector.add(p5.Vector.mult(this.vel, this.mass / totalMass)   ,
                            p5.Vector.mult(other_planet.vel, other_planet.mass / totalMass));

    this.mass = totalMass;
    
    this.r = 
      (this.mass);
    this.loc = loc;
    this.vel = vel;

    other_planet.is_alive = false;
  }
}

function Ship(loc_x,loc_y,vel_x, vel_y) {
  this.loc = new p5.Vector(loc_x,loc_y);
  this.vel = new p5.Vector(vel_x,vel_y);
  this.acc = new p5.Vector();
  
  this.rotation = 0; 
  this.rotation_vel = 0;
  
  this.height = 20;
  this.width = 10;
  this.tipHeight = 6; 
  
  this.mass = 100;
  this.locHistory=[];
 
}
function getPlanetSize(mass, density) {  
  if (typeof density === "undefined") density = 1;
  
  var r = Math.pow(mass/(16500*density*Math.PI), 1/3);
  
  return r;
}
Ship.prototype.update = function(delta_t) {
  if (typeof delta_t == "undefined") delta_t = 1;
  
  this.vel.add(p5.Vector.mult(this.acc,delta_t));
  this.loc.add(p5.Vector.mult(this.vel,delta_t));
  
  this.acc.mult(0);
  this.rotation += this.rotation_vel * delta_t;
  this.rotation_vel*=.98;
}
Ship.prototype.attract = function(p) {
  var force = p5.Vector.sub(this.loc,p.loc); // find the vector of the force
  var d = force.mag();              // and the distance
  force.normalize();   
  var strength =  -(G * this.mass * p.mass) / Math.pow(d, GRAVITY_POW);  
  force.mult(strength);   
  this.applyForce(force);
}
Ship.prototype.applyRadialForce = function(r) {
  this.rotation_vel += r; 
}
Ship.prototype.applyForce = function(v) {
  this.acc.add(v);
}
Ship.prototype.bounceOff = function(p) {
  var direction = p5.Vector.sub(this.loc, p.loc);
  var relative_vel= p5.Vector.sub(p.vel, this.vel);
  
  this.vel.add(relative_vel);
  
}
Ship.prototype.draw = function() {
  //translate(x_pos,y_pos);
  var x_pos = this.loc.x*zoom + zoom_translate_x;
  var y_pos = this.loc.y*zoom + zoom_translate_y;
  
  if (enableTrails && this !== myCameraObject) {
    
    noFill();
    stroke(200);
    strokeWeight(2);
    
    beginShape();
    for (var x = 0; x < this.locHistory.length; x++) {
      curveVertex(this.locHistory[x].x, this.locHistory[x].y);
    }
    endShape();
    
    
    if (this.locHistory.length > 1000) this.locHistory.splice(0,1);
    this.locHistory.push({
      x:x_pos,y:y_pos
    });
    noStroke();
  }
  translate(x_pos,y_pos);
  rotate(this.rotation);
  
     stroke(255,255,0);
  if (THRUST_FORWARDS) {
    fill(255-random(50),128+random(50),0);
    
    triangle( + -this.width/2+2,+this.height/2, 
              + random(-4,4),  + this.height/2 + random(8,11),
              + this.width/2-2,  + this.height/2);
  }
  if (THRUST_BACKWARDS) {
    fill(255-random(50),128+random(50),0);
    
    triangle( + -this.width/2+1,  + -this.height/2+this.tipHeight,
              + random(-2,2), -this.height/2 - random(8,11),
              + this.width/2-1, -this.height/2+ this.tipHeight);
  }
  
    noStroke();
    fill(200);
    triangle(-this.width/2, -this.height/2+this.tipHeight, 0, -this.height/2,  + this.width/2, -this.height/2 + this.tipHeight); 
    rect(-this.width/2,-this.height/2+this.tipHeight, this.width, this.height-this.tipHeight);
}
function getRandomColor(saturation, brightness) {
  if (typeof saturation == "undefined") saturation = 100;
  if (typeof brightness == "undefined") brightness = 100;
  
  return color(random(360),saturation,brightness);  
}
  



  function setup_space_layers() {
  
  space_layers[0] = createGraphics(width,height);
  fillWithRandomStars(space_layers[0],100,128,2);
  
  space_layers[1] = createGraphics(width,height);
  fillWithRandomStars(space_layers[1],500,112,2);
  
  space_layers[2] = createGraphics(width,height);
  fillWithRandomStars(space_layers[2],1000,96,2);
  
  space_layers[3] = createGraphics(width,height);
  fillWithRandomStars(space_layers[3],2000,80,1);
  
  space_layers[4] = createGraphics(width,height);
  fillWithRandomStars(space_layers[4],5000,64,1);
  
}

function draw_space_layers(offset_x, offset_y) {
  push();
  
  for (var x = 0; x < space_layers.length; x++) {
    if (space_layers[x]) {
      
      var ox = (offset_x)/(x+1);
      var oy = (offset_y)/((x+1));
      
      var z = Math.pow(zoom,1/10);
      var w = width*z; // *z+zoom_translate_x; 
      var h = height*z; // *z+zoom_translate_y;
      
      var ox = ((1e24 - ox) % w) * z;
      var oy = ((1e24 - ox) % h) * z; 
      
     // console.log("ox: ", ox, "oy: ", oy);
//       while(ox > width) ox -= width;
//       while (oy > height) oy -= height;
//       while (ox < -width) ox += width;
//       while (oy < -height) oy += height;
      image(space_layers[x],ox-w/2,oy-h/2,w,h);
      if (ox>w/2) {
        image(space_layers[x],ox-w/2*3,oy-h/2,w,h);
      }
      if (ox < -w/2) { 
        image(space_layers[x],ox+w/2,oy-h/2,w,h);
      }
      
      if(oy > 0) {
        image(space_layers[x],ox-w/2,oy-h/2*3,w,h);
      }
      if(oy < zoom_translate_y) {
        image(space_layers[x],ox-w/2,oy+h/2,w,h);
      }
      
      if (ox > zoom_translate_x && oy > zoom_translate_y) {
        image(space_layers[x],ox-w/2*3,oy-h/2*3,w,h);
      }
     
      if (ox > zoom_translate_x && oy < zoom_translate_y) {
        image(space_layers[x],ox-w/2*3,oy+h/2,w,h);
      }
            
      if (ox < zoom_translate_x && oy > zoom_translate_y) {
        image(space_layers[x],ox+w/2,oy-h/2*3,w,h);
      }
            
      if (ox < zoom_translate_x && oy < zoom_translate_y) {
        image(space_layers[x],ox+w/2,oy+h/2,w,h);
      }
    }
  }
  pop();
}
function fillWithRandomStars(g,n, alpha,size) {
  if (typeof alpha == "undefined") alpha = 255;
  if (typeof size == "undefined") size = 2;
  g.stroke(255,alpha);
  g.strokeWeight(size);
  for (var x = 0; x < n; x++)  {
    g.point(random(width), random(height));
  }
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js
  2. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js