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}}