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

172 lines (153 loc) 5.32 kB
/** * @reference http://www.codezealot.org/archives/180 * @reference https://github.com/LSFN/dyn4go/blob/383474ee3924d626a1c106bc717f07deb76b9635/collision/narrowphase/EPA.go * @reference https://github.com/BrandonLittell/PinballGL/blob/d2678f0e916cdb8b8a234243feeda03a196e5bc8/Group3_FinalProject/Collision.cpp */ var Vec2 = require("../vec2.js"), vec2_sub = Vec2.sub, vec2_cross = Vec2.cross, vec2_tripleProduct = Vec2.tripleProduct, vec2_normalize = Vec2.normalize, vec2_dot = Vec2.dot, Polygon = require("../polygon.js"), clear = require("./response.js").clear, polygon_furthestMinkowski = Polygon.furthestMinkowski; var gw_aux = [0, 0]; var gw_aux2 = [0, 0]; /** * @param {Polygon} simplex * @return {Number} */ function _getWinding(simplex) { vec2_sub(gw_aux, simplex[1], simplex[0]); vec2_sub(gw_aux2, simplex[2], simplex[1]); return vec2_cross(gw_aux, gw_aux2); var len = simplex.length, i = 0, j, f; for (; i < len; ++i) { j = i + 1; if (j === len) { j = 0; } f = vec2_cross(simplex[i], simplex[j]); if (f > 0) { return 1; } else if (f < 0) { return -1; } } return 0; } var edge = [0, 0]; /** * @param {Polygon} simplex * @param {Number} winding */ function _findClosestEdge(simplex, winding) { // prime the distance of the edge to the max var distance = Infinity, a, i, j, d, n = [0, 0], normal = [0, 0], index, len = simplex.length, lenm1 = len - 1; // simplex is the passed in simplex for (i = 0; i < len; i++) { // compute the next points index j = i === lenm1 ? 0 : i + 1; // get the current point and the next one a = simplex[i]; // b = simplex[j]; // create the edge vector vec2_sub(edge, simplex[j], a); // or a.to(b); // console.log("edge", edge, "@", i, j, a, simplex[j]); // get the vector from the origin to a //Vector oa = a; // or a - ORIGIN // get the vector from the edge towards the origin //vec2_tripleProduct(n, edge, a, edge); //---------------------------------------------------------------------- // @llafuente: winding method seems to be more reliable to find MTV if (winding > 0) { Vec2.rotateCW(n, edge); } else { Vec2.rotateCCW(n, edge); } // normalize the vector //---------------------------------------------------------------------- vec2_normalize(n, n); // console.log("tripleProduct", n, edge, a, edge); // calculate the distance from the origin to the edge d = vec2_dot(a, n); // could use b or a here // check the distance against the other distances // console.log("d = a.dot(n) =", d, distance); if (d < distance) { // if this edge is closer then use it distance = d; normal[0] = n[0]; normal[1] = n[1]; index = j; } } // return the closest edge we found return { distance: distance, normal: normal, index: index, }; } /** * @TODO the current implementation has no max iterations, but seems to work review edge cases * * @param {Response} out_response * @param {Polygon} A * @param {Polygon} B * @param {Polygon} simplex result of GJK */ function EPA(out_response, A, B, simplex) { //simplex = [[4, 2], [-8, -2], [-1, -2]]; clear(out_response); var edge, depth, p, winding = _getWinding(simplex); console.log("winding", winding); //redo the simplex if (winding <= 0) { simplex.reverse(); } // loop to find the collision information while (true) { // console.log("***************************", Polygon.toString(simplex)); // obtain the feature (edge for 2D) closest to the origin on the Minkowski Difference edge = _findClosestEdge(simplex, winding); // console.log("_findClosestEdge", edge); // obtain a new support point in the direction of the edge normal p = polygon_furthestMinkowski([0, 0], A, B, edge.normal); // check the distance from the origin to the edge against the // distance p is along edge.normal depth = vec2_dot(p, edge.normal); // console.log(max, "distance", depth, edge.distance); if (depth - edge.distance < Math.EPS) { // the tolerance should be something positive close to zero (ex. 0.00001) // if the difference is less than the tolerance then we can // assume that we cannot expand the simplex any further and // we have our solution out_response.mtv = edge.normal; out_response.depth = depth; return true; } else { // we haven't reached the edge of the Minkowski Difference // so continue expanding by adding the new point to the simplex // in between the points that made the closest edge // console.log(simplex[edge.index], p); simplex.splice(edge.index, 0, p); } } } module.exports = EPA;