Skip to content

Instantly share code, notes, and snippets.

@heyLu
Created May 6, 2012 18:20
Show Gist options
  • Save heyLu/2623626 to your computer and use it in GitHub Desktop.
Save heyLu/2623626 to your computer and use it in GitHub Desktop.
Oh, circles! :)
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()
<!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