@esotericsoftware/spine-core
Version:
The official Spine Runtimes for the web.
216 lines • 33.6 kB
JavaScript
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { BoundingBoxAttachment } from "./attachments/BoundingBoxAttachment.js";
import { Pool, Utils } from "./Utils.js";
/** Collects each visible {@link BoundingBoxAttachment} and computes the world vertices for its polygon. The polygon vertices are
* provided along with convenience methods for doing hit detection. */
export class SkeletonBounds {
/** The left edge of the axis aligned bounding box. */
minX = 0;
/** The bottom edge of the axis aligned bounding box. */
minY = 0;
/** The right edge of the axis aligned bounding box. */
maxX = 0;
/** The top edge of the axis aligned bounding box. */
maxY = 0;
/** The visible bounding boxes. */
boundingBoxes = [];
/** The world vertices for the bounding box polygons. */
polygons = [];
polygonPool = new Pool(() => {
return Utils.newFloatArray(16);
});
/** Clears any previous polygons, finds all visible bounding box attachments, and computes the world vertices for each bounding
* box's polygon.
* @param updateAabb If true, the axis aligned bounding box containing all the polygons is computed. If false, the
* SkeletonBounds AABB methods will always return true. */
update(skeleton, updateAabb) {
if (!skeleton)
throw new Error("skeleton cannot be null.");
const boundingBoxes = this.boundingBoxes;
const polygons = this.polygons;
const polygonPool = this.polygonPool;
const slots = skeleton.slots;
const slotCount = slots.length;
boundingBoxes.length = 0;
polygonPool.freeAll(polygons);
polygons.length = 0;
for (let i = 0; i < slotCount; i++) {
const slot = slots[i];
if (!slot.bone.active)
continue;
const attachment = slot.appliedPose.attachment;
if (attachment instanceof BoundingBoxAttachment) {
boundingBoxes.push(attachment);
let polygon = polygonPool.obtain();
if (polygon.length !== attachment.worldVerticesLength) {
polygon = Utils.newFloatArray(attachment.worldVerticesLength);
}
polygons.push(polygon);
attachment.computeWorldVertices(skeleton, slot, 0, attachment.worldVerticesLength, polygon, 0, 2);
}
}
if (updateAabb) {
this.aabbCompute();
}
else {
this.minX = Number.POSITIVE_INFINITY;
this.minY = Number.POSITIVE_INFINITY;
this.maxX = Number.NEGATIVE_INFINITY;
this.maxY = Number.NEGATIVE_INFINITY;
}
}
aabbCompute() {
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
const polygons = this.polygons;
for (let i = 0, n = polygons.length; i < n; i++) {
const polygon = polygons[i];
const vertices = polygon;
for (let ii = 0, nn = polygon.length; ii < nn; ii += 2) {
const x = vertices[ii];
const y = vertices[ii + 1];
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
}
}
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
/** Returns true if the axis aligned bounding box contains the point. */
aabbContainsPoint(x, y) {
return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY;
}
/** Returns true if the axis aligned bounding box intersects the line segment. */
aabbIntersectsSegment(x1, y1, x2, y2) {
const minX = this.minX;
const minY = this.minY;
const maxX = this.maxX;
const maxY = this.maxY;
if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
return false;
const m = (y2 - y1) / (x2 - x1);
let y = m * (minX - x1) + y1;
if (y > minY && y < maxY)
return true;
y = m * (maxX - x1) + y1;
if (y > minY && y < maxY)
return true;
let x = (minY - y1) / m + x1;
if (x > minX && x < maxX)
return true;
x = (maxY - y1) / m + x1;
if (x > minX && x < maxX)
return true;
return false;
}
/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
aabbIntersectsSkeleton(bounds) {
return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY;
}
/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
* efficient to only call this method if {@link aabbContainsPoint} returns true. */
containsPoint(x, y) {
const polygons = this.polygons;
for (let i = 0, n = polygons.length; i < n; i++)
if (this.containsPointPolygon(polygons[i], x, y))
return this.boundingBoxes[i];
return null;
}
/** Returns true if the polygon contains the point. */
containsPointPolygon(polygon, x, y) {
const vertices = polygon;
const nn = polygon.length;
let prevIndex = nn - 2;
let inside = false;
for (let ii = 0; ii < nn; ii += 2) {
const vertexY = vertices[ii + 1];
const prevY = vertices[prevIndex + 1];
if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
const vertexX = vertices[ii];
if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x)
inside = !inside;
}
prevIndex = ii;
}
return inside;
}
/** Returns the first bounding box attachment that contains any part of the line segment, or null. When doing many checks, it
* is usually more efficient to only call this method if {@link aabbIntersectsSegment} returns
* true. */
intersectsSegment(x1, y1, x2, y2) {
const polygons = this.polygons;
for (let i = 0, n = polygons.length; i < n; i++)
if (this.intersectsSegmentPolygon(polygons[i], x1, y1, x2, y2))
return this.boundingBoxes[i];
return null;
}
/** Returns true if the polygon contains any part of the line segment. */
intersectsSegmentPolygon(polygon, x1, y1, x2, y2) {
const vertices = polygon;
const nn = polygon.length;
const width12 = x1 - x2, height12 = y1 - y2;
const det1 = x1 * y2 - y1 * x2;
let x3 = vertices[nn - 2], y3 = vertices[nn - 1];
for (let ii = 0; ii < nn; ii += 2) {
const x4 = vertices[ii], y4 = vertices[ii + 1];
const det2 = x3 * y4 - y3 * x4;
const width34 = x3 - x4, height34 = y3 - y4;
const det3 = width12 * height34 - height12 * width34;
const x = (det1 * width34 - width12 * det2) / det3;
if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
const y = (det1 * height34 - height12 * det2) / det3;
if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1)))
return true;
}
x3 = x4;
y3 = y4;
}
return false;
}
/** Returns the polygon for the specified bounding box, or null. */
getPolygon(boundingBox) {
if (!boundingBox)
throw new Error("boundingBox cannot be null.");
const index = this.boundingBoxes.indexOf(boundingBox);
return index === -1 ? null : this.polygons[index];
}
/** The width of the axis aligned bounding box. */
getWidth() {
return this.maxX - this.minX;
}
/** The height of the axis aligned bounding box. */
getHeight() {
return this.maxY - this.minY;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2tlbGV0b25Cb3VuZHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvU2tlbGV0b25Cb3VuZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsrRUEyQitFO0FBRS9FLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBRS9FLE9BQU8sRUFBd0IsSUFBSSxFQUFFLEtBQUssRUFBRSxNQUFNLFlBQVksQ0FBQztBQUUvRDtzRUFDc0U7QUFDdEUsTUFBTSxPQUFPLGNBQWM7SUFFMUIsc0RBQXNEO0lBQ3RELElBQUksR0FBRyxDQUFDLENBQUM7SUFFVCx3REFBd0Q7SUFDeEQsSUFBSSxHQUFHLENBQUMsQ0FBQztJQUVULHVEQUF1RDtJQUN2RCxJQUFJLEdBQUcsQ0FBQyxDQUFDO0lBRVQscURBQXFEO0lBQ3JELElBQUksR0FBRyxDQUFDLENBQUM7SUFFVCxrQ0FBa0M7SUFDbEMsYUFBYSxHQUFHLEVBQTZCLENBQUM7SUFFOUMsd0RBQXdEO0lBQ3hELFFBQVEsR0FBRyxFQUF1QixDQUFDO0lBRTNCLFdBQVcsR0FBRyxJQUFJLElBQUksQ0FBa0IsR0FBRyxFQUFFO1FBQ3BELE9BQU8sS0FBSyxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNoQyxDQUFDLENBQUMsQ0FBQztJQUVIOzs7d0VBR29FO0lBQ3BFLE1BQU0sQ0FBRSxRQUFrQixFQUFFLFVBQW1CO1FBQzlDLElBQUksQ0FBQyxRQUFRO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1FBQzNELE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUM7UUFDekMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQztRQUMvQixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDO1FBQ3JDLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxLQUFLLENB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QUFDLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFBRSxPQUFPLElBQUksQ0FBQztZQUNuSCxDQUFDO1lBQ0QsRUFBRSxHQUFHLEVBQUUsQ0FBQztZQUNSLEVBQUUsR0FBRyxFQUFFLENBQUM7UUFDVCxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDZCxDQUFDO0lBRUQsbUVBQW1FO0lBQ25FLFVBQVUsQ0FBRSxXQUFrQztRQUM3QyxJQUFJLENBQUMsV0FBVztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQztRQUNqRSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN0RCxPQUFPLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFRCxrREFBa0Q7SUFDbEQsUUFBUTtRQUNQLE9BQU8sSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO0lBQzlCLENBQUM7SUFFRCxtREFBbUQ7SUFDbkQsU0FBUztRQUNSLE9BQU8sSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO0lBQzlCLENBQUM7Q0FDRCIsInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbiAqIFNwaW5lIFJ1bnRpbWVzIExpY2Vuc2UgQWdyZWVtZW50XG4gKiBMYXN0IHVwZGF0ZWQgQXByaWwgNSwgMjAyNS4gUmVwbGFjZXMgYWxsIHByaW9yIHZlcnNpb25zLlxuICpcbiAqIENvcHlyaWdodCAoYykgMjAxMy0yMDI1LCBFc290ZXJpYyBTb2Z0d2FyZSBMTENcbiAqXG4gKiBJbnRlZ3JhdGlvbiBvZiB0aGUgU3BpbmUgUnVudGltZXMgaW50byBzb2Z0d2FyZSBvciBvdGhlcndpc2UgY3JlYXRpbmdcbiAqIGRlcml2YXRpdmUgd29ya3Mgb2YgdGhlIFNwaW5lIFJ1bnRpbWVzIGlzIHBlcm1pdHRlZCB1bmRlciB0aGUgdGVybXMgYW5kXG4gKiBjb25kaXRpb25zIG9mIFNlY3Rpb24gMiBvZiB0aGUgU3BpbmUgRWRpdG9yIExpY2Vuc2UgQWdyZWVtZW50OlxuICogaHR0cDovL2Vzb3Rlcmljc29mdHdhcmUuY29tL3NwaW5lLWVkaXRvci1saWNlbnNlXG4gKlxuICogT3RoZXJ3aXNlLCBpdCBpcyBwZXJtaXR0ZWQgdG8gaW50ZWdyYXRlIHRoZSBTcGluZSBSdW50aW1lcyBpbnRvIHNvZnR3YXJlXG4gKiBvciBvdGhlcndpc2UgY3JlYXRlIGRlcml2YXRpdmUgd29ya3Mgb2YgdGhlIFNwaW5lIFJ1bnRpbWVzIChjb2xsZWN0aXZlbHksXG4gKiBcIlByb2R1Y3RzXCIpLCBwcm92aWRlZCB0aGF0IGVhY2ggdXNlciBvZiB0aGUgUHJvZHVjdHMgbXVzdCBvYnRhaW4gdGhlaXIgb3duXG4gKiBTcGluZSBFZGl0b3IgbGljZW5zZSBhbmQgcmVkaXN0cmlidXRpb24gb2YgdGhlIFByb2R1Y3RzIGluIGFueSBmb3JtIG11c3RcbiAqIGluY2x1ZGUgdGhpcyBsaWNlbnNlIGFuZCBjb3B5cmlnaHQgbm90aWNlLlxuICpcbiAqIFRIRSBTUElORSBSVU5USU1FUyBBUkUgUFJPVklERUQgQlkgRVNPVEVSSUMgU09GVFdBUkUgTExDIFwiQVMgSVNcIiBBTkQgQU5ZXG4gKiBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEXG4gKiBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFXG4gKiBESVNDTEFJTUVELiBJTiBOTyBFVkVOVCBTSEFMTCBFU09URVJJQyBTT0ZUV0FSRSBMTEMgQkUgTElBQkxFIEZPUiBBTllcbiAqIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTXG4gKiAoSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVMsXG4gKiBCVVNJTkVTUyBJTlRFUlJVUFRJT04sIE9SIExPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTKSBIT1dFVkVSIENBVVNFRCBBTkRcbiAqIE9OIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUXG4gKiAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOIEFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0ZcbiAqIFRIRSBTUElORSBSVU5USU1FUywgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRSBQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS5cbiAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuaW1wb3J0IHsgQm91bmRpbmdCb3hBdHRhY2htZW50IH0gZnJvbSBcIi4vYXR0YWNobWVudHMvQm91bmRpbmdCb3hBdHRhY2htZW50LmpzXCI7XG5pbXBvcnQgdHlwZSB7IFNrZWxldG9uIH0gZnJvbSBcIi4vU2tlbGV0b24uanNcIjtcbmltcG9ydCB7IHR5cGUgTnVtYmVyQXJyYXlMaWtlLCBQb29sLCBVdGlscyB9IGZyb20gXCIuL1V0aWxzLmpzXCI7XG5cbi8qKiBDb2xsZWN0cyBlYWNoIHZpc2libGUge0BsaW5rIEJvdW5kaW5nQm94QXR0YWNobWVudH0gYW5kIGNvbXB1dGVzIHRoZSB3b3JsZCB2ZXJ0aWNlcyBmb3IgaXRzIHBvbHlnb24uIFRoZSBwb2x5Z29uIHZlcnRpY2VzIGFyZVxuICogcHJvdmlkZWQgYWxvbmcgd2l0aCBjb252ZW5pZW5jZSBtZXRob2RzIGZvciBkb2luZyBoaXQgZGV0ZWN0aW9uLiAqL1xuZXhwb3J0IGNsYXNzIFNrZWxldG9uQm91bmRzIHtcblxuXHQvKiogVGhlIGxlZnQgZWRnZSBvZiB0aGUgYXhpcyBhbGlnbmVkIGJvdW5kaW5nIGJveC4gKi9cblx0bWluWCA9IDA7XG5cblx0LyoqIFRoZSBib3R0b20gZWRnZSBvZiB0aGUgYXhpcyBhbGlnbmVkIGJvdW5kaW5nIGJveC4gKi9cblx0bWluWSA9IDA7XG5cblx0LyoqIFRoZSByaWdodCBlZGdlIG9mIHRoZSBheGlzIGFsaWduZWQgYm91bmRpbmcgYm94LiAqL1xuXHRtYXhYID0gMDtcblxuXHQvKiogVGhlIHRvcCBlZGdlIG9mIHRoZSBheGlzIGFsaWduZWQgYm91bmRpbmcgYm94LiAqL1xuXHRtYXhZID0gMDtcblxuXHQvKiogVGhlIHZpc2libGUgYm91bmRpbmcgYm94ZXMuICovXG5cdGJvdW5kaW5nQm94ZXMgPSBbXSBhcyBCb3VuZGluZ0JveEF0dGFjaG1lbnRbXTtcblxuXHQvKiogVGhlIHdvcmxkIHZlcnRpY2VzIGZvciB0aGUgYm91bmRpbmcgYm94IHBvbHlnb25zLiAqL1xuXHRwb2x5Z29ucyA9IFtdIGFzIE51bWJlckFycmF5TGlrZVtdO1xuXG5cdHByaXZhdGUgcG9seWdvblBvb2wgPSBuZXcgUG9vbDxOdW1iZXJBcnJheUxpa2U+KCgpID0+IHtcblx0XHRyZXR1cm4gVXRpbHMubmV3RmxvYXRBcnJheSgxNik7XG5cdH0pO1xuXG5cdC8qKiBDbGVhcnMgYW55IHByZXZpb3VzIHBvbHlnb25zLCBmaW5kcyBhbGwgdmlzaWJsZSBib3VuZGluZyBib3ggYXR0YWNobWVudHMsIGFuZCBjb21wdXRlcyB0aGUgd29ybGQgdmVydGljZXMgZm9yIGVhY2ggYm91bmRpbmdcblx0ICogYm94J3MgcG9seWdvbi5cblx0ICogQHBhcmFtIHVwZGF0ZUFhYmIgSWYgdHJ1ZSwgdGhlIGF4aXMgYWxpZ25lZCBib3VuZGluZyBib3ggY29udGFpbmluZyBhbGwgdGhlIHBvbHlnb25zIGlzIGNvbXB1dGVkLiBJZiBmYWxzZSwgdGhlXG5cdCAqICAgICAgICAgICBTa2VsZXRvbkJvdW5kcyBBQUJCIG1ldGhvZHMgd2lsbCBhbHdheXMgcmV0dXJuIHRydWUuICovXG5cdHVwZGF0ZSAoc2tlbGV0b246IFNrZWxldG9uLCB1cGRhdGVBYWJiOiBib29sZWFuKSB7XG5cdFx0aWYgKCFza2VsZXRvbikgdGhyb3cgbmV3IEVycm9yKFwic2tlbGV0b24gY2Fubm90IGJlIG51bGwuXCIpO1xuXHRcdGNvbnN0IGJvdW5kaW5nQm94ZXMgPSB0aGlzLmJvdW5kaW5nQm94ZXM7XG5cdFx0Y29uc3QgcG9seWdvbnMgPSB0aGlzLnBvbHlnb25zO1xuXHRcdGNvbnN0IHBvbHlnb25Qb29sID0gdGhpcy5wb2x5Z29uUG9vbDtcblx0XHRjb25zdCBzbG90cyA9IHNrZWxldG9uLnNsb3RzO1xuXHRcdGNvbnN0IHNsb3RDb3VudCA9IHNsb3RzLmxlbmd0aDtcblxuXHRcdGJvdW5kaW5nQm94ZXMubGVuZ3RoID0gMDtcblx0XHRwb2x5Z29uUG9vbC5mcmVlQWxsKHBvbHlnb25zKTtcblx0XHRwb2x5Z29ucy5sZW5ndGggPSAwO1xuXG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBzbG90Q291bnQ7IGkrKykge1xuXHRcdFx0Y29uc3Qgc2xvdCA9IHNsb3RzW2ldO1xuXHRcdFx0aWYgKCFzbG90LmJvbmUuYWN0aXZlKSBjb250aW51ZTtcblx0XHRcdGNvbnN0IGF0dGFjaG1lbnQgPSBzbG90LmFwcGxpZWRQb3NlLmF0dGFjaG1lbnQ7XG5cdFx0XHRpZiAoYXR0YWNobWVudCBpbnN0YW5jZW9mIEJvdW5kaW5nQm94QXR0YWNobWVudCkge1xuXHRcdFx0XHRib3VuZGluZ0JveGVzLnB1c2goYXR0YWNobWVudCk7XG5cblx0XHRcdFx0bGV0IHBvbHlnb24gPSBwb2x5Z29uUG9vbC5vYnRhaW4oKTtcblx0XHRcdFx0aWYgKHBvbHlnb24ubGVuZ3RoICE9PSBhdHRhY2htZW50LndvcmxkVmVydGljZXNMZW5ndGgpIHtcblx0XHRcdFx0XHRwb2x5Z29uID0gVXRpbHMubmV3RmxvYXRBcnJheShhdHRhY2htZW50LndvcmxkVmVydGljZXNMZW5ndGgpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHBvbHlnb25zLnB1c2gocG9seWdvbik7XG5cdFx0XHRcdGF0dGFjaG1lbnQuY29tcHV0ZVdvcmxkVmVydGljZXMoc2tlbGV0b24sIHNsb3QsIDAsIGF0dGFjaG1lbnQud29ybGRWZXJ0aWNlc0xlbmd0aCwgcG9seWdvbiwgMCwgMik7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKHVwZGF0ZUFhYmIpIHtcblx0XHRcdHRoaXMuYWFiYkNvbXB1dGUoKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5taW5YID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuXHRcdFx0dGhpcy5taW5ZID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuXHRcdFx0dGhpcy5tYXhYID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuXHRcdFx0dGhpcy5tYXhZID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuXHRcdH1cblx0fVxuXG5cdGFhYmJDb21wdXRlICgpIHtcblx0XHRsZXQgbWluWCA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSwgbWluWSA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSwgbWF4WCA9IE51bWJlci5ORUdBVElWRV9JTkZJTklUWSwgbWF4WSA9IE51bWJlci5ORUdBVElWRV9JTkZJTklUWTtcblx0XHRjb25zdCBwb2x5Z29ucyA9IHRoaXMucG9seWdvbnM7XG5cdFx0Zm9yIChsZXQgaSA9IDAsIG4gPSBwb2x5Z29ucy5sZW5ndGg7IGkgPCBuOyBpKyspIHtcblx0XHRcdGNvbnN0IHBvbHlnb24gPSBwb2x5Z29uc1tpXTtcblx0XHRcdGNvbnN0IHZlcnRpY2VzID0gcG9seWdvbjtcblx0XHRcdGZvciAobGV0IGlpID0gMCwgbm4gPSBwb2x5Z29uLmxlbmd0aDsgaWkgPCBubjsgaWkgKz0gMikge1xuXHRcdFx0XHRjb25zdCB4ID0gdmVydGljZXNbaWldO1xuXHRcdFx0XHRjb25zdCB5ID0gdmVydGljZXNbaWkgKyAxXTtcblx0XHRcdFx0bWluWCA9IE1hdGgubWluKG1pblgsIHgpO1xuXHRcdFx0XHRtaW5ZID0gTWF0aC5taW4obWluWSwgeSk7XG5cdFx0XHRcdG1heFggPSBNYXRoLm1heChtYXhYLCB4KTtcblx0XHRcdFx0bWF4WSA9IE1hdGgubWF4KG1heFksIHkpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHR0aGlzLm1pblggPSBtaW5YO1xuXHRcdHRoaXMubWluWSA9IG1pblk7XG5cdFx0dGhpcy5tYXhYID0gbWF4WDtcblx0XHR0aGlzLm1heFkgPSBtYXhZO1xuXHR9XG5cblx0LyoqIFJldHVybnMgdHJ1ZSBpZiB0aGUgYXhpcyBhbGlnbmVkIGJvdW5kaW5nIGJveCBjb250YWlucyB0aGUgcG9pbnQuICovXG5cdGFhYmJDb250YWluc1BvaW50ICh4OiBudW1iZXIsIHk6IG51bWJlcikge1xuXHRcdHJldHVybiB4ID49IHRoaXMubWluWCAmJiB4IDw9IHRoaXMubWF4WCAmJiB5ID49IHRoaXMubWluWSAmJiB5IDw9IHRoaXMubWF4WTtcblx0fVxuXG5cdC8qKiBSZXR1cm5zIHRydWUgaWYgdGhlIGF4aXMgYWxpZ25lZCBib3VuZGluZyBib3ggaW50ZXJzZWN0cyB0aGUgbGluZSBzZWdtZW50LiAqL1xuXHRhYWJiSW50ZXJzZWN0c1NlZ21lbnQgKHgxOiBudW1iZXIsIHkxOiBudW1iZXIsIHgyOiBudW1iZXIsIHkyOiBudW1iZXIpIHtcblx0XHRjb25zdCBtaW5YID0gdGhpcy5taW5YO1xuXHRcdGNvbnN0IG1pblkgPSB0aGlzLm1pblk7XG5cdFx0Y29uc3QgbWF4WCA9IHRoaXMubWF4WDtcblx0XHRjb25zdCBtYXhZID0gdGhpcy5tYXhZO1xuXHRcdGlmICgoeDEgPD0gbWluWCAmJiB4MiA8PSBtaW5YKSB8fCAoeTEgPD0gbWluWSAmJiB5MiA8PSBtaW5ZKSB8fCAoeDEgPj0gbWF4WCAmJiB4MiA+PSBtYXhYKSB8fCAoeTEgPj0gbWF4WSAmJiB5MiA+PSBtYXhZKSlcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRjb25zdCBtID0gKHkyIC0geTEpIC8gKHgyIC0geDEpO1xuXHRcdGxldCB5ID0gbSAqIChtaW5YIC0geDEpICsgeTE7XG5cdFx0aWYgKHkgPiBtaW5ZICYmIHkgPCBtYXhZKSByZXR1cm4gdHJ1ZTtcblx0XHR5ID0gbSAqIChtYXhYIC0geDEpICsgeTE7XG5cdFx0aWYgKHkgPiBtaW5ZICYmIHkgPCBtYXhZKSByZXR1cm4gdHJ1ZTtcblx0XHRsZXQgeCA9IChtaW5ZIC0geTEpIC8gbSArIHgxO1xuXHRcdGlmICh4ID4gbWluWCAmJiB4IDwgbWF4WCkgcmV0dXJuIHRydWU7XG5cdFx0eCA9IChtYXhZIC0geTEpIC8gbSArIHgxO1xuXHRcdGlmICh4ID4gbWluWCAmJiB4IDwgbWF4WCkgcmV0dXJuIHRydWU7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG5cblx0LyoqIFJldHVybnMgdHJ1ZSBpZiB0aGUgYXhpcyBhbGlnbmVkIGJvdW5kaW5nIGJveCBpbnRlcnNlY3RzIHRoZSBheGlzIGFsaWduZWQgYm91bmRpbmcgYm94IG9mIHRoZSBzcGVjaWZpZWQgYm91bmRzLiAqL1xuXHRhYWJiSW50ZXJzZWN0c1NrZWxldG9uIChib3VuZHM6IFNrZWxldG9uQm91bmRzKSB7XG5cdFx0cmV0dXJuIHRoaXMubWluWCA8IGJvdW5kcy5tYXhYICYmIHRoaXMubWF4WCA+IGJvdW5kcy5taW5YICYmIHRoaXMubWluWSA8IGJvdW5kcy5tYXhZICYmIHRoaXMubWF4WSA+IGJvdW5kcy5taW5ZO1xuXHR9XG5cblx0LyoqIFJldHVybnMgdGhlIGZpcnN0IGJvdW5kaW5nIGJveCBhdHRhY2htZW50IHRoYXQgY29udGFpbnMgdGhlIHBvaW50LCBvciBudWxsLiBXaGVuIGRvaW5nIG1hbnkgY2hlY2tzLCBpdCBpcyB1c3VhbGx5IG1vcmVcblx0ICogZWZmaWNpZW50IHRvIG9ubHkgY2FsbCB0aGlzIG1ldGhvZCBpZiB7QGxpbmsgYWFiYkNvbnRhaW5zUG9pbnR9IHJldHVybnMgdHJ1ZS4gKi9cblx0Y29udGFpbnNQb2ludCAoeDogbnVtYmVyLCB5OiBudW1iZXIpOiBCb3VuZGluZ0JveEF0dGFjaG1lbnQgfCBudWxsIHtcblx0XHRjb25zdCBwb2x5Z29ucyA9IHRoaXMucG9seWdvbnM7XG5cdFx0Zm9yIChsZXQgaSA9IDAsIG4gPSBwb2x5Z29ucy5sZW5ndGg7IGkgPCBuOyBpKyspXG5cdFx0XHRpZiAodGhpcy5jb250YWluc1BvaW50UG9seWdvbihwb2x5Z29uc1tpXSwgeCwgeSkpIHJldHVybiB0aGlzLmJvdW5kaW5nQm94ZXNbaV07XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cblxuXHQvKiogUmV0dXJucyB0cnVlIGlmIHRoZSBwb2x5Z29uIGNvbnRhaW5zIHRoZSBwb2ludC4gKi9cblx0Y29udGFpbnNQb2ludFBvbHlnb24gKHBvbHlnb246IE51bWJlckFycmF5TGlrZSwgeDogbnVtYmVyLCB5OiBudW1iZXIpIHtcblx0XHRjb25zdCB2ZXJ0aWNlcyA9IHBvbHlnb247XG5cdFx0Y29uc3Qgbm4gPSBwb2x5Z29uLmxlbmd0aDtcblxuXHRcdGxldCBwcmV2SW5kZXggPSBubiAtIDI7XG5cdFx0bGV0IGluc2lkZSA9IGZhbHNlO1xuXHRcdGZvciAobGV0IGlpID0gMDsgaWkgPCBubjsgaWkgKz0gMikge1xuXHRcdFx0Y29uc3QgdmVydGV4WSA9IHZlcnRpY2VzW2lpICsgMV07XG5cdFx0XHRjb25zdCBwcmV2WSA9IHZlcnRpY2VzW3ByZXZJbmRleCArIDFdO1xuXHRcdFx0aWYgKCh2ZXJ0ZXhZIDwgeSAmJiBwcmV2WSA+PSB5KSB8fCAocHJldlkgPCB5ICYmIHZlcnRleFkgPj0geSkpIHtcblx0XHRcdFx0Y29uc3QgdmVydGV4WCA9IHZlcnRpY2VzW2lpXTtcblx0XHRcdFx0aWYgKHZlcnRleFggKyAoeSAtIHZlcnRleFkpIC8gKHByZXZZIC0gdmVydGV4WSkgKiAodmVydGljZXNbcHJldkluZGV4XSAtIHZlcnRleFgpIDwgeCkgaW5zaWRlID0gIWluc2lkZTtcblx0XHRcdH1cblx0XHRcdHByZXZJbmRleCA9IGlpO1xuXHRcdH1cblx0XHRyZXR1cm4gaW5zaWRlO1xuXHR9XG5cblx0LyoqIFJldHVybnMgdGhlIGZpcnN0IGJvdW5kaW5nIGJveCBhdHRhY2htZW50IHRoYXQgY29udGFpbnMgYW55IHBhcnQgb2YgdGhlIGxpbmUgc2VnbWVudCwgb3IgbnVsbC4gV2hlbiBkb2luZyBtYW55IGNoZWNrcywgaXRcblx0ICogaXMgdXN1YWxseSBtb3JlIGVmZmljaWVudCB0byBvbmx5IGNhbGwgdGhpcyBtZXRob2QgaWYge0BsaW5rIGFhYmJJbnRlcnNlY3RzU2VnbWVudH0gcmV0dXJuc1xuXHQgKiB0cnVlLiAqL1xuXHRpbnRlcnNlY3RzU2VnbWVudCAoeDE6IG51bWJlciwgeTE6IG51bWJlciwgeDI6IG51bWJlciwgeTI6IG51bWJlcikge1xuXHRcdGNvbnN0IHBvbHlnb25zID0gdGhpcy5wb2x5Z29ucztcblx0XHRmb3IgKGxldCBpID0gMCwgbiA9IHBvbHlnb25zLmxlbmd0aDsgaSA8IG47IGkrKylcblx0XHRcdGlmICh0aGlzLmludGVyc2VjdHNTZWdtZW50UG9seWdvbihwb2x5Z29uc1tpXSwgeDEsIHkxLCB4MiwgeTIpKSByZXR1cm4gdGhpcy5ib3VuZGluZ0JveGVzW2ldO1xuXHRcdHJldHVybiBudWxsO1xuXHR9XG5cblx0LyoqIFJldHVybnMgdHJ1ZSBpZiB0aGUgcG9seWdvbiBjb250YWlucyBhbnkgcGFydCBvZiB0aGUgbGluZSBzZWdtZW50LiAqL1xuXHRpbnRlcnNlY3RzU2VnbWVudFBvbHlnb24gKHBvbHlnb246IE51bWJlckFycmF5TGlrZSwgeDE6IG51bWJlciwgeTE6IG51bWJlciwgeDI6IG51bWJlciwgeTI6IG51bWJlcikge1xuXHRcdGNvbnN0IHZlcnRpY2VzID0gcG9seWdvbjtcblx0XHRjb25zdCBubiA9IHBvbHlnb24ubGVuZ3RoO1xuXG5cdFx0Y29uc3Qgd2lkdGgxMiA9IHgxIC0geDIsIGhlaWdodDEyID0geTEgLSB5Mjtcblx0XHRjb25zdCBkZXQxID0geDEgKiB5MiAtIHkxICogeDI7XG5cdFx0bGV0IHgzID0gdmVydGljZXNbbm4gLSAyXSwgeTMgPSB2ZXJ0aWNlc1tubiAtIDFdO1xuXHRcdGZvciAobGV0IGlpID0gMDsgaWkgPCBubjsgaWkgKz0gMikge1xuXHRcdFx0Y29uc3QgeDQgPSB2ZXJ0aWNlc1tpaV0sIHk0ID0gdmVydGljZXNbaWkgKyAxXTtcblx0XHRcdGNvbnN0IGRldDIgPSB4MyAqIHk0IC0geTMgKiB4NDtcblx0XHRcdGNvbnN0IHdpZHRoMzQgPSB4MyAtIHg0LCBoZWlnaHQzNCA9IHkzIC0geTQ7XG5cdFx0XHRjb25zdCBkZXQzID0gd2lkdGgxMiAqIGhlaWdodDM0IC0gaGVpZ2h0MTIgKiB3aWR0aDM0O1xuXHRcdFx0Y29uc3QgeCA9IChkZXQxICogd2lkdGgzNCAtIHdpZHRoMTIgKiBkZXQyKSAvIGRldDM7XG5cdFx0XHRpZiAoKCh4ID49IHgzICYmIHggPD0geDQpIHx8ICh4ID49IHg0ICYmIHggPD0geDMpKSAmJiAoKHggPj0geDEgJiYgeCA8PSB4MikgfHwgKHggPj0geDIgJiYgeCA8PSB4MSkpKSB7XG5cdFx0XHRcdGNvbnN0IHkgPSAoZGV0MSAqIGhlaWdodDM0IC0gaGVpZ2h0MTIgKiBkZXQyKSAvIGRldDM7XG5cdFx0XHRcdGlmICgoKHkgPj0geTMgJiYgeSA8PSB5NCkgfHwgKHkgPj0geTQgJiYgeSA8PSB5MykpICYmICgoeSA+PSB5MSAmJiB5IDw9IHkyKSB8fCAoeSA+PSB5MiAmJiB5IDw9IHkxKSkpIHJldHVybiB0cnVlO1xuXHRcdFx0fVxuXHRcdFx0eDMgPSB4NDtcblx0XHRcdHkzID0geTQ7XG5cdFx0fVxuXHRcdHJldHVybiBmYWxzZTtcblx0fVxuXG5cdC8qKiBSZXR1cm5zIHRoZSBwb2x5Z29uIGZvciB0aGUgc3BlY2lmaWVkIGJvdW5kaW5nIGJveCwgb3IgbnVsbC4gKi9cblx0Z2V0UG9seWdvbiAoYm91bmRpbmdCb3g6IEJvdW5kaW5nQm94QXR0YWNobWVudCkge1xuXHRcdGlmICghYm91bmRpbmdCb3gpIHRocm93IG5ldyBFcnJvcihcImJvdW5kaW5nQm94IGNhbm5vdCBiZSBudWxsLlwiKTtcblx0XHRjb25zdCBpbmRleCA9IHRoaXMuYm91bmRpbmdCb3hlcy5pbmRleE9mKGJvdW5kaW5nQm94KTtcblx0XHRyZXR1cm4gaW5kZXggPT09IC0xID8gbnVsbCA6IHRoaXMucG9seWdvbnNbaW5kZXhdO1xuXHR9XG5cblx0LyoqIFRoZSB3aWR0aCBvZiB0aGUgYXhpcyBhbGlnbmVkIGJvdW5kaW5nIGJveC4gKi9cblx0Z2V0V2lkdGggKCkge1xuXHRcdHJldHVybiB0aGlzLm1heFggLSB0aGlzLm1pblg7XG5cdH1cblxuXHQvKiogVGhlIGhlaWdodCBvZiB0aGUgYXhpcyBhbGlnbmVkIGJvdW5kaW5nIGJveC4gKi9cblx0Z2V0SGVpZ2h0ICgpIHtcblx0XHRyZXR1cm4gdGhpcy5tYXhZIC0gdGhpcy5taW5ZO1xuXHR9XG59XG4iXX0=