UNPKG

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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <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>