Skip to content

Instantly share code, notes, and snippets.

@y-lohse
Created January 3, 2013 21:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save y-lohse/4447634 to your computer and use it in GitHub Desktop.
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
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;
}
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;
}
<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>
<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