UNPKG

collider2d

Version:

A 2D collision checker for modern JavaScript games.

595 lines (449 loc) 67.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _box = _interopRequireDefault(require("./geometry/box")); var _vector = _interopRequireDefault(require("./geometry/vector")); var _collision_details = _interopRequireDefault(require("./collision_details")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var Collider2D = /*#__PURE__*/function () { /** * A pool of `Vector objects that are used in calculations to avoid allocating memory. * * @private * * @property {Array<Vector>} */ /** * A pool of arrays of numbers used in calculations to avoid allocating memory. * * @private * * @property {Array<Array<number>>} */ /** * Temporary collision details object used for hit detection. * * @private * * @property {CollisionDetails} */ /** * Tiny "point" Polygon used for Polygon hit detection. * * @private * * @property {Polygon} */ /** * Constant used for left voronoi region. * * @private * * @property {number} */ /** * Constant used for middle voronoi region. * * @private * * @property {number} */ /** * Constant used for right voronoi region. * * @private * * @property {number} */ function Collider2D() { _classCallCheck(this, Collider2D); _defineProperty(this, "_T_VECTORS", []); _defineProperty(this, "_T_ARRAYS", []); _defineProperty(this, "_T_COLLISION_DETAILS", new _collision_details["default"]()); _defineProperty(this, "_TEST_POINT", new _box["default"](new _vector["default"](), 0.000001, 0.000001).toPolygon()); _defineProperty(this, "_LEFT_VORONOI_REGION", -1); _defineProperty(this, "_MIDDLE_VORONOI_REGION", 0); _defineProperty(this, "_RIGHT_VORONOI_REGION", 1); // Populate T_VECTORS for (var i = 0; i < 10; i++) { this._T_VECTORS.push(new _vector["default"]()); } // Populate T_ARRAYS for (var _i = 0; _i < 5; _i++) { this._T_ARRAYS.push([]); } } /** * Check if a point is inside a circle. * * @param {Vector} point The point to test. * @param {Circle} circle The circle to test. * * @returns {boolean} Returns true if the point is inside the circle or false otherwise. */ _createClass(Collider2D, [{ key: "pointInCircle", value: function pointInCircle(point, circle) { var differenceV = this._T_VECTORS.pop().copy(point).sub(circle.position).sub(circle.offset); var radiusSq = circle.radius * circle.radius; var distanceSq = differenceV.len2(); this._T_VECTORS.push(differenceV); // If the distance between is smaller than the radius then the point is inside the circle. return distanceSq <= radiusSq; } /** * Check if a point is inside a convex polygon. * * @param {Vector} point The point to test. * @param {Polygon} polygon The polygon to test. * * @returns {boolean} Returns true if the point is inside the polygon or false otherwise. */ }, { key: "pointInPolygon", value: function pointInPolygon(point, polygon) { this._TEST_POINT.position.copy(point); this._T_COLLISION_DETAILS.clear(); var result = this.testPolygonPolygon(this._TEST_POINT, polygon, true); if (result) result = this._T_COLLISION_DETAILS.aInB; return result; } /** * Check if two circles collide. * * @param {Circle} a The first circle. * @param {Circle} b The second circle. * @param {boolean} [details=false] If set to true and there is a collision, an object highlighting details about the collision will be returned instead of just returning true. * * @returns {boolean} Returns true if the circles intersect or false otherwise. */ }, { key: "testCircleCircle", value: function testCircleCircle(a, b) { var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; // Check if the distance between the centers of the two circles is greater than their combined radius. var differenceV = this._T_VECTORS.pop().copy(b.position).add(b.offset).sub(a.position).sub(a.offset); var totalRadius = a.radius + b.radius; var totalRadiusSq = totalRadius * totalRadius; var distanceSq = differenceV.len2(); // If the distance is bigger than the combined radius, they don't intersect. if (distanceSq > totalRadiusSq) { this._T_VECTORS.push(differenceV); return false; } if (details) { this._T_COLLISION_DETAILS.clear(); var dist = Math.sqrt(distanceSq); this._T_COLLISION_DETAILS.a = a; this._T_COLLISION_DETAILS.b = b; this._T_COLLISION_DETAILS.overlap = totalRadius - dist; this._T_COLLISION_DETAILS.overlapN.copy(differenceV.normalize()); this._T_COLLISION_DETAILS.overlapV.copy(differenceV).scale(this._T_COLLISION_DETAILS.overlap); this._T_COLLISION_DETAILS.aInB = a.radius <= b.radius && dist <= b.radius - a.radius; this._T_COLLISION_DETAILS.bInA = b.radius <= a.radius && dist <= a.radius - b.radius; return this._T_COLLISION_DETAILS; } this._T_VECTORS.push(differenceV); return true; } /** * Checks whether polygons collide. * * @param {Polygon} a The first polygon. * @param {Polygon} b The second polygon. * @param {boolean} [details=false] If set to true and there is a collision, an object highlighting details about the collision will be returned instead of just returning true. * * @returns {boolean} Returns true if they intersect or false otherwise. */ }, { key: "testPolygonPolygon", value: function testPolygonPolygon(a, b) { var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; this._T_COLLISION_DETAILS.clear(); var aPoints = a.calcPoints; var aLen = aPoints.length; var bPoints = b.calcPoints; var bLen = bPoints.length; // If any of the edge normals of A is a separating axis, no intersection. for (var i = 0; i < aLen; i++) { if (this._isSeparatingAxis(a.position, b.position, aPoints, bPoints, a.normals[i], this._T_COLLISION_DETAILS)) { return false; } } // If any of the edge normals of B is a separating axis, no intersection. for (var _i2 = 0; _i2 < bLen; _i2++) { if (this._isSeparatingAxis(a.position, b.position, aPoints, bPoints, b.normals[_i2], this._T_COLLISION_DETAILS)) { return false; } } // Since none of the edge normals of A or B are a separating axis, there is an intersection // and we've already calculated the smallest overlap (in isSeparatingAxis). // Calculate the final overlap vector. if (details) { this._T_COLLISION_DETAILS.a = a; this._T_COLLISION_DETAILS.b = b; this._T_COLLISION_DETAILS.overlapV.copy(this._T_COLLISION_DETAILS.overlapN).scale(this._T_COLLISION_DETAILS.overlap); return this._T_COLLISION_DETAILS; } return true; } /** * Check if a polygon and a circle collide. * * @param {Polygon} polygon The polygon. * @param {Circle} circle The circle. * @param {boolean} [details=false] If set to true and there is a collision, an object highlighting details about the collision will be returned instead of just returning true. * * @returns {boolean} Returns true if they intersect or false otherwise. */ }, { key: "testPolygonCircle", value: function testPolygonCircle(polygon, circle) { var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; this._T_COLLISION_DETAILS.clear(); // Get the position of the circle relative to the polygon. var circlePos = this._T_VECTORS.pop().copy(circle.position).add(circle.offset).sub(polygon.position); var radius = circle.radius; var radius2 = radius * radius; var points = polygon.calcPoints; var len = points.length; var edge = this._T_VECTORS.pop(); var point = this._T_VECTORS.pop(); // For each edge in the polygon: for (var i = 0; i < len; i++) { var next = i === len - 1 ? 0 : i + 1; var prev = i === 0 ? len - 1 : i - 1; var overlap = 0; var overlapN = null; // Get the edge. edge.copy(polygon.edges[i]); // Calculate the center of the circle relative to the starting point of the edge. point.copy(circlePos).sub(points[i]); // If the distance between the center of the circle and the point is bigger than the radius, the polygon is definitely not fully in the circle. if (details && point.len2() > radius2) this._T_COLLISION_DETAILS.aInB = false; // Calculate which Voronoi region the center of the circle is in. var region = this._voronoiRegion(edge, point); // If it's the left region: if (region === this._LEFT_VORONOI_REGION) { // We need to make sure we're in the RIGHT_VORONOI_REGION of the previous edge. edge.copy(polygon.edges[prev]); // Calculate the center of the circle relative the starting point of the previous edge var point2 = this._T_VECTORS.pop().copy(circlePos).sub(points[prev]); region = this._voronoiRegion(edge, point2); if (region === this._RIGHT_VORONOI_REGION) { // It's in the region we want. Check if the circle intersects the point. var dist = point.len(); if (dist > radius) { // No intersection this._T_VECTORS.push(circlePos); this._T_VECTORS.push(edge); this._T_VECTORS.push(point); this._T_VECTORS.push(point2); return false; } else if (details) { // It intersects, calculate the overlap. this._T_COLLISION_DETAILS.bInA = false; overlapN = point.normalize(); overlap = radius - dist; } } this._T_VECTORS.push(point2); // If it's the right region: } else if (region === this._RIGHT_VORONOI_REGION) { // We need to make sure we're in the left region on the next edge edge.copy(polygon.edges[next]); // Calculate the center of the circle relative to the starting point of the next edge. point.copy(circlePos).sub(points[next]); region = this._voronoiRegion(edge, point); if (region === this._LEFT_VORONOI_REGION) { // It's in the region we want. Check if the circle intersects the point. var _dist = point.len(); if (_dist > radius) { // No intersection this._T_VECTORS.push(circlePos); this._T_VECTORS.push(edge); this._T_VECTORS.push(point); return false; } else if (details) { // It intersects, calculate the overlap. this._T_COLLISION_DETAILS.bInA = false; overlapN = point.normalize(); overlap = radius - _dist; } } // Otherwise, it's the middle region: } else { // Need to check if the circle is intersecting the edge, change the edge into its "edge normal". var normal = edge.perp().normalize(); // Find the perpendicular distance between the center of the circle and the edge. var _dist2 = point.dot(normal); var distAbs = Math.abs(_dist2); // If the circle is on the outside of the edge, there is no intersection. if (_dist2 > 0 && distAbs > radius) { // No intersection this._T_VECTORS.push(circlePos); this._T_VECTORS.push(normal); this._T_VECTORS.push(point); return false; } else if (details) { // It intersects, calculate the overlap. overlapN = normal; overlap = radius - _dist2; // If the center of the circle is on the outside of the edge, or part of the circle is on the outside, the circle is not fully inside the polygon. if (_dist2 >= 0 || overlap < 2 * radius) this._T_COLLISION_DETAILS.bInA = false; } } // If this is the smallest overlap we've seen, keep it. // (overlapN may be null if the circle was in the wrong Voronoi region). if (overlapN && details && Math.abs(overlap) < Math.abs(this._T_COLLISION_DETAILS.overlap)) { this._T_COLLISION_DETAILS.overlap = overlap; this._T_COLLISION_DETAILS.overlapN.copy(overlapN); } } // Calculate the final overlap vector - based on the smallest overlap. if (details) { this._T_COLLISION_DETAILS.a = polygon; this._T_COLLISION_DETAILS.b = circle; this._T_COLLISION_DETAILS.overlapV.copy(this._T_COLLISION_DETAILS.overlapN).scale(this._T_COLLISION_DETAILS.overlap); } this._T_VECTORS.push(circlePos); this._T_VECTORS.push(edge); this._T_VECTORS.push(point); if (details) return this._T_COLLISION_DETAILS; return true; } /** * Check if a circle and a polygon collide. * * **NOTE:** This is slightly less efficient than polygonCircle as it just runs polygonCircle and reverses everything * at the end. * * @param {Circle} circle The circle. * @param {Polygon} polygon The polygon. * @param {boolean} [details=false] If set to true and there is a collision, an object highlighting details about the collision will be returned instead of just returning true. * * @returns {boolean} Returns true if they intersect or false otherwise. */ }, { key: "testCirclePolygon", value: function testCirclePolygon(circle, polygon) { var details = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; // Test the polygon against the circle. var result = this.testPolygonCircle(polygon, circle, details); if (result && details) { var collisionDetails = result; // Swap A and B in the collision details. var a = collisionDetails.a; var aInB = collisionDetails.aInB; collisionDetails.overlapN.reverse(); collisionDetails.overlapV.reverse(); collisionDetails.a = collisionDetails.b; collisionDetails.b = a; collisionDetails.aInB = collisionDetails.bInA; collisionDetails.bInA = aInB; } return result; } /** * Check whether two convex polygons are separated by the specified axis (must be a unit vector). * * @private * * @param {Vector} aPos The position of the first polygon. * @param {Vector} bPos The position of the second polygon. * @param {Array<Vector>} aPoints The points in the first polygon. * @param {Array<Vector>} bPoints The points in the second polygon. * @param {Vector} axis The axis (unit sized) to test against. The points of both polygons will be projected onto this axis. * @param {CollisionDetails} collisionDetails A CollisionDetails object (optional) which will be populated if the axis is not a separating axis. * * @return {boolean} true if it is a separating axis, false otherwise. If false, and a CollisionDetails is passed in, information about how much overlap and the direction of the overlap will be populated. */ }, { key: "_isSeparatingAxis", value: function _isSeparatingAxis(aPos, bPos, aPoints, bPoints, axis, collisionDetails) { var rangeA = this._T_ARRAYS.pop(); var rangeB = this._T_ARRAYS.pop(); // The magnitude of the offset between the two polygons var offsetV = this._T_VECTORS.pop().copy(bPos).sub(aPos); var projectedOffset = offsetV.dot(axis); // Project the polygons onto the axis. this._flattenPointsOn(aPoints, axis, rangeA); this._flattenPointsOn(bPoints, axis, rangeB); // Move B's range to its position relative to A. rangeB[0] += projectedOffset; rangeB[1] += projectedOffset; // Check if there is a gap. If there is, this is a separating axis and we can stop if (rangeA[0] > rangeB[1] || rangeB[0] > rangeA[1]) { this._T_VECTORS.push(offsetV); this._T_ARRAYS.push(rangeA); this._T_ARRAYS.push(rangeB); return true; } // This is not a separating axis. If we're calculating collision details, calculate the overlap. if (collisionDetails) { var overlap = 0; // A starts further left than B if (rangeA[0] < rangeB[0]) { collisionDetails.aInB = false; // A ends before B does. We have to pull A out of B if (rangeA[1] < rangeB[1]) { overlap = rangeA[1] - rangeB[0]; collisionDetails.bInA = false; // B is fully inside A. Pick the shortest way out. } else { var option1 = rangeA[1] - rangeB[0]; var option2 = rangeB[1] - rangeA[0]; overlap = option1 < option2 ? option1 : -option2; } // B starts further left than A } else { collisionDetails.bInA = false; // B ends before A ends. We have to push A out of B if (rangeA[1] > rangeB[1]) { overlap = rangeA[0] - rangeB[1]; collisionDetails.aInB = false; // A is fully inside B. Pick the shortest way out. } else { var _option = rangeA[1] - rangeB[0]; var _option2 = rangeB[1] - rangeA[0]; overlap = _option < _option2 ? _option : -_option2; } } // If this is the smallest amount of overlap we've seen so far, set it as the minimum overlap. var absOverlap = Math.abs(overlap); if (absOverlap < collisionDetails.overlap) { collisionDetails.overlap = absOverlap; collisionDetails.overlapN.copy(axis); if (overlap < 0) collisionDetails.overlapN.reverse(); } } this._T_VECTORS.push(offsetV); this._T_ARRAYS.push(rangeA); this._T_ARRAYS.push(rangeB); return false; } /** * Flattens the specified array of points onto a unit vector axis resulting in a one dimensionsl * range of the minimum and maximum value on that axis. * * @private * * @param {Array<Vector>} points The points to flatten. * @param {Vector} normal The unit vector axis to flatten on. * @param {Array<number>} result An array. After calling this function, result[0] will be the minimum value, result[1] will be the maximum value. */ }, { key: "_flattenPointsOn", value: function _flattenPointsOn(points, normal, result) { var min = Number.MAX_VALUE; var max = -Number.MAX_VALUE; var len = points.length; for (var i = 0; i < len; i++) { // The magnitude of the projection of the point onto the normal. var dot = points[i].dot(normal); if (dot < min) min = dot; if (dot > max) max = dot; } result[0] = min; result[1] = max; } /** * Calculates which Voronoi region a point is on a line segment. * * It is assumed that both the line and the point are relative to `(0,0)` * * | (0) | * (-1) [S]--------------[E] (1) * | (0) | * * @param {Vector} line The line segment. * @param {Vector} point The point. * @return {number} LEFT_VORONOI_REGION (-1) if it is the left region, * MIDDLE_VORONOI_REGION (0) if it is the middle region, * RIGHT_VORONOI_REGION (1) if it is the right region. */ }, { key: "_voronoiRegion", value: function _voronoiRegion(line, point) { var len2 = line.len2(); var dp = point.dot(line); // If the point is beyond the start of the line, it is in the left voronoi region. if (dp < 0) return this._LEFT_VORONOI_REGION; // If the point is beyond the end of the line, it is in the right voronoi region. else if (dp > len2) return this._RIGHT_VORONOI_REGION; // Otherwise, it's in the middle one. else return this._MIDDLE_VORONOI_REGION; } }]); return Collider2D; }(); exports["default"] = Collider2D; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9jb2xsaWRlcjJkLnRzIl0sIm5hbWVzIjpbIkNvbGxpZGVyMkQiLCJDb2xsaXNpb25EZXRhaWxzIiwiQm94IiwiVmVjdG9yIiwidG9Qb2x5Z29uIiwiaSIsIl9UX1ZFQ1RPUlMiLCJwdXNoIiwiX1RfQVJSQVlTIiwicG9pbnQiLCJjaXJjbGUiLCJkaWZmZXJlbmNlViIsInBvcCIsImNvcHkiLCJzdWIiLCJwb3NpdGlvbiIsIm9mZnNldCIsInJhZGl1c1NxIiwicmFkaXVzIiwiZGlzdGFuY2VTcSIsImxlbjIiLCJwb2x5Z29uIiwiX1RFU1RfUE9JTlQiLCJfVF9DT0xMSVNJT05fREVUQUlMUyIsImNsZWFyIiwicmVzdWx0IiwidGVzdFBvbHlnb25Qb2x5Z29uIiwiYUluQiIsImEiLCJiIiwiZGV0YWlscyIsImFkZCIsInRvdGFsUmFkaXVzIiwidG90YWxSYWRpdXNTcSIsImRpc3QiLCJNYXRoIiwic3FydCIsIm92ZXJsYXAiLCJvdmVybGFwTiIsIm5vcm1hbGl6ZSIsIm92ZXJsYXBWIiwic2NhbGUiLCJiSW5BIiwiYVBvaW50cyIsImNhbGNQb2ludHMiLCJhTGVuIiwibGVuZ3RoIiwiYlBvaW50cyIsImJMZW4iLCJfaXNTZXBhcmF0aW5nQXhpcyIsIm5vcm1hbHMiLCJjaXJjbGVQb3MiLCJyYWRpdXMyIiwicG9pbnRzIiwibGVuIiwiZWRnZSIsIm5leHQiLCJwcmV2IiwiZWRnZXMiLCJyZWdpb24iLCJfdm9yb25vaVJlZ2lvbiIsIl9MRUZUX1ZPUk9OT0lfUkVHSU9OIiwicG9pbnQyIiwiX1JJR0hUX1ZPUk9OT0lfUkVHSU9OIiwibm9ybWFsIiwicGVycCIsImRvdCIsImRpc3RBYnMiLCJhYnMiLCJ0ZXN0UG9seWdvbkNpcmNsZSIsImNvbGxpc2lvbkRldGFpbHMiLCJyZXZlcnNlIiwiYVBvcyIsImJQb3MiLCJheGlzIiwicmFuZ2VBIiwicmFuZ2VCIiwib2Zmc2V0ViIsInByb2plY3RlZE9mZnNldCIsIl9mbGF0dGVuUG9pbnRzT24iLCJvcHRpb24xIiwib3B0aW9uMiIsImFic092ZXJsYXAiLCJtaW4iLCJOdW1iZXIiLCJNQVhfVkFMVUUiLCJtYXgiLCJsaW5lIiwiZHAiLCJfTUlERExFX1ZPUk9OT0lfUkVHSU9OIl0sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztBQUVBOztBQUNBOztBQUdBOzs7Ozs7Ozs7Ozs7SUFFcUJBLFU7QUFDbkI7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBR0U7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBR0U7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBR0U7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBR0U7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBR0U7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBR0U7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFHRSx3QkFBYztBQUFBOztBQUFBLHdDQXhEc0IsRUF3RHRCOztBQUFBLHVDQS9DNEIsRUErQzVCOztBQUFBLGtEQXRDaUIsSUFBSUMsNkJBQUosRUFzQ2pCOztBQUFBLHlDQTdCUSxJQUFJQyxlQUFKLENBQVEsSUFBSUMsa0JBQUosRUFBUixFQUFzQixRQUF0QixFQUFnQyxRQUFoQyxFQUEwQ0MsU0FBMUMsRUE2QlI7O0FBQUEsa0RBcEJpQixDQUFDLENBb0JsQjs7QUFBQSxvREFYbUIsQ0FXbkI7O0FBQUEsbURBRmtCLENBRWxCOztBQUNaO0FBQ0EsU0FBSyxJQUFJQyxDQUFDLEdBQUcsQ0FBYixFQUFnQkEsQ0FBQyxHQUFHLEVBQXBCLEVBQXdCQSxDQUFDLEVBQXpCO0FBQTZCLFdBQUtDLFVBQUwsQ0FBZ0JDLElBQWhCLENBQXFCLElBQUlKLGtCQUFKLEVBQXJCO0FBQTdCLEtBRlksQ0FJWjs7O0FBQ0EsU0FBSyxJQUFJRSxFQUFDLEdBQUcsQ0FBYixFQUFnQkEsRUFBQyxHQUFHLENBQXBCLEVBQXVCQSxFQUFDLEVBQXhCO0FBQTRCLFdBQUtHLFNBQUwsQ0FBZUQs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QUFyQyxFQUE0RDtBQUMxRCxVQUFNVyxJQUFJLEdBQUdtRSxJQUFJLENBQUNuRSxJQUFMLEVBQWI7QUFDQSxVQUFNb0UsRUFBRSxHQUFHL0UsS0FBSyxDQUFDeUQsR0FBTixDQUFVcUIsSUFBVixDQUFYLENBRjBELENBSTFEOztBQUNBLFVBQUlDLEVBQUUsR0FBRyxDQUFULEVBQVksT0FBTyxLQUFLM0Isb0JBQVosQ0FBWixDQUVBO0FBRkEsV0FHSyxJQUFJMkIsRUFBRSxHQUFHcEUsSUFBVCxFQUFlLE9BQU8sS0FBSzJDLHFCQUFaLENBQWYsQ0FFTDtBQUZLLGFBR0EsT0FBTyxLQUFLMEIsc0JBQVo7QUFDTiIsInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0J1xyXG5cclxuaW1wb3J0IEJveCBmcm9tICcuL2dlb21ldHJ5L2JveCc7XHJcbmltcG9ydCBWZWN0b3IgZnJvbSAnLi9nZW9tZXRyeS92ZWN0b3InO1xyXG5pbXBvcnQgQ2lyY2xlIGZyb20gJy4vZ2VvbWV0cnkvY2lyY2xlJztcclxuaW1wb3J0IFBvbHlnb24gZnJvbSAnLi9nZW9tZXRyeS9wb2x5Z29uJztcclxuaW1wb3J0IENvbGxpc2lvbkRldGFpbHMgZnJvbSAnLi9jb2xsaXNpb25fZGV0YWlscyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBDb2xsaWRlcjJEIHtcclxuICAvKipcclxuICAgKiBBIHBvb2wgb2YgYFZlY3RvciBvYmplY3RzIHRoYXQgYXJlIHVzZWQgaW4gY2FsY3VsYXRpb25zIHRvIGF2b2lkIGFsbG9jYXRpbmcgbWVtb3J5LlxyXG4gICAqIFxyXG4gICAqIEBwcml2YXRlXHJcbiAgICogXHJcbiAgICogQHByb3BlcnR5IHtBcnJheTxWZWN0b3I+fVxyXG4gICAqL1xyXG4gIHByaXZhdGUgX1RfVkVDVE9SUzogQXJyYXk8VmVjdG9yPiA9IFtdO1xyXG5cclxuICAvKipcclxuICAgKiBBIHBvb2wgb2YgYXJyYXlzIG9mIG51bWJlcnMgdXNlZCBpbiBjYWxjdWxhdGlvbnMgdG8gYXZvaWQgYWxsb2NhdGluZyBtZW1vcnkuXHJcbiAgICogXHJcbiAgICogQHByaXZhdGVcclxuICAgKiBcclxuICAgKiBAcHJvcGVydHkge0FycmF5PEFycmF5PG51bWJlcj4+fVxyXG4gICAqL1xyXG4gIHByaXZhdGUgX1RfQVJSQVlTOiBBcnJheTxBcnJheTxudW1iZXI+PiA9IFtdO1xyXG5cclxuICAvKipcclxuICAgKiBUZW1wb3JhcnkgY29sbGlzaW9uIGRldGFpbHMgb2JqZWN0IHVzZWQgZm9yIGhpdCBkZXRlY3Rpb24uXHJcbiAgICogXHJcbiAgICogQHByaXZhdGVcclxuICAgKiBcclxuICAgKiBAcHJvcGVydHkge0NvbGxpc2lvbkRldGFpbHN9XHJcbiAgICovXHJcbiAgcHJpdmF0ZSBfVF9DT0xMSVNJT05fREVUQUlMUyA9IG5ldyBDb2xsaXNpb25EZXRhaWxzKCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIFRpbnkgXCJwb2ludFwiIFBvbHlnb24gdXNlZCBmb3IgUG9seWdvbiBoaXQgZGV0ZWN0aW9uLlxyXG4gICAqIFxyXG4gICAqIEBwcml2YXRlXHJcbiAgICogXHJcbiAgICogQHByb3BlcnR5IHtQb2x5Z29ufVxyXG4gICAqL1xyXG4gIHByaXZhdGUgX1RFU1RfUE9JTlQgPSBuZXcgQm94KG5ldyBWZWN0b3IoKSwgMC4wMDAwMDEsIDAuMDAwMDAxKS50b1BvbHlnb24oKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQ29uc3RhbnQgdXNlZCBmb3IgbGVmdCB2b3Jvbm9pIHJlZ2lvbi5cclxuICAgKiBcclxuICAgKiBAcHJpdmF0ZVxyXG4gICAqIFxyXG4gICAqIEBwcm9wZXJ0eSB7bnVtYmVyfVxyXG4gICAqL1xyXG4gIHByaXZhdGUgX0xFRlRfVk9ST05PSV9SRUdJT04gPSAtMTtcclxuXHJcbiAgLyoqXHJcbiAgICogQ29uc3RhbnQgdXNlZCBmb3IgbWlkZGxlIHZvcm9ub2kgcmVnaW9uLlxyXG4gICAqIFxyXG4gICAqIEBwcml2YXRlXHJcbiAgICogXHJcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9XHJcbiAgICovXHJcbiAgcHJpdmF0ZSBfTUlERExFX1ZPUk9OT0lfUkVHSU9OID0gMDtcclxuXHJcbiAgLyoqXHJcbiAgICogQ29uc3RhbnQgdXNlZCBmb3IgcmlnaHQgdm9yb25vaSByZWdpb24uXHJcbiAgICogXHJcbiAgICogQHByaXZhdGVcclxuICAgKiBcclxuICAgKiBAcHJvcGVydHkge251bWJlcn1cclxuICAgKi9cclxuICBwcml2YXRlIF9SSUdIVF9WT1JPTk9JX1JFR0lPTiA9IDE7XHJcblxyXG4gIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgLy8gUG9wdWxhdGUgVF9WRUNUT1JTXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IDEwOyBpKyspIHRoaXMuX1RfVkVDVE9SUy5wdXNoKG5ldyBWZWN0b3IoKSk7XHJcblxyXG4gICAgLy8gUG9wdWxhdGUgVF9BUlJBWVNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgNTsgaSsrKSB0aGlzLl9UX0FSUkFZUy5wdXNoKFtdKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENoZWNrIGlmIGEgcG9pbnQgaXMgaW5zaWRlIGEgY2lyY2xlLlxyXG4gICAqIFxyXG4gICAqIEBwYXJhbSB7VmVjdG9yfSBwb2ludCBUaGUgcG9pbnQgdG8gdGVzdC5cclxuICAgKiBAcGFyYW0ge0NpcmNsZX0gY2lyY2xlIFRoZSBjaXJjbGUgdG8gdGVzdC5cclxuICAgKiBcclxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyB0cnVlIGlmIHRoZSBwb2ludCBpcyBpbnNpZGUgdGhlIGNpcmNsZSBvciBmYWxzZSBvdGhlcndpc2UuXHJcbiAgICovXHJcbiAgcG9pbnRJbkNpcmNsZShwb2ludDogVmVjdG9yLCBjaXJjbGU6IENpcmNsZSk6IGJvb2xlYW4ge1xyXG4gICAgY29uc3QgZGlmZmVyZW5jZVYgPSB0aGlzLl9UX1ZFQ1RPUlMucG9wKCkhLmNvcHkocG9pbnQpLnN1YihjaXJjbGUucG9zaXRpb24pLnN1YihjaXJjbGUub2Zmc2V0KTtcclxuXHJcbiAgICBjb25zdCByYWRpdXNTcSA9IGNpcmNsZS5yYWRpdXMgKiBjaXJjbGUucmFkaXVzO1xyXG4gICAgY29uc3QgZGlzdGFuY2VTcSA9IGRpZmZlcmVuY2VWLmxlbjIoKTtcclxuXHJcbiAgICB0aGlzLl9UX1ZFQ1RPUlMucHVzaChkaWZmZXJlbmNlVik7XHJcblxyXG4gICAgLy8gSWYgdGhlIGRpc3RhbmNlIGJldHdlZW4gaXMgc21hbGxlciB0aGFuIHRoZSByYWRpdXMgdGhlbiB0aGUgcG9pbnQgaXMgaW5zaWRlIHRoZSBjaXJjbGUuXHJcbiAgICByZXR1cm4gZGlzdGFuY2VTcSA8PSByYWRpdXNTcTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENoZWNrIGlmIGEgcG9pbnQgaXMgaW5zaWRlIGEgY29udmV4IHBvbHlnb24uXHJcbiAgICogXHJcbiAgICogQHBhcmFtIHtWZWN0b3J9IHBvaW50IFRoZSBwb2ludCB0byB0ZXN0LlxyXG4gICAqIEBwYXJhbSB7UG9seWdvbn0gcG9seWdvbiBUaGUgcG9seWdvbiB0byB0ZXN0LlxyXG4gICAqIFxyXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIHRydWUgaWYgdGhlIHBvaW50IGlzIGluc2lkZSB0aGUgcG9seWdvbiBvciBmYWxzZSBvdGhlcndpc2UuXHJcbiAgICovXHJcbiAgcG9pbnRJblBvbHlnb24ocG9pbnQ6IFZlY3RvciwgcG9seWdvbjogUG9seWdvbik6IGJvb2xlYW4ge1xyXG4gICAgdGhpcy5fVEVTVF9QT0lOVC5wb3NpdGlvbi5jb3B5KHBvaW50KTtcclxuICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMuY2xlYXIoKTtcclxuXHJcbiAgICBsZXQgcmVzdWx0OiAoYm9vbGVhbiB8IENvbGxpc2lvbkRldGFpbHMpID0gdGhpcy50ZXN0UG9seWdvblBvbHlnb24odGhpcy5fVEVTVF9QT0lOVCwgcG9seWdvbiwgdHJ1ZSk7XHJcblxyXG4gICAgaWYgKHJlc3VsdCkgcmVzdWx0ID0gdGhpcy5fVF9DT0xMSVNJT05fREVUQUlMUy5hSW5CO1xyXG5cclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBDaGVjayBpZiB0d28gY2lyY2xlcyBjb2xsaWRlLlxyXG4gICAqIFxyXG4gICAqIEBwYXJhbSB7Q2lyY2xlfSBhIFRoZSBmaXJzdCBjaXJjbGUuXHJcbiAgICogQHBhcmFtIHtDaXJjbGV9IGIgVGhlIHNlY29uZCBjaXJjbGUuXHJcbiAgICogQHBhcmFtIHtib29sZWFufSBbZGV0YWlscz1mYWxzZV0gSWYgc2V0IHRvIHRydWUgYW5kIHRoZXJlIGlzIGEgY29sbGlzaW9uLCBhbiBvYmplY3QgaGlnaGxpZ2h0aW5nIGRldGFpbHMgYWJvdXQgdGhlIGNvbGxpc2lvbiB3aWxsIGJlIHJldHVybmVkIGluc3RlYWQgb2YganVzdCByZXR1cm5pbmcgdHJ1ZS5cclxuICAgKiBcclxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyB0cnVlIGlmIHRoZSBjaXJjbGVzIGludGVyc2VjdCBvciBmYWxzZSBvdGhlcndpc2UuXHJcbiAgICovXHJcbiAgdGVzdENpcmNsZUNpcmNsZShhOiBDaXJjbGUsIGI6IENpcmNsZSwgZGV0YWlsczogYm9vbGVhbiA9IGZhbHNlKTogKGJvb2xlYW4gfCBDb2xsaXNpb25EZXRhaWxzKSB7XHJcbiAgICAvLyBDaGVjayBpZiB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0aGUgY2VudGVycyBvZiB0aGUgdHdvIGNpcmNsZXMgaXMgZ3JlYXRlciB0aGFuIHRoZWlyIGNvbWJpbmVkIHJhZGl1cy5cclxuICAgIGNvbnN0IGRpZmZlcmVuY2VWID0gdGhpcy5fVF9WRUNUT1JTLnBvcCgpIS5jb3B5KGIucG9zaXRpb24pLmFkZChiLm9mZnNldCkuc3ViKGEucG9zaXRpb24pLnN1YihhLm9mZnNldCk7XHJcblxyXG4gICAgY29uc3QgdG90YWxSYWRpdXMgPSBhLnJhZGl1cyArIGIucmFkaXVzO1xyXG4gICAgY29uc3QgdG90YWxSYWRpdXNTcSA9IHRvdGFsUmFkaXVzICogdG90YWxSYWRpdXM7XHJcbiAgICBjb25zdCBkaXN0YW5jZVNxID0gZGlmZmVyZW5jZVYubGVuMigpO1xyXG5cclxuICAgIC8vIElmIHRoZSBkaXN0YW5jZSBpcyBiaWdnZXIgdGhhbiB0aGUgY29tYmluZWQgcmFkaXVzLCB0aGV5IGRvbid0IGludGVyc2VjdC5cclxuICAgIGlmIChkaXN0YW5jZVNxID4gdG90YWxSYWRpdXNTcSkge1xyXG4gICAgICB0aGlzLl9UX1ZFQ1RPUlMucHVzaChkaWZmZXJlbmNlVik7XHJcblxyXG4gICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGRldGFpbHMpIHtcclxuICAgICAgdGhpcy5fVF9DT0xMSVNJT05fREVUQUlMUy5jbGVhcigpO1xyXG5cclxuICAgICAgY29uc3QgZGlzdCA9IE1hdGguc3FydChkaXN0YW5jZVNxKTtcclxuXHJcbiAgICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMuYSA9IGE7XHJcbiAgICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMuYiA9IGI7XHJcblxyXG4gICAgICB0aGlzLl9UX0NPTExJU0lPTl9ERVRBSUxTLm92ZXJsYXAgPSB0b3RhbFJhZGl1cyAtIGRpc3Q7XHJcbiAgICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMub3ZlcmxhcE4uY29weShkaWZmZXJlbmNlVi5ub3JtYWxpemUoKSk7XHJcbiAgICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMub3ZlcmxhcFYuY29weShkaWZmZXJlbmNlVikuc2NhbGUodGhpcy5fVF9DT0xMSVNJT05fREVUQUlMUy5vdmVybGFwKTtcclxuXHJcbiAgICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMuYUluQiA9IGEucmFkaXVzIDw9IGIucmFkaXVzICYmIGRpc3QgPD0gYi5yYWRpdXMgLSBhLnJhZGl1cztcclxuICAgICAgdGhpcy5fVF9DT0xMSVNJT05fREVUQUlMUy5iSW5BID0gYi5yYWRpdXMgPD0gYS5yYWRpdXMgJiYgZGlzdCA8PSBhLnJhZGl1cyAtIGIucmFkaXVzO1xyXG5cclxuICAgICAgcmV0dXJuIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFM7XHJcbiAgICB9XHJcblxyXG4gICAgdGhpcy5fVF9WRUNUT1JTLnB1c2goZGlmZmVyZW5jZVYpO1xyXG5cclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogQ2hlY2tzIHdoZXRoZXIgcG9seWdvbnMgY29sbGlkZS5cclxuICAgKiBcclxuICAgKiBAcGFyYW0ge1BvbHlnb259IGEgVGhlIGZpcnN0IHBvbHlnb24uXHJcbiAgICogQHBhcmFtIHtQb2x5Z29ufSBiIFRoZSBzZWNvbmQgcG9seWdvbi5cclxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtkZXRhaWxzPWZhbHNlXSBJZiBzZXQgdG8gdHJ1ZSBhbmQgdGhlcmUgaXMgYSBjb2xsaXNpb24sIGFuIG9iamVjdCBoaWdobGlnaHRpbmcgZGV0YWlscyBhYm91dCB0aGUgY29sbGlzaW9uIHdpbGwgYmUgcmV0dXJuZWQgaW5zdGVhZCBvZiBqdXN0IHJldHVybmluZyB0cnVlLlxyXG4gICAqIFxyXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIHRydWUgaWYgdGhleSBpbnRlcnNlY3Qgb3IgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gICAqL1xyXG4gIHRlc3RQb2x5Z29uUG9seWdvbihhOiBQb2x5Z29uLCBiOiBQb2x5Z29uLCBkZXRhaWxzOiBib29sZWFuID0gZmFsc2UpOiAoYm9vbGVhbiB8IENvbGxpc2lvbkRldGFpbHMpIHtcclxuICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMuY2xlYXIoKTtcclxuXHJcbiAgICBjb25zdCBhUG9pbnRzID0gYS5jYWxjUG9pbnRzO1xyXG4gICAgY29uc3QgYUxlbiA9IGFQb2ludHMubGVuZ3RoO1xyXG5cclxuICAgIGNvbnN0IGJQb2ludHMgPSBiLmNhbGNQb2ludHM7XHJcbiAgICBjb25zdCBiTGVuID0gYlBvaW50cy5sZW5ndGg7XHJcblxyXG4gICAgLy8gSWYgYW55IG9mIHRoZSBlZGdlIG5vcm1hbHMgb2YgQSBpcyBhIHNlcGFyYXRpbmcgYXhpcywgbm8gaW50ZXJzZWN0aW9uLlxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhTGVuOyBpKyspIHtcclxuICAgICAgaWYgKHRoaXMuX2lzU2VwYXJhdGluZ0F4aXMoYS5wb3NpdGlvbiwgYi5wb3NpdGlvbiwgYVBvaW50cywgYlBvaW50cywgYS5ub3JtYWxzW2ldLCB0aGlzLl9UX0NPTExJU0lPTl9ERVRBSUxTKSkge1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIElmIGFueSBvZiB0aGUgZWRnZSBub3JtYWxzIG9mIEIgaXMgYSBzZXBhcmF0aW5nIGF4aXMsIG5vIGludGVyc2VjdGlvbi5cclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYkxlbjsgaSsrKSB7XHJcbiAgICAgIGlmICh0aGlzLl9pc1NlcGFyYXRpbmdBeGlzKGEucG9zaXRpb24sIGIucG9zaXRpb24sIGFQb2ludHMsIGJQb2ludHMsIGIubm9ybWFsc1tpXSwgdGhpcy5fVF9DT0xMSVNJT05fREVUQUlMUykpIHtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBTaW5jZSBub25lIG9mIHRoZSBlZGdlIG5vcm1hbHMgb2YgQSBvciBCIGFyZSBhIHNlcGFyYXRpbmcgYXhpcywgdGhlcmUgaXMgYW4gaW50ZXJzZWN0aW9uXHJcbiAgICAvLyBhbmQgd2UndmUgYWxyZWFkeSBjYWxjdWxhdGVkIHRoZSBzbWFsbGVzdCBvdmVybGFwIChpbiBpc1NlcGFyYXRpbmdBeGlzKS4gXHJcbiAgICAvLyBDYWxjdWxhdGUgdGhlIGZpbmFsIG92ZXJsYXAgdmVjdG9yLlxyXG4gICAgaWYgKGRldGFpbHMpIHtcclxuICAgICAgdGhpcy5fVF9DT0xMSVNJT05fREVUQUlMUy5hID0gYTtcclxuICAgICAgdGhpcy5fVF9DT0xMSVNJT05fREVUQUlMUy5iID0gYjtcclxuXHJcbiAgICAgIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFMub3ZlcmxhcFYuY29weSh0aGlzLl9UX0NPTExJU0lPTl9ERVRBSUxTLm92ZXJsYXBOKS5zY2FsZSh0aGlzLl9UX0NPTExJU0lPTl9ERVRBSUxTLm92ZXJsYXApO1xyXG5cclxuICAgICAgcmV0dXJuIHRoaXMuX1RfQ09MTElTSU9OX0RFVEFJTFM7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBDaGVjayBpZiBhIHBvbHlnb24gYW5kIGEgY2lyY2xlIGNvbGxpZGUuXHJcbiAgICogXHJcbiAgICogQHBhcmFtIHtQb2x5Z29ufSBwb2x5Z29uIFRoZSBwb2x5Z29uLlxyXG4gICAqIEBwYXJhbSB7Q2lyY2xlfSBjaXJjbGUgVGhlIGNpcmNsZS5cclxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtkZXRhaWxzPWZhbHNlXSBJZiBzZXQgdG8gdHJ1ZSBhbmQgdGhlcmUgaXMgYSBjb2xsaXNpb24sIGFuIG9iamVjdCBoaWdobGlnaHRpbmcgZGV0YWlscyBhYm91dCB0aGUgY29sbGlzaW9uIHdpbGwgYmUgcmV0dXJuZWQgaW5zdGVhZCBvZiBqdXN0IHJldHVybmluZyB0cnVlLlxyXG4gICAqIFxyXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIHRydWUgaWYgdGhleSBpbnRlcnNlY3Qgb3IgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gICAqL1xyXG4gIHRlc3RQb2x5Z29