Permalink
Browse files

Port of the gravity app to a Rack app (for serving the files)

  • Loading branch information...
rbgrouleff committed Jun 20, 2012
1 parent 7b28261 commit dec9b47c8c106fa4aa41acaebf7038c72fee7d4a
View
@@ -0,0 +1,5 @@
+source 'http://rubygems.org'
+
+gem 'sprockets'
+gem 'coffee-script'
+gem 'therubyracer'
View
@@ -0,0 +1,29 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ coffee-script (2.2.0)
+ coffee-script-source
+ execjs
+ coffee-script-source (1.3.3)
+ execjs (1.4.0)
+ multi_json (~> 1.0)
+ hike (1.2.1)
+ libv8 (3.3.10.4)
+ multi_json (1.3.6)
+ rack (1.4.1)
+ sprockets (2.4.3)
+ hike (~> 1.2)
+ multi_json (~> 1.0)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ therubyracer (0.10.1)
+ libv8 (~> 3.3.10)
+ tilt (1.3.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ coffee-script
+ sprockets
+ therubyracer
@@ -0,0 +1,4 @@
+# =require_self
+# =require_tree ./gravity
+
+@Gravity = {}
@@ -0,0 +1,33 @@
+class Gravity.Body
+ @G: 6.6742e-11
+
+ # radius, mass - float
+ # position, velocity - Vector
+ constructor: (@name, @radius, @mass, @position, @velocity) ->
+
+ exertPullOn: (otherBody, dt) ->
+ # Gravitational pull on itself does not make sense (here at least...)
+ return if otherBody is @
+ radius = otherBody.position.sub(@position)
+ otherBody.updateVelocity radius, @mass, dt
+
+ updateVelocity: (r, mass, dt) ->
+ sumOfSquares = r.sumOfSquares()
+ hypot = Math.sqrt sumOfSquares
+ acceleration = -Gravity.Body.G * mass/sumOfSquares
+ @velocity = @velocity.add r.div(hypot).mult(acceleration * dt)
+
+ calculateGravitationalPull: (otherBody, dt) ->
+ unless otherBody is @
+ radius = @position.sub otherBody.position
+ radiusSquared = radius.sumOfSquares()
+ hyp = Math.sqrt radiusSquared
+ acceleration = -Gravity.Body.G * otherBody.mass/radiusSquared
+ @velocity = @velocity.add radius.div(hyp).mult(acceleration * dt)
+
+ move: (dt) ->
+ @oldPosition = @position
+ @position = @position.add @velocity.mult(dt)
+
+ distanceToCenter: ->
+ Math.sqrt @position.sumOfSquares()
@@ -0,0 +1,33 @@
+# =require ./timer
+# =require ./solar_system
+# =require ./screen
+
+class Gravity.Runner
+ constructor: ->
+ canvas = document.getElementById 'universe'
+ @timer = new Gravity.Timer 60, canvas
+ @startStopButton = document.getElementById 'start_stop'
+ @planets = Gravity.solarSystem.activatePlanets ['Sun', 'Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter'] #[solarSystem.sun, solarSystem.mercury, solarSystem.venus, solarSystem.earth, solarSystem.mars, solarSystem.jupiter, solarSystem.saturn, solarSystem.uranus, solarSystem.neptune, solarSystem.pluto]
+ @screen = new Gravity.Screen canvas, @planets, Gravity.solarSystem.sun, Gravity.solarSystem.farthestPlanet().distanceToCenter()
+ @dt = 24 * 60 * 60
+
+ startStop: ->
+ if @timer.running
+ @timer.stop()
+ else
+ @timer.start()
+
+ tick: ->
+ for planet in @planets
+ planet.calculateGravitationalPull otherPlanet, @dt for otherPlanet in @planets
+ planet.move @dt for planet in @planets
+ @screen.draw @planets
+
+ render: ->
+ @screen.draw @planets
+ @timer.registerCallback @tick.bind(@)
+ @startStopButton.onclick = @startStop.bind(@)
+
+ @init: ->
+ runner = new Gravity.Runner
+ runner.render()
@@ -0,0 +1,45 @@
+# =require ./vector
+
+class Gravity.Screen
+ constructor: (canvas, bodies, @center, @maxDistance) ->
+ @height = canvas.height = canvas.clientHeight
+ @width = canvas.width = canvas.clientWidth
+ @canvasCenter = new Gravity.Vector Math.floor(@width/2), Math.floor(@height/2)
+ @ctx = canvas.getContext '2d'
+ @prerenderedBodies = {}
+
+ @prerenderBody body for body in bodies
+
+ draw: (bodies) ->
+ @ctx.clearRect 0, 0, @width, @height
+ @ctx.save()
+ @ctx.translate @canvasCenter.x, @canvasCenter.y
+ for body in bodies
+ @renderBody body
+ @ctx.restore()
+
+ renderBody: (body) ->
+ bodyRadius = @radiusInPixels body.radius
+ translatedPosition = @positionInPixels body.position.sub(@center.position)
+ translatedPosition = new Gravity.Vector(Math.floor(translatedPosition.x), Math.floor(translatedPosition.y))
+ @ctx.drawImage @prerenderedBodies[body.name], Math.floor(translatedPosition.x) - bodyRadius, Math.floor(translatedPosition.y) - bodyRadius
+
+ prerenderBody: (body) ->
+ bodyRadius = @radiusInPixels body.radius
+ bodyCanvas = document.createElement 'canvas'
+ bodyCanvas.width = bodyCanvas.height = bodyRadius*2
+ bodyCtx = bodyCanvas.getContext '2d'
+ bodyCtx.fillStyle = "rgb(255, 255, 255)"
+ bodyCtx.beginPath()
+ bodyCtx.arc bodyRadius, bodyRadius, bodyRadius, 0, 2*Math.PI
+ bodyCtx.fill()
+ @prerenderedBodies[body.name] = bodyCanvas
+
+ scale: ->
+ Math.ceil @maxDistance/(@height/2 - 10)
+
+ positionInPixels: (position) ->
+ position.div @scale() if position
+
+ radiusInPixels: (radius) ->
+ Math.ceil(Math.log(radius)) - 13
@@ -0,0 +1,31 @@
+# =require ./body
+# =require ./vector
+
+Gravity.solarSystem =
+ sun: new Gravity.Body("Sun", 695000000, 1.989e+030, new Gravity.Vector(0,0), new Gravity.Vector(0,0))
+ mercury: new Gravity.Body("Mercury", 2440000, 3.33e+023, new Gravity.Vector(0, 57900000000), new Gravity.Vector(47900, 0))
+ venus: new Gravity.Body("Venus", 6050000, 4.869e+024, new Gravity.Vector(0, 108000000000), new Gravity.Vector(35000, 0))
+ earth: new Gravity.Body("Earth", 6378140, 5.976e+024, new Gravity.Vector(0, 150000000000), new Gravity.Vector(29800, 0))
+ mars: new Gravity.Body("Mars", 3397200, 6.421e+023, new Gravity.Vector(0, 227940000000), new Gravity.Vector(24100, 0))
+ jupiter: new Gravity.Body("Jupiter", 71492000, 1.9e+027, new Gravity.Vector(0, 778330000000), new Gravity.Vector(13100, 0))
+ saturn: new Gravity.Body("Saturn", 60268000, 5.688e+026, new Gravity.Vector(0, 1429400000000), new Gravity.Vector(9640, 0))
+ uranus: new Gravity.Body("Uranus", 25559000, 8.686e+025, new Gravity.Vector(0, 2870990000000), new Gravity.Vector(6810, 0))
+ neptune: new Gravity.Body("Neptune", 24746000, 1.024e+026, new Gravity.Vector(0, 4504300000000), new Gravity.Vector(5430, 0))
+ pluto: new Gravity.Body("Pluto", 1137000, 1.27e+022, new Gravity.Vector(0, 5913520000000), new Gravity.Vector(4740, 0))
+
+ activatePlanets: (list) ->
+ @activatedPlanets = list
+ @activePlanets()
+
+ activePlanets: ->
+ (planet for name, planet of @ when @activatedPlanets.indexOf(planet.name) > -1)
+
+ farthestPlanet: ->
+ @max @activePlanets(), (planet) ->
+ Math.sqrt planet.position.sumOfSquares()
+
+ max: (list, max) ->
+ maxTemp = list[0]
+ for planet in list
+ maxTemp = planet if max(maxTemp) < max(planet)
+ maxTemp
@@ -0,0 +1,38 @@
+requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
+
+class Gravity.Timer
+ constructor: (@fps, @canvas, @debug = false) ->
+ @callbacks = []
+ @msPrFrame = Math.ceil(1000/@fps)
+ @ms = 0
+ @ticksDuringLastSecond = 0
+ if debug
+ @registerCallback @fpsCounter.bind(@)
+
+ tick: (timestamp) ->
+ delta = timestamp - @lastTickTime
+ if delta > @msPrFrame
+ @lastTickTime = timestamp
+ callback(delta) for callback in @callbacks
+ requestAnimationFrame @tick.bind(@), @canvas if @running
+
+ start: ->
+ return if @running
+ @running = true
+ @lastTickTime = Date.now()
+ requestAnimationFrame @tick.bind(@), @canvas
+
+ stop: ->
+ @running = false
+
+ registerCallback: (callback) ->
+ @callbacks.push callback
+
+ fpsCounter: (delta) ->
+ if @ms + delta > 1000
+ console.log "FPS: #{@ticksDuringLastSecond}"
+ @ms = @ticksDuringLastSecond = 0
+ else
+ @ms += delta
+ @ticksDuringLastSecond += 1
@@ -0,0 +1,32 @@
+class Gravity.Vector
+ constructor: (@x, @y) ->
+
+ mult: (v) ->
+ if @constructor == v.constructor
+ new Gravity.Vector(@x * v.x, @y * v.y)
+ else
+ new Gravity.Vector(@x * v, @y * v)
+
+ div: (v) ->
+ if @constructor == v.constructor
+ new Gravity.Vector @x / v.x, @y / v.y
+ else
+ new Gravity.Vector @x / v, @y / v
+
+ add: (v) ->
+ new Gravity.Vector @x + v.x, @y + v.y
+
+ sub: (v) ->
+ new Gravity.Vector @x - v.x, @y - v.y
+
+ sumOfSquares: ->
+ @x*@x + @y*@y
+
+ eq: (v) ->
+ if v
+ @x == v.x && @y == v.y
+ else
+ false
+
+ toString: ->
+ "(#{@x}, #{@y})"
View
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Gravity</title>
+ <script src="/assets/app.js" type="text/javascript" charset="utf-8"></script>
+ <style type="text/css" media="screen">
+ #universe {
+ width: 650px;
+ height: 650px;
+ background: #000;
+ -webkit-transform: translateZ(0);
+ }
+ #universe, .controls {
+ float: left;
+ }
+ </style>
+ </head>
+ <body onload="Gravity.Runner.init();">
+ <canvas id="universe"></canvas>
+ <div class="controls">
+ <button id="start_stop">Start</button>
+ </div>
+ </body>
+</html>
View
@@ -0,0 +1,13 @@
+require 'coffee_script'
+require 'sprockets'
+
+map '/assets' do
+ environment = Sprockets::Environment.new
+ environment.append_path 'app/assets/javascripts'
+ run environment
+end
+
+map '/' do
+ #run Rack::Directory.new 'app/html'
+ run Rack::File.new 'app/html/index.html'
+end

0 comments on commit dec9b47

Please sign in to comment.