Monday, June 25, 2012

Going in Circles via Html5

Today's post is just a simple little HTML5 animation using canvas that moves a spaceship in orbit around a star (not to scale).

Demo

Mult:    Refresh: ms   

JavaScript
This may be over-engineering for such a simple program, but there are 3 javascript classes used to accomplish this.

Vector
(not a Java Vector but a math vector)
  1 var Vector = function(x, y) {
  2   if (arguments.length == 0) {
  3     x = 0.0;
  4     y = 0.0;
  5   }
  6   this.x = x;
  7   this.y = y;
  8 };
  9 
 10 Vector.prototype.adjust = function(v, mult) {
 11   this.x += v.x * mult;
 12   this.y += v.y * mult;
 13 }

Ship
A ship consists of 3 vectors (position, velocity, and acceleration), and also knows how to draw itself.
  1 var Ship = function() {
  2     this.pos = new Vector();
  3     this.vel = new Vector();
  4     this.acc = new Vector();
  5     this.color = "255,255,255";
  6 }
  7 
  8 Ship.prototype.adjust = function(time) {
  9     this.vel.adjust(this.acc, time);
 10     this.pos.adjust(this.vel, time);
 11 }
 12 
 13 Ship.prototype.draw = function(ctx) {
 14     ctx.save();
 15     ctx.translate(this.pos.x, this.pos.y);
 16     ctx.strokeStyle = "rgb(" + this.color + ")";
 17     ctx.beginPath();
 18     ctx.moveTo(0, -10);
 19     ctx.lineTo(5, 10);
 20     ctx.lineTo(-5, 10);
 21     ctx.lineTo(0, -10);
 22     ctx.stroke();
 23 
 24     ctx.restore();
 25 }

Simulation
A simulation consists of the ship, knows how to accelerate the ship around in an orbit, and has the commands for actually starting, stopping, and running the simulation.
  1 var Simulation = function(size) {
  2     this.size = size;
  3     this.reset();
  4 }
  5 
  6 Simulation.prototype.drawStar = function(ctx) {
  7     ctx.fillStyle= "#FF0";
  8     ctx.beginPath();
  9     ctx.arc(0, 0, 20, 0, 2*Math.PI, false);
 10     ctx.fill();
 11 }
 12 
 13 Simulation.prototype.draw = function(ctx) {
 14     ctx.save();
 15     ctx.fillStyle = "rgba(0,0,0,1.0)";
 16     ctx.fillRect(0, 0, this.size, this.size);
 17     ctx.translate(this.size/2, this.size/2);
 18     this.drawStar(ctx);
 19     this.ship.draw(ctx);
 20     ctx.restore();
 21 }
 22 
 23 Simulation.prototype.step = function() {
 24     var ship = this.ship;
 25     var acc = ship.acc;
 26     acc.x = -ship.pos.x;
 27     acc.y = -ship.pos.y;
 28     ship.adjust(this.mult);
 29 }
 30 
 31 
 32 Simulation.prototype.reset = function() {
 33     this.stop();
 34     var ship = new Ship();
 35     ship.pos.x = this.size / 4;
 36     ship.pos.y = 0;
 37     ship.vel.x = 0;
 38     ship.vel.y = -this.size / 4;
 39     this.ship = ship;
 40 }
 41 
 42 Simulation.prototype.run = function(ctx, mult, refresh) {
 43     this.stop();
 44     this.mult = mult;
 45     var self = this;
 46     this.timer = setInterval(function() {
 47         self.step();
 48         self.draw(ctx);
 49     }, refresh);
 50 }
 51 
 52 Simulation.prototype.stop = function() {
 53     clearInterval(this.timer);
 54 }

On Load
Then there is the little bit of code to kick everything off, which leverages jQuery. This just sets the size of the canvas, and links the input controls from the HTML to the Simulation object.
  1 $(function() {
  2     var canvas = document.getElementById("canvas");
  3     var size = 500;
  4     canvas.width = size;
  5     canvas.height = size;
  6     var ctx = canvas.getContext('2d');
  7     var sim = new Simulation(size);
  8     sim.draw(ctx);
  9 
 10     $("#run").click(function() {sim.run(ctx, $("#mult").val(), $("#refresh").val())});
 11     $("#stop").click(function() {sim.stop()});
 12     $("#reset").click(function() {sim.reset(); sim.draw(ctx);});
 13 });

HTML
The HTML is just a canvas along with some inputs to control the simulation.
  1 <canvas id="canvas"></canvas>
  2 <br />
  3 Mult: <input id="mult" type="text" value="0.01" size="4" />
  4   
  5 Refresh: <input id="refresh" type="text" value="15" size="3" />ms
  6   
  7 <button id="run">Run</button>
  8 <button id="stop">Stop</button>
  9 <button id="reset">Reset</button>