Jason S avatar Jason S committed 1a5be8a

added statistics display and blinking arrow for off-screen

Comments (0)

Files changed (2)

demos/gravity1/gravity1.html

 <html>
 <head>
+<style type="text/css">
+    div#statistics p {
+        font-family: sans-serif;
+        padding: 0;
+        margin: 0;
+    }
+</style>
 <script type="text/javascript" src="gravity1.js" ></script>
 <script type="text/javascript">
 window.onload = function() {
     var canvas = document.getElementById('gravity1-canvas');
     var gravity1 = new Gravity1Simulation();
+    window.gravity1 = gravity1;
     var fps = 20;
     var tick = 0;
     var k = 16;
     var dtsim = 1.0/k/fps;
-    var keymap = {37: 'left', 38: 'up', 39: 'right', 40: 'down'};
-    var keystate = {'up':false, 'down':false, 'left':false, 'right':false};
+    var keymap = {37: 'left', 38: 'up', 39: 'right', 40: 'down', 73: 'e+', 74:'E-', 75:'e-', 76:'E+'};
+    var keystate = {'up':false, 'down':false, 'left':false, 'right':false, 'e+':false, 'e-': false, 'E+':false, 'E-':false};
     function modkey(keycode,isdown)
     {
         if (keycode in keymap)
         {
             keystate[keymap[keycode]] = isdown;
-            console.log(keystate);
+            //console.log(keystate);
             return true;
         }
         else
     document.addEventListener('keyup',function(event) { if (modkey(event.keyCode, false)) {  event.preventDefault(true); return false;} });
     function settextvalue(id,value)
     {
-        document.getElementById(id).textContent = ''+value.toFixed(3);
+        var s = isNaN(value) ? '---' : (''+value.toFixed(5)); 
+        document.getElementById(id).textContent = s;
     }
     function showStatistics(sim)
     {
-        settextvalue('speed',sim.getSpeed());
-        settextvalue('radius',sim.getRadius());
-        settextvalue('angmomentum',sim.getAngularMomentum());
-        settextvalue('eccentricity',sim.getEccentricity());
-        var ke = sim.getKineticEnergy();
-        var pe = sim.getPotentialEnergy();
+        var stats = sim.getStatistics();
+        settextvalue('speed',stats.speed);
+        settextvalue('radius',stats.radius);
+        settextvalue('minradius',stats.minRadius);
+        settextvalue('maxradius',stats.maxRadius);
+        settextvalue('angmomentum',stats.angularMomentum);
+        settextvalue('eccentricity',stats.eccentricity);
+        var ke = stats.kineticEnergy;
+        var pe = stats.potentialEnergy;
         settextvalue('total energy',ke+pe);
+        settextvalue('period',stats.period);
     }
     setInterval(function() {
         ++tick;
         gravity1.thruster(keystate,k*dtsim);
         for (var i = 0; i < k; ++i)
             gravity1.update(dtsim);
-        gravity1.draw(canvas);
+        var blink = (tick & 7) > 1;
+        gravity1.draw(canvas, blink);
         var ctx = canvas.getContext('2d');
         ctx.strokeStyle = '#ff0000';
         gravity1.drawOrbitPath(canvas);
         ctx.stroke();
         showStatistics(gravity1);
     }, 1000/fps)
+    var form = document.forms['options'];
+    for (var i = 0; i < form.solver.length; ++i)
+    {
+        form.solver[i].addEventListener('change', function(event) {
+            gravity1.solver = event.target.value;
+         });
+    }
 }
 </script>
 </head>
     <div>
     <canvas id="gravity1-canvas" height="480" width="640" />
     </div>
-    <div>
+    <div id='statistics'>
         <p>Speed = <span id='speed'></span></p>
         <p>Radius = <span id='radius'></span></p>
+        <p>Min radius = <span id='minradius'></span></p>
+        <p>Max radius = <span id='maxradius'></span></p>
+        <p>Period = <span id='period'></span></p>
         <p>Angular momentum = <span id='angmomentum'></span></p>
         <p>Eccentricity = <span id='eccentricity'></span></p>
         <p>Total energy = <span id='total energy'></span></p>
     </div>
+    <div id='optionsForm'>
+        <form name='options'>
+            <input type="radio" name="solver" value="Euler" />Euler<br>
+            <input type="radio" name="solver" value="Trapezoidal" />Trapezoidal
+        </form>
+    </div>
 </body>
 </html>

demos/gravity1/gravity1.js

     this.solver = 'Trapezoidal';
     this.initialEnergy = 1+this.getKineticEnergy() + this.getPotentialEnergy();
     this.thrusterstrength = 0.05;
+    this.krhowarp = 0;
 }
 
 function weightedsum(A,x,len)
     }
     return result;
 }
-
+function arrowpath(ctx,x,y,dx,dy)
+{
+    ctx.moveTo(x,y);
+    var d = hypot(dx,dy);
+    if (d > 1e-9)
+    {
+      var ux=dx/d;
+      var uy=dy/d;
+      ctx.lineTo(x+dx,y+dy);
+      var hw = 5;
+      var hl = 10;
+      ctx.lineTo(x+dx-hw*uy-hl*ux,y+dy+hw*ux-hl*uy);
+      ctx.lineTo(x+dx,y+dy);
+      ctx.lineTo(x+dx+hw*uy-hl*ux,y+dy-hw*ux-hl*uy);
+    }
+}
 function dotpath(ctx,x,y,r)
 {
     ctx.arc(x, y, r, 0, 2 * Math.PI, false);
     },
     thruster: function(keymap, dt)
     {
+        var ax = 0;
+        var ay = 0;
         if (keymap.up)
-            this.velpos[1] += this.thrusterstrength * dt;
+            ay += this.thrusterstrength;
         if (keymap.down)
-            this.velpos[1] -= this.thrusterstrength * dt;
+            ay -= this.thrusterstrength;
         if (keymap.right)
-            this.velpos[0] += this.thrusterstrength * dt;
+            ax += this.thrusterstrength;
         if (keymap.left)
-            this.velpos[0] -= this.thrusterstrength * dt;
+            ax -= this.thrusterstrength;
+        if (keymap['E-'])
+        {
+            ax -= this.velpos[2];
+            ay -= this.velpos[3];
+        }
+        if (keymap['E+'])
+        {
+            ax += this.velpos[2];
+            ay += this.velpos[3];
+        }
+        this.velpos[0] += ax*dt;
+        this.velpos[1] += ay*dt;
     },
     solvers: { 
         'Euler': function(vx, f, dt)
             return weightedsum([1,dt/2,dt/2], [vx,dvxdt1,dvxdt2], 4);
         }
     },
+    getStatistics: function() {
+        var result = {};
+        result.radius = this.getRadius();
+        result.kineticEnergy = this.getKineticEnergy();
+        var mu = this.mu;
+        result.potentialEnergy = -mu/result.radius;
+        result.speed = Math.sqrt(2*result.kineticEnergy);
+        result.angularMomentum = this.getAngularMomentum();
+        var h = result.angularMomentum;
+        var r = result.radius;
+        var mu_e = [this.velpos[1]*h  - mu/r*this.velpos[2],
+                    -this.velpos[0]*h - mu/r*this.velpos[3]];
+        var e = hypot(mu_e[0],mu_e[1])/mu;
+        var p = h*h/mu;
+        result.eccentricity = e;
+        result.minRadius = p/(1+e);
+        if (e < 1)
+        {
+            var a = p/(1-e*e);
+            result.semimajorAxis = a;
+            result.maxRadius = p/(1-e);
+            result.period = 2*Math.PI*Math.sqrt(a*a*a/mu);
+        }
+        else
+        {
+            result.period = NaN;
+            result.semimajorAxis = NaN;
+            result.maxRadius = NaN;
+        }
+        return result;
+    },
     getRadius: function() {
         return hypot(this.velpos[2],this.velpos[3]);
     },
                     -this.velpos[0]*h - mu/r*this.velpos[3]];
         return hypot(mu_e[0],mu_e[1])/mu;     
     },
-    draw: function(canvas)
+    draw: function(canvas, blink)
     {
         var ctx = canvas.getContext('2d');
         var cw = canvas.width;
         ctx.fillRect(0,0,cw,ch);
         
         ctx.fillStyle = '#ffffff';        
+        // draw star
         ctx.beginPath();
         dotpath(ctx, cw/2,ch/2,5);
         ctx.fill();        
 
+        // draw satellite
         ctx.beginPath();
-        dotpath(ctx,x1,y1,2);
-        ctx.fill();  
-        
+        if (x1 > 0 && x1 < cw && y1 > 0 && y1 < ch)
+        { 
+           dotpath(ctx,x1,y1,2);
+           ctx.fill();  
+           ctx.beginPath()
+           ctx.strokeStyle = '#00ff80';
+           var vscale = rmax*0.5;
+           arrowpath(ctx,x1,y1,vscale*this.velpos[0],-vscale*this.velpos[1]);
+           ctx.stroke();
+        }
+        else
+        {
+            if (blink)
+            {
+                var marg = 5;
+                var alen = 30;
+                var mw = cw/2 - marg;
+                var mh = ch/2 - marg;
+                var x2 = x1 - cw/2;
+                var y2 = y1 - ch/2;
+                var ax2 = Math.abs(x2);
+                var ay2 = Math.abs(y2);
+                var r = hypot(x2,y2);
+                var k = 1;
+              
+                if (ax2 > mw)
+                    k = mw/ax2;
+                if (ay2 > mh)
+                    k = Math.min(k,mh/ay2);
+                ctx.beginPath();
+                ctx.strokeStyle = '#ffffff';
+                var x3 = cw/2+k*x2;
+                var y3 = ch/2+k*y2;
+                var tx = x2/r*alen;
+                var ty = y2/r*alen;
+                arrowpath(ctx, x3-tx, y3-ty, tx,ty);
+                ctx.stroke();
+            }
+        }
+          
         var e0=0.2 / this.initialEnergy;
         var ke=e0*this.getKineticEnergy();
         var pe=e0*this.getPotentialEnergy();
             var rk = 2*rmax+rc;
             var cx = cw/2 + rc*Math.cos(th_c);
             var cy = ch/2 - rc*Math.sin(th_c);
-            ctx.moveTo(cx + rk*Math.cos(th_c-th_d),
+            /*ctx.moveTo(cx + rk*Math.cos(th_c-th_d),
                        cy - rk*Math.sin(th_c-th_d));
             ctx.lineTo(cx,cy);
             ctx.lineTo(cx + rk*Math.cos(th_c+th_d),
-                       cy - rk*Math.sin(th_c+th_d));
+                       cy - rk*Math.sin(th_c+th_d));*/
         }
         else if (e > 0.05)
         {
             var y = ch/2 - rmax*ry;
             return [x,y];
         }
-        var N = 16;
+        var N = 64;
+        var e1 = Math.min(e,0.7);
+        var e2 = 1-Math.sqrt(1-e1*e1);
         for (var i = 0; i <= N; ++i)
         {   
             var rho = i / N;
-            var rhowarp = rho - 0.04*Math.sin(2*Math.PI*rho);
+            // this seems to work... :/
+            var drho = 0.5*e2*Math.sin(2*Math.PI*rho);
+            var rhowarp = rho - drho + drho*drho - drho*drho*drho;
             var th_i = th_start + rhowarp*(th_stop-th_start);
             var xy = f(th_i);
-            /*if (i == 0)
+            if (i == 0)
             {
                 ctx.moveTo(xy[0],xy[1]);
-                dotpath(ctx,xy[0],xy[1],2);
             }
             else
                 ctx.lineTo(xy[0],xy[1]);
-            if (i == N)*/
-                dotpath(ctx,xy[0],xy[1],2);
+            //    dotpath(ctx,xy[0],xy[1],2);
         }
     }
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.