Skip to content

Commit

Permalink
Port of the gravity app to a Rack app (for serving the files)
Browse files Browse the repository at this point in the history
  • Loading branch information
rbgrouleff committed Jun 20, 2012
1 parent 7b28261 commit dec9b47
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Gemfile
@@ -0,0 +1,5 @@
source 'http://rubygems.org'

gem 'sprockets'
gem 'coffee-script'
gem 'therubyracer'
29 changes: 29 additions & 0 deletions Gemfile.lock
@@ -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
4 changes: 4 additions & 0 deletions app/assets/javascripts/app.js.coffee
@@ -0,0 +1,4 @@
# =require_self
# =require_tree ./gravity

@Gravity = {}
33 changes: 33 additions & 0 deletions app/assets/javascripts/gravity/body.js.coffee
@@ -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()
33 changes: 33 additions & 0 deletions app/assets/javascripts/gravity/main.js.coffee
@@ -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()
45 changes: 45 additions & 0 deletions app/assets/javascripts/gravity/screen.js.coffee
@@ -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
31 changes: 31 additions & 0 deletions app/assets/javascripts/gravity/solar_system.js.coffee
@@ -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
38 changes: 38 additions & 0 deletions app/assets/javascripts/gravity/timer.js.coffee
@@ -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
32 changes: 32 additions & 0 deletions app/assets/javascripts/gravity/vector.js.coffee
@@ -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})"
25 changes: 25 additions & 0 deletions app/html/index.html
@@ -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>
13 changes: 13 additions & 0 deletions config.ru
@@ -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.