diff --git a/PongGame/Hubs/PongGameState.cs b/PongGame/Hubs/PongGameState.cs index 39c4e83..a389240 100644 --- a/PongGame/Hubs/PongGameState.cs +++ b/PongGame/Hubs/PongGameState.cs @@ -9,20 +9,22 @@ namespace PongGame.Hubs; public struct PongGameState { public const float HEIGHT = 500.0f; public const float WIDTH = 2 * HEIGHT; - public const float PADDLE1_OFFSET = 50.0f; - public const float PADDLE2_OFFSET = 1000 - PADDLE1_OFFSET; + public const float PADDLE1_OFFSET = WIDTH / 20; + public const float PADDLE2_OFFSET = WIDTH - PADDLE1_OFFSET; public static readonly PongGameState Initial = new() { BallState = PongBallState.Initial, Paddle1 = PongPaddleState.Initial, Paddle2 = PongPaddleState.Initial, - Status = GameStatus.WaitingForPlayers + Status = GameStatus.WaitingForPlayers, + WinnerLeft = null }; public PongPaddleState Paddle1; public PongPaddleState Paddle2; public PongBallState BallState; public GameStatus Status; + public bool? WinnerLeft; public static void Update(ref PongGameState state) { if (state.Status is not GameStatus.InProgress) return; @@ -30,6 +32,12 @@ public struct PongGameState { PongPaddleState.Update(ref state.Paddle1); PongPaddleState.Update(ref state.Paddle2); PongBallState.Update(ref state.BallState); + var ballX = state.BallState.Pos.X; + if (ballX is < 0 or > WIDTH) { + state.WinnerLeft = ballX < 0; + state.Status = GameStatus.Finished; + return; + } Collide(in state.Paddle1, ref state.BallState, true); Collide(in state.Paddle2, ref state.BallState, false); @@ -41,40 +49,48 @@ public struct PongGameState { if (intersection.IsEmpty) return; // TODO: continuous collision - var ratio = (ballState.Y - paddle.Height + PongPaddleState.PADDLE_HALF_LENGTH) / PongPaddleState.PADDLE_LENGTH; - var upAngle = left ? MathF.PI * 3 / 8 : MathF.PI * 5 / 8; - var downAngle = -upAngle; + var ratio = (ballState.Pos.Y - paddle.Height + PongPaddleState.PADDLE_HALF_LENGTH) / PongPaddleState.PADDLE_LENGTH; + // TODO: lesser angles + var upAngle = left ? MathF.PI * 3 / 8 : MathF.PI * 5 / 8; + var downAngle = left ? -MathF.PI * 3 / 8 : MathF.PI * 11 / 8; + + // TODO: reflect ball on surface instead of launching ballState.BallAngle = ratio * downAngle + (1 - ratio) * upAngle; } public struct PongBallState { - public const float BALL_SPEED = 8; - public const float BALL_RADIUS = 2; + public const float BALL_SPEED = 2 * BALL_RADIUS; + public const float BALL_RADIUS = HEIGHT / 125; public static readonly PongBallState Initial = new() { BallAngle = 0.0f, - X = WIDTH / 2, - Y = HEIGHT / 2 + Pos = new() { + X = WIDTH / 2, + Y = HEIGHT / 2 + } }; - public float X; - public float Y; + public PointF Pos; public float BallAngle; public static void Update(ref PongBallState state) { var (dy, dx) = MathF.SinCos(state.BallAngle); - state.X += BALL_SPEED * dx; - state.Y -= BALL_SPEED * dy; + state.Pos.X += BALL_SPEED * dx; + state.Pos.Y -= BALL_SPEED * dy; + + if (state.Pos.Y < BALL_RADIUS + || state.Pos.Y > HEIGHT - BALL_RADIUS) + state.BallAngle = 2 * MathF.PI - state.BallAngle; } - public RectangleF GetCollider() => new(X - BALL_RADIUS, Y - BALL_RADIUS, 2 * BALL_RADIUS, 2 * BALL_RADIUS); + public RectangleF GetCollider() => new(Pos.X - BALL_RADIUS, Pos.Y - BALL_RADIUS, 2 * BALL_RADIUS, 2 * BALL_RADIUS); } public struct PongPaddleState { public const float PADDLE_LENGTH = HEIGHT / 10; public const float PADDLE_HALF_LENGTH = PADDLE_LENGTH / 2; - public const float PADDLE_WIDTH = PADDLE_LENGTH / 6; + public const float PADDLE_WIDTH = PADDLE_LENGTH / 5; public const float PADDLE_SPEED = 8; public static readonly PongPaddleState Initial = new() { @@ -86,9 +102,9 @@ public struct PongGameState { public PongPaddleDirection Direction; public static void Update(ref PongPaddleState state) { - state.Height = Math.Clamp(state.Height + ((int)state.Direction) * PADDLE_SPEED, PADDLE_HALF_LENGTH, HEIGHT - PADDLE_HALF_LENGTH); + state.Height = Math.Clamp(state.Height - ((int)state.Direction) * PADDLE_SPEED, PADDLE_HALF_LENGTH, HEIGHT - PADDLE_HALF_LENGTH); } - public RectangleF GetCollider(float x) => new(x - PADDLE_HALF_LENGTH, Height - PADDLE_HALF_LENGTH, PADDLE_WIDTH, PADDLE_LENGTH); + public RectangleF GetCollider(float x) => new(x - PADDLE_WIDTH / 2, Height - PADDLE_HALF_LENGTH, PADDLE_WIDTH, PADDLE_LENGTH); } } \ No newline at end of file diff --git a/PongGame/Pages/Pong.cshtml b/PongGame/Pages/Pong.cshtml index cca747c..0a4d17b 100644 --- a/PongGame/Pages/Pong.cshtml +++ b/PongGame/Pages/Pong.cshtml @@ -20,8 +20,11 @@ + +
+ \ No newline at end of file diff --git a/PongGame/libman.json b/PongGame/libman.json index 0477ee3..4ab55de 100644 --- a/PongGame/libman.json +++ b/PongGame/libman.json @@ -21,6 +21,17 @@ "dist/browser/signalr.min.js", "dist/browser/signalr.min.js.map" ] + }, + { + "provider": "jsdelivr", + "library": "pixi.js@7.0.2", + "destination": "wwwroot/lib/pixi/", + "files": [ + "dist/pixi.js", + "dist/pixi.js.map", + "dist/pixi.min.js.map", + "dist/pixi.min.js" + ] } ] } \ No newline at end of file diff --git a/PongGame/wwwroot/js/pong.js b/PongGame/wwwroot/js/pong.js index 966ba8b..cbafd99 100644 --- a/PongGame/wwwroot/js/pong.js +++ b/PongGame/wwwroot/js/pong.js @@ -1,7 +1,5 @@ "use strict"; -console.log("Pong script was run!"); - const connection = new signalR.HubConnectionBuilder() .withUrl("/pong/hub") .withAutomaticReconnect() @@ -14,7 +12,7 @@ function getElement(id) { const connectionStatus = getElement("connection"); const createlobby = getElement("createlobby"); -const roomid = getElement("roomid"); +const roomidinput = getElement("roomid"); const joinroom = getElement("joinroom"); const usernameinput = getElement("username"); const setusername = getElement("setusername"); @@ -44,7 +42,10 @@ connection.onreconnected(function (connectionId) { }); createlobby.addEventListener("click", function (event) { - connection.invoke("CreateRoom").catch(function (err) { + connection.invoke("CreateRoom").then(function (roomId) { + roomidinput.value = roomId; + console.info(`Joined room [${roomId}]`); + }).catch(function (err) { return console.error(err.toString()); }); event.preventDefault(); @@ -59,7 +60,7 @@ connection.on("UsernameChanged", function (username) { }); joinroom.addEventListener("click", function (event) { - connection.invoke("JoinRoom", roomid.value).catch(function (err) { + connection.invoke("JoinRoom", roomidinput.value).catch(function (err) { return console.error(err.toString()); }); event.preventDefault(); @@ -89,6 +90,76 @@ function moveUp() { return movePaddle(-1); } function stopPaddle() { return movePaddle(0); } function moveDown() { return movePaddle(1); } +// Create the application helper and add its render target to the page +let app = new PIXI.Application({ width: 1000, height: 500 }); +getElement('canvas-container').appendChild(app.view); + +let graphics = new PIXI.Graphics(); +app.stage.addChild(graphics); + +function renderPaddle(graphics, state, xMid) { + var xLeft = xMid - 5; + graphics.beginFill(0x00FFFF); + graphics.drawRect(xLeft, state.Height - 25, 10, 50); + graphics.endFill(); +} + +function renderBall(graphics, state) { + graphics.beginFill(0xFF00FF); + graphics.drawCircle(state.Pos.X, state.Pos.Y, 4); + graphics.endFill(); +} + +function renderGameState(graphics, state) { + graphics.clear(); + renderPaddle(graphics, state.Paddle1, 50); + renderPaddle(graphics, state.Paddle2, 1000 - 50); + renderBall(graphics, state.BallState); +} + +connection.on("ReceiveGameState", function (state) { + renderGameState(graphics, state); +}); + +const keyEvent = (function () { + var upPressed = false; + var downPressed = false; + + function moveUpdated() { + if (upPressed == downPressed) stopPaddle(); + else if (upPressed) moveUp(); + else if (downPressed) moveDown(); + else console.error("unknown move!"); + } + + function handler(event) { + if (event.repeat) return; + if (event.path.indexOf(document.body) != 0) return; // only use key if it was pressed on empty space + var pressed = event.type == "keyup"; + // W Key is 87, Up arrow is 87 + // S Key is 83, Down arrow is 40 + switch (event.keyCode) { + case 87: + case 38: + upPressed = pressed; + break; + case 83: + case 40: + downPressed = pressed; + break; + default: return; + } + + event.preventDefault(); + moveUpdated(); + } + + return handler; +})(); + +document.addEventListener('keydown', keyEvent); +document.addEventListener('keyup', keyEvent); + connection.start().then(function () { console.info(`Connected!`); connectionStatus.textContent = "Connected!"; diff --git a/PongGame/wwwroot/lib/pixi/dist/pixi.js b/PongGame/wwwroot/lib/pixi/dist/pixi.js new file mode 100644 index 0000000..c9be2b0 --- /dev/null +++ b/PongGame/wwwroot/lib/pixi/dist/pixi.js @@ -0,0 +1,24183 @@ +/*! + * pixi.js - v7.0.2 + * Compiled Mon, 31 Oct 2022 15:20:53 UTC + * + * pixi.js is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +var PIXI = (function (exports) { + 'use strict'; + + var ENV = /* @__PURE__ */ ((ENV2) => { + ENV2[ENV2["WEBGL_LEGACY"] = 0] = "WEBGL_LEGACY"; + ENV2[ENV2["WEBGL"] = 1] = "WEBGL"; + ENV2[ENV2["WEBGL2"] = 2] = "WEBGL2"; + return ENV2; + })(ENV || {}); + var RENDERER_TYPE = /* @__PURE__ */ ((RENDERER_TYPE2) => { + RENDERER_TYPE2[RENDERER_TYPE2["UNKNOWN"] = 0] = "UNKNOWN"; + RENDERER_TYPE2[RENDERER_TYPE2["WEBGL"] = 1] = "WEBGL"; + RENDERER_TYPE2[RENDERER_TYPE2["CANVAS"] = 2] = "CANVAS"; + return RENDERER_TYPE2; + })(RENDERER_TYPE || {}); + var BUFFER_BITS = /* @__PURE__ */ ((BUFFER_BITS2) => { + BUFFER_BITS2[BUFFER_BITS2["COLOR"] = 16384] = "COLOR"; + BUFFER_BITS2[BUFFER_BITS2["DEPTH"] = 256] = "DEPTH"; + BUFFER_BITS2[BUFFER_BITS2["STENCIL"] = 1024] = "STENCIL"; + return BUFFER_BITS2; + })(BUFFER_BITS || {}); + var BLEND_MODES = /* @__PURE__ */ ((BLEND_MODES2) => { + BLEND_MODES2[BLEND_MODES2["NORMAL"] = 0] = "NORMAL"; + BLEND_MODES2[BLEND_MODES2["ADD"] = 1] = "ADD"; + BLEND_MODES2[BLEND_MODES2["MULTIPLY"] = 2] = "MULTIPLY"; + BLEND_MODES2[BLEND_MODES2["SCREEN"] = 3] = "SCREEN"; + BLEND_MODES2[BLEND_MODES2["OVERLAY"] = 4] = "OVERLAY"; + BLEND_MODES2[BLEND_MODES2["DARKEN"] = 5] = "DARKEN"; + BLEND_MODES2[BLEND_MODES2["LIGHTEN"] = 6] = "LIGHTEN"; + BLEND_MODES2[BLEND_MODES2["COLOR_DODGE"] = 7] = "COLOR_DODGE"; + BLEND_MODES2[BLEND_MODES2["COLOR_BURN"] = 8] = "COLOR_BURN"; + BLEND_MODES2[BLEND_MODES2["HARD_LIGHT"] = 9] = "HARD_LIGHT"; + BLEND_MODES2[BLEND_MODES2["SOFT_LIGHT"] = 10] = "SOFT_LIGHT"; + BLEND_MODES2[BLEND_MODES2["DIFFERENCE"] = 11] = "DIFFERENCE"; + BLEND_MODES2[BLEND_MODES2["EXCLUSION"] = 12] = "EXCLUSION"; + BLEND_MODES2[BLEND_MODES2["HUE"] = 13] = "HUE"; + BLEND_MODES2[BLEND_MODES2["SATURATION"] = 14] = "SATURATION"; + BLEND_MODES2[BLEND_MODES2["COLOR"] = 15] = "COLOR"; + BLEND_MODES2[BLEND_MODES2["LUMINOSITY"] = 16] = "LUMINOSITY"; + BLEND_MODES2[BLEND_MODES2["NORMAL_NPM"] = 17] = "NORMAL_NPM"; + BLEND_MODES2[BLEND_MODES2["ADD_NPM"] = 18] = "ADD_NPM"; + BLEND_MODES2[BLEND_MODES2["SCREEN_NPM"] = 19] = "SCREEN_NPM"; + BLEND_MODES2[BLEND_MODES2["NONE"] = 20] = "NONE"; + BLEND_MODES2[BLEND_MODES2["SRC_OVER"] = 0] = "SRC_OVER"; + BLEND_MODES2[BLEND_MODES2["SRC_IN"] = 21] = "SRC_IN"; + BLEND_MODES2[BLEND_MODES2["SRC_OUT"] = 22] = "SRC_OUT"; + BLEND_MODES2[BLEND_MODES2["SRC_ATOP"] = 23] = "SRC_ATOP"; + BLEND_MODES2[BLEND_MODES2["DST_OVER"] = 24] = "DST_OVER"; + BLEND_MODES2[BLEND_MODES2["DST_IN"] = 25] = "DST_IN"; + BLEND_MODES2[BLEND_MODES2["DST_OUT"] = 26] = "DST_OUT"; + BLEND_MODES2[BLEND_MODES2["DST_ATOP"] = 27] = "DST_ATOP"; + BLEND_MODES2[BLEND_MODES2["ERASE"] = 26] = "ERASE"; + BLEND_MODES2[BLEND_MODES2["SUBTRACT"] = 28] = "SUBTRACT"; + BLEND_MODES2[BLEND_MODES2["XOR"] = 29] = "XOR"; + return BLEND_MODES2; + })(BLEND_MODES || {}); + var DRAW_MODES = /* @__PURE__ */ ((DRAW_MODES2) => { + DRAW_MODES2[DRAW_MODES2["POINTS"] = 0] = "POINTS"; + DRAW_MODES2[DRAW_MODES2["LINES"] = 1] = "LINES"; + DRAW_MODES2[DRAW_MODES2["LINE_LOOP"] = 2] = "LINE_LOOP"; + DRAW_MODES2[DRAW_MODES2["LINE_STRIP"] = 3] = "LINE_STRIP"; + DRAW_MODES2[DRAW_MODES2["TRIANGLES"] = 4] = "TRIANGLES"; + DRAW_MODES2[DRAW_MODES2["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP"; + DRAW_MODES2[DRAW_MODES2["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN"; + return DRAW_MODES2; + })(DRAW_MODES || {}); + var FORMATS = /* @__PURE__ */ ((FORMATS2) => { + FORMATS2[FORMATS2["RGBA"] = 6408] = "RGBA"; + FORMATS2[FORMATS2["RGB"] = 6407] = "RGB"; + FORMATS2[FORMATS2["RG"] = 33319] = "RG"; + FORMATS2[FORMATS2["RED"] = 6403] = "RED"; + FORMATS2[FORMATS2["RGBA_INTEGER"] = 36249] = "RGBA_INTEGER"; + FORMATS2[FORMATS2["RGB_INTEGER"] = 36248] = "RGB_INTEGER"; + FORMATS2[FORMATS2["RG_INTEGER"] = 33320] = "RG_INTEGER"; + FORMATS2[FORMATS2["RED_INTEGER"] = 36244] = "RED_INTEGER"; + FORMATS2[FORMATS2["ALPHA"] = 6406] = "ALPHA"; + FORMATS2[FORMATS2["LUMINANCE"] = 6409] = "LUMINANCE"; + FORMATS2[FORMATS2["LUMINANCE_ALPHA"] = 6410] = "LUMINANCE_ALPHA"; + FORMATS2[FORMATS2["DEPTH_COMPONENT"] = 6402] = "DEPTH_COMPONENT"; + FORMATS2[FORMATS2["DEPTH_STENCIL"] = 34041] = "DEPTH_STENCIL"; + return FORMATS2; + })(FORMATS || {}); + var TARGETS = /* @__PURE__ */ ((TARGETS2) => { + TARGETS2[TARGETS2["TEXTURE_2D"] = 3553] = "TEXTURE_2D"; + TARGETS2[TARGETS2["TEXTURE_CUBE_MAP"] = 34067] = "TEXTURE_CUBE_MAP"; + TARGETS2[TARGETS2["TEXTURE_2D_ARRAY"] = 35866] = "TEXTURE_2D_ARRAY"; + TARGETS2[TARGETS2["TEXTURE_CUBE_MAP_POSITIVE_X"] = 34069] = "TEXTURE_CUBE_MAP_POSITIVE_X"; + TARGETS2[TARGETS2["TEXTURE_CUBE_MAP_NEGATIVE_X"] = 34070] = "TEXTURE_CUBE_MAP_NEGATIVE_X"; + TARGETS2[TARGETS2["TEXTURE_CUBE_MAP_POSITIVE_Y"] = 34071] = "TEXTURE_CUBE_MAP_POSITIVE_Y"; + TARGETS2[TARGETS2["TEXTURE_CUBE_MAP_NEGATIVE_Y"] = 34072] = "TEXTURE_CUBE_MAP_NEGATIVE_Y"; + TARGETS2[TARGETS2["TEXTURE_CUBE_MAP_POSITIVE_Z"] = 34073] = "TEXTURE_CUBE_MAP_POSITIVE_Z"; + TARGETS2[TARGETS2["TEXTURE_CUBE_MAP_NEGATIVE_Z"] = 34074] = "TEXTURE_CUBE_MAP_NEGATIVE_Z"; + return TARGETS2; + })(TARGETS || {}); + var TYPES = /* @__PURE__ */ ((TYPES2) => { + TYPES2[TYPES2["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE"; + TYPES2[TYPES2["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT"; + TYPES2[TYPES2["UNSIGNED_SHORT_5_6_5"] = 33635] = "UNSIGNED_SHORT_5_6_5"; + TYPES2[TYPES2["UNSIGNED_SHORT_4_4_4_4"] = 32819] = "UNSIGNED_SHORT_4_4_4_4"; + TYPES2[TYPES2["UNSIGNED_SHORT_5_5_5_1"] = 32820] = "UNSIGNED_SHORT_5_5_5_1"; + TYPES2[TYPES2["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT"; + TYPES2[TYPES2["UNSIGNED_INT_10F_11F_11F_REV"] = 35899] = "UNSIGNED_INT_10F_11F_11F_REV"; + TYPES2[TYPES2["UNSIGNED_INT_2_10_10_10_REV"] = 33640] = "UNSIGNED_INT_2_10_10_10_REV"; + TYPES2[TYPES2["UNSIGNED_INT_24_8"] = 34042] = "UNSIGNED_INT_24_8"; + TYPES2[TYPES2["UNSIGNED_INT_5_9_9_9_REV"] = 35902] = "UNSIGNED_INT_5_9_9_9_REV"; + TYPES2[TYPES2["BYTE"] = 5120] = "BYTE"; + TYPES2[TYPES2["SHORT"] = 5122] = "SHORT"; + TYPES2[TYPES2["INT"] = 5124] = "INT"; + TYPES2[TYPES2["FLOAT"] = 5126] = "FLOAT"; + TYPES2[TYPES2["FLOAT_32_UNSIGNED_INT_24_8_REV"] = 36269] = "FLOAT_32_UNSIGNED_INT_24_8_REV"; + TYPES2[TYPES2["HALF_FLOAT"] = 36193] = "HALF_FLOAT"; + return TYPES2; + })(TYPES || {}); + var SAMPLER_TYPES = /* @__PURE__ */ ((SAMPLER_TYPES2) => { + SAMPLER_TYPES2[SAMPLER_TYPES2["FLOAT"] = 0] = "FLOAT"; + SAMPLER_TYPES2[SAMPLER_TYPES2["INT"] = 1] = "INT"; + SAMPLER_TYPES2[SAMPLER_TYPES2["UINT"] = 2] = "UINT"; + return SAMPLER_TYPES2; + })(SAMPLER_TYPES || {}); + var SCALE_MODES = /* @__PURE__ */ ((SCALE_MODES2) => { + SCALE_MODES2[SCALE_MODES2["NEAREST"] = 0] = "NEAREST"; + SCALE_MODES2[SCALE_MODES2["LINEAR"] = 1] = "LINEAR"; + return SCALE_MODES2; + })(SCALE_MODES || {}); + var WRAP_MODES = /* @__PURE__ */ ((WRAP_MODES2) => { + WRAP_MODES2[WRAP_MODES2["CLAMP"] = 33071] = "CLAMP"; + WRAP_MODES2[WRAP_MODES2["REPEAT"] = 10497] = "REPEAT"; + WRAP_MODES2[WRAP_MODES2["MIRRORED_REPEAT"] = 33648] = "MIRRORED_REPEAT"; + return WRAP_MODES2; + })(WRAP_MODES || {}); + var MIPMAP_MODES = /* @__PURE__ */ ((MIPMAP_MODES2) => { + MIPMAP_MODES2[MIPMAP_MODES2["OFF"] = 0] = "OFF"; + MIPMAP_MODES2[MIPMAP_MODES2["POW2"] = 1] = "POW2"; + MIPMAP_MODES2[MIPMAP_MODES2["ON"] = 2] = "ON"; + MIPMAP_MODES2[MIPMAP_MODES2["ON_MANUAL"] = 3] = "ON_MANUAL"; + return MIPMAP_MODES2; + })(MIPMAP_MODES || {}); + var ALPHA_MODES = /* @__PURE__ */ ((ALPHA_MODES2) => { + ALPHA_MODES2[ALPHA_MODES2["NPM"] = 0] = "NPM"; + ALPHA_MODES2[ALPHA_MODES2["UNPACK"] = 1] = "UNPACK"; + ALPHA_MODES2[ALPHA_MODES2["PMA"] = 2] = "PMA"; + ALPHA_MODES2[ALPHA_MODES2["NO_PREMULTIPLIED_ALPHA"] = 0] = "NO_PREMULTIPLIED_ALPHA"; + ALPHA_MODES2[ALPHA_MODES2["PREMULTIPLY_ON_UPLOAD"] = 1] = "PREMULTIPLY_ON_UPLOAD"; + ALPHA_MODES2[ALPHA_MODES2["PREMULTIPLIED_ALPHA"] = 2] = "PREMULTIPLIED_ALPHA"; + return ALPHA_MODES2; + })(ALPHA_MODES || {}); + var CLEAR_MODES = /* @__PURE__ */ ((CLEAR_MODES2) => { + CLEAR_MODES2[CLEAR_MODES2["NO"] = 0] = "NO"; + CLEAR_MODES2[CLEAR_MODES2["YES"] = 1] = "YES"; + CLEAR_MODES2[CLEAR_MODES2["AUTO"] = 2] = "AUTO"; + CLEAR_MODES2[CLEAR_MODES2["BLEND"] = 0] = "BLEND"; + CLEAR_MODES2[CLEAR_MODES2["CLEAR"] = 1] = "CLEAR"; + CLEAR_MODES2[CLEAR_MODES2["BLIT"] = 2] = "BLIT"; + return CLEAR_MODES2; + })(CLEAR_MODES || {}); + var GC_MODES = /* @__PURE__ */ ((GC_MODES2) => { + GC_MODES2[GC_MODES2["AUTO"] = 0] = "AUTO"; + GC_MODES2[GC_MODES2["MANUAL"] = 1] = "MANUAL"; + return GC_MODES2; + })(GC_MODES || {}); + var PRECISION = /* @__PURE__ */ ((PRECISION2) => { + PRECISION2["LOW"] = "lowp"; + PRECISION2["MEDIUM"] = "mediump"; + PRECISION2["HIGH"] = "highp"; + return PRECISION2; + })(PRECISION || {}); + var MASK_TYPES = /* @__PURE__ */ ((MASK_TYPES2) => { + MASK_TYPES2[MASK_TYPES2["NONE"] = 0] = "NONE"; + MASK_TYPES2[MASK_TYPES2["SCISSOR"] = 1] = "SCISSOR"; + MASK_TYPES2[MASK_TYPES2["STENCIL"] = 2] = "STENCIL"; + MASK_TYPES2[MASK_TYPES2["SPRITE"] = 3] = "SPRITE"; + MASK_TYPES2[MASK_TYPES2["COLOR"] = 4] = "COLOR"; + return MASK_TYPES2; + })(MASK_TYPES || {}); + var COLOR_MASK_BITS = /* @__PURE__ */ ((COLOR_MASK_BITS2) => { + COLOR_MASK_BITS2[COLOR_MASK_BITS2["RED"] = 1] = "RED"; + COLOR_MASK_BITS2[COLOR_MASK_BITS2["GREEN"] = 2] = "GREEN"; + COLOR_MASK_BITS2[COLOR_MASK_BITS2["BLUE"] = 4] = "BLUE"; + COLOR_MASK_BITS2[COLOR_MASK_BITS2["ALPHA"] = 8] = "ALPHA"; + return COLOR_MASK_BITS2; + })(COLOR_MASK_BITS || {}); + var MSAA_QUALITY = /* @__PURE__ */ ((MSAA_QUALITY2) => { + MSAA_QUALITY2[MSAA_QUALITY2["NONE"] = 0] = "NONE"; + MSAA_QUALITY2[MSAA_QUALITY2["LOW"] = 2] = "LOW"; + MSAA_QUALITY2[MSAA_QUALITY2["MEDIUM"] = 4] = "MEDIUM"; + MSAA_QUALITY2[MSAA_QUALITY2["HIGH"] = 8] = "HIGH"; + return MSAA_QUALITY2; + })(MSAA_QUALITY || {}); + var BUFFER_TYPE = /* @__PURE__ */ ((BUFFER_TYPE2) => { + BUFFER_TYPE2[BUFFER_TYPE2["ELEMENT_ARRAY_BUFFER"] = 34963] = "ELEMENT_ARRAY_BUFFER"; + BUFFER_TYPE2[BUFFER_TYPE2["ARRAY_BUFFER"] = 34962] = "ARRAY_BUFFER"; + BUFFER_TYPE2[BUFFER_TYPE2["UNIFORM_BUFFER"] = 35345] = "UNIFORM_BUFFER"; + return BUFFER_TYPE2; + })(BUFFER_TYPE || {}); + + const BrowserAdapter = { + createCanvas: (width, height) => { + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + return canvas; + }, + getWebGLRenderingContext: () => WebGLRenderingContext, + getNavigator: () => navigator, + getBaseUrl: () => document.baseURI ?? window.location.href, + getFontFaceSet: () => document.fonts, + fetch: (url, options) => fetch(url, options) + }; + + var appleIphone = /iPhone/i; + var appleIpod = /iPod/i; + var appleTablet = /iPad/i; + var appleUniversal = /\biOS-universal(?:.+)Mac\b/i; + var androidPhone = /\bAndroid(?:.+)Mobile\b/i; + var androidTablet = /Android/i; + var amazonPhone = /(?:SD4930UR|\bSilk(?:.+)Mobile\b)/i; + var amazonTablet = /Silk/i; + var windowsPhone = /Windows Phone/i; + var windowsTablet = /\bWindows(?:.+)ARM\b/i; + var otherBlackBerry = /BlackBerry/i; + var otherBlackBerry10 = /BB10/i; + var otherOpera = /Opera Mini/i; + var otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i; + var otherFirefox = /Mobile(?:.+)Firefox\b/i; + var isAppleTabletOnIos13 = function (navigator) { + return (typeof navigator !== 'undefined' && + navigator.platform === 'MacIntel' && + typeof navigator.maxTouchPoints === 'number' && + navigator.maxTouchPoints > 1 && + typeof MSStream === 'undefined'); + }; + function createMatch(userAgent) { + return function (regex) { return regex.test(userAgent); }; + } + function isMobile$1(param) { + var nav = { + userAgent: '', + platform: '', + maxTouchPoints: 0 + }; + if (!param && typeof navigator !== 'undefined') { + nav = { + userAgent: navigator.userAgent, + platform: navigator.platform, + maxTouchPoints: navigator.maxTouchPoints || 0 + }; + } + else if (typeof param === 'string') { + nav.userAgent = param; + } + else if (param && param.userAgent) { + nav = { + userAgent: param.userAgent, + platform: param.platform, + maxTouchPoints: param.maxTouchPoints || 0 + }; + } + var userAgent = nav.userAgent; + var tmp = userAgent.split('[FBAN'); + if (typeof tmp[1] !== 'undefined') { + userAgent = tmp[0]; + } + tmp = userAgent.split('Twitter'); + if (typeof tmp[1] !== 'undefined') { + userAgent = tmp[0]; + } + var match = createMatch(userAgent); + var result = { + apple: { + phone: match(appleIphone) && !match(windowsPhone), + ipod: match(appleIpod), + tablet: !match(appleIphone) && + (match(appleTablet) || isAppleTabletOnIos13(nav)) && + !match(windowsPhone), + universal: match(appleUniversal), + device: (match(appleIphone) || + match(appleIpod) || + match(appleTablet) || + match(appleUniversal) || + isAppleTabletOnIos13(nav)) && + !match(windowsPhone) + }, + amazon: { + phone: match(amazonPhone), + tablet: !match(amazonPhone) && match(amazonTablet), + device: match(amazonPhone) || match(amazonTablet) + }, + android: { + phone: (!match(windowsPhone) && match(amazonPhone)) || + (!match(windowsPhone) && match(androidPhone)), + tablet: !match(windowsPhone) && + !match(amazonPhone) && + !match(androidPhone) && + (match(amazonTablet) || match(androidTablet)), + device: (!match(windowsPhone) && + (match(amazonPhone) || + match(amazonTablet) || + match(androidPhone) || + match(androidTablet))) || + match(/\bokhttp\b/i) + }, + windows: { + phone: match(windowsPhone), + tablet: match(windowsTablet), + device: match(windowsPhone) || match(windowsTablet) + }, + other: { + blackberry: match(otherBlackBerry), + blackberry10: match(otherBlackBerry10), + opera: match(otherOpera), + firefox: match(otherFirefox), + chrome: match(otherChrome), + device: match(otherBlackBerry) || + match(otherBlackBerry10) || + match(otherOpera) || + match(otherFirefox) || + match(otherChrome) + }, + any: false, + phone: false, + tablet: false + }; + result.any = + result.apple.device || + result.android.device || + result.windows.device || + result.other.device; + result.phone = + result.apple.phone || result.android.phone || result.windows.phone; + result.tablet = + result.apple.tablet || result.android.tablet || result.windows.tablet; + return result; + } + + const isMobile = isMobile$1(globalThis.navigator); + + function canUploadSameBuffer() { + return !isMobile.apple.device; + } + + function maxRecommendedTextures(max) { + let allowMax = true; + if (isMobile.tablet || isMobile.phone) { + if (isMobile.apple.device) { + const match = navigator.userAgent.match(/OS (\d+)_(\d+)?/); + if (match) { + const majorVersion = parseInt(match[1], 10); + if (majorVersion < 11) { + allowMax = false; + } + } + } + if (isMobile.android.device) { + const match = navigator.userAgent.match(/Android\s([0-9.]*)/); + if (match) { + const majorVersion = parseInt(match[1], 10); + if (majorVersion < 7) { + allowMax = false; + } + } + } + } + return allowMax ? max : 4; + } + + const settings = { + ADAPTER: BrowserAdapter, + MIPMAP_TEXTURES: MIPMAP_MODES.POW2, + ANISOTROPIC_LEVEL: 0, + RESOLUTION: 1, + FILTER_RESOLUTION: 1, + FILTER_MULTISAMPLE: MSAA_QUALITY.NONE, + SPRITE_MAX_TEXTURES: maxRecommendedTextures(32), + SPRITE_BATCH_SIZE: 4096, + RENDER_OPTIONS: { + view: null, + antialias: false, + autoDensity: false, + backgroundColor: 0, + backgroundAlpha: 1, + useContextAlpha: true, + clearBeforeRender: true, + preserveDrawingBuffer: false, + width: 800, + height: 600, + legacy: false, + hello: false + }, + GC_MODE: GC_MODES.AUTO, + GC_MAX_IDLE: 60 * 60, + GC_MAX_CHECK_COUNT: 60 * 10, + WRAP_MODE: WRAP_MODES.CLAMP, + SCALE_MODE: SCALE_MODES.LINEAR, + PRECISION_VERTEX: PRECISION.HIGH, + PRECISION_FRAGMENT: isMobile.apple.device ? PRECISION.HIGH : PRECISION.MEDIUM, + CAN_UPLOAD_SAME_BUFFER: canUploadSameBuffer(), + CREATE_IMAGE_BITMAP: false, + ROUND_PIXELS: false + }; + + settings.PREFER_ENV = ENV.WEBGL2; + settings.STRICT_TEXTURE_CACHE = false; + + var ExtensionType = /* @__PURE__ */ ((ExtensionType2) => { + ExtensionType2["Renderer"] = "renderer"; + ExtensionType2["Application"] = "application"; + ExtensionType2["RendererSystem"] = "renderer-webgl-system"; + ExtensionType2["RendererPlugin"] = "renderer-webgl-plugin"; + ExtensionType2["CanvasRendererSystem"] = "renderer-canvas-system"; + ExtensionType2["CanvasRendererPlugin"] = "renderer-canvas-plugin"; + ExtensionType2["Asset"] = "asset"; + ExtensionType2["LoadParser"] = "load-parser"; + ExtensionType2["ResolveParser"] = "resolve-parser"; + ExtensionType2["CacheParser"] = "cache-parser"; + ExtensionType2["DetectionParser"] = "detection-parser"; + return ExtensionType2; + })(ExtensionType || {}); + const normalizeExtension = (ext) => { + if (typeof ext === "function" || typeof ext === "object" && ext.extension) { + const metadata = typeof ext.extension !== "object" ? { type: ext.extension } : ext.extension; + ext = { ...metadata, ref: ext }; + } + if (typeof ext === "object") { + ext = { ...ext }; + } else { + throw new Error("Invalid extension type"); + } + if (typeof ext.type === "string") { + ext.type = [ext.type]; + } + return ext; + }; + const extensions$1 = { + _addHandlers: {}, + _removeHandlers: {}, + _queue: {}, + remove(...extensions2) { + extensions2.map(normalizeExtension).forEach((ext) => { + ext.type.forEach((type) => this._removeHandlers[type]?.(ext)); + }); + return this; + }, + add(...extensions2) { + extensions2.map(normalizeExtension).forEach((ext) => { + ext.type.forEach((type) => { + const handlers = this._addHandlers; + const queue = this._queue; + if (!handlers[type]) { + queue[type] = queue[type] || []; + queue[type].push(ext); + } else { + handlers[type](ext); + } + }); + }); + return this; + }, + handle(type, onAdd, onRemove) { + const addHandlers = this._addHandlers; + const removeHandlers = this._removeHandlers; + addHandlers[type] = onAdd; + removeHandlers[type] = onRemove; + const queue = this._queue; + if (queue[type]) { + queue[type].forEach((ext) => onAdd(ext)); + delete queue[type]; + } + return this; + }, + handleByMap(type, map) { + return this.handle(type, (extension) => { + map[extension.name] = extension.ref; + }, (extension) => { + delete map[extension.name]; + }); + }, + handleByList(type, list) { + return this.handle(type, (extension) => { + list.push(extension.ref); + list.sort((a, b) => (b.priority ?? -1) - (a.priority ?? -1)); + }, (extension) => { + const index = list.indexOf(extension.ref); + if (index !== -1) { + list.splice(index, 1); + } + }); + } + }; + + const PI_2 = Math.PI * 2; + const RAD_TO_DEG = 180 / Math.PI; + const DEG_TO_RAD = Math.PI / 180; + var SHAPES = /* @__PURE__ */ ((SHAPES2) => { + SHAPES2[SHAPES2["POLY"] = 0] = "POLY"; + SHAPES2[SHAPES2["RECT"] = 1] = "RECT"; + SHAPES2[SHAPES2["CIRC"] = 2] = "CIRC"; + SHAPES2[SHAPES2["ELIP"] = 3] = "ELIP"; + SHAPES2[SHAPES2["RREC"] = 4] = "RREC"; + return SHAPES2; + })(SHAPES || {}); + + class Point { + constructor(x = 0, y = 0) { + this.x = 0; + this.y = 0; + this.x = x; + this.y = y; + } + clone() { + return new Point(this.x, this.y); + } + copyFrom(p) { + this.set(p.x, p.y); + return this; + } + copyTo(p) { + p.set(this.x, this.y); + return p; + } + equals(p) { + return p.x === this.x && p.y === this.y; + } + set(x = 0, y = x) { + this.x = x; + this.y = y; + return this; + } + } + + const tempPoints$1 = [new Point(), new Point(), new Point(), new Point()]; + class Rectangle { + constructor(x = 0, y = 0, width = 0, height = 0) { + this.x = Number(x); + this.y = Number(y); + this.width = Number(width); + this.height = Number(height); + this.type = SHAPES.RECT; + } + get left() { + return this.x; + } + get right() { + return this.x + this.width; + } + get top() { + return this.y; + } + get bottom() { + return this.y + this.height; + } + static get EMPTY() { + return new Rectangle(0, 0, 0, 0); + } + clone() { + return new Rectangle(this.x, this.y, this.width, this.height); + } + copyFrom(rectangle) { + this.x = rectangle.x; + this.y = rectangle.y; + this.width = rectangle.width; + this.height = rectangle.height; + return this; + } + copyTo(rectangle) { + rectangle.x = this.x; + rectangle.y = this.y; + rectangle.width = this.width; + rectangle.height = this.height; + return rectangle; + } + contains(x, y) { + if (this.width <= 0 || this.height <= 0) { + return false; + } + if (x >= this.x && x < this.x + this.width) { + if (y >= this.y && y < this.y + this.height) { + return true; + } + } + return false; + } + intersects(other, transform) { + if (!transform) { + const x02 = this.x < other.x ? other.x : this.x; + const x12 = this.right > other.right ? other.right : this.right; + if (x12 <= x02) { + return false; + } + const y02 = this.y < other.y ? other.y : this.y; + const y12 = this.bottom > other.bottom ? other.bottom : this.bottom; + return y12 > y02; + } + const x0 = this.left; + const x1 = this.right; + const y0 = this.top; + const y1 = this.bottom; + if (x1 <= x0 || y1 <= y0) { + return false; + } + const lt = tempPoints$1[0].set(other.left, other.top); + const lb = tempPoints$1[1].set(other.left, other.bottom); + const rt = tempPoints$1[2].set(other.right, other.top); + const rb = tempPoints$1[3].set(other.right, other.bottom); + if (rt.x <= lt.x || lb.y <= lt.y) { + return false; + } + const s = Math.sign(transform.a * transform.d - transform.b * transform.c); + if (s === 0) { + return false; + } + transform.apply(lt, lt); + transform.apply(lb, lb); + transform.apply(rt, rt); + transform.apply(rb, rb); + if (Math.max(lt.x, lb.x, rt.x, rb.x) <= x0 || Math.min(lt.x, lb.x, rt.x, rb.x) >= x1 || Math.max(lt.y, lb.y, rt.y, rb.y) <= y0 || Math.min(lt.y, lb.y, rt.y, rb.y) >= y1) { + return false; + } + const nx = s * (lb.y - lt.y); + const ny = s * (lt.x - lb.x); + const n00 = nx * x0 + ny * y0; + const n10 = nx * x1 + ny * y0; + const n01 = nx * x0 + ny * y1; + const n11 = nx * x1 + ny * y1; + if (Math.max(n00, n10, n01, n11) <= nx * lt.x + ny * lt.y || Math.min(n00, n10, n01, n11) >= nx * rb.x + ny * rb.y) { + return false; + } + const mx = s * (lt.y - rt.y); + const my = s * (rt.x - lt.x); + const m00 = mx * x0 + my * y0; + const m10 = mx * x1 + my * y0; + const m01 = mx * x0 + my * y1; + const m11 = mx * x1 + my * y1; + if (Math.max(m00, m10, m01, m11) <= mx * lt.x + my * lt.y || Math.min(m00, m10, m01, m11) >= mx * rb.x + my * rb.y) { + return false; + } + return true; + } + pad(paddingX = 0, paddingY = paddingX) { + this.x -= paddingX; + this.y -= paddingY; + this.width += paddingX * 2; + this.height += paddingY * 2; + return this; + } + fit(rectangle) { + const x1 = Math.max(this.x, rectangle.x); + const x2 = Math.min(this.x + this.width, rectangle.x + rectangle.width); + const y1 = Math.max(this.y, rectangle.y); + const y2 = Math.min(this.y + this.height, rectangle.y + rectangle.height); + this.x = x1; + this.width = Math.max(x2 - x1, 0); + this.y = y1; + this.height = Math.max(y2 - y1, 0); + return this; + } + ceil(resolution = 1, eps = 1e-3) { + const x2 = Math.ceil((this.x + this.width - eps) * resolution) / resolution; + const y2 = Math.ceil((this.y + this.height - eps) * resolution) / resolution; + this.x = Math.floor((this.x + eps) * resolution) / resolution; + this.y = Math.floor((this.y + eps) * resolution) / resolution; + this.width = x2 - this.x; + this.height = y2 - this.y; + return this; + } + enlarge(rectangle) { + const x1 = Math.min(this.x, rectangle.x); + const x2 = Math.max(this.x + this.width, rectangle.x + rectangle.width); + const y1 = Math.min(this.y, rectangle.y); + const y2 = Math.max(this.y + this.height, rectangle.y + rectangle.height); + this.x = x1; + this.width = x2 - x1; + this.y = y1; + this.height = y2 - y1; + return this; + } + } + + class Circle { + constructor(x = 0, y = 0, radius = 0) { + this.x = x; + this.y = y; + this.radius = radius; + this.type = SHAPES.CIRC; + } + clone() { + return new Circle(this.x, this.y, this.radius); + } + contains(x, y) { + if (this.radius <= 0) { + return false; + } + const r2 = this.radius * this.radius; + let dx = this.x - x; + let dy = this.y - y; + dx *= dx; + dy *= dy; + return dx + dy <= r2; + } + getBounds() { + return new Rectangle(this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2); + } + } + + class Ellipse { + constructor(x = 0, y = 0, halfWidth = 0, halfHeight = 0) { + this.x = x; + this.y = y; + this.width = halfWidth; + this.height = halfHeight; + this.type = SHAPES.ELIP; + } + clone() { + return new Ellipse(this.x, this.y, this.width, this.height); + } + contains(x, y) { + if (this.width <= 0 || this.height <= 0) { + return false; + } + let normx = (x - this.x) / this.width; + let normy = (y - this.y) / this.height; + normx *= normx; + normy *= normy; + return normx + normy <= 1; + } + getBounds() { + return new Rectangle(this.x - this.width, this.y - this.height, this.width, this.height); + } + } + + class Polygon { + constructor(...points) { + let flat = Array.isArray(points[0]) ? points[0] : points; + if (typeof flat[0] !== "number") { + const p = []; + for (let i = 0, il = flat.length; i < il; i++) { + p.push(flat[i].x, flat[i].y); + } + flat = p; + } + this.points = flat; + this.type = SHAPES.POLY; + this.closeStroke = true; + } + clone() { + const points = this.points.slice(); + const polygon = new Polygon(points); + polygon.closeStroke = this.closeStroke; + return polygon; + } + contains(x, y) { + let inside = false; + const length = this.points.length / 2; + for (let i = 0, j = length - 1; i < length; j = i++) { + const xi = this.points[i * 2]; + const yi = this.points[i * 2 + 1]; + const xj = this.points[j * 2]; + const yj = this.points[j * 2 + 1]; + const intersect = yi > y !== yj > y && x < (xj - xi) * ((y - yi) / (yj - yi)) + xi; + if (intersect) { + inside = !inside; + } + } + return inside; + } + } + + class RoundedRectangle { + constructor(x = 0, y = 0, width = 0, height = 0, radius = 20) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.radius = radius; + this.type = SHAPES.RREC; + } + clone() { + return new RoundedRectangle(this.x, this.y, this.width, this.height, this.radius); + } + contains(x, y) { + if (this.width <= 0 || this.height <= 0) { + return false; + } + if (x >= this.x && x <= this.x + this.width) { + if (y >= this.y && y <= this.y + this.height) { + const radius = Math.max(0, Math.min(this.radius, Math.min(this.width, this.height) / 2)); + if (y >= this.y + radius && y <= this.y + this.height - radius || x >= this.x + radius && x <= this.x + this.width - radius) { + return true; + } + let dx = x - (this.x + radius); + let dy = y - (this.y + radius); + const radius2 = radius * radius; + if (dx * dx + dy * dy <= radius2) { + return true; + } + dx = x - (this.x + this.width - radius); + if (dx * dx + dy * dy <= radius2) { + return true; + } + dy = y - (this.y + this.height - radius); + if (dx * dx + dy * dy <= radius2) { + return true; + } + dx = x - (this.x + radius); + if (dx * dx + dy * dy <= radius2) { + return true; + } + } + } + return false; + } + } + + class ObservablePoint { + constructor(cb, scope, x = 0, y = 0) { + this._x = x; + this._y = y; + this.cb = cb; + this.scope = scope; + } + clone(cb = this.cb, scope = this.scope) { + return new ObservablePoint(cb, scope, this._x, this._y); + } + set(x = 0, y = x) { + if (this._x !== x || this._y !== y) { + this._x = x; + this._y = y; + this.cb.call(this.scope); + } + return this; + } + copyFrom(p) { + if (this._x !== p.x || this._y !== p.y) { + this._x = p.x; + this._y = p.y; + this.cb.call(this.scope); + } + return this; + } + copyTo(p) { + p.set(this._x, this._y); + return p; + } + equals(p) { + return p.x === this._x && p.y === this._y; + } + get x() { + return this._x; + } + set x(value) { + if (this._x !== value) { + this._x = value; + this.cb.call(this.scope); + } + } + get y() { + return this._y; + } + set y(value) { + if (this._y !== value) { + this._y = value; + this.cb.call(this.scope); + } + } + } + + class Matrix { + constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) { + this.array = null; + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tx = tx; + this.ty = ty; + } + fromArray(array) { + this.a = array[0]; + this.b = array[1]; + this.c = array[3]; + this.d = array[4]; + this.tx = array[2]; + this.ty = array[5]; + } + set(a, b, c, d, tx, ty) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tx = tx; + this.ty = ty; + return this; + } + toArray(transpose, out) { + if (!this.array) { + this.array = new Float32Array(9); + } + const array = out || this.array; + if (transpose) { + array[0] = this.a; + array[1] = this.b; + array[2] = 0; + array[3] = this.c; + array[4] = this.d; + array[5] = 0; + array[6] = this.tx; + array[7] = this.ty; + array[8] = 1; + } else { + array[0] = this.a; + array[1] = this.c; + array[2] = this.tx; + array[3] = this.b; + array[4] = this.d; + array[5] = this.ty; + array[6] = 0; + array[7] = 0; + array[8] = 1; + } + return array; + } + apply(pos, newPos) { + newPos = newPos || new Point(); + const x = pos.x; + const y = pos.y; + newPos.x = this.a * x + this.c * y + this.tx; + newPos.y = this.b * x + this.d * y + this.ty; + return newPos; + } + applyInverse(pos, newPos) { + newPos = newPos || new Point(); + const id = 1 / (this.a * this.d + this.c * -this.b); + const x = pos.x; + const y = pos.y; + newPos.x = this.d * id * x + -this.c * id * y + (this.ty * this.c - this.tx * this.d) * id; + newPos.y = this.a * id * y + -this.b * id * x + (-this.ty * this.a + this.tx * this.b) * id; + return newPos; + } + translate(x, y) { + this.tx += x; + this.ty += y; + return this; + } + scale(x, y) { + this.a *= x; + this.d *= y; + this.c *= x; + this.b *= y; + this.tx *= x; + this.ty *= y; + return this; + } + rotate(angle) { + const cos = Math.cos(angle); + const sin = Math.sin(angle); + const a1 = this.a; + const c1 = this.c; + const tx1 = this.tx; + this.a = a1 * cos - this.b * sin; + this.b = a1 * sin + this.b * cos; + this.c = c1 * cos - this.d * sin; + this.d = c1 * sin + this.d * cos; + this.tx = tx1 * cos - this.ty * sin; + this.ty = tx1 * sin + this.ty * cos; + return this; + } + append(matrix) { + const a1 = this.a; + const b1 = this.b; + const c1 = this.c; + const d1 = this.d; + this.a = matrix.a * a1 + matrix.b * c1; + this.b = matrix.a * b1 + matrix.b * d1; + this.c = matrix.c * a1 + matrix.d * c1; + this.d = matrix.c * b1 + matrix.d * d1; + this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx; + this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty; + return this; + } + setTransform(x, y, pivotX, pivotY, scaleX, scaleY, rotation, skewX, skewY) { + this.a = Math.cos(rotation + skewY) * scaleX; + this.b = Math.sin(rotation + skewY) * scaleX; + this.c = -Math.sin(rotation - skewX) * scaleY; + this.d = Math.cos(rotation - skewX) * scaleY; + this.tx = x - (pivotX * this.a + pivotY * this.c); + this.ty = y - (pivotX * this.b + pivotY * this.d); + return this; + } + prepend(matrix) { + const tx1 = this.tx; + if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1) { + const a1 = this.a; + const c1 = this.c; + this.a = a1 * matrix.a + this.b * matrix.c; + this.b = a1 * matrix.b + this.b * matrix.d; + this.c = c1 * matrix.a + this.d * matrix.c; + this.d = c1 * matrix.b + this.d * matrix.d; + } + this.tx = tx1 * matrix.a + this.ty * matrix.c + matrix.tx; + this.ty = tx1 * matrix.b + this.ty * matrix.d + matrix.ty; + return this; + } + decompose(transform) { + const a = this.a; + const b = this.b; + const c = this.c; + const d = this.d; + const pivot = transform.pivot; + const skewX = -Math.atan2(-c, d); + const skewY = Math.atan2(b, a); + const delta = Math.abs(skewX + skewY); + if (delta < 1e-5 || Math.abs(PI_2 - delta) < 1e-5) { + transform.rotation = skewY; + transform.skew.x = transform.skew.y = 0; + } else { + transform.rotation = 0; + transform.skew.x = skewX; + transform.skew.y = skewY; + } + transform.scale.x = Math.sqrt(a * a + b * b); + transform.scale.y = Math.sqrt(c * c + d * d); + transform.position.x = this.tx + (pivot.x * a + pivot.y * c); + transform.position.y = this.ty + (pivot.x * b + pivot.y * d); + return transform; + } + invert() { + const a1 = this.a; + const b1 = this.b; + const c1 = this.c; + const d1 = this.d; + const tx1 = this.tx; + const n = a1 * d1 - b1 * c1; + this.a = d1 / n; + this.b = -b1 / n; + this.c = -c1 / n; + this.d = a1 / n; + this.tx = (c1 * this.ty - d1 * tx1) / n; + this.ty = -(a1 * this.ty - b1 * tx1) / n; + return this; + } + identity() { + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.tx = 0; + this.ty = 0; + return this; + } + clone() { + const matrix = new Matrix(); + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + return matrix; + } + copyTo(matrix) { + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + return matrix; + } + copyFrom(matrix) { + this.a = matrix.a; + this.b = matrix.b; + this.c = matrix.c; + this.d = matrix.d; + this.tx = matrix.tx; + this.ty = matrix.ty; + return this; + } + static get IDENTITY() { + return new Matrix(); + } + static get TEMP_MATRIX() { + return new Matrix(); + } + } + + const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; + const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; + const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; + const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; + const rotationCayley = []; + const rotationMatrices = []; + const signum = Math.sign; + function init() { + for (let i = 0; i < 16; i++) { + const row = []; + rotationCayley.push(row); + for (let j = 0; j < 16; j++) { + const _ux = signum(ux[i] * ux[j] + vx[i] * uy[j]); + const _uy = signum(uy[i] * ux[j] + vy[i] * uy[j]); + const _vx = signum(ux[i] * vx[j] + vx[i] * vy[j]); + const _vy = signum(uy[i] * vx[j] + vy[i] * vy[j]); + for (let k = 0; k < 16; k++) { + if (ux[k] === _ux && uy[k] === _uy && vx[k] === _vx && vy[k] === _vy) { + row.push(k); + break; + } + } + } + } + for (let i = 0; i < 16; i++) { + const mat = new Matrix(); + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + rotationMatrices.push(mat); + } + } + init(); + const groupD8 = { + E: 0, + SE: 1, + S: 2, + SW: 3, + W: 4, + NW: 5, + N: 6, + NE: 7, + MIRROR_VERTICAL: 8, + MAIN_DIAGONAL: 10, + MIRROR_HORIZONTAL: 12, + REVERSE_DIAGONAL: 14, + uX: (ind) => ux[ind], + uY: (ind) => uy[ind], + vX: (ind) => vx[ind], + vY: (ind) => vy[ind], + inv: (rotation) => { + if (rotation & 8) { + return rotation & 15; + } + return -rotation & 7; + }, + add: (rotationSecond, rotationFirst) => rotationCayley[rotationSecond][rotationFirst], + sub: (rotationSecond, rotationFirst) => rotationCayley[rotationSecond][groupD8.inv(rotationFirst)], + rotate180: (rotation) => rotation ^ 4, + isVertical: (rotation) => (rotation & 3) === 2, + byDirection: (dx, dy) => { + if (Math.abs(dx) * 2 <= Math.abs(dy)) { + if (dy >= 0) { + return groupD8.S; + } + return groupD8.N; + } else if (Math.abs(dy) * 2 <= Math.abs(dx)) { + if (dx > 0) { + return groupD8.E; + } + return groupD8.W; + } else if (dy > 0) { + if (dx > 0) { + return groupD8.SE; + } + return groupD8.SW; + } else if (dx > 0) { + return groupD8.NE; + } + return groupD8.NW; + }, + matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0) => { + const mat = rotationMatrices[groupD8.inv(rotation)]; + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + } + }; + + const _Transform = class { + constructor() { + this.worldTransform = new Matrix(); + this.localTransform = new Matrix(); + this.position = new ObservablePoint(this.onChange, this, 0, 0); + this.scale = new ObservablePoint(this.onChange, this, 1, 1); + this.pivot = new ObservablePoint(this.onChange, this, 0, 0); + this.skew = new ObservablePoint(this.updateSkew, this, 0, 0); + this._rotation = 0; + this._cx = 1; + this._sx = 0; + this._cy = 0; + this._sy = 1; + this._localID = 0; + this._currentLocalID = 0; + this._worldID = 0; + this._parentID = 0; + } + onChange() { + this._localID++; + } + updateSkew() { + this._cx = Math.cos(this._rotation + this.skew.y); + this._sx = Math.sin(this._rotation + this.skew.y); + this._cy = -Math.sin(this._rotation - this.skew.x); + this._sy = Math.cos(this._rotation - this.skew.x); + this._localID++; + } + updateLocalTransform() { + const lt = this.localTransform; + if (this._localID !== this._currentLocalID) { + lt.a = this._cx * this.scale.x; + lt.b = this._sx * this.scale.x; + lt.c = this._cy * this.scale.y; + lt.d = this._sy * this.scale.y; + lt.tx = this.position.x - (this.pivot.x * lt.a + this.pivot.y * lt.c); + lt.ty = this.position.y - (this.pivot.x * lt.b + this.pivot.y * lt.d); + this._currentLocalID = this._localID; + this._parentID = -1; + } + } + updateTransform(parentTransform) { + const lt = this.localTransform; + if (this._localID !== this._currentLocalID) { + lt.a = this._cx * this.scale.x; + lt.b = this._sx * this.scale.x; + lt.c = this._cy * this.scale.y; + lt.d = this._sy * this.scale.y; + lt.tx = this.position.x - (this.pivot.x * lt.a + this.pivot.y * lt.c); + lt.ty = this.position.y - (this.pivot.x * lt.b + this.pivot.y * lt.d); + this._currentLocalID = this._localID; + this._parentID = -1; + } + if (this._parentID !== parentTransform._worldID) { + const pt = parentTransform.worldTransform; + const wt = this.worldTransform; + wt.a = lt.a * pt.a + lt.b * pt.c; + wt.b = lt.a * pt.b + lt.b * pt.d; + wt.c = lt.c * pt.a + lt.d * pt.c; + wt.d = lt.c * pt.b + lt.d * pt.d; + wt.tx = lt.tx * pt.a + lt.ty * pt.c + pt.tx; + wt.ty = lt.tx * pt.b + lt.ty * pt.d + pt.ty; + this._parentID = parentTransform._worldID; + this._worldID++; + } + } + setFromMatrix(matrix) { + matrix.decompose(this); + this._localID++; + } + get rotation() { + return this._rotation; + } + set rotation(value) { + if (this._rotation !== value) { + this._rotation = value; + this.updateSkew(); + } + } + }; + let Transform = _Transform; + Transform.IDENTITY = new _Transform(); + + class Runner { + constructor(name) { + this.items = []; + this._name = name; + this._aliasCount = 0; + } + emit(a0, a1, a2, a3, a4, a5, a6, a7) { + if (arguments.length > 8) { + throw new Error("max arguments reached"); + } + const { name, items } = this; + this._aliasCount++; + for (let i = 0, len = items.length; i < len; i++) { + items[i][name](a0, a1, a2, a3, a4, a5, a6, a7); + } + if (items === this.items) { + this._aliasCount--; + } + return this; + } + ensureNonAliasedItems() { + if (this._aliasCount > 0 && this.items.length > 1) { + this._aliasCount = 0; + this.items = this.items.slice(0); + } + } + add(item) { + if (item[this._name]) { + this.ensureNonAliasedItems(); + this.remove(item); + this.items.push(item); + } + return this; + } + remove(item) { + const index = this.items.indexOf(item); + if (index !== -1) { + this.ensureNonAliasedItems(); + this.items.splice(index, 1); + } + return this; + } + contains(item) { + return this.items.includes(item); + } + removeAll() { + this.ensureNonAliasedItems(); + this.items.length = 0; + return this; + } + destroy() { + this.removeAll(); + this.items = null; + this._name = null; + } + get empty() { + return this.items.length === 0; + } + get name() { + return this._name; + } + } + Object.defineProperties(Runner.prototype, { + dispatch: { value: Runner.prototype.emit }, + run: { value: Runner.prototype.emit } + }); + + settings.TARGET_FPMS = 0.06; + + var UPDATE_PRIORITY = /* @__PURE__ */ ((UPDATE_PRIORITY2) => { + UPDATE_PRIORITY2[UPDATE_PRIORITY2["HIGH"] = 25] = "HIGH"; + UPDATE_PRIORITY2[UPDATE_PRIORITY2["NORMAL"] = 0] = "NORMAL"; + UPDATE_PRIORITY2[UPDATE_PRIORITY2["LOW"] = -25] = "LOW"; + UPDATE_PRIORITY2[UPDATE_PRIORITY2["UTILITY"] = -50] = "UTILITY"; + return UPDATE_PRIORITY2; + })(UPDATE_PRIORITY || {}); + + class TickerListener { + constructor(fn, context = null, priority = 0, once = false) { + this.next = null; + this.previous = null; + this._destroyed = false; + this.fn = fn; + this.context = context; + this.priority = priority; + this.once = once; + } + match(fn, context = null) { + return this.fn === fn && this.context === context; + } + emit(deltaTime) { + if (this.fn) { + if (this.context) { + this.fn.call(this.context, deltaTime); + } else { + this.fn(deltaTime); + } + } + const redirect = this.next; + if (this.once) { + this.destroy(true); + } + if (this._destroyed) { + this.next = null; + } + return redirect; + } + connect(previous) { + this.previous = previous; + if (previous.next) { + previous.next.previous = this; + } + this.next = previous.next; + previous.next = this; + } + destroy(hard = false) { + this._destroyed = true; + this.fn = null; + this.context = null; + if (this.previous) { + this.previous.next = this.next; + } + if (this.next) { + this.next.previous = this.previous; + } + const redirect = this.next; + this.next = hard ? null : redirect; + this.previous = null; + return redirect; + } + } + + class Ticker { + constructor() { + this.autoStart = false; + this.deltaTime = 1; + this.lastTime = -1; + this.speed = 1; + this.started = false; + this._requestId = null; + this._maxElapsedMS = 100; + this._minElapsedMS = 0; + this._protected = false; + this._lastFrame = -1; + this._head = new TickerListener(null, null, Infinity); + this.deltaMS = 1 / settings.TARGET_FPMS; + this.elapsedMS = 1 / settings.TARGET_FPMS; + this._tick = (time) => { + this._requestId = null; + if (this.started) { + this.update(time); + if (this.started && this._requestId === null && this._head.next) { + this._requestId = requestAnimationFrame(this._tick); + } + } + }; + } + _requestIfNeeded() { + if (this._requestId === null && this._head.next) { + this.lastTime = performance.now(); + this._lastFrame = this.lastTime; + this._requestId = requestAnimationFrame(this._tick); + } + } + _cancelIfNeeded() { + if (this._requestId !== null) { + cancelAnimationFrame(this._requestId); + this._requestId = null; + } + } + _startIfPossible() { + if (this.started) { + this._requestIfNeeded(); + } else if (this.autoStart) { + this.start(); + } + } + add(fn, context, priority = UPDATE_PRIORITY.NORMAL) { + return this._addListener(new TickerListener(fn, context, priority)); + } + addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL) { + return this._addListener(new TickerListener(fn, context, priority, true)); + } + _addListener(listener) { + let current = this._head.next; + let previous = this._head; + if (!current) { + listener.connect(previous); + } else { + while (current) { + if (listener.priority > current.priority) { + listener.connect(previous); + break; + } + previous = current; + current = current.next; + } + if (!listener.previous) { + listener.connect(previous); + } + } + this._startIfPossible(); + return this; + } + remove(fn, context) { + let listener = this._head.next; + while (listener) { + if (listener.match(fn, context)) { + listener = listener.destroy(); + } else { + listener = listener.next; + } + } + if (!this._head.next) { + this._cancelIfNeeded(); + } + return this; + } + get count() { + if (!this._head) { + return 0; + } + let count = 0; + let current = this._head; + while (current = current.next) { + count++; + } + return count; + } + start() { + if (!this.started) { + this.started = true; + this._requestIfNeeded(); + } + } + stop() { + if (this.started) { + this.started = false; + this._cancelIfNeeded(); + } + } + destroy() { + if (!this._protected) { + this.stop(); + let listener = this._head.next; + while (listener) { + listener = listener.destroy(true); + } + this._head.destroy(); + this._head = null; + } + } + update(currentTime = performance.now()) { + let elapsedMS; + if (currentTime > this.lastTime) { + elapsedMS = this.elapsedMS = currentTime - this.lastTime; + if (elapsedMS > this._maxElapsedMS) { + elapsedMS = this._maxElapsedMS; + } + elapsedMS *= this.speed; + if (this._minElapsedMS) { + const delta = currentTime - this._lastFrame | 0; + if (delta < this._minElapsedMS) { + return; + } + this._lastFrame = currentTime - delta % this._minElapsedMS; + } + this.deltaMS = elapsedMS; + this.deltaTime = this.deltaMS * settings.TARGET_FPMS; + const head = this._head; + let listener = head.next; + while (listener) { + listener = listener.emit(this.deltaTime); + } + if (!head.next) { + this._cancelIfNeeded(); + } + } else { + this.deltaTime = this.deltaMS = this.elapsedMS = 0; + } + this.lastTime = currentTime; + } + get FPS() { + return 1e3 / this.elapsedMS; + } + get minFPS() { + return 1e3 / this._maxElapsedMS; + } + set minFPS(fps) { + const minFPS = Math.min(this.maxFPS, fps); + const minFPMS = Math.min(Math.max(0, minFPS) / 1e3, settings.TARGET_FPMS); + this._maxElapsedMS = 1 / minFPMS; + } + get maxFPS() { + if (this._minElapsedMS) { + return Math.round(1e3 / this._minElapsedMS); + } + return 0; + } + set maxFPS(fps) { + if (fps === 0) { + this._minElapsedMS = 0; + } else { + const maxFPS = Math.max(this.minFPS, fps); + this._minElapsedMS = 1 / (maxFPS / 1e3); + } + } + static get shared() { + if (!Ticker._shared) { + const shared = Ticker._shared = new Ticker(); + shared.autoStart = true; + shared._protected = true; + } + return Ticker._shared; + } + static get system() { + if (!Ticker._system) { + const system = Ticker._system = new Ticker(); + system.autoStart = true; + system._protected = true; + } + return Ticker._system; + } + } + + class TickerPlugin { + static init(options) { + options = Object.assign({ + autoStart: true, + sharedTicker: false + }, options); + Object.defineProperty(this, "ticker", { + set(ticker) { + if (this._ticker) { + this._ticker.remove(this.render, this); + } + this._ticker = ticker; + if (ticker) { + ticker.add(this.render, this, UPDATE_PRIORITY.LOW); + } + }, + get() { + return this._ticker; + } + }); + this.stop = () => { + this._ticker.stop(); + }; + this.start = () => { + this._ticker.start(); + }; + this._ticker = null; + this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); + if (options.autoStart) { + this.start(); + } + } + static destroy() { + if (this._ticker) { + const oldTicker = this._ticker; + this.ticker = null; + oldTicker.destroy(); + } + } + } + TickerPlugin.extension = ExtensionType.Application; + extensions$1.add(TickerPlugin); + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; + } + + function createCommonjsModule(fn, basedir, module) { + return module = { + path: basedir, + exports: {}, + require: function (path, base) { + return commonjsRequire(path, (base === undefined || base === null) ? module.path : base); + } + }, fn(module, module.exports), module.exports; + } + + function getDefaultExportFromNamespaceIfPresent (n) { + return n && Object.prototype.hasOwnProperty.call(n, 'default') ? n['default'] : n; + } + + function getDefaultExportFromNamespaceIfNotNamed (n) { + return n && Object.prototype.hasOwnProperty.call(n, 'default') && Object.keys(n).length === 1 ? n['default'] : n; + } + + function getAugmentedNamespace(n) { + if (n.__esModule) return n; + var a = Object.defineProperty({}, '__esModule', {value: true}); + Object.keys(n).forEach(function (k) { + var d = Object.getOwnPropertyDescriptor(n, k); + Object.defineProperty(a, k, d.get ? d : { + enumerable: true, + get: function () { + return n[k]; + } + }); + }); + return a; + } + + function commonjsRequire () { + throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); + } + + var eventemitter3 = createCommonjsModule(function (module) { + 'use strict'; + + var has = Object.prototype.hasOwnProperty + , prefix = '~'; + + /** + * Constructor to create a storage for our `EE` objects. + * An `Events` instance is a plain object whose properties are event names. + * + * @constructor + * @private + */ + function Events() {} + + // + // We try to not inherit from `Object.prototype`. In some engines creating an + // instance in this way is faster than calling `Object.create(null)` directly. + // If `Object.create(null)` is not supported we prefix the event names with a + // character to make sure that the built-in object properties are not + // overridden or used as an attack vector. + // + if (Object.create) { + Events.prototype = Object.create(null); + + // + // This hack is needed because the `__proto__` property is still inherited in + // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. + // + if (!new Events().__proto__) prefix = false; + } + + /** + * Representation of a single event listener. + * + * @param {Function} fn The listener function. + * @param {*} context The context to invoke the listener with. + * @param {Boolean} [once=false] Specify if the listener is a one-time listener. + * @constructor + * @private + */ + function EE(fn, context, once) { + this.fn = fn; + this.context = context; + this.once = once || false; + } + + /** + * Add a listener for a given event. + * + * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} context The context to invoke the listener with. + * @param {Boolean} once Specify if the listener is a one-time listener. + * @returns {EventEmitter} + * @private + */ + function addListener(emitter, event, fn, context, once) { + if (typeof fn !== 'function') { + throw new TypeError('The listener must be a function'); + } + + var listener = new EE(fn, context || emitter, once) + , evt = prefix ? prefix + event : event; + + if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; + else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); + else emitter._events[evt] = [emitter._events[evt], listener]; + + return emitter; + } + + /** + * Clear event by name. + * + * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. + * @param {(String|Symbol)} evt The Event name. + * @private + */ + function clearEvent(emitter, evt) { + if (--emitter._eventsCount === 0) emitter._events = new Events(); + else delete emitter._events[evt]; + } + + /** + * Minimal `EventEmitter` interface that is molded against the Node.js + * `EventEmitter` interface. + * + * @constructor + * @public + */ + function EventEmitter() { + this._events = new Events(); + this._eventsCount = 0; + } + + /** + * Return an array listing the events for which the emitter has registered + * listeners. + * + * @returns {Array} + * @public + */ + EventEmitter.prototype.eventNames = function eventNames() { + var names = [] + , events + , name; + + if (this._eventsCount === 0) return names; + + for (name in (events = this._events)) { + if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); + } + + if (Object.getOwnPropertySymbols) { + return names.concat(Object.getOwnPropertySymbols(events)); + } + + return names; + }; + + /** + * Return the listeners registered for a given event. + * + * @param {(String|Symbol)} event The event name. + * @returns {Array} The registered listeners. + * @public + */ + EventEmitter.prototype.listeners = function listeners(event) { + var evt = prefix ? prefix + event : event + , handlers = this._events[evt]; + + if (!handlers) return []; + if (handlers.fn) return [handlers.fn]; + + for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { + ee[i] = handlers[i].fn; + } + + return ee; + }; + + /** + * Return the number of listeners listening to a given event. + * + * @param {(String|Symbol)} event The event name. + * @returns {Number} The number of listeners. + * @public + */ + EventEmitter.prototype.listenerCount = function listenerCount(event) { + var evt = prefix ? prefix + event : event + , listeners = this._events[evt]; + + if (!listeners) return 0; + if (listeners.fn) return 1; + return listeners.length; + }; + + /** + * Calls each of the listeners registered for a given event. + * + * @param {(String|Symbol)} event The event name. + * @returns {Boolean} `true` if the event had listeners, else `false`. + * @public + */ + EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return false; + + var listeners = this._events[evt] + , len = arguments.length + , args + , i; + + if (listeners.fn) { + if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); + + switch (len) { + case 1: return listeners.fn.call(listeners.context), true; + case 2: return listeners.fn.call(listeners.context, a1), true; + case 3: return listeners.fn.call(listeners.context, a1, a2), true; + case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; + case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; + case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; + } + + for (i = 1, args = new Array(len -1); i < len; i++) { + args[i - 1] = arguments[i]; + } + + listeners.fn.apply(listeners.context, args); + } else { + var length = listeners.length + , j; + + for (i = 0; i < length; i++) { + if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); + + switch (len) { + case 1: listeners[i].fn.call(listeners[i].context); break; + case 2: listeners[i].fn.call(listeners[i].context, a1); break; + case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; + case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; + default: + if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { + args[j - 1] = arguments[j]; + } + + listeners[i].fn.apply(listeners[i].context, args); + } + } + } + + return true; + }; + + /** + * Add a listener for a given event. + * + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.on = function on(event, fn, context) { + return addListener(this, event, fn, context, false); + }; + + /** + * Add a one-time listener for a given event. + * + * @param {(String|Symbol)} event The event name. + * @param {Function} fn The listener function. + * @param {*} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.once = function once(event, fn, context) { + return addListener(this, event, fn, context, true); + }; + + /** + * Remove the listeners of a given event. + * + * @param {(String|Symbol)} event The event name. + * @param {Function} fn Only remove the listeners that match this function. + * @param {*} context Only remove the listeners that have this context. + * @param {Boolean} once Only remove one-time listeners. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return this; + if (!fn) { + clearEvent(this, evt); + return this; + } + + var listeners = this._events[evt]; + + if (listeners.fn) { + if ( + listeners.fn === fn && + (!once || listeners.once) && + (!context || listeners.context === context) + ) { + clearEvent(this, evt); + } + } else { + for (var i = 0, events = [], length = listeners.length; i < length; i++) { + if ( + listeners[i].fn !== fn || + (once && !listeners[i].once) || + (context && listeners[i].context !== context) + ) { + events.push(listeners[i]); + } + } + + // + // Reset the array, or remove it completely if we have no more listeners. + // + if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; + else clearEvent(this, evt); + } + + return this; + }; + + /** + * Remove all listeners, or those of the specified event. + * + * @param {(String|Symbol)} [event] The event name. + * @returns {EventEmitter} `this`. + * @public + */ + EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { + var evt; + + if (event) { + evt = prefix ? prefix + event : event; + if (this._events[evt]) clearEvent(this, evt); + } else { + this._events = new Events(); + this._eventsCount = 0; + } + + return this; + }; + + // + // Alias methods names because people roll like that. + // + EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + EventEmitter.prototype.addListener = EventEmitter.prototype.on; + + // + // Expose the prefix. + // + EventEmitter.prefixed = prefix; + + // + // Allow `EventEmitter` to be imported as module namespace. + // + EventEmitter.EventEmitter = EventEmitter; + + // + // Expose the module. + // + if ('undefined' !== 'object') { + module.exports = EventEmitter; + } + }); + + 'use strict'; + + var earcut_1 = earcut; + var _default = earcut; + + function earcut(data, holeIndices, dim) { + + dim = dim || 2; + + var hasHoles = holeIndices && holeIndices.length, + outerLen = hasHoles ? holeIndices[0] * dim : data.length, + outerNode = linkedList(data, 0, outerLen, dim, true), + triangles = []; + + if (!outerNode || outerNode.next === outerNode.prev) return triangles; + + var minX, minY, maxX, maxY, x, y, invSize; + + if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + if (data.length > 80 * dim) { + minX = maxX = data[0]; + minY = maxY = data[1]; + + for (var i = dim; i < outerLen; i += dim) { + x = data[i]; + y = data[i + 1]; + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + } + + // minX, minY and invSize are later used to transform coords into integers for z-order calculation + invSize = Math.max(maxX - minX, maxY - minY); + invSize = invSize !== 0 ? 32767 / invSize : 0; + } + + earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); + + return triangles; + } + + // create a circular doubly linked list from polygon points in the specified winding order + function linkedList(data, start, end, dim, clockwise) { + var i, last; + + if (clockwise === (signedArea(data, start, end, dim) > 0)) { + for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); + } else { + for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); + } + + if (last && equals(last, last.next)) { + removeNode(last); + last = last.next; + } + + return last; + } + + // eliminate colinear or duplicate points + function filterPoints(start, end) { + if (!start) return start; + if (!end) end = start; + + var p = start, + again; + do { + again = false; + + if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { + removeNode(p); + p = end = p.prev; + if (p === p.next) break; + again = true; + + } else { + p = p.next; + } + } while (again || p !== end); + + return end; + } + + // main ear slicing loop which triangulates a polygon (given as a linked list) + function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && invSize) indexCurve(ear, minX, minY, invSize); + + var stop = ear, + prev, next; + + // iterate through ears, slicing them one by one + while (ear.prev !== ear.next) { + prev = ear.prev; + next = ear.next; + + if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { + // cut off the triangle + triangles.push(prev.i / dim | 0); + triangles.push(ear.i / dim | 0); + triangles.push(next.i / dim | 0); + + removeNode(ear); + + // skipping the next vertex leads to less sliver triangles + ear = next.next; + stop = next.next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear === stop) { + // try filtering points and slicing again + if (!pass) { + earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); + + // if this didn't work, try curing all small self-intersections locally + } else if (pass === 1) { + ear = cureLocalIntersections(filterPoints(ear), triangles, dim); + earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass === 2) { + splitEarcut(ear, triangles, dim, minX, minY, invSize); + } + + break; + } + } + } + + // check whether a polygon node forms a valid ear with adjacent nodes + function isEar(ear) { + var a = ear.prev, + b = ear, + c = ear.next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + + // triangle bbox; min & max are calculated like this for speed + var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx), + y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy), + x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx), + y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy); + + var p = c.next; + while (p !== a) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && + pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && + area(p.prev, p, p.next) >= 0) return false; + p = p.next; + } + + return true; + } + + function isEarHashed(ear, minX, minY, invSize) { + var a = ear.prev, + b = ear, + c = ear.next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + + // triangle bbox; min & max are calculated like this for speed + var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx), + y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy), + x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx), + y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy); + + // z-order range for the current triangle bbox; + var minZ = zOrder(x0, y0, minX, minY, invSize), + maxZ = zOrder(x1, y1, minX, minY, invSize); + + var p = ear.prevZ, + n = ear.nextZ; + + // look for points inside the triangle in both directions + while (p && p.z >= minZ && n && n.z <= maxZ) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && + pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; + p = p.prevZ; + + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && + pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; + n = n.nextZ; + } + + // look for remaining points in decreasing z-order + while (p && p.z >= minZ) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && + pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; + p = p.prevZ; + } + + // look for remaining points in increasing z-order + while (n && n.z <= maxZ) { + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && + pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; + n = n.nextZ; + } + + return true; + } + + // go through all polygon nodes and cure small local self-intersections + function cureLocalIntersections(start, triangles, dim) { + var p = start; + do { + var a = p.prev, + b = p.next.next; + + if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { + + triangles.push(a.i / dim | 0); + triangles.push(p.i / dim | 0); + triangles.push(b.i / dim | 0); + + // remove two nodes involved + removeNode(p); + removeNode(p.next); + + p = start = b; + } + p = p.next; + } while (p !== start); + + return filterPoints(p); + } + + // try splitting polygon into two and triangulate them independently + function splitEarcut(start, triangles, dim, minX, minY, invSize) { + // look for a valid diagonal that divides the polygon into two + var a = start; + do { + var b = a.next.next; + while (b !== a.prev) { + if (a.i !== b.i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + var c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a.next); + c = filterPoints(c, c.next); + + // run earcut on each half + earcutLinked(a, triangles, dim, minX, minY, invSize, 0); + earcutLinked(c, triangles, dim, minX, minY, invSize, 0); + return; + } + b = b.next; + } + a = a.next; + } while (a !== start); + } + + // link every hole into the outer loop, producing a single-ring polygon without holes + function eliminateHoles(data, holeIndices, outerNode, dim) { + var queue = [], + i, len, start, end, list; + + for (i = 0, len = holeIndices.length; i < len; i++) { + start = holeIndices[i] * dim; + end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; + list = linkedList(data, start, end, dim, false); + if (list === list.next) list.steiner = true; + queue.push(getLeftmost(list)); + } + + queue.sort(compareX); + + // process holes from left to right + for (i = 0; i < queue.length; i++) { + outerNode = eliminateHole(queue[i], outerNode); + } + + return outerNode; + } + + function compareX(a, b) { + return a.x - b.x; + } + + // find a bridge between vertices that connects hole with an outer ring and and link it + function eliminateHole(hole, outerNode) { + var bridge = findHoleBridge(hole, outerNode); + if (!bridge) { + return outerNode; + } + + var bridgeReverse = splitPolygon(bridge, hole); + + // filter collinear points around the cuts + filterPoints(bridgeReverse, bridgeReverse.next); + return filterPoints(bridge, bridge.next); + } + + // David Eberly's algorithm for finding a bridge between hole and outer polygon + function findHoleBridge(hole, outerNode) { + var p = outerNode, + hx = hole.x, + hy = hole.y, + qx = -Infinity, + m; + + // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + do { + if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { + var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); + if (x <= hx && x > qx) { + qx = x; + m = p.x < p.next.x ? p : p.next; + if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint + } + } + p = p.next; + } while (p !== outerNode); + + if (!m) return null; + + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point + + var stop = m, + mx = m.x, + my = m.y, + tanMin = Infinity, + tan; + + p = m; + + do { + if (hx >= p.x && p.x >= mx && hx !== p.x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { + + tan = Math.abs(hy - p.y) / (hx - p.x); // tangential + + if (locallyInside(p, hole) && + (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) { + m = p; + tanMin = tan; + } + } + + p = p.next; + } while (p !== stop); + + return m; + } + + // whether sector in vertex m contains sector in vertex p in the same coordinates + function sectorContainsSector(m, p) { + return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; + } + + // interlink polygon nodes in z-order + function indexCurve(start, minX, minY, invSize) { + var p = start; + do { + if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; + } while (p !== start); + + p.prevZ.nextZ = null; + p.prevZ = null; + + sortLinked(p); + } + + // Simon Tatham's linked list merge sort algorithm + // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + function sortLinked(list) { + var i, p, q, e, tail, numMerges, pSize, qSize, + inSize = 1; + + do { + p = list; + list = null; + tail = null; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q.nextZ; + if (!q) break; + } + qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { + e = p; + p = p.nextZ; + pSize--; + } else { + e = q; + q = q.nextZ; + qSize--; + } + + if (tail) tail.nextZ = e; + else list = e; + + e.prevZ = tail; + tail = e; + } + + p = q; + } + + tail.nextZ = null; + inSize *= 2; + + } while (numMerges > 1); + + return list; + } + + // z-order of a point given coords and inverse of the longer side of data bbox + function zOrder(x, y, minX, minY, invSize) { + // coords are transformed into non-negative 15-bit integer range + x = (x - minX) * invSize | 0; + y = (y - minY) * invSize | 0; + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); + } + + // find the leftmost node of a polygon ring + function getLeftmost(start) { + var p = start, + leftmost = start; + do { + if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; + p = p.next; + } while (p !== start); + + return leftmost; + } + + // check if a point lies within a convex triangle + function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { + return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && + (ax - px) * (by - py) >= (bx - px) * (ay - py) && + (bx - px) * (cy - py) >= (cx - px) * (by - py); + } + + // check if a diagonal between two polygon nodes is valid (lies in polygon interior) + function isValidDiagonal(a, b) { + return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges + (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible + (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors + equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case + } + + // signed area of a triangle + function area(p, q, r) { + return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); + } + + // check if two points are equal + function equals(p1, p2) { + return p1.x === p2.x && p1.y === p2.y; + } + + // check if two segments intersect + function intersects(p1, q1, p2, q2) { + var o1 = sign$1(area(p1, q1, p2)); + var o2 = sign$1(area(p1, q1, q2)); + var o3 = sign$1(area(p2, q2, p1)); + var o4 = sign$1(area(p2, q2, q1)); + + if (o1 !== o2 && o3 !== o4) return true; // general case + + if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; + } + + // for collinear points p, q, r, check if point q lies on segment pr + function onSegment(p, q, r) { + return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); + } + + function sign$1(num) { + return num > 0 ? 1 : num < 0 ? -1 : 0; + } + + // check if a polygon diagonal intersects any polygon segments + function intersectsPolygon(a, b) { + var p = a; + do { + if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && + intersects(p, p.next, a, b)) return true; + p = p.next; + } while (p !== a); + + return false; + } + + // check if a polygon diagonal is locally inside the polygon + function locallyInside(a, b) { + return area(a.prev, a, a.next) < 0 ? + area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : + area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; + } + + // check if the middle point of a polygon diagonal is inside the polygon + function middleInside(a, b) { + var p = a, + inside = false, + px = (a.x + b.x) / 2, + py = (a.y + b.y) / 2; + do { + if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y && + (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) + inside = !inside; + p = p.next; + } while (p !== a); + + return inside; + } + + // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; + // if one belongs to the outer ring and another to a hole, it merges it into a single ring + function splitPolygon(a, b) { + var a2 = new Node(a.i, a.x, a.y), + b2 = new Node(b.i, b.x, b.y), + an = a.next, + bp = b.prev; + + a.next = b; + b.prev = a; + + a2.next = an; + an.prev = a2; + + b2.next = a2; + a2.prev = b2; + + bp.next = b2; + b2.prev = bp; + + return b2; + } + + // create a node and optionally link it with previous one (in a circular doubly linked list) + function insertNode(i, x, y, last) { + var p = new Node(i, x, y); + + if (!last) { + p.prev = p; + p.next = p; + + } else { + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; + } + return p; + } + + function removeNode(p) { + p.next.prev = p.prev; + p.prev.next = p.next; + + if (p.prevZ) p.prevZ.nextZ = p.nextZ; + if (p.nextZ) p.nextZ.prevZ = p.prevZ; + } + + function Node(i, x, y) { + // vertex index in coordinates array + this.i = i; + + // vertex coordinates + this.x = x; + this.y = y; + + // previous and next vertex nodes in a polygon ring + this.prev = null; + this.next = null; + + // z-order curve value + this.z = 0; + + // previous and next nodes in z-order + this.prevZ = null; + this.nextZ = null; + + // indicates whether this is a steiner point + this.steiner = false; + } + + // return a percentage difference between the polygon area and its triangulation area; + // used to verify correctness of triangulation + earcut.deviation = function (data, holeIndices, dim, triangles) { + var hasHoles = holeIndices && holeIndices.length; + var outerLen = hasHoles ? holeIndices[0] * dim : data.length; + + var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim)); + if (hasHoles) { + for (var i = 0, len = holeIndices.length; i < len; i++) { + var start = holeIndices[i] * dim; + var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; + polygonArea -= Math.abs(signedArea(data, start, end, dim)); + } + } + + var trianglesArea = 0; + for (i = 0; i < triangles.length; i += 3) { + var a = triangles[i] * dim; + var b = triangles[i + 1] * dim; + var c = triangles[i + 2] * dim; + trianglesArea += Math.abs( + (data[a] - data[c]) * (data[b + 1] - data[a + 1]) - + (data[a] - data[b]) * (data[c + 1] - data[a + 1])); + } + + return polygonArea === 0 && trianglesArea === 0 ? 0 : + Math.abs((trianglesArea - polygonArea) / polygonArea); + }; + + function signedArea(data, start, end, dim) { + var sum = 0; + for (var i = start, j = end - dim; i < end; i += dim) { + sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); + j = i; + } + return sum; + } + + // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts + earcut.flatten = function (data) { + var dim = data[0][0].length, + result = {vertices: [], holes: [], dimensions: dim}, + holeIndex = 0; + + for (var i = 0; i < data.length; i++) { + for (var j = 0; j < data[i].length; j++) { + for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]); + } + if (i > 0) { + holeIndex += data[i - 1].length; + result.holes.push(holeIndex); + } + } + return result; + }; + earcut_1.default = _default; + + var punycode = createCommonjsModule(function (module, exports) { + /*! https://mths.be/punycode v1.3.2 by @mathias */ + ;(function(root) { + + /** Detect free variables */ + var freeExports = 'object' == 'object' && exports && + !exports.nodeType && exports; + var freeModule = 'object' == 'object' && module && + !module.nodeType && module; + var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal; + if ( + freeGlobal.global === freeGlobal || + freeGlobal.window === freeGlobal || + freeGlobal.self === freeGlobal + ) { + root = freeGlobal; + } + + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, + + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' + + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators + + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, + + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, + + /** Temporary variable */ + key; + + /*--------------------------------------------------------------------------*/ + + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw RangeError(errors[type]); + } + + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + var result = []; + while (length--) { + result[length] = fn(array[length]); + } + return result; + } + + /** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; + } + + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see