js-2dmath
Version:
Fast 2d geometry math: Vector2, Rectangle, Circle, Matrix2x3 (2D transformation), Circle, BoundingBox, Line2, Segment2, Intersections, Distances, Transitions (animation/tween), Random numbers, Noise
271 lines (217 loc) • 8.16 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" ></script>
<title>js-2dmath: Some Vec2 functions in action</title>
<script src="../dist/js-2dmath-browser.min.js" type="text/javascript" ></script>
<!--
<script src="../dist/js-2dmath-browser.min.js" type="text/javascript" ></script>
-->
</head>
<body>
<h1>js-2dmath: Some Vec2 functions in action</h1>
<canvas id="canvas-axis" width="640" height="640" style="position: absolute; border: 2px solid blue;"></canvas>
<canvas id="canvas" width="640" height="640" style="position: absolute; z-index: 10; border: 1px solid red;"></canvas>
</body>
</html>
<script>
/**
* @source http://chrishecker.com/Rigid_Body_Dynamics
* @source http://www.wildbunny.co.uk/blog/2011/04/06/physics-engines-for-dummies/
* @source http://physics.gac.edu/~miller/jterm_2013/physics_engine_tutorial.html
*/
require("js-2dmath").globalize(window);
window.requestAnimFrame = function (callback) {
window.setTimeout(callback, 1000);
};
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvas_axis = document.getElementById("canvas-axis");
var ctx_axis = canvas_axis.getContext("2d");
function World(maxdt, max_iterations, gravity) {
this.gravity = gravity || [0, -9.8];
this.maxdt = maxdt;
this.max_iterations = max_iterations || 10;
}
World.prototype.bodies = [];
World.prototype.maxdt = 0;
World.prototype.gravity = [0, -9.8];
World.prototype.addBody = function(body) {
this.bodies.push(body);
};
World.prototype._update = function(dt) {
var self = this;
// update world position & gravity
var gravity = Vec2.scale([0, 0], this.gravity, dt);
this.forEachBody(function (b) {
//apply gravity only to movable objects
if (b.imass !== 0) {
b.update();
//Vec2.add(b.velocity, b.velocity, gravity);
b.velocity[0] = b.velocity[0] + gravity[0] * b.mass;
b.velocity[1] = b.velocity[1] + gravity[1] * b.mass;
}
});
// collisions
this.forEachBodyPair(function (a, b) {
var result = new Collision.SAT.Response();
var aEdges = [];
var aNormals = [];
Polygon.edges(aEdges, a.wbody);
Polygon.normals(aNormals, aEdges);
var bEdges = [];
var bNormals = [];
Polygon.edges(bEdges, b.wbody);
Polygon.normals(bNormals, bEdges);
Collision.SAT.getPolygonPolygon(result, a.wbody, aNormals, [0, 0], b.wbody, bNormals, [0, 0]);
console.log("SAT", JSON.stringify(result));
if (result.depth) {
//self.stop = true;
//Draw.polygon(ctx, result.simplex, "rgba(0,255,0,0.25)", true);
//console.log("GJK", JSON.stringify(result));
//var epa = Polygon.EPA(a.wbody, b.wbody, result.simplex);
//console.log("EPA", JSON.stringify(epa));
var ec = Collision.Manifold.EdgeClipping(a.wbody, b.wbody, result.mtv);
console.log("EC", JSON.stringify(ec));
if (ec) {
ctx.lineWidth = 3;
Draw.segment2(ctx,
[ec.incidentEdge.v1[0], ec.incidentEdge.v1[1], ec.incidentEdge.v2[0], ec.incidentEdge.v2[1]], "green");
Draw.segment2(ctx,
[ec.referenceEdge.v1[0], ec.referenceEdge.v1[1], ec.referenceEdge.v2[0], ec.referenceEdge.v2[1]], "yellow");
ctx.lineWidth = 1;
var aPos = Vec2.midPoint([0, 0], ec.incidentEdge.v1, ec.incidentEdge.v2);
var bPos = Vec2.midPoint([0, 0], ec.referenceEdge.v1, ec.referenceEdge.v2);
Draw.vec2(ctx, aPos, "orange");
Draw.vec2(ctx, bPos, "orange");
/*
// two dynamics use elastic
// aPos, aVelocity, aMass, afVelocity, bPos, bVelocity, bMass, bfVelocity
Collision.elastic(aPos, a.velocity, a.mass, bPos, b.velocity, b.mass);
*/
Collision.Resolve.linear(a.velocity, a.restitution, a.imass, aPos, b.velocity, b.restitution, b.imass, bPos, result.mtv);
}
}
});
// - air friction
// - ground friction
// integrate
var hdt = dt * 0.5,
dt6 = dt * 0.166666667;
this.forEachBody(function (b) {
//apply gravity only to movable objects
if (b.imass !== 0) {
var stateX = NumericalIntegration.RK4(b.position[0], b.velocity[0], b.nimass, 1, 0, dt, hdt, dt6);
var stateY = NumericalIntegration.RK4(b.position[1], b.velocity[1], b.nimass, 1, 0, dt, hdt, dt6);
b.velocity[0] = stateX[1];
b.velocity[1] = stateY[1];
b.translate2(stateX[0] - b.position[0], stateY[0] - b.position[1]);
console.log(b.position);
}
});
}
World.prototype.update = function(dt) {
if (this.stop) return;
// fix dt
this._update(dt);
}
World.prototype.forEachBody = function(fn) {
var i = 0,
bs = this.bodies,
max = bs.length
for (i = 0; i < max; ++i) {
fn(bs[i]);
}
}
World.prototype.forEachBodyPair = function(fn) {
var i = 0,
bs = this.bodies,
max = bs.length
for (i = 0; i < max; ++i) {
for (j = i + 1; j < max; ++j) {
fn(bs[i], bs[j]);
}
}
}
var BodyUID = 0;
function Body(body, mass, restitution) {
this.uid = ++BodyUID;
this.body = body;
this.type = "polygon";
//this.hash = 1;
this.restitution = restitution || 0;
this.matrix = Matrix23.create();
this.matrix[7] = true;
this.wbody = new Array(body.length);
this.mass = mass || 1;
if (mass === 0) {
this.imass = 0;
this.nimass = 0;
this.update(); // manual update
} else {
this.imass = 1 / this.mass;
this.nimass = -(1 / this.mass);
}
// arrays must be instanced again
this.velocity = [0, 0];
this.position = [0, 0];
//this.moi = Polygon.area(body) *
}
Body.prototype.translate2 = function (x, y) {
Matrix23.translate(this.matrix, this.matrix, [x, y]);
this.position[0] = +x;
this.position[1] = +y;
}
Body.prototype.update = function () {
if (this.matrix[7]) {
this.matrix[7] = false;
Polygon.transform(this.wbody, this.body, this.matrix);
}
}
Body.prototype.position = [0, 0];
Body.prototype.velocity = [0, 0];
Body.prototype.hash = 1;
Body.prototype.avelocity = 0;
Body.prototype.torque = 0;
Body.prototype.orientation = 0;
Body.prototype.restitution = 0;
Body.prototype.mass = 0;
Body.prototype.imass = 0;
Body.prototype.nimass = 0;
Body.prototype.matrix = [];
Body.prototype.wbody = [];
Body.prototype.moi = 0;
var world = new World();
var b1 = new Body (Polygon.fromCircle(Circle.create(0, 0, 50), 10, 0), 0, 1);
world.addBody(b1);
var b2 = new Body (Polygon.fromCircle(Circle.create(0, 0, 50), 10, Math.PI / 16), 1, 1);
b2.translate2(50, 100);
world.addBody(b2);
var b3 = new Body (Polygon.fromCircle(Circle.create(0, 0, 25), 10, Math.PI / 16), 5, 1);
b3.translate2(-40, 80);
world.addBody(b3);
var b4 = new Body (Polygon.fromCircle(Circle.create(0, 0, 10), 10, Math.PI / 16), 50, 1);
b4.translate2(0, 150);
world.addBody(b4);
function update() {
// do not clear canvas we dont see debug information
if (world.stop) return ;
window.requestAnimFrame(update);
ctx.save();
canvas.width = canvas.width;
Draw.invertAxis(canvas, ctx); // y-up
ctx.translate(canvas.width * 0.5, canvas.height * 0.5); // center
world.update(0.05);
b1.update();
b2.update();
Draw.polygon(ctx, b1.wbody, "red");
Draw.polygon(ctx, b2.wbody, "blue");
Draw.polygon(ctx, b3.wbody, "blue");
Draw.polygon(ctx, b4.wbody, "blue");
ctx.restore();
};
window.requestAnimFrame(update);
Draw.invertAxis(canvas_axis, ctx); // y-up
ctx_axis.translate(canvas_axis.width * 0.5, canvas_axis.height * 0.5); // center
Draw.cartesianAxis(ctx_axis, 320, 16); // coordinates
</script>