WaBis

walter.bislins.ch

Source Code: Solar System Animation

This page shows the source code of the page Gravity and how the Heliocentric Model works. The code itself is stored in the page Special: Solar System Animation Source Code. For an explantion of the essential parts see Simulation Code of Solar System Animation explained.

The description of the graphic module JsGraph, the ControlPanels module and the Simulator module can be found here:

#INCLUDE JsGraph.inc
#INCLUDE ControlPanel.inc
#INCLUDE Sim.inc


<style>
#ControlPanel3 { margin-top: 0; }
</style>

<jscript>

var ThisPageUrl = 'http://walter.bislins.ch/bloge/index.asp?page=Gravity+and+how+the+Heliocentric+Model+works';

// some global constants and functions

var PI = Math.PI;
var PI2 = PI * PI;
var sqrt = Math.sqrt;
var sin = Math.sin;
var cos = Math.cos;
var pow = Math.pow;
var abs = Math.abs;
var V2 = JsgVect2;   // some vector functions inherited from graphic module

function square(x) { return x*x; }
function cube(x) { return x*x*x; }
function cubeRoot(x) { return Math.pow( x , 1/3 ); }

// moon trail object

function Trail( maxSize, size ) {
  size = xDefNum( size, maxSize );
  this.trail = [];        // circular buffer for trail points
  this.maxSize = maxSize; // max n points in trail
  this.size = size;       // current choosen size <= maxSize
  this.currSize = 0;      // current number of valid trail entries in trail
  this.startIx = 0;       // current start position in trail
  this.endIx = 0;         // current position behind end of trail, = startIx if tail is empty
  this.startAlpha = 1;
  this.endAlpha = 0;
  this.minDist = 0.01;
  this.trail.push( [0,0] );
}

Trail.prototype.WrapIx = function( i ) {
  return (i + this.maxSize) % this.maxSize;
}

Trail.prototype.SetSize = function( size ) {
  if (size > this.maxSize) size = this.maxSize;
  if (size < this.currSize) {
    this.endIx = this.WrapIx( this.startIx - size );
    this.currSize = size;
  }
  this.size = size;
}

Trail.prototype.Clear = function() {
  this.startIx = 0;
  this.endIx = 0;
  this.currSize = 0;
}

Trail.prototype.Add = function( p ) {
  // p as V2 vector
  if (this.size == 0) return;
  if (this.currSize > 0) {
    // check whether new point is too near
    var d = V2.Length( V2.Sub( p, this.trail[this.startIx] ) );
    if (d < this.minDist) {
      return;
    }
  }
  this.startIx = this.WrapIx( this.startIx + 1 );
  if (this.startIx >= this.trail.length) {
    this.trail[this.startIx] = V2.Copy( p );
  } else {
    V2.CopyTo( p, this.trail[this.startIx] );
  }
  if (this.currSize < this.size) {
    this.currSize++;
  } else {
    this.endIx = this.WrapIx( this.endIx + 1 );
  }
}

Trail.prototype.Move = function( v ) {
  if (this.currSize == 0) return;
  for (var i = 0; i < this.currSize; i++) {
    var ix = this.WrapIx( this.startIx - i );
    this.trail[ix][0] += v[0];
    this.trail[ix][1] += v[1];
  }
}

Trail.prototype.Draw = function( g, r, mode ) {
  if (this.currSize == 0) return;
  if (r*g.ScaleWinX() < 1) r = 1 / g.ScaleWinX();
  var dAlpha = (this.startAlpha - this.endAlpha) / this.currSize;
  var alpha = this.startAlpha;
  for (var i = 0; i < this.currSize; i++) {
    g.SetAlpha( alpha/2 );
    alpha -= dAlpha;
    var ix = this.WrapIx( this.startIx - i );
    g.Circle( this.trail[ix], r, mode );
  }
  g.SetAlpha( 1 );
}

// The metadata are used to serialize and parse the state of the App.
// The metadata properties represent all properties of the App, that can be changed by Demos.

var SolSystemMetaData = {
  Compact: false,
  DefaultPrec: 4,
  Properties: [

    { Name: 'Zoom',                Type: 'num', Default: 1 },
    { Name: 'TrailSize',           Type: 'int', Default: 100 },
    { Name: 'ForceDisplayScaling', Type: 'num', Default: 0.5 },
    { Name: 'TimeSpeed',           Type: 'num', Default: 1 },
    { Name: 'arrowType',           Type: 'int', Default: 1 },
    { Name: 'arrowKind',           Type: 'int', Default: 0 },
    { Name: 'coordSystem',         Type: 'int', Default: 1 },
    { Name: 'DistanceSunEarth',    Type: 'num', Default: 1000 },
    { Name: 'OrbitPeriodEarth',    Type: 'num', Default: 30.17 },
    { Name: 'DistanceEarthMoon',   Type: 'num', Default: 100 },
    { Name: 'OrbitPeriodMoon',     Type: 'num', Default: 3.7 },
    { Name: 'DistCM_EarthMoon',    Type: 'num', Default: 0.1 },
    { Name: 'showOrbit',           Type: 'bool', Default: true },
    { Name: 'showCM',              Type: 'bool', Default: false },
    { Name: 'showSpeed',           Type: 'bool', Default: true },

  ],
};

// the solar system model

var SolSystem = {

  // Sol system parameters like distaces Dx, orbital periods Tx and visuals of the bodies

  // The App uses specific values for distances (D) [px], densities [1/px^3] and masses (M) [px^3/s^2] and assumes G' = 1 px^3/s^2,
  // to get all sized in pixel.
  // The masses and object sizes are calculated from D and T on creation of the sol system model:
  //
  // Masses are computed from Periods T [s] and distances D [px] using Keplers law:
  // using Keplers law:
  //
  // (1)  M = 1/G' * 4 * pi^2 * D^3 / T^2, specific mass [unitless]
  //
  // (2)  R = cubeRoot(M) / DensityCoeff, radius [px], derived from (3)
  //
  // From:
  //
  // (3)  M = 4/3 * pi * R^3 * SpecificDensity = DensityCoeff^3 * R^3
  //
  // follows:
  //
  // (4)  DensityCoeff = cubeRoot( 4/3 * pi * SpecificDensity ), [1/px]
  //
  // where
  //   SpecificDensity sun   = 4.39 1/px^3
  //   SpecificDensity earth = 8.90 1/px^3
  //   SpecificDensity moon  = 8.55 1/px^3
  //
  // The specificDensities are choosen to get decent radii in pixels (8-80 px in default config).

  Zoom: 1,                   // Zoom value
  ZoomSlider: 0,             // Zoom slider value
  ZoomSliderLast: 0,
  TrailSize: 100,            // Trail slider value
  ForceDisplayScaling: 0.5,  // Force slider value
  TimeSpeed: 1,

  DensityCoeffSun: 4.39,
  BorderColorSun: 'orange',
  AreaColorSun: 'yellow',
  ArrowColorSun: 'black',

  OrbitPeriodEarth: 30.17,   // periods are in seconds
  DistanceSunEarth: 1000,    // geometric entities are in pixels
  DensityCoeffEarth: 8.90,   // defines radius of object by: r = cubeRoot(M) / coeff
  BorderColorEarth: 'blue',
  AreaColorEarth: 'lightblue',
  ArrowColorEarth: 'blue',

  OrbitPeriodMoon: 3.7,
  DistanceEarthMoon: 100,
  DensityCoeffMoon: 8.55,    // defines radius of object by: r = cubeRoot(M) / coeff
  BorderColorMoon: 'black',
  AreaColorMoon: 'gray',
  ArrowColorMoon: 'black',

  SolSystemSpeed: 50,        // pixel per seconds

  DistCM_EarthMoon: 0.10,  // center of mass earth-moon as a fraction of DistanceEarthMoon

  // private members

  sim: null,           // simulator
  graph: null,         // graphic module
  moonTrail: null,
  earthTrail: null,
  sunTrail: null,
  trailSizeScale: 0.75,

  releaseMoon: false,  // cut sun-moon force
  releaseEarth: false, // cut sun-earth force
  releaseSun: false,   // cut sun-earth/moon force

  coordSystem: 1,      // 0: galaxy, 1: sol system, 2: earth, 3: moon
  oldCoordSystem: 1,
  centerEarth: false,  // center erath (true) or sun (false) on screen
  arrowType: 1,        // show arrow off (0) as force (1) or acceleration (2)
  arrowKind: 0,        // show single arrows (0), sum arrows (1), both (2)

  showOrbit: true,
  showCM: false,
  showSpeed: true,

  bodies: [],          // list of sol system bodies

  distCM_SunEarth: 0,  // distance to common center of mass will be computed
  distCM_EarthSun: 0,  // distance to common center of mass will be computed
  rHillSphere: 0,      // as long as the moon is inside the sphere he stayes on orbit around earth

  Initialize: function( sim ) {
    // create the graphic canvas and the sol system model

    this.sim = sim;
    var me = this;
    this.graph = NewGraph2D( {
      Width: '100%',
      Height: '66%',
      DrawFunc: function(){ me.Draw(); },
      OnClick: function() { startStop(); },
      AutoReset: false,
      AutoClear: false,
      AutoScalePix: true,
    } );

    // create moon and earth trails object
    this.moonTrail = new Trail( 500 );
    this.moonTrail.minDist = 20;
    this.moonTrail.startAlpha = 0.9;
    this.moonTrail.endAlpha = 0.2;

    this.earthTrail = new Trail( 500 );
    this.earthTrail.minDist = 20;
    this.earthTrail.startAlpha = 0.8;
    this.earthTrail.endAlpha = 0.2;

    this.sunTrail = new Trail( 125 );
    this.sunTrail.minDist = 20;
    this.sunTrail.startAlpha = 0.8;
    this.sunTrail.endAlpha = 0.2;

    this.CheckParameters();
    this.Create();
  },

  CheckParameters: function() {
    // sliders snapping to defaults
    if (this.ZoomSlider != this.ZoomSliderLast) {
      this.Zoom = (7.5 - 0.05) * Math.pow( this.ZoomSlider, 2 ) + 0.05;
      if (abs(this.Zoom-1) < 0.1) this.Zoom = 1;
    }
    this.ZoomSlider = Math.pow( (this.Zoom - 0.05) / (7.5 - 0.05), 1/2 );
    this.ZoomSliderLast = this.ZoomSlider;

    // time snapping
    var timeSnap = (10 - 0.1) / 50;
    if (abs(this.TimeSpeed-1) < timeSnap) this.TimeSpeed = 1;
  },

  Create: function() {

    this.bodies = [];

    // check some input parameters
    if (this.DistanceSunEarth < 500) this.DistanceSunEarth = 500;
    if (this.DistanceSunEarth > 15000) this.DistanceSunEarth = 15000;
    if (this.DistanceEarthMoon < 50) this.DistanceEarthMoon = 50;
    if (this.DistanceEarthMoon > this.DistanceSunEarth/2) this.DistanceEarthMoon = this.DistanceSunEarth/2;
    if (this.DistCM_EarthMoon < 0.001) this.DistCM_EarthMoon = 0.001;
    if (this.DistCM_EarthMoon > 0.5) this.DistCM_EarthMoon = 0.5;
    if (this.OrbitPeriodEarth < 1) this.OrbitPeriodEarth = 1;
    if (this.OrbitPeriodEarth > 60) this.OrbitPeriodEarth = 60;
    if (this.OrbitPeriodMoon < 1) this.OrbitPeriodMoon = 1;
    if (this.OrbitPeriodMoon > 60) this.OrbitPeriodMoon = 60;

    var sun = {
      M: 0, // mass will be computed in ResetSolSystem()
      R: 0, // radius will be computed in ResetSolSystem()
      BorderColor: this.BorderColorSun,
      AreaColor: this.AreaColorSun,
      ArrowColor: this.ArrowColorSun,

      // initial states are computed in ResetSolSystem() and updated in Update()
      OldAccel: [ 0, 0 ],
      Accel: [ 0, 0 ],
      Speed: [ 0, 0 ],
      Pos: [ 0, 0 ],
      Force: [ 0, 0 ],
      ForceList: [ [ 0, 0 ], [ 0, 0 ] ],
    };
    this.bodies.push( sun );

    var earth = {
      M: 0, // mass will be computed in ResetSolSystem()
      R: 0, // radius will be computed in ResetSolSystem()
      BorderColor: this.BorderColorEarth,
      AreaColor: this.AreaColorEarth,
      ArrowColor: this.ArrowColorEarth,

      // initial states are computed in ResetSolSystem() and updated in Update()
      OldAccel: [ 0, 0 ],
      Accel: [ 0, 0 ],
      Speed: [ 0, 0 ],
      Pos: [ 0, 0 ],
      Force: [ 0, 0 ],
      ForceList: [ [ 0, 0 ], [ 0, 0 ] ],
    };
    this.bodies.push( earth );

    var moon = {
      M: 0, // mass will be computed in ResetSolSystem()
      R: 0, // radius will be computed in ResetSolSystem()
      BorderColor: this.BorderColorMoon,
      AreaColor: this.AreaColorMoon,
      ArrowColor: this.ArrowColorMoon,

      // initial states are computed in ResetSolSystem() and updated in Update()
      OldAccel: [ 0, 0 ],
      Accel: [ 0, 0 ],
      Speed: [ 0, 0 ],
      Pos: [ 0, 0 ],
      Force: [ 0, 0 ],
      ForceList: [ [ 0, 0 ], [ 0, 0 ] ],
    };
    this.bodies.push( moon );

    // compute initial states
    this.Reset();
  },

  Reset: function() {
    // this function is called from the simulator to reset the App
    this.moonTrail.Clear();
    this.earthTrail.Clear();
    this.sunTrail.Clear();
    var oldCoordSystem = this.coordSystem;
    var oldCenterEarth = this.centerEarth;
    var oldArrowType = this.arrowType;
    var oldArrowKind = this.arrowKind;
    var oldShowOrbit = this.showOrbit;
    var oldShowCM = this.showCM;
    var oldShowSpeed = this.showSpeed;
    this.ResetSolSystem();
    this.coordSystem = oldCoordSystem;
    this.centerEarth = oldCenterEarth;
    this.arrowType = oldArrowType;
    this.arrowKind = oldArrowKind;
    this.showOrbit = oldShowOrbit;
    this.showCM = oldShowCM;
    this.showSpeed = oldShowSpeed;
    this.CompAccel();
    this.sim.InitStates( this.bodies );
  },

  ResetSolSystem: function() {

    this.releaseSun = false;
    this.releaseEarth = false;
    this.releaseMoon = false;
    this.centerEarth = false;
    this.CoordSystem = 1;
    this.arrowType = 1;
    this.arrowKind = 0;
    this.showOrbit = true;
    this.showCM = false;
    this.showSpeed = true;

    var sun = this.bodies[0];
    var earth = this.bodies[1];
    var moon = this.bodies[2];

    // compute masses from T and D using Keplers law: M = 4 * pi^2 * D^3 / T^2
    // Note: the masses are calculated assuming a normalized gravitational constant G = 1 m^3/(kg*s^2).
    sun.M   = 4 * PI2 * cube( this.DistanceSunEarth ) / square( this.OrbitPeriodEarth );
    earth.M = 4 * PI2 * cube( this.DistanceEarthMoon ) / square( this.OrbitPeriodMoon );
    // mass of moon is computed from mass of earth and center of mass earth-moon
    moon.M  = earth.M * this.DistCM_EarthMoon / (1 - this.DistCM_EarthMoon);

    // compute initial positions and velocities sun and earth-moon system: V = D * 2 * pi / T
    var Mem = earth.M + moon.M;
    var Msys = sun.M + Mem;
    // distance sun earth-moon system, Kepplers law: D = ( T^2 * Mtot / (4 * pi^2) )^1/3
    var Dse = cubeRoot( square(this.OrbitPeriodEarth) * Msys / (4 * PI2) );
    var Dse2 = Dse * sun.M / Msys;
    var Dse1 = Dse - Dse2;
    var Vs = Dse1 * 2 * PI / this.OrbitPeriodEarth;
    var Vem = Dse2 * 2 * PI / this.OrbitPeriodEarth;
    this.distCM_SunEarth = Dse1;
    this.distCM_EarthSun = Dse2;

    sun.Accel[0] = 0;
    sun.Accel[1] = 0;
    sun.Speed[0] = 0 - this.SolSystemSpeed;
    sun.Speed[1] = -Vs;
    sun.Pos[0] = -Dse1;
    sun.Pos[1] = 0;
    sun.Force[0] = 0;
    sun.Force[0] = 0;

    // compute initial positions and velocities earth and moon: V = D * 2 * pi / T
    // distance earth moon, Kepplers law: D = ( T^2 * (Me+Mm) / (4 * pi^2) )^1/3
    var Dem = cubeRoot( square(this.OrbitPeriodMoon) * Mem / (4 * PI2) );
    var Dem2 = Dem * earth.M / Mem;
    var Dem1 = Dem - Dem2;
    var Ve = Dem1 * 2 * PI / this.OrbitPeriodMoon;
    var Vm = Dem2 * 2 * PI / this.OrbitPeriodMoon;

    earth.Accel[0] = 0;
    earth.Accel[1] = 0;
    earth.Speed[0] = -this.SolSystemSpeed;
    earth.Speed[1] = Vem - Ve;
    earth.Pos[0] = Dse2 - Dem1;
    earth.Pos[1] = 0;
    earth.Force[0] = 0;
    earth.Force[0] = 0;

    moon.Accel[0] = 0;
    moon.Accel[1] = 0;
    moon.Speed[0] = -this.SolSystemSpeed;
    moon.Speed[1] = Vem + Vm;
    moon.Pos[0] = Dse2 + Dem2;
    moon.Pos[1] = 0;
    moon.Force[0] = 0;
    moon.Force[0] = 0;

    // compute radii from mass and DensityCoeff:
    // r = cubeRoot(M) / DensityCoeff, in pixels, derived from r = cubeRoot( 3 * M / (4 * pi * density) )
    // The DensityCoeffictions are choosen such that the radii are about 10 to 8 to 80 pixels.
    // DensityCoeff = cubeRoot( 4 * pi * density / 3 )
    sun.R = cubeRoot( sun.M ) / this.DensityCoeffSun;
    earth.R = cubeRoot( earth.M ) / this.DensityCoeffEarth;
    moon.R = cubeRoot( moon.M ) / this.DensityCoeffMoon;
    this.moonTrail.minDist = 2 * moon.R * this.trailSizeScale;
    this.earthTrail.minDist = 2 * earth.R * this.trailSizeScale;
    this.sunTrail.minDist = sun.R * this.trailSizeScale/2;

  },

  CenterEarth: function( bEarth ) {
    this.centerEarth = bEarth;
  },

  Update: function() {
    // this function is called from the simulator to compute the current body accelerations

    this.CompAccel();

    // compute new velocities and positions by integration of the accelerations for each body
    this.sim.CompNewStates( this.bodies );
  },

  IsCrash: function() {
    // returns true if 2 bodies are closer than the sum of their radius
    var nBodies = this.bodies.length;
    for (var i = 0; i < nBodies-1; i++) {
      var body1 = this.bodies[i];
      for (var j = i+1; j < nBodies; j++) {
        var body2 = this.bodies[j];
        var distance = V2.Length( V2.Sub( body1.Pos, body2.Pos ) );
        if (distance < body1.R + body2.R) {
          return true;
        }
      }
    }
    return false;
  },

  CompAccel: function() {
    // compute acceleration of each body from the forces acting on each body

    // first compute all forces
    this.CompForces();

    // then the acceleration for each body is: a = F / M (Newton F = M * a)
    // Note: acceleration and force are 2D vectors
    var nBodies = this.bodies.length;
    for (var i = 0; i < nBodies; i++) {
      var body = this.bodies[i];
      body.Accel[0] = body.Force[0] / body.M;
      body.Accel[1] = body.Force[1] / body.M;
    }
  },

  CompForces: function() {
    // compute gravitational forces between all bodies

    // clear all force vectors for the new calculation
    var nBodies = this.bodies.length;
    for (var i = 0; i < nBodies; i++) {
      var body = this.bodies[i];
      body.Force[0] = 0;
      body.Force[1] = 0;
      body.ForceList = [];
    }

    // compute all force vectors between all bodies using newtons formula for gravitational attraction
    for (var i = 0; i < nBodies-1; i++) {
      for (var j = i+1; j < nBodies; j++) {
        var body1 = this.bodies[i];
        var body2 = this.bodies[j];

        var v12 = V2.Sub( body2.Pos, body1.Pos );  // vector from body1 to body2
        var dist12 = V2.Length( v12 );  // distance between body1 and body2
        var vNorm12 = V2.Scale( v12, 1/dist12 ); // normalized vector from body1 to body2

        // Newtons gravitational force
        // Note: the whole App is scaled so that G = 1
        var F = body1.M * body2.M / square(dist12);

        // disable some forces according to user pressing a button
        if (this.releaseMoon && i == 0 && j == 2) F = 0;
        if (this.releaseEarth && i == 0 && j == 1) F = 0;
        if (this.releaseSun && i == 0) F = 0;

        // compute the force vectors between the bodies
        var vF12 = V2.Scale( vNorm12, F );
        var vF21 = V2.Scale( vF12, -1 );

        // add the current calculated force vectorial to the already calculated force of body1 and body2
        // Note: the forces point from one body in the direction of the other body
        body1.Force[0] += vF12[0];
        body1.Force[1] += vF12[1];
        body2.Force[0] += vF21[0];
        body2.Force[1] += vF21[1];

        // store the force vectors in the list of forces of each object for later display
        body1.ForceList.push( vF12 );
        body2.ForceList.push( vF21 );
      }
    }
  },

  Draw: function() {

    function scaleForce( f, scale ) {
      // scale force vector f according to slider.
      // Note: to equalize the vector lengths they are scaled by a f^1/3 function
      var flength = V2.Length( f );
      if (flength == 0) return [ 0, 0 ];
      var fnorm = V2.Scale( f, 1/flength );
      return V2.Scale( fnorm, pow(flength,1/3) * scale / 5 );
    }

    function scaleAccel( f, m, scale ) {
      // compute and scale acceleration vector from f according to slider.
      // Note: to equalize the vector lengths they are scaled by a sqrt(a) function
      var a = V2.Scale( f, 1/m );
      var alength = V2.Length( a );
      if (alength == 0) return [ 0, 0 ];
      var anorm = V2.Scale( a, 1/alength );
      return V2.Scale( anorm, sqrt(alength) * scale * 7 );
    }

    // update time speed of simulator
    if (this.sim.TimeSpeed != this.TimeSpeed) {
      if (this.TimeSpeed < 0.1) this.TimeSpeed = 0.1;
      if (this.TimeSpeed > 10) this.TimeSpeed = 10;
      this.sim.TimeSpeed = this.TimeSpeed;
    }

    var sun = this.bodies[0];
    var earth = this.bodies[1];
    var moon = this.bodies[2];

    // compute center of masses
    var Mem = earth.M + moon.M;
    var Mse = sun.M + earth.M;
    var Msys = Mse + moon.M;

    // center of mass of earth moon system
    var vEM = V2.Sub( moon.Pos, earth.Pos );
    var pEMcm = V2.Add( earth.Pos, V2.Scale( V2.Scale( vEM, moon.M ), 1/Mem ) );

    // center of mass of sun earth system
    var vSE = V2.Sub( earth.Pos, sun.Pos );
    var pSEcm = V2.Add( sun.Pos, V2.Scale( V2.Scale( vSE, earth.M ), 1/Mse ) );

    // center of mass of solar system
    var vSM = V2.Sub( moon.Pos, sun.Pos );
    var vsum = V2.Add( V2.Scale( vSE, earth.M ), V2.Scale( vSM, moon.M ) );
    var pSysCm = V2.Add( sun.Pos, V2.Scale( vsum, 1/Msys ) );

    // setup graphics window
    var g = this.graph;

    g.Reset();
    g.SetGraphClipping( true, 'canvas', 0 );
    var winXCenter = 0;
    var winYCenter = 0;
    var winWidth = 0; // auto
    var winHeight = 2140 / this.Zoom;
    var pCoordCenter = [ 0, 0 ];
    if (this.coordSystem == 0) {
      var winXCenter = pSysCm[0];
      var winYCenter = pSysCm[1];
    } else if (this.coordSystem == 1) {
      pCoordCenter = pSysCm;
    } else if (this.coordSystem == 2) {
      pCoordCenter = earth.Pos;
    } else if (this.coordSystem == 3) {
      pCoordCenter = moon.Pos;
    }
    g.MapWindow( winXCenter, winYCenter, winWidth, winHeight );

    g.SetLineAttr( '#ddd', 1 );
    g.Grid( 500, 500, false, false );

    if (this.showOrbit) {
      // draw Hills Sphere
      var Dse = V2.Length( V2.Sub( sun.Pos, earth.Pos ) );
      this.rHillSphere = Dse * Math.pow( earth.M / (3 * sun.M ) , 1/3 );
      g.SetLineAttr( '#ddd', 2 );
      g.Circle( V2.Sub(earth.Pos,pCoordCenter), this.rHillSphere, 1 );
      g.SetLineAttr( '#eee', 4 );
      g.Circle( V2.Sub(earth.Pos,pCoordCenter), this.rHillSphere/2, 1 );

      // draw earth orbit circle
      g.SetLineAttr( 'orange', 1 );
      g.Circle( V2.Sub(pSysCm,pCoordCenter), this.distCM_EarthSun, 1 );

      // draw moon orbit circle
      g.SetLineAttr( 'lightblue', 1 );
      g.Circle( V2.Sub(earth.Pos,pCoordCenter), this.DistanceEarthMoon, 1 );
    }

    // add current positions to trails
    if (this.coordSystem != this.oldCoordSystem) {
      this.moonTrail.Clear();
      this.earthTrail.Clear();
      this.sunTrail.Clear();
      this.oldCoordSystem = this.coordSystem;
    }
    if (this.moonTrail.size != this.TrailSize) {
      this.moonTrail.SetSize( this.TrailSize );
      this.earthTrail.SetSize( this.TrailSize );
      this.sunTrail.SetSize( Math.floor(this.TrailSize / 4) );
    }

    // draw trails
    g.SetAreaAttr( sun.BorderColor, sun.BorderColor, 1 );
    this.sunTrail.Draw( g, sun.R * this.trailSizeScale/2, 2 );
    g.SetAreaAttr( earth.BorderColor, earth.BorderColor, 1 );
    this.earthTrail.Draw( g, earth.R * this.trailSizeScale, 2 );
    g.SetAreaAttr( moon.BorderColor, moon.BorderColor, 1 );
    this.moonTrail.Draw( g, moon.R * this.trailSizeScale, 2 );

    // draw bodies
    var nBodies = this.bodies.length;
    for (var i = 0; i < nBodies; i++) {
      if (this.releaseSun && i == 0) continue;
      var body = this.bodies[i];
      g.SetAreaAttr( body.AreaColor, body.BorderColor, 1 );
      g.Circle( V2.Sub(body.Pos,pCoordCenter), body.R, 3 );
    }

    if (this.showCM) {
      // draw center of masses
      g.SetMarkerAttr( 'Plus', 9, 'red', 'red', 1 );
      g.Marker( V2.Sub(pSysCm,pCoordCenter), 1 );
      g.SetMarkerAttr( 'Cross', 5, earth.BorderColor, earth.BorderColor, 1 );
      g.Marker( V2.Sub(pSEcm,pCoordCenter), 1 );
      g.SetColor( moon.BorderColor );
      g.Marker( V2.Sub(pEMcm,pCoordCenter), 1 );
    }

    function GetForceSum( body, scale ) {
      var vsum = [ 0, 0 ];
      for (var k = 0; k < 2; k++) {
        var F = body.ForceList[k];
        var Fscaled = scaleForce( F, scale );
        vsum = V2.Add( vsum, Fscaled );
      }
      return vsum;
    }

    function GetAccelSum( body, scale ) {
      var vsum = [ 0, 0 ];
      for (var k = 0; k < 2; k++) {
        var F = body.ForceList[k];
        var ascaled = scaleAccel( F, body.M, scale );
        vsum = V2.Add( vsum, ascaled );
      }
      return vsum;
    }

    // draw force or acceleration vectors
    if (this.arrowType != 0) {
      g.SetMarkerAttr( 'Arrow1', 3, 'red', 'red', 1 );
      for (var i = 0; i < nBodies; i++) {
        var body = this.bodies[i];
        if (!(this.arrowKind & 1) ) {
          for (var j = 0; j < 2; j++) {
            var F = body.ForceList[j];
            if (F[0] != 0 || F[1] != 0) {
              if (this.arrowType == 1) {
                var Fscaled = scaleForce( F, this.ForceDisplayScaling );
                g.SetAreaAttr( body.ArrowColor, body.ArrowColor, 1 );
                g.Arrow( V2.Sub(body.Pos,pCoordCenter), V2.Sub(V2.Add( body.Pos, Fscaled ),pCoordCenter) );
              } else {
                var ascaled = scaleAccel( F, body.M, this.ForceDisplayScaling );
                g.SetAreaAttr( body.ArrowColor, body.ArrowColor, 1 );
                g.Arrow( V2.Sub(body.Pos,pCoordCenter), V2.Sub(V2.Add( body.Pos, ascaled ),pCoordCenter) );
              }
            }
          }
        }
        if (this.arrowKind > 0) {
          var F = body.Force;
          if (F[0] != 0 || F[1] != 0) {
            if (this.arrowType == 1) {
              var Fscaled = GetForceSum( body, this.ForceDisplayScaling );
              g.SetAreaAttr( 'darkgreen', 'darkgreen', 1 );
              g.Arrow( V2.Sub(body.Pos,pCoordCenter), V2.Sub(V2.Add( body.Pos, Fscaled ),pCoordCenter) );
            } else {
              var ascaled = GetAccelSum( body, this.ForceDisplayScaling );
              g.SetAreaAttr( 'red', 'red', 1 );
              g.Arrow( V2.Sub(body.Pos,pCoordCenter), V2.Sub(V2.Add( body.Pos, ascaled ),pCoordCenter) );
            }
          }
        }
      }
    }

    if (this.showSpeed) {
      var speedRef = [ 0, 0 ];
      var txtRef = '';
      if (this.coordSystem == 0) {
        txtRef = 'Galaxy';
      } else if (this.coordSystem == 1) {
        speedRef = [ -this.SolSystemSpeed, 0 ];
        //speedRef = sun.Speed;
        txtRef = 'Solar System';
      } else if (this.coordSystem == 2) {
        speedRef = earth.Speed;
        txtRef = 'Earth';
      } else if (this.coordSystem == 3) {
        speedRef = moon.Speed;
        txtRef = 'Moon';
      }
      var sunSpeed = V2.Length( V2.Sub( sun.Speed, speedRef ) );
      var earthSpeed = V2.Length( V2.Sub( earth.Speed, speedRef ) );
      var moonSpeed = V2.Length( V2.Sub( moon.Speed, speedRef ) );
      var lineHeight = g.ScalePix( 18 );
      var txty = g.VpInnerHeight - 3.5 * lineHeight;
      var txtx = 8;

      function speedText( body, speed ) {
        var blanks = '     ';
        var speedStr = speed.toFixed(0);
        var pad = 5 - speedStr.length;
        if (pad < 0) pad = 0;
        speedStr = blanks.substr(0,pad) + speedStr;
        var pad = 5 - body.length;
        var bodyStr = body + blanks.substr(0,pad);
        return bodyStr + ' = ' + speedStr + ' px/s';
      }

      var oldTrans = g.SelectTrans( 'viewport' );
      g.SetTextAttr( 'Courier', 16, 'black', 'normal', 'normal', 'left', 'bottom' );
      g.Text( 'Speed wrt. ' + txtRef + ':', txtx, txty );
      txty += lineHeight;
      g.Text( speedText( 'Sun', sunSpeed ), txtx, txty );
      txty += lineHeight;
      g.Text( speedText( 'Earth', earthSpeed ), txtx, txty );
      txty += lineHeight;
      g.Text( speedText( 'Moon', moonSpeed ), txtx, txty );
      g.SelectTrans( oldTrans );
    }

    var oldTrans = g.SelectTrans( 'viewport' );
    g.SetTextAttr( 'Arial', 18, 'red', 'bold', 'normal', 'center', 'bottom', 8, 8 );
    if (this.releaseSun) {
      g.Text( 'Sun disabled!', g.VpInnerWidth/2, g.VpInnerHeight );
    } else if (this.releaseMoon || this.releaseEarth) {
      var txt = '';
      if (this.releaseEarth) txt += ' Sun/Earth';
      if (this.releaseMoon) {
        if (txt != '') txt += ',';
        txt += ' Sun/Moon';
      }
      txt = 'Forces' + txt + ' disabled!';
      g.Text( txt, g.VpInnerWidth/2, g.VpInnerHeight );
    }
    g.SelectTrans( oldTrans );

    // add current positions to trails
    this.moonTrail.Add( V2.Sub( this.bodies[2].Pos, pCoordCenter ) );
    this.earthTrail.Add( V2.Sub( this.bodies[1].Pos, pCoordCenter ) );
    this.sunTrail.Add( V2.Sub( this.bodies[0].Pos, pCoordCenter ) );

    // crash test
    if (this.IsCrash()) stop();
  },
};

var SolSysSim = new Sim( {
  SimObj:        SolSystem,
  TimeSpeed:     1,
  TimeStep:      0.00005,
  ResetFuncs:    function(sim) { sim.SimObj.Reset(); },
  TimeStepFuncs: function(sim) { sim.SimObj.Update(); },
  FrameFuncs:    function(sim) { sim.SimObj.Draw(); },
} );

SolSystem.Initialize( SolSysSim );

SolSysSim.Run();

function UpdateButtons() {
  xInnerHTML( 'StartStopButton', SolSysSim.Running ? 'Stop' : 'Continue' );
  xInnerHTML( 'MoonOnOffButton', SolSystem.releaseMoon ? 'Moon-Sun on' : 'Moon-Sun off' );
  xInnerHTML( 'EarthOnOffButton', SolSystem.releaseEarth ? 'Earth-Sun on' : 'Earth-Sun off' );
  xInnerHTML( 'SunOnOffButton', SolSystem.releaseSun ? 'Sun on' : 'Sun off' );
}

function restart() {
  SolSysSim.Run(true);
  SolSystem.centerEarth = false;
  UpdateButtons();
}

function stop() {
  if (SolSysSim.Running) SolSysSim.Stop();
  UpdateButtons();
}

function startStop() {
  if (SolSysSim.Running) {
    SolSysSim.Stop();
  } else {
    SolSysSim.Run(false);
  }
  UpdateButtons();
}

function releaseMoon() {
  SolSystem.releaseMoon = !SolSystem.releaseMoon;
  UpdateButtons();
}

function releaseEarth() {
  SolSystem.releaseEarth = !SolSystem.releaseEarth;
  UpdateButtons();
}

function releaseSun() {
  SolSystem.releaseSun = !SolSystem.releaseSun;
  UpdateButtons();
}

</jscript>

[javascript:restart()|{{ButtonText|Restart|green}}]
[javascript:startStop()|{{ButtonText|Stop|id=StartStopButton|red}}]
[javascript:releaseMoon()|{{ButtonText|Moon-Sun off|id=MoonOnOffButton|blue}}]
[javascript:releaseEarth()|{{ButtonText|Earth-Sun off|id=EarthOnOffButton|blue}}]
[javascript:releaseSun()|{{ButtonText|Sun off|id=SunOnOffButton|blue}}]
[javascript:resetAll()|{{ButtonText|Reset|red}}]

<jscript>

function UpdateAll() {
  SolSystem.CheckParameters();
  ControlPanels.Update();
  SolSystem.Draw();
}

function CreateNewSolSystem() {
  SolSystem.Create();
  UpdateAll();
  restart();
}

function resetAll() {
  ControlPanels.Reset();
  CreateNewSolSystem();
}

function SetAppState( state ) {
  DataX.StreamToAppState( state, true );
  location.hash = '#AppTop';
}

DataX.AssignApp( 'SolSystem', SolSystem, SolSystemMetaData, null, CreateNewSolSystem );
DataX.AssignSaveRestoreDomObj( 'SaveRestorePanel' );
DataX.SetupUrlStateHandler( 'AppTop' );

</jscript>

{{col|50}}

<jscript>

ControlPanels.NewPanel( {
  ModelRef: 'SolSystem',
  NCols: 1,
  OnModelChange: UpdateAll,

} ).AddSliderField( {
  Name: 'ZoomSlider',
  Label: 'Zoom',
  Color: 'black',
  Min: 0,
  Max: 1,

} ).AddSliderField( {
  Name: 'TrailSize',
  Label: 'Trail',
  Color: 'gray',
  Min: 0,
  Max: 500,
  Steps: 500,

} ).Render();

</jscript>

{{col}}

<jscript>

ControlPanels.NewPanel( {
  ModelRef: 'SolSystem',
  NCols: 1,
  OnModelChange: UpdateAll,

} ).AddSliderField( {
  Name: 'TimeSpeed',
  Label: 'Time',
  Color: 'blue',
  Min: 0.1,
  Max: 10,

} ).AddSliderField( {
  Name: 'ForceDisplayScaling',
  Label: 'Arrows',
  Color: 'red',
  Min: 0.1,
  Max: 5,

} ).Render();

</jscript>

{{col|end}}

{{scroll}}

<jscript>

ControlPanels.NewPanel( {
  ModelRef: 'SolSystem',
  NCols: 2,
  ValuePos: 'left',
  OnModelChange: UpdateAll,
  Format: 'fix0',
  Digits: 2,
  ReadOnly: true,
  PanelFormat: 'InputSmallWidth'

} ).AddRadiobuttonField( {
  Name: 'coordSystem',
  Label: 'CoordSys',
  ValueType: 'int',
  Items: [
    {
      Name: 'Galaxy',
      Value: 0
    }, {
      Name: 'SolSys',
      Value: 1
    }, {
      Name: 'Earth',
      Value: 2
    }, {
      Name: 'Moon',
      Value: 3
    }
  ]

} ).AddCheckboxField( {
  Name: 'Show',
  Items: [
    {
      Name: 'showOrbit',
      Text: 'Orbits',
    }, {
      Name: 'showCM',
      Text: 'CM',
    }, {
      Name: 'showSpeed',
      Text: 'Speeds',
    }
  ]

} ).AddRadiobuttonField( {
  Name: 'arrowType',
  Label: 'ArrowType',
  ValueType: 'int',
  Items: [
    {
      Name: 'off',
      Value: 0
    }, {
      Name: 'Force',
      Value: 1
    }, {
      Name: 'Accel',
      Value: 2
    }
  ]

} ).AddRadiobuttonField( {
  Name: 'arrowKind',
  Label: 'Arrows',
  ValueType: 'int',
  Items: [
    {
      Name: 'Single',
      Value: 0
    }, {
      Name: 'Sum',
      Value: 1
    }, {
      Name: 'All',
      Value: 2
    }
  ]

} ).Render();

</jscript>

{{end scroll}}
{{scroll}}

<jscript>

ControlPanels.NewPanel( {
    ModelRef: 'SolSystem',
    OnModelChange: CreateNewSolSystem,
    PanelFormat: 'InputNormalWidth',
    NCols: 2,
    Format: 'std',
    Digits: 4
  }

).AddTextField( {
    Name: 'DistanceSunEarth',
    Label: 'D<sub>SunEarth</sub>',
    Units: 'px',
    Inc: 100,
  }

).AddTextField( {
    Name: 'OrbitPeriodEarth',
    Label: 'T<sub>Earth</sub>',
    Units: 's',
    Inc: 1,
  }

).AddTextField( {
    Name: 'DistanceEarthMoon',
    Label: 'D<sub>EarthMoon</sub>',
    Units: 'px',
    Inc: 10,
  }

).AddTextField( {
    Name: 'OrbitPeriodMoon',
    Label: 'T<sub>Moon</sub>',
    Units: 's',
    Inc: 1,
  }

).AddTextField( {
    Name: 'DistCM_EarthMoon',
    Label: 'CM<sub>EarthMoon</sub>',
    Mult: 0.01,
    Units: '%',
    Inc: 10,
  }

).Render();

</jscript>

{{end scroll}}

{{OnOff|Show Save-Restore Panel|Hide Save-Restore Panel|noborder|$SaveRestorePanel}}

{{form textarea|SaveRestorePanel|4|spellcheck=false|class=ListingDisplay|}}

[javascript:void DataX.GetAppState(true)|{{ButtonText|Get App State|blue}}]
[javascript:void DataX.GetAppStateUrl(ThisPageUrl)|{{ButtonText|Get App Url|blue}}]
[javascript:void DataX.SetAppState()|{{ButtonText|Set App State|green}}]
[javascript:void DataX.ClearSaveRestoreDomObj()|{{ButtonText|Clear|red}}]

{{OnOffEnd}}



Weitere Infos zur Seite
Erzeugt Monday, July 16, 2018
von wabis
Zum Seitenanfang
Geändert Friday, September 7, 2018
von wabis