Skip to content

Instantly share code, notes, and snippets.

@keiya
Created February 12, 2012 16:53
Show Gist options
  • Select an option

  • Save keiya/1809622 to your computer and use it in GitHub Desktop.

Select an option

Save keiya/1809622 to your computer and use it in GitHub Desktop.
physical simulator on dom
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>phyjics</title>
<meta name="viewport" content="initial-scale=0.9, minimum-scale=0.9, maximum-scale=1.1" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<script src="http://cdn.keiyac.org/common/jquery/jquery.min.js" type="text/javascript"></script>
<script src="ph.js" defer></script>
<style type="text/css">
.collision {
border: 1px solid #f00;
}
.wall-a {
position:absolute;
bottom: 120px;
left: 100px;
width: 800px;
height: 50px;
background-color: #ccc;
border:1px solid #111;
}
.ball-a {
position:absolute;
top: 100px;
right: 190px;
width: 100px;
height: 100px;
background-color: #ccc;
border:1px solid #111;
}
.ball-b {
position:absolute;
top: 0px;
left: 190px;
width: 100px;
height: 100px;
background-color: #ccc;
border:1px solid #111;
}
</style>
</head>
<body>
<div
class='collision-a wall-a editable'
data-mass='10'
data-acc-x='0'
data-acc-y='0'
data-iv-x='0'
data-iv-y='0'
data-fixed='1'
>
</div>
<div
class='collision-a ball-b editable'
data-mass='10'
data-acc-x='0'
data-acc-y='9.81'
data-iv-x='0'
data-iv-y='0'
>
</div>
<pre id='dbg'></pre>
<script>
function dbg(text) {
$('#dbg').append(text+"\n");
}
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback, element){
window.setTimeout(callback, 1000 / 60);
};
})();
$(document).ready(function(){
// phjics init
// inherit from Objects
var ObjClassA = function () {
this._constructor('.collision-a');
}
ObjClassA.prototype = new Objects();
// instance
var coll_a = new ObjClassA();
//var coll_a = new Objects('.collision-a');
var pos = coll_a.getPositionAll();
var lastClock = +new Date();
function mainLoop() {
var currClock = +new Date();
// coll_a.applyGravity((currClock-lastClock)/1000);
coll_a.collisionChk();
coll_a.applyVector((currClock-lastClock)/1000);
coll_a.setPosition();
lastClock = currClock;
requestAnimationFrame(mainLoop);
}
mainLoop();
// Interface
var coll_a = new Objects('.collision-a');
});
</script>
</body>
</html>
// keiyac
// 2012.02.12
// 2012.02.16
// physical constants
const G = 6.67;
const g = 9.81;
// pseudo constructor
var Objects = function (selector) {
this._constructor(selector);
}
// methods
Objects.prototype = {
_constructor : function(selector) {
var objs = [];
var _this = this;
$(selector).each(function(){
var obj = {};
obj.pos = _this.getPosition(this);
obj.mass = parseFloat($(this).attr('data-mass'));
var regionEndsX = (obj.pos.x + obj.pos.width)/2;
var barycenterOfsX = obj.pos.width/2;
var barycenterOfsY = obj.pos.height/2;
var barycenterAbsX = obj.pos.x + barycenterOfsX;
var barycenterAbsY = obj.pos.y + barycenterOfsY;
obj.absBarycenter = {x:barycenterOfsX,y:barycenterOfsY};
obj.offsetBarycenter = {x:barycenterAbsX,y:barycenterOfsY};
obj.acceleration = {};
obj.acceleration.x = parseFloat($(this).attr('data-acc-x'));
obj.acceleration.y = parseFloat($(this).attr('data-acc-y'));
obj.velocity = {};
obj.velocity.x = parseFloat($(this).attr('data-iv-x'));
obj.velocity.y = parseFloat($(this).attr('data-iv-y'));
obj.fixed = parseInt($(this).attr('data-fixed'));
obj.dom = this;
objs.push(obj);
});
$(selector+',editable').click(function(){
});
this.objs = objs;
},
dumpObj : function() {
console.dir(this.objs);
},
getPositionAll : function() {
var _this = this;
var positions = [];
$.each(this.objs,function(){
positions.push(_this.getPosition(this.dom));
});
return positions;
},
getPosition : function(selection) {
var pos = {};
pos.width= $(selection).width();
pos.height = $(selection).height();
var offset = $(selection).offset();
pos.x = parseFloat(offset.left);
pos.y = parseFloat(offset.top);
return pos;
},
setPosition : function() {
$.each(this.objs,function(){
$(this.dom).css({'left':this.pos.x,'top':this.pos.y});
});
},
collisionChk : function() {
//$.each(_this.objs,function(){
skip: for (i in this.objs) {
//var obj_a = this;
var obj_a = this.objs[i];
//$.each(_this.objs,function(){
for (j in this.objs) {
// var obj_b = this;
var obj_b = this.objs[j];
if (obj_a !== obj_b) {
// vertices
var objectA = {};
var objectB = {};
objectA.A = {};
objectA.B = {};
objectA.C = {};
objectA.D = {};
objectB.A = {};
objectB.B = {};
objectB.C = {};
objectB.D = {};
objectA.A.x = obj_a.pos.x; // top left
objectA.B.x = obj_a.pos.x + obj_a.pos.width; // top right
objectA.C.x = obj_a.pos.x; // bottom left
objectA.D.x = obj_a.pos.x + obj_a.pos.width; // bottom right
objectA.A.y = obj_a.pos.y; // top left
objectA.B.y = obj_a.pos.y + obj_a.pos.height; // bottom left
objectA.C.y = obj_a.pos.y; // top right
objectA.D.y = obj_a.pos.y + obj_a.pos.height; // bottom right
objectB.A.x = obj_b.pos.x; // top left
objectB.B.x = obj_b.pos.x + obj_b.pos.width; // top right
objectB.C.x = obj_b.pos.x; // bottom left
objectB.D.x = obj_b.pos.x + obj_b.pos.width; //bottom right
objectB.A.y = obj_b.pos.y; // top left
objectB.B.y = obj_b.pos.y + obj_b.pos.height; // bottom left
objectB.C.y = obj_b.pos.y; // top right
objectB.D.y = obj_b.pos.y + obj_b.pos.height; // bottom right
var combination = [
['A','B'],
['A','C'],
['D','B'],
['D','C'],
];
nextObj: for (lineA in combination) {
startVertexNameA = combination[[lineA]][0];
endVertexNameA = combination[[lineA]][1];
for (lineB in combination) {
startVertexNameB = combination[[lineB]][0];
endVertexNameB = combination[[lineB]][1];
var collision = this._isCrossLine(
objectA[startVertexNameA].x, objectA[startVertexNameA].y,
objectA[endVertexNameA].x, objectA[endVertexNameA].y,
objectB[startVertexNameB].x, objectB[startVertexNameB].y,
objectB[endVertexNameB].x, objectB[endVertexNameB].y
)
if (collision) {
//;//console.log(collision);
this.applyCollision(obj_a,obj_b);
//this.objs[i] = obj[0];
//this.objs[j] = obj[1];
break skip;
}
}
}
}
//});
// });
}
}
},
applyCollision : function(a,b) {
var t;
var vx = (b.pos.x - a.pos.x);
var vy = (b.pos.y - a.pos.y);
t = -(vx * a.velocity.x + vy * a.velocity.y) / (vx * vx + vy * vy);
var arx = a.velocity.x + vx * t;
var ary = a.velocity.y + vy * t;
t = -(-vy * a.velocity.x + vx * a.velocity.y) / (vy * vy + vx * vx);
var amx = a.velocity.x - vy * t;
var amy = a.velocity.y + vx * t;
t = -(vx * b.velocity.x + vy * b.velocity.y) / (vx * vx + vy * vy);
var brx = b.velocity.x + vx * t;
var bry = b.velocity.y + vy * t;
t = -(-vy * b.velocity.x + vx * b.velocity.y) / (vy * vy + vx * vx);
var bmx = b.velocity.x - vy * t;
var bmy = b.velocity.y + vx * t;
var e = 0.9;
if (a.fixed != 1) {
a.velocity.x = (a.mass * amx + b.mass * bmx + bmx * e * b.mass - amx * e * b.mass) / (a.mass + b.mass);
a.velocity.y = (a.mass * amy + b.mass * bmy + bmy * e * b.mass - amy * e * b.mass) / (a.mass + b.mass);
a.acceleration.x = amx;
a.acceleration.y = amy;
}
if (b.fixed != 1) {
b.velocity.x = - e * (bmx - amx) + a.velocity.x;
b.velocity.y = - e * (bmy - amy) + a.velocity.y;
b.acceleration.x = amx;
b.acceleration.y = bmy;
}
/*
a.velocity.x = a.velocity.x + arx;
a.velocity.y = a.velocity.y + ary;
b.velocity.x = b.velocity.x + brx;
b.velocity.y = b.velocity.y + bry;
*/
},
applyGravity : function(t) {
for (idx in this.objs) {
//var z = -0.5*g*t*t + Math.sin() * t;
var delta_z = 0.5*g*t*t;
if (this.objs[idx]['pos']['y'] < $(window).height() - this.objs[idx]['pos']['height'])
this.objs[idx]['pos']['y'] += delta_z;
}
},
applyVector : function(dt) {
for (idx in this.objs) {
this.objs[idx]['velocity']['x'] += this.objs[idx]['acceleration']['x'] * dt;
this.objs[idx]['velocity']['y'] += this.objs[idx]['acceleration']['y'] * dt;
var x = this.objs[idx]['velocity']['x'] * dt;
var y = this.objs[idx]['velocity']['y'] * dt;
// if (this.objs[idx]['pos']['y'] < $(window).height() - this.objs[idx]['pos']['height']) {
this.objs[idx]['pos']['y'] += y;
// }
this.objs[idx]['pos']['x'] += x;
}
},
_isCrossLine : function(
x1,y1,
x2,y2,
x3,y3,
x4,y4
) {
if (((x1-x2)*(y3-y1) + (y1-y2)*(x1-x3)) *
((x1-x2)*(y4-y1) + (y1-y2)*(x1-x4)) < 0 ) {
if (((x3-x4)*(y1-y3) + (y3-y4)*(x3-x1)) *
((x3-x4)*(y2-y3) + (y3-y4)*(x3-x2)) < 0 ) {
return true;
}
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment