Created
January 3, 2013 21:45
-
-
Save y-lohse/4447634 to your computer and use it in GitHub Desktop.
A little experiment in which a couple of circles roam around a canvas behaving like in outerspace. You can find my blog article explaining more about it here: http://yannick-lohse.fr/2012/09/planetarium/ This experiment is based on my canvas framework https://github.com/y-lohse/Cannon
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
Cannon.include('lib/math.js'); | |
Cannon.include('lib/display.js'); | |
Cannon.include('lib/misc.js'); | |
var canvas, | |
creating = null, | |
planets = [], | |
CONSTANT = 30, | |
tilemap, | |
NB_PLANET = 20, | |
MASS_MULTIPLICATOR = 5, | |
SPEED_MULTIPLICATOR = 20, | |
MIN_EXPLOSION_SPEED = 5; | |
var TileMap = Cannon.ClassFactory.extend({ | |
__construct: function(tilesize, width, height){ | |
this.tilesize = tilesize; | |
this.width = width; | |
this.height = height; | |
this.xtiles = Math.ceil(this.width/this.tilesize); | |
this.ytiles = Math.ceil(this.height/this.tilesize); | |
this.grid = []; | |
for (var i = 0; i < this.xtiles; i++) | |
{ | |
this.grid.push([]); | |
for (var j = 0; j < this.ytiles; j++){ | |
this.grid[i].push(new Tile(i, j, this.tilesize)); | |
} | |
} | |
} | |
}); | |
var Tile = Cannon.ClassFactory.extend({ | |
__construct: function(x, y, size){ | |
this.x = x; | |
this.y = y; | |
this.size = size; | |
var rect = new Rectangle(x*size, y*size, size, size); | |
canvas.addChild(rect); | |
rect.fillStyle = new Color(200, 200, 200, .3); | |
rect.strokeStyle = new Color(200, 200, 200, .8); | |
this.objects = []; | |
} | |
}); | |
function createField(planet){ | |
var field = new Circle(0, 0, CONSTANT*Math.pow(planet.masse, 2)); | |
planet.addChild(field); | |
planet.field = field; | |
field.fillStyle = new Color(150,0,0,.3); | |
field.strokeStyle = new Color(150,0,0,.7); | |
field.lineWidth = 1; | |
var speedvector = new Line([new Vertex2D(0,0), new Vertex2D(planet.vitesse.x*SPEED_MULTIPLICATOR, planet.vitesse.y*SPEED_MULTIPLICATOR)], 0, 0); | |
speedvector.lineWidth = 1; | |
speedvector.strokeStyle = new Color(0,150,0,255); | |
planet.vector = speedvector; | |
planet.addChild(speedvector); | |
} | |
Cannon.onReady = function(){ | |
Cannon.use('*'); | |
Cannon.Logger.autolog('logs'); | |
canvas = new Cannon.Canvas('canvas'); | |
tilemap = new TileMap(100, canvas.width, canvas.height); | |
for (var i = 0; i < NB_PLANET; i++){ | |
var planet = new Circle(0, 0, 0); | |
canvas.addChild(planet); | |
planet.vitesse = new Vector2D(Cannon.Math.Utils.randomIn(-1, 1), Cannon.Math.Utils.randomIn(-1, 1)); | |
planet.masse = Cannon.Math.Utils.randomIn(.05, .2); | |
planet.radius = MASS_MULTIPLICATOR*Math.sqrt(planet.masse); | |
planet.newPos = new Point2D(0, 0); | |
planets.push(planet); | |
createField(planet); | |
moveTo(planet, Cannon.Math.Utils.randomIn(planet.radius, canvas.width-planet.radius), Cannon.Math.Utils.randomIn(planet.radius, canvas.height-planet.radius)); | |
planet.fillStyle = '#333333'; | |
planet.lineWidth = 0; | |
} | |
canvas.on('canvas:render', onRender); | |
canvas.on('canvas:FPS', onFPS); | |
canvas.on('canvas:mousedown', function(event){ | |
creating = new Circle(event.mouseX, event.mouseY, 1); | |
canvas.addChild(creating); | |
creating.fillStyle = '#ffffff'; | |
}); | |
canvas.on('canvas:mouseup', function(event){ | |
var vx = event.mouseX-creating.x; | |
var vy = event.mouseY-creating.y; | |
creating.vitesse = new Vector2D(vx/SPEED_MULTIPLICATOR, vy/SPEED_MULTIPLICATOR); | |
creating.masse = Math.pow(creating.radius/MASS_MULTIPLICATOR, 2); | |
creating.newPos = new Point2D(0, 0); | |
planets.push(creating); | |
createField(creating); | |
creating.fillStyle = '#333333'; | |
creating.lineWidth = 0; | |
creating = null; | |
}); | |
} | |
function onRender(event){ | |
if (creating != null){ | |
creating.radius += .3; | |
} | |
var toRemove = []; | |
for (var p = 0, pnum = planets.length; p < pnum; p++){ | |
planet = planets[p]; | |
var champ = CONSTANT*Math.pow(planet.masse, 2); | |
var tocheck = []; | |
var minx = Math.floor(Math.max(Math.ceil(planet.x-champ), 0)/tilemap.tilesize), | |
maxx = Math.ceil(Math.min(Math.ceil(planet.x+champ), canvas.width)/tilemap.tilesize), | |
miny = Math.floor(Math.max(Math.ceil(planet.y-champ), 0)/tilemap.tilesize), | |
maxy = Math.ceil(Math.min(Math.ceil(planet.y+champ), canvas.height)/tilemap.tilesize); | |
for (var i = minx; i < maxx; i++){ | |
for (var j = miny; j < maxy; j++){ | |
tocheck = tocheck.concat(tilemap.grid[i][j].objects); | |
} | |
} | |
tocheck = Cannon.Utils.arrayWithout(tocheck, planet); | |
//var newPos = new Point2D(0, 0); | |
for (var i = 0; i < tocheck.length; i++){ | |
var checking = tocheck[i]; | |
var distance = Cannon.Math.Utils.distance(planet.x, planet.y, checking.x, checking.y); | |
if (distance < planet.radius+checking.radius){ | |
//collision or absorbtion? | |
var norme = planet.vitesse.clone().substract(checking.vitesse); | |
var colspeed = Cannon.Math.Utils.distance(0, 0, norme.x, norme.y); | |
var absorbed, absorbing; | |
if (planet.masse >= checking.masse) { | |
absorbed = checking; | |
absorbing = planet; | |
toRemove.push(absorbed); | |
} | |
else { | |
absorbed = planet; | |
absorbing = checking; | |
toRemove.push(absorbed); | |
} | |
absorbing.masse += absorbed.masse; | |
absorbing.field.radius = CONSTANT*Math.pow(absorbing.masse, 2); | |
Tween.create(absorbing, {radius: MASS_MULTIPLICATOR*Math.sqrt(absorbing.masse)}, 100); | |
var vx = (absorbed.masse*absorbed.vitesse.x)/(absorbing.masse+absorbed.masse); | |
var vy = (absorbed.masse*absorbed.vitesse.y)/(absorbing.masse+absorbed.masse); | |
var tmp = new Point2D(vx, vy); | |
absorbing.newPos.add(tmp); | |
} | |
else{ | |
var len = CONSTANT*planet.masse/Math.pow(Math.max(distance, checking.radius), 2); | |
var dir = Math.atan2(planet.y-checking.y, planet.x-checking.x); | |
var tmp = new Point2D(0, 0); | |
tmp = Cannon.Math.Point2D.polar(len, dir); | |
checking.newPos.add(tmp); | |
} | |
} | |
} | |
toRemove = Cannon.Utils.uniqueArray(toRemove); | |
for (var i = 0, rmlen = toRemove.length; i < rmlen; i++){ | |
var col = Math.floor(toRemove[i].x/tilemap.tilesize); | |
var row = Math.floor(toRemove[i].y/tilemap.tilesize); | |
if (col >= 0 && col < tilemap.xtiles && row >= 0 && row < tilemap.ytiles){ | |
tilemap.grid[col][row].objects = Cannon.Utils.arrayWithout(tilemap.grid[col][row].objects, toRemove[i]); | |
} | |
planets = Cannon.Utils.arrayWithout(planets, toRemove[i]); | |
Tween.create(toRemove[i], {radius: 1}, 100, function(item){ | |
//Cannon.Logger.log(item.__depth); | |
canvas.removeChild(item); | |
}); | |
} | |
for (var i = 0, pnum = planets.length; i < pnum; i++){ | |
var planet = planets[i]; | |
planet.vitesse = planet.vitesse.add(planet.newPos); | |
moveTo(planet, planet.x+planet.vitesse.x, planet.y+planet.vitesse.y); | |
planet.vector.nodes[1] = new Vertex2D(planet.vitesse.x*SPEED_MULTIPLICATOR, planet.vitesse.y*SPEED_MULTIPLICATOR); | |
planet.newPos = new Point2D(0, 0); | |
} | |
} | |
function moveTo(element, x, y){ | |
//bouding | |
if ((x+element.radius > canvas.width && element.vitesse.x > 0) || (x-element.radius < 0 && element.vitesse.x < 0)) element.vitesse.x *= -1; | |
if ((y+element.radius > canvas.height && element.vitesse.y > 0) || (y-element.radius < 0 && element.vitesse.y < 0)) element.vitesse.y *= -1; | |
//on eneve de la cellule courante | |
var col = Math.floor(element.x/tilemap.tilesize); | |
var row = Math.floor(element.y/tilemap.tilesize); | |
if (col >= 0 && col < tilemap.xtiles && row >= 0 && row < tilemap.ytiles){ | |
tilemap.grid[col][row].objects = Cannon.Utils.arrayWithout(tilemap.grid[col][row].objects, element); | |
} | |
//mouvemlent | |
element.x = x; | |
element.y = y; | |
//recalcul | |
var col = Math.floor(element.x/tilemap.tilesize); | |
var row = Math.floor(element.y/tilemap.tilesize); | |
if (col >= 0 && col < tilemap.xtiles && row >= 0 && row < tilemap.ytiles){ | |
tilemap.grid[col][row].objects.push(element); | |
} | |
} | |
function onFPS(event){ | |
document.getElementById('fps').innerHTML = event.fps; | |
document.getElementById('planets').innerHTML = 'Planets :'+planets.length; | |
} |
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
Cannon.include('lib/math.js'); | |
Cannon.include('lib/display.js'); | |
Cannon.include('lib/misc.js'); | |
var canvas, | |
creating = null, | |
planets = [], | |
CONSTANT = 30, | |
tilemap, | |
NB_PLANET = 20, | |
MASS_MULTIPLICATOR = 5, | |
SPEED_MULTIPLICATOR = 20, | |
MIN_EXPLOSION_SPEED = 5; | |
var TileMap = Cannon.ClassFactory.extend({ | |
__construct: function(tilesize, width, height){ | |
this.tilesize = tilesize; | |
this.width = width; | |
this.height = height; | |
this.xtiles = Math.ceil(this.width/this.tilesize); | |
this.ytiles = Math.ceil(this.height/this.tilesize); | |
this.grid = []; | |
for (var i = 0; i < this.xtiles; i++) | |
{ | |
this.grid.push([]); | |
for (var j = 0; j < this.ytiles; j++){ | |
this.grid[i].push(new Tile(i, j, this.tilesize)); | |
} | |
} | |
} | |
}); | |
var Tile = Cannon.ClassFactory.extend({ | |
__construct: function(x, y, size){ | |
this.x = x; | |
this.y = y; | |
this.size = size; | |
this.objects = []; | |
} | |
}); | |
Cannon.onReady = function(){ | |
Cannon.use('*'); | |
canvas = new Cannon.Canvas('canvas'); | |
tilemap = new TileMap(100, canvas.width, canvas.height); | |
for (var i = 0; i < NB_PLANET; i++){ | |
var planet = new Circle(0, 0, 0); | |
canvas.addChild(planet); | |
planet.vitesse = new Vector2D(Cannon.Math.Utils.randomIn(-1, 1), Cannon.Math.Utils.randomIn(-1, 1)); | |
planet.masse = Cannon.Math.Utils.randomIn(.05, .2); | |
planet.radius = MASS_MULTIPLICATOR*Math.sqrt(planet.masse); | |
planet.newPos = new Point2D(0, 0); | |
planets.push(planet); | |
moveTo(planet, Cannon.Math.Utils.randomIn(planet.radius, canvas.width-planet.radius), Cannon.Math.Utils.randomIn(planet.radius, canvas.height-planet.radius)); | |
planet.fillStyle = '#333333'; | |
planet.lineWidth = 0; | |
} | |
canvas.on('canvas:render', onRender); | |
canvas.on('canvas:FPS', onFPS); | |
canvas.on('canvas:mousedown', function(event){ | |
creating = new Circle(event.mouseX, event.mouseY, 1); | |
canvas.addChild(creating); | |
creating.fillStyle = '#ffffff'; | |
}); | |
canvas.on('canvas:mouseup', function(event){ | |
var vx = event.mouseX-creating.x; | |
var vy = event.mouseY-creating.y; | |
creating.vitesse = new Vector2D(vx/SPEED_MULTIPLICATOR, vy/SPEED_MULTIPLICATOR); | |
creating.masse = Math.pow(creating.radius/MASS_MULTIPLICATOR, 2); | |
creating.newPos = new Point2D(0, 0); | |
planets.push(creating); | |
creating.fillStyle = '#333333'; | |
creating.lineWidth = 0; | |
creating = null; | |
}); | |
} | |
function onRender(event){ | |
if (creating != null){ | |
creating.radius += .3; | |
} | |
var toRemove = []; | |
for (var p = 0, pnum = planets.length; p < pnum; p++){ | |
planet = planets[p]; | |
var champ = CONSTANT*Math.pow(planet.masse, 2); | |
var tocheck = []; | |
var minx = Math.floor(Math.max(Math.ceil(planet.x-champ), 0)/tilemap.tilesize), | |
maxx = Math.ceil(Math.min(Math.ceil(planet.x+champ), canvas.width)/tilemap.tilesize), | |
miny = Math.floor(Math.max(Math.ceil(planet.y-champ), 0)/tilemap.tilesize), | |
maxy = Math.ceil(Math.min(Math.ceil(planet.y+champ), canvas.height)/tilemap.tilesize); | |
for (var i = minx; i < maxx; i++){ | |
for (var j = miny; j < maxy; j++){ | |
tocheck = tocheck.concat(tilemap.grid[i][j].objects); | |
} | |
} | |
tocheck = Cannon.Utils.arrayWithout(tocheck, planet); | |
//var newPos = new Point2D(0, 0); | |
for (var i = 0; i < tocheck.length; i++){ | |
var checking = tocheck[i]; | |
var distance = Cannon.Math.Utils.distance(planet.x, planet.y, checking.x, checking.y); | |
if (distance < planet.radius+checking.radius){ | |
//collision or absorbtion? | |
var norme = planet.vitesse.clone().substract(checking.vitesse); | |
var colspeed = Cannon.Math.Utils.distance(0, 0, norme.x, norme.y); | |
var absorbed, absorbing; | |
if (planet.masse >= checking.masse) { | |
absorbed = checking; | |
absorbing = planet; | |
toRemove.push(absorbed); | |
} | |
else { | |
absorbed = planet; | |
absorbing = checking; | |
toRemove.push(absorbed); | |
} | |
absorbing.masse += absorbed.masse; | |
Tween.create(absorbing, {radius: MASS_MULTIPLICATOR*Math.sqrt(absorbing.masse)}, 100); | |
var vx = (absorbed.masse*absorbed.vitesse.x)/(absorbing.masse+absorbed.masse); | |
var vy = (absorbed.masse*absorbed.vitesse.y)/(absorbing.masse+absorbed.masse); | |
var tmp = new Point2D(vx, vy); | |
absorbing.newPos.add(tmp); | |
} | |
else{ | |
var len = CONSTANT*planet.masse/Math.pow(Math.max(distance, checking.radius), 2); | |
var dir = Math.atan2(planet.y-checking.y, planet.x-checking.x); | |
var tmp = new Point2D(0, 0); | |
tmp = Cannon.Math.Point2D.polar(len, dir); | |
checking.newPos.add(tmp); | |
} | |
} | |
} | |
toRemove = Cannon.Utils.uniqueArray(toRemove); | |
for (var i = 0, rmlen = toRemove.length; i < rmlen; i++){ | |
var col = Math.floor(toRemove[i].x/tilemap.tilesize); | |
var row = Math.floor(toRemove[i].y/tilemap.tilesize); | |
if (col >= 0 && col < tilemap.xtiles && row >= 0 && row < tilemap.ytiles){ | |
tilemap.grid[col][row].objects = Cannon.Utils.arrayWithout(tilemap.grid[col][row].objects, toRemove[i]); | |
} | |
planets = Cannon.Utils.arrayWithout(planets, toRemove[i]); | |
Tween.create(toRemove[i], {radius: 1}, 100, function(item){ | |
canvas.removeChild(item); | |
}); | |
} | |
for (var i = 0, pnum = planets.length; i < pnum; i++){ | |
var planet = planets[i]; | |
planet.vitesse = planet.vitesse.add(planet.newPos); | |
moveTo(planet, planet.x+planet.vitesse.x, planet.y+planet.vitesse.y); | |
planet.newPos = new Point2D(0, 0); | |
} | |
} | |
function moveTo(element, x, y){ | |
//bouding | |
if ((x+element.radius > canvas.width && element.vitesse.x > 0) || (x-element.radius < 0 && element.vitesse.x < 0)) element.vitesse.x *= -1; | |
if ((y+element.radius > canvas.height && element.vitesse.y > 0) || (y-element.radius < 0 && element.vitesse.y < 0)) element.vitesse.y *= -1; | |
//on eneve de la cellule courante | |
var col = Math.floor(element.x/tilemap.tilesize); | |
var row = Math.floor(element.y/tilemap.tilesize); | |
if (col >= 0 && col < tilemap.xtiles && row >= 0 && row < tilemap.ytiles){ | |
tilemap.grid[col][row].objects = Cannon.Utils.arrayWithout(tilemap.grid[col][row].objects, element); | |
} | |
//mouvemlent | |
element.x = x; | |
element.y = y; | |
//recalcul | |
var col = Math.floor(element.x/tilemap.tilesize); | |
var row = Math.floor(element.y/tilemap.tilesize); | |
if (col >= 0 && col < tilemap.xtiles && row >= 0 && row < tilemap.ytiles){ | |
tilemap.grid[col][row].objects.push(element); | |
} | |
} | |
function onFPS(event){ | |
document.getElementById('fps').innerHTML = event.fps; | |
document.getElementById('planets').innerHTML = 'Planets :'+planets.length; | |
} |
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
<html> | |
<head> | |
<title>Planetarium</title> | |
<script src="lib/cannon.js"></script> | |
<script src="engine-debug.js"></script> | |
</head> | |
<body style="text-align: center;"> | |
<div id="canvas" style="width: 700px; height: 600px; border: 1px solid #555; margin: auto;"></div> | |
<div id="fps"></div> | |
<div id="planets"></div> | |
<div id="logs"></div> | |
</body> | |
</html> |
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
<html> | |
<head> | |
<title>Planetarium</title> | |
<script src="lib/cannon.js"></script> | |
<script src="engine.js"></script> | |
</head> | |
<body style="text-align: center;"> | |
<div id="canvas" style="width: 700px; height: 600px; border: 1px solid #555; margin: auto;"></div> | |
<div id="fps"></div> | |
<div id="planets"></div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment