Skip to content

Commit

Permalink
initial, gravity simulator - no constraints yet
Browse files Browse the repository at this point in the history
  • Loading branch information
dominictarr committed Jan 20, 2012
0 parents commit e923be6
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
node_modules
node_modules/*
npm_debug.log
145 changes: 145 additions & 0 deletions index.html
@@ -0,0 +1,145 @@
<html>

<body>

<canvas id=canvas width=2000 height=1600></canvas>
</body>

<script src="lib/vector.js"></script>
<script>
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
var center = new Vector(canvas.width/2,canvas.height/2)
//paint whole canvas black

var bodies = []
function randV() {
return Vector.random(canvas.width,canvas.height).iabs()
}
function randS() {
return ((Math.random()*50)+ 10)
}
function Body (v, r) {
this.origin = v || randV()
this.velocity = Vector.a2v(Math.random()*2*Math.PI).imul(10)
this._origin = this.origin.sub(this.velocity)
this.speed = this.velocity.length()
this.radius = r || randS()
}

function each(ary, iter) {
for(var i in ary) iter(ary[i],i,ary)
}
function times(n, iter) {
var i = 0; while (i < n) iter(i++, n)
}
function drawBody (b) {
context.strokeStyle = 'rgba(0,255,0,1)';
context.lineWidth = 1;
context.beginPath();
context.arc(b.origin.x, b.origin.y, b.radius, 0, Math.PI*2, false);
context.stroke()
}

function draw() {
context.fillStyle = 'rgba(40,40,40,0.1)';
context.fillRect(0, 0, canvas.width, canvas.height);
each(bodies, drawBody)
}

times(36, function () {bodies.push(new Body())})
draw()

var G = 150000;
function getGravity (a, b) {
var dir = a.sub(b)
var l = dir.length() //there are unnecessary Math.sqrt, since nor also gets length
return dir.nor().imul(G/(l*l))
}

function assert(test, message) {
if(!test) throw new Error(message)
}

/*
VERTLET INTEGRATION
it make a big difference which way around this is done.
I had it like this before:
body.origin
.imul(2).isub(_origin)
.iadd(getGravity(center, body.origin).imul(delta))
I noticed that all my planets would fall into the gravity well and be slingshot
out. some would then fall back into the well...
they where loosing energy. I changed it, so that I add the acceleration
due to gravity first -- and it looks right. more stable orbits.
another idea:
variable timeslices with euler integration
-- give faster moving slices more steps.
*/
setInterval(function () {
var delta = 0.1 //seconds

//sort bodies by thier speed, decending
bodies.sort(function (a,b) {
return b.speed - a.speed
})
var totalSpeed = 0
console.log(bodies.map(function (b) {
var s = Math.round(b.speed)
totalSpeed += s
return s
}).join(','))


function step(body, delta) {
/*
// verlet integration (_origin is last tick's origin)
body.origin
.iadd(getGravity(center, body.origin).imul(delta))
.imul(2).isub(_origin)
*/
body.velocity.iadd(getGravity(center, body.origin).imul(delta))
body.speed = body.velocity.length()
body.origin.iadd(body.velocity.mul(delta))
}

/*
give each body steps depending on the square of it's speed.
this works fairly well. getting lots of quite stable highly eliptical orbits.
interestingly, I'm not seeing many regular orbits.
bodies do have a tendency to fall into the gravity well eventually...(rounding errors?)
the body moves quickly at the perigee, but slowly at the apogee.
approximating the curve with many straight lines is more accurate when
you use more lines. the apogee uses many slices, since it's moving slowly.
I have made it use more slices at the perigee, but is it the same number
of steps per distance traveled?
also, note at the perigee the steps (thus the delta) is very small.
could this cause rounding error? at the apogee the speed is very small,
could this balance out the small delta at the perigee?
currently, I always doing at least one step per frame, maybe it would be better
to update steps per object independent of frames...
so that it does not loose energy.
another (heavy) approach would just be to use BigDecimals.
though, that will be much slower it would be better to just not care.
*/

each(bodies, function (b) {
var steps = Math.round((b.speed*b.speed)/100) || 1
var l = steps
while(l --)
step(b,delta/steps)
})
draw()
}, 100)

</script>
</html>
112 changes: 112 additions & 0 deletions lib/vector.js
@@ -0,0 +1,112 @@

function v(x, y) {
return new Vector(x, y)
}

Vector.a2v = function (a) { return new Vector(Math.cos(a), Math.sin(a)) }
function r() {
return Math.random()*2 - 1
}
Vector.random = function (x,y) {
return new Vector(r()*x, r()*y)
}
Vector.center = function () {
var c = new Vector(0,0)
for (var i = 0; i < arguments.length; i ++) {
c.iadd(arguments[i])
}
return c.idiv(arguments.length)
}

function Vector (x, y) {
if('object' === typeof x && x) {
this.x = x.x || 0;
this.y = x.y || 0;
} else {
this.x = x || 0;
this.y = y || 0;
}
}

Vector.prototype = {
dot: function (v) {
return this.x * v.x + this.y * v.y
},
length: function () {
return Math.sqrt(this.x*this.x + this.y*this.y)
},
iadd: function (v) {
this.x += v.x;
this.y += v.y;
return this;
},
isub: function (v) {
this.x -= v.x;
this.y -= v.y;
return this;
},
imul: function (scalar) {
this.x *= scalar;
this.y *= scalar;
return this;
},
idiv: function (scalar) {
this.x /= scalar;
this.y /= scalar;
return this;
},
izero: function () {
this.x = 0;
this.y = 0;
return this;
},
inor: function () {
var l = this.length();
this.x /= l;
this.y /= l;
return this;
},
iabs: function () {
this.x = Math.abs(this.x)
this.y = Math.abs(this.y)
return this
},
add: function (v) {
return new Vector(this).iadd(v);
},
sub: function (v) {
return new Vector(this).isub(v);
},
mul: function (v) {
return new Vector(this).imul(v);
},
div: function (v) {
return new Vector(this).idiv(v);
},
nor: function () {
return new Vector(this).inor();
},
abs: function () {
return new Vector(this).iabs();
},
//always positive
diff: function (v) {
return new Vector(
Math.abs(this.x - v.x),
Math.abs(this.y - v.y)
)
},
//distance between this and the other point.
dist: function (v) {
return this.diff(v).length()
},
angle: function () {
return Math.atan(this.x/this.y) * (this.x > 0 ? 1 : -1 )
},
clone: function () {
return new Vector(this)
},
toString: function () {
return '('+this.x+','+this.y+')'
}
}
11 changes: 11 additions & 0 deletions package.json
@@ -0,0 +1,11 @@
{ "name": "phyxel"
, "version": "0.0.0"
, "description": ""
, "homepage": "http://github.com/dominictarr/phyxel"
, "repository":
{ "type": "git"
, "url": "https://github.com/dominictarr/phyxel.git" }
, "dependencies": {}
, "devDependencies": {}
, "author": "Dominic Tarr <dominic.tarr@gmail.com> (http://bit.ly/dominictarr)"
, "scripts": { "test": "meta-test test/*.js" } }
37 changes: 37 additions & 0 deletions readme.markdown
@@ -0,0 +1,37 @@
# Phyxel

experimental voxel based 2d physics.


THIS IS JUST A ORBIT SIMULATOR, FOR NOW


CRAZY IDEA

fluids:

gas light, non dense, possibly moving partices, not sticky.

liquids: denser, sticky, surface tension.

solids:

solid: dense, stiffly stuck to other solid things, but can be flexible or brittle.
connections can have a direction, so a chain can have stiffness, and brittleness.

advantages:

simpler simulation,
each phyxel has a few simple properties, that my affect neibouring phyxels. (say, 6)
instead of having alot of complicated rules and checks. just make every thing a circle.

ideas:

would be cool to be able to freeze or melt parts of a game level,
freeze to make it brittle, then smash it. melt through things,
float on water or fly in the air maybe?

maybe it'll be better to make the phyxels "large"... and lift wont work right.
that is okay, also, the entities in the game might be werid. walking and stuff...
maybe should just have them roll sideways, hover, or allow a few 'magical' forces.

0 comments on commit e923be6

Please sign in to comment.