Created
May 6, 2012 18:20
-
-
Save heyLu/2623626 to your computer and use it in GitHub Desktop.
Oh, circles! :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Vector | |
constructor: (p1, p2) -> | |
console.log "Vector: Warning", p1, p2 unless p1? and p2? | |
@x = p2.x - p1.x | |
@y = p2.y - p1.y | |
@random: (p1 = { x: 0, y: 0}, p2 = { x: window.innerWidth * 0.01, y: window.innerHeight * 0.01}) -> | |
flippy = (n) -> if Math.random() < 0.5 then -1 else 1 | |
new Vector p1, { x: flippy(p2.x), y: flippy(p2.y)} | |
length: () -> | |
@_length ?= Math.sqrt(@x * @x + @y * @y) | |
normalize: () -> | |
new Vector { x: 0, y: 0 }, { x: @x / @length(), y: @y / @length() } | |
add: (otherVector) -> | |
@x += otherVector.x | |
@y += otherVector.y | |
@ | |
mult: (scalar) -> | |
@x *= scalar | |
@y *= scalar | |
@ | |
class Circle | |
constructor: (@origin, @radius, @color = "#000", @fill = true) -> | |
@alive = true | |
@movement = new Vector({ x: 0, y: 0 }, { x: 0, y: 0 }) | |
@random = (x, y, radius, color, fill) -> | |
x = Math.random() * (x ? window.innerWidth) | |
y = Math.random() * (y ? window.innerHeight) | |
radius = Math.random() * (radius ? Math.min(window.innerWidth, window.innerHeight) * (1/25)) | |
new Circle { x: x, y: y }, radius, color, fill | |
touches: (otherCircle) -> | |
console.log "Circle.touches: Warning", otherCircle unless otherCircle? | |
@radius > new Vector(@origin, otherCircle.origin).length() - otherCircle.radius | |
absorb: (otherCircle) -> | |
return unless otherCircle.alive | |
return unless @alive | |
otherCircle.alive = false | |
@radius += otherCircle.radius * 0.25 | |
@ | |
accelerate: (vec) -> | |
return @ unless @radius - vec.length() > 2 | |
@movement.add vec | |
@radius -= vec.length() | |
@ | |
move: () -> | |
if @origin.x - @radius < 0 or @origin.x + @radius > w.canvas.width | |
@movement.x *= -1 | |
else if @origin.y - @radius < 0 or @origin.y + @radius > w.canvas.height | |
@movement.y *= -1 | |
@origin.x += @movement.x | |
@origin.y += @movement.y | |
@ | |
draw: (ctx) -> | |
ctx.fillStyle = ctx.strokeStyle = "#fff" | |
ctx[if @fill then "fillStyle" else "strokeStyle"] = if @radius > w.self.radius then "#f00" else @color | |
ctx.save() | |
ctx.translate @origin.x, @origin.y | |
ctx.beginPath() | |
ctx.arc 0, 0, @radius, 0, 360 | |
ctx[if @fill then "fill" else "stroke"]() | |
ctx.shadowColor = "rgba(255, 255, 255, #{if @alive then 0.8 else 0.3 })" | |
ctx.shadowBlur = @radius * if @radius > w.self.radius then 0.6 else 0.3 | |
ctx.stroke() | |
ctx.restore() | |
class World | |
constructor: () -> | |
@canvas = document.createElement "canvas" | |
@ctx = @canvas.getContext '2d' | |
@objects = [] | |
@self = new Circle.random null, null, null, "#00f" | |
@self.radius = 30 | |
@calculating = false | |
@stop = false | |
@v = null | |
@canvas.width = window.innerWidth | |
@canvas.height = window.innerHeight | |
document.body.appendChild @canvas | |
@canvas.onmousedown = (ev) => | |
switch ev.which | |
when 1 | |
@v = new Vector(@self.origin, { x: ev.clientX , y: ev.clientY }).normalize().mult(-0.15) | |
@self.accelerate @v | |
#@ctx.translate ev.clientX, ev.clientY | |
#@ctx.scale 2, 2 | |
when 2 then document.location.reload() | |
when 3 | |
@stop = if @stop then false else true | |
@canvas.onmouseup = (ev) => @v = null | |
@canvas.oncontextmenu = (ev) -> false | |
window.onkeydown = (ev) => | |
if ev.keyCode is 32 # space | |
@stop = if @stop then false else true | |
ev.preventDefault() | |
@random: (n = Math.random() * Math.min(window.innerWidth, window.innerHeight) * 0.2) -> | |
w = new World | |
for i in [1..n] | |
w.add Circle.random().accelerate(Vector.random().normalize().mult(Math.random())) | |
w | |
add: (obj) -> | |
@objects.push obj | |
remove: (obj) -> | |
index = @objects.indexOf obj | |
return if index is -1 | |
@objects.splice index, 1 | |
recalc: () -> | |
for obj in @objects | |
if @self.touches(obj) | |
if @self.radius > obj.radius | |
@self.absorb obj | |
else | |
obj.absorb @self | |
@self.fill = false | |
for obj in @objects | |
continue unless obj? # FIXME: Why is it undefined? | |
@remove obj unless obj.alive | |
oldObjects = (o = {}; o.__proto__ = @objects; o) | |
for obj in oldObjects | |
for otherObj in oldObjects | |
continue if obj == otherObj | |
if obj.touches(otherObj) and obj.radius > otherObj.radius | |
obj.absorb otherObj | |
for obj in oldObjects | |
continue unless obj? # FIXME: Why is it undefined? | |
@remove obj unless obj.alive | |
undefined | |
clear: () -> | |
@ctx.clearRect 0, 0, @canvas.width, @canvas.height | |
draw: () -> | |
@clear() | |
@ctx.fillStyle = "#333" | |
@ctx.fillRect 0, 0, @canvas.width, @canvas.height | |
o.draw @ctx for o in @objects | |
@self.draw @ctx | |
drawOnce: (obj) -> | |
@draw() | |
obj.draw @ctx | |
animationLoop: () -> | |
return if @stop | |
if @v? | |
@self.accelerate @v | |
unless @calculating | |
@calculating = true | |
@recalc() | |
@calculating = false | |
for obj in @objects | |
obj.move() | |
@self.move() | |
@draw() | |
window.w = World.random() | |
w.draw() | |
w.self.accelerate new Vector { x: 0, y: 0 }, { x: 0.5, y: 0.5 } | |
last = new Date().getTime() | |
animate = () -> | |
requestAnimFrame animate | |
fps = new Date().getTime() - last | |
document.title = "O circles! (#{fps})" | |
last = new Date().getTime() | |
w.animationLoop(fps) | |
animate() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Oh, circles! :)</title> | |
<meta charset="utf-8" /> | |
<style type="text/css"> | |
canvas { | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
</style> | |
</head> | |
<body> | |
</body> | |
<script type="text/javascript"> | |
window.requestAnimFrame = (function(){ | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function( callback ){ | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
</script> | |
<script type="text/javascript" src="coffee-script.js"></script> | |
<script type="text/coffeescript" src="ocircle.coffee"></script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment