UNPKG

pixi-cull

Version:

a library to visibly cull objects designed to work with pixi.js

1,667 lines (1,473 loc) 1.91 MB
(function () { 'use strict'; /** * @this {Promise} */ function finallyConstructor(callback) { var constructor = this.constructor; return this.then( function(value) { // @ts-ignore return constructor.resolve(callback()).then(function() { return value; }); }, function(reason) { // @ts-ignore return constructor.resolve(callback()).then(function() { // @ts-ignore return constructor.reject(reason); }); } ); } function allSettled(arr) { var P = this; return new P(function(resolve, reject) { if (!(arr && typeof arr.length !== 'undefined')) { return reject( new TypeError( typeof arr + ' ' + arr + ' is not iterable(cannot read property Symbol(Symbol.iterator))' ) ); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call( val, function(val) { res(i, val); }, function(e) { args[i] = { status: 'rejected', reason: e }; if (--remaining === 0) { resolve(args); } } ); return; } } args[i] = { status: 'fulfilled', value: val }; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); } // Store setTimeout reference so promise-polyfill will be unaffected by // other code modifying setTimeout (like sinon.useFakeTimers()) var setTimeoutFunc = setTimeout; function isArray(x) { return Boolean(x && typeof x.length !== 'undefined'); } function noop() {} // Polyfill for Function.prototype.bind function bind(fn, thisArg) { return function() { fn.apply(thisArg, arguments); }; } /** * @constructor * @param {Function} fn */ function Promise$1(fn) { if (!(this instanceof Promise$1)) throw new TypeError('Promises must be constructed via new'); if (typeof fn !== 'function') throw new TypeError('not a function'); /** @type {!number} */ this._state = 0; /** @type {!boolean} */ this._handled = false; /** @type {Promise|undefined} */ this._value = undefined; /** @type {!Array<!Function>} */ this._deferreds = []; doResolve(fn, this); } function handle(self, deferred) { while (self._state === 3) { self = self._value; } if (self._state === 0) { self._deferreds.push(deferred); return; } self._handled = true; Promise$1._immediateFn(function() { var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { (self._state === 1 ? resolve$1 : reject)(deferred.promise, self._value); return; } var ret; try { ret = cb(self._value); } catch (e) { reject(deferred.promise, e); return; } resolve$1(deferred.promise, ret); }); } function resolve$1(self, newValue) { try { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.'); if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { var then = newValue.then; if (newValue instanceof Promise$1) { self._state = 3; self._value = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(bind(then, newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); } catch (e) { reject(self, e); } } function reject(self, newValue) { self._state = 2; self._value = newValue; finale(self); } function finale(self) { if (self._state === 2 && self._deferreds.length === 0) { Promise$1._immediateFn(function() { if (!self._handled) { Promise$1._unhandledRejectionFn(self._value); } }); } for (var i = 0, len = self._deferreds.length; i < len; i++) { handle(self, self._deferreds[i]); } self._deferreds = null; } /** * @constructor */ function Handler(onFulfilled, onRejected, promise) { this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, self) { var done = false; try { fn( function(value) { if (done) return; done = true; resolve$1(self, value); }, function(reason) { if (done) return; done = true; reject(self, reason); } ); } catch (ex) { if (done) return; done = true; reject(self, ex); } } Promise$1.prototype['catch'] = function(onRejected) { return this.then(null, onRejected); }; Promise$1.prototype.then = function(onFulfilled, onRejected) { // @ts-ignore var prom = new this.constructor(noop); handle(this, new Handler(onFulfilled, onRejected, prom)); return prom; }; Promise$1.prototype['finally'] = finallyConstructor; Promise$1.all = function(arr) { return new Promise$1(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.all accepts an array')); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { try { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call( val, function(val) { res(i, val); }, reject ); return; } } args[i] = val; if (--remaining === 0) { resolve(args); } } catch (ex) { reject(ex); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; Promise$1.allSettled = allSettled; Promise$1.resolve = function(value) { if (value && typeof value === 'object' && value.constructor === Promise$1) { return value; } return new Promise$1(function(resolve) { resolve(value); }); }; Promise$1.reject = function(value) { return new Promise$1(function(resolve, reject) { reject(value); }); }; Promise$1.race = function(arr) { return new Promise$1(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.race accepts an array')); } for (var i = 0, len = arr.length; i < len; i++) { Promise$1.resolve(arr[i]).then(resolve, reject); } }); }; // Use polyfill for setImmediate for performance gains Promise$1._immediateFn = // @ts-ignore (typeof setImmediate === 'function' && function(fn) { // @ts-ignore setImmediate(fn); }) || function(fn) { setTimeoutFunc(fn, 0); }; Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { if (typeof console !== 'undefined' && console) { console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console } }; /* object-assign (c) Sindre Sorhus @license MIT */ /* eslint-disable no-unused-vars */ var getOwnPropertySymbols = Object.getOwnPropertySymbols; var hasOwnProperty$1 = Object.prototype.hasOwnProperty; var propIsEnumerable = Object.prototype.propertyIsEnumerable; function toObject(val) { if (val === null || val === undefined) { throw new TypeError('Object.assign cannot be called with null or undefined'); } return Object(val); } function shouldUseNative() { try { if (!Object.assign) { return false; } // Detect buggy property enumeration order in older V8 versions. // https://bugs.chromium.org/p/v8/issues/detail?id=4118 var test1 = new String('abc'); // eslint-disable-line no-new-wrappers test1[5] = 'de'; if (Object.getOwnPropertyNames(test1)[0] === '5') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test2 = {}; for (var i = 0; i < 10; i++) { test2['_' + String.fromCharCode(i)] = i; } var order2 = Object.getOwnPropertyNames(test2).map(function (n) { return test2[n]; }); if (order2.join('') !== '0123456789') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test3 = {}; 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { test3[letter] = letter; }); if (Object.keys(Object.assign({}, test3)).join('') !== 'abcdefghijklmnopqrst') { return false; } return true; } catch (err) { // We don't expect any of the above to throw, but better to be safe. return false; } } var objectAssign = shouldUseNative() ? Object.assign : function (target, source) { var from; var to = toObject(target); var symbols; for (var s = 1; s < arguments.length; s++) { from = Object(arguments[s]); for (var key in from) { if (hasOwnProperty$1.call(from, key)) { to[key] = from[key]; } } if (getOwnPropertySymbols) { symbols = getOwnPropertySymbols(from); for (var i = 0; i < symbols.length; i++) { if (propIsEnumerable.call(from, symbols[i])) { to[symbols[i]] = from[symbols[i]]; } } } } return to; }; /*! * @pixi/polyfill - v6.0.1 * Compiled Wed, 24 Mar 2021 20:02:24 UTC * * @pixi/polyfill is licensed under the MIT License. * http://www.opensource.org/licenses/mit-license */ // Support for IE 9 - 11 which does not include Promises if (!self.Promise) { self.Promise = Promise$1; } // References: if (!Object.assign) { Object.assign = objectAssign; } // References: // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ // https://gist.github.com/1579671 // http://updates.html5rocks.com/2012/05/requestAnimationFrame-API-now-with-sub-millisecond-precision // https://gist.github.com/timhall/4078614 // https://github.com/Financial-Times/polyfill-service/tree/master/polyfills/requestAnimationFrame // Expected to be used with Browserfiy // Browserify automatically detects the use of `global` and passes the // correct reference of `global`, `self`, and finally `window` var ONE_FRAME_TIME = 16; // Date.now if (!(Date.now && Date.prototype.getTime)) { Date.now = function now() { return new Date().getTime(); }; } // performance.now if (!(self.performance && self.performance.now)) { var startTime_1 = Date.now(); if (!self.performance) { self.performance = {}; } self.performance.now = function () { return Date.now() - startTime_1; }; } // requestAnimationFrame var lastTime = Date.now(); var vendors = ['ms', 'moz', 'webkit', 'o']; for (var x = 0; x < vendors.length && !self.requestAnimationFrame; ++x) { var p = vendors[x]; self.requestAnimationFrame = self[p + "RequestAnimationFrame"]; self.cancelAnimationFrame = self[p + "CancelAnimationFrame"] || self[p + "CancelRequestAnimationFrame"]; } if (!self.requestAnimationFrame) { self.requestAnimationFrame = function (callback) { if (typeof callback !== 'function') { throw new TypeError(callback + "is not a function"); } var currentTime = Date.now(); var delay = ONE_FRAME_TIME + lastTime - currentTime; if (delay < 0) { delay = 0; } lastTime = currentTime; return self.setTimeout(function () { lastTime = Date.now(); callback(performance.now()); }, delay); }; } if (!self.cancelAnimationFrame) { self.cancelAnimationFrame = function (id) { return clearTimeout(id); }; } // References: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign if (!Math.sign) { Math.sign = function mathSign(x) { x = Number(x); if (x === 0 || isNaN(x)) { return x; } return x > 0 ? 1 : -1; }; } // References: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger if (!Number.isInteger) { Number.isInteger = function numberIsInteger(value) { return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; }; } if (!self.ArrayBuffer) { self.ArrayBuffer = Array; } if (!self.Float32Array) { self.Float32Array = Array; } if (!self.Uint32Array) { self.Uint32Array = Array; } if (!self.Uint16Array) { self.Uint16Array = Array; } if (!self.Uint8Array) { self.Uint8Array = Array; } if (!self.Int32Array) { self.Int32Array = Array; } 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; } /*! * @pixi/settings - v6.0.1 * Compiled Wed, 24 Mar 2021 20:02:24 UTC * * @pixi/settings is licensed under the MIT License. * http://www.opensource.org/licenses/mit-license */ // The ESM/CJS versions of ismobilejs only var isMobile = isMobile$1(self.navigator); /** * The maximum recommended texture units to use. * In theory the bigger the better, and for desktop we'll use as many as we can. * But some mobile devices slow down if there is to many branches in the shader. * So in practice there seems to be a sweet spot size that varies depending on the device. * * In v4, all mobile devices were limited to 4 texture units because for this. * In v5, we allow all texture units to be used on modern Apple or Android devices. * * @private * @param {number} max * @returns {number} */ function maxRecommendedTextures(max) { var allowMax = true; if (isMobile.tablet || isMobile.phone) { if (isMobile.apple.device) { var match = (navigator.userAgent).match(/OS (\d+)_(\d+)?/); if (match) { var majorVersion = parseInt(match[1], 10); // Limit texture units on devices below iOS 11, which will be older hardware if (majorVersion < 11) { allowMax = false; } } } if (isMobile.android.device) { var match = (navigator.userAgent).match(/Android\s([0-9.]*)/); if (match) { var majorVersion = parseInt(match[1], 10); // Limit texture units on devices below Android 7 (Nougat), which will be older hardware if (majorVersion < 7) { allowMax = false; } } } } return allowMax ? max : 4; } /** * Uploading the same buffer multiple times in a single frame can cause performance issues. * Apparent on iOS so only check for that at the moment * This check may become more complex if this issue pops up elsewhere. * * @private * @returns {boolean} */ function canUploadSameBuffer() { return !isMobile.apple.device; } /** * User's customizable globals for overriding the default PIXI settings, such * as a renderer's default resolution, framerate, float precision, etc. * @example * // Use the native window resolution as the default resolution * // will support high-density displays when rendering * PIXI.settings.RESOLUTION = window.devicePixelRatio; * * // Disable interpolation when scaling, will make texture be pixelated * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST; * @namespace PIXI.settings */ var settings = { /** * If set to true WebGL will attempt make textures mimpaped by default. * Mipmapping will only succeed if the base texture uploaded has power of two dimensions. * * @static * @name MIPMAP_TEXTURES * @memberof PIXI.settings * @type {PIXI.MIPMAP_MODES} * @default PIXI.MIPMAP_MODES.POW2 */ MIPMAP_TEXTURES: 1, /** * Default anisotropic filtering level of textures. * Usually from 0 to 16 * * @static * @name ANISOTROPIC_LEVEL * @memberof PIXI.settings * @type {number} * @default 0 */ ANISOTROPIC_LEVEL: 0, /** * Default resolution / device pixel ratio of the renderer. * * @static * @name RESOLUTION * @memberof PIXI.settings * @type {number} * @default 1 */ RESOLUTION: 1, /** * Default filter resolution. * * @static * @name FILTER_RESOLUTION * @memberof PIXI.settings * @type {number} * @default 1 */ FILTER_RESOLUTION: 1, /** * The maximum textures that this device supports. * * @static * @name SPRITE_MAX_TEXTURES * @memberof PIXI.settings * @type {number} * @default 32 */ SPRITE_MAX_TEXTURES: maxRecommendedTextures(32), // TODO: maybe change to SPRITE.BATCH_SIZE: 2000 // TODO: maybe add PARTICLE.BATCH_SIZE: 15000 /** * The default sprite batch size. * * The default aims to balance desktop and mobile devices. * * @static * @name SPRITE_BATCH_SIZE * @memberof PIXI.settings * @type {number} * @default 4096 */ SPRITE_BATCH_SIZE: 4096, /** * The default render options if none are supplied to {@link PIXI.Renderer} * or {@link PIXI.CanvasRenderer}. * * @static * @name RENDER_OPTIONS * @memberof PIXI.settings * @type {object} * @property {HTMLCanvasElement} view=null * @property {number} resolution=1 * @property {boolean} antialias=false * @property {boolean} autoDensity=false * @property {boolean} useContextAlpha=true * @property {number} backgroundColor=0x000000 * @property {number} backgroundAlpha=1 * @property {boolean} clearBeforeRender=true * @property {boolean} preserveDrawingBuffer=false * @property {number} width=800 * @property {number} height=600 * @property {boolean} legacy=false */ RENDER_OPTIONS: { view: null, antialias: false, autoDensity: false, backgroundColor: 0x000000, backgroundAlpha: 1, useContextAlpha: true, clearBeforeRender: true, preserveDrawingBuffer: false, width: 800, height: 600, legacy: false, }, /** * Default Garbage Collection mode. * * @static * @name GC_MODE * @memberof PIXI.settings * @type {PIXI.GC_MODES} * @default PIXI.GC_MODES.AUTO */ GC_MODE: 0, /** * Default Garbage Collection max idle. * * @static * @name GC_MAX_IDLE * @memberof PIXI.settings * @type {number} * @default 3600 */ GC_MAX_IDLE: 60 * 60, /** * Default Garbage Collection maximum check count. * * @static * @name GC_MAX_CHECK_COUNT * @memberof PIXI.settings * @type {number} * @default 600 */ GC_MAX_CHECK_COUNT: 60 * 10, /** * Default wrap modes that are supported by pixi. * * @static * @name WRAP_MODE * @memberof PIXI.settings * @type {PIXI.WRAP_MODES} * @default PIXI.WRAP_MODES.CLAMP */ WRAP_MODE: 33071, /** * Default scale mode for textures. * * @static * @name SCALE_MODE * @memberof PIXI.settings * @type {PIXI.SCALE_MODES} * @default PIXI.SCALE_MODES.LINEAR */ SCALE_MODE: 1, /** * Default specify float precision in vertex shader. * * @static * @name PRECISION_VERTEX * @memberof PIXI.settings * @type {PIXI.PRECISION} * @default PIXI.PRECISION.HIGH */ PRECISION_VERTEX: 'highp', /** * Default specify float precision in fragment shader. * iOS is best set at highp due to https://github.com/pixijs/pixi.js/issues/3742 * * @static * @name PRECISION_FRAGMENT * @memberof PIXI.settings * @type {PIXI.PRECISION} * @default PIXI.PRECISION.MEDIUM */ PRECISION_FRAGMENT: isMobile.apple.device ? 'highp' : 'mediump', /** * Can we upload the same buffer in a single frame? * * @static * @name CAN_UPLOAD_SAME_BUFFER * @memberof PIXI.settings * @type {boolean} */ CAN_UPLOAD_SAME_BUFFER: canUploadSameBuffer(), /** * Enables bitmap creation before image load. This feature is experimental. * * @static * @name CREATE_IMAGE_BITMAP * @memberof PIXI.settings * @type {boolean} * @default false */ CREATE_IMAGE_BITMAP: false, /** * If true PixiJS will Math.floor() x/y values when rendering, stopping pixel interpolation. * Advantages can include sharper image quality (like text) and faster rendering on canvas. * The main disadvantage is movement of objects may appear less smooth. * * @static * @constant * @memberof PIXI.settings * @type {boolean} * @default false */ ROUND_PIXELS: false, }; var commonjsGlobal$1 = 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 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 createCommonjsModule$1(fn) { var module = { exports: {} }; return fn(module, module.exports), module.exports; } var eventemitter3 = createCommonjsModule$1(function (module) { 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. // { module.exports = EventEmitter; } }); 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 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); 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); triangles.push(ear.i / dim); triangles.push(next.i / dim); 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 p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, 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 // triangle bbox; min & max are calculated like this for speed var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x), minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y), maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x), maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y); // z-order range for the current triangle bbox; var minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, 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 !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, 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 !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, 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 !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, 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); triangles.push(p.i / dim); triangles.push(b.i / dim); // 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); earcutLinked(c, triangles, dim, minX, minY, invSize); 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++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } 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) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { var b = splitPolygon(outerNode, hole); // filter collinear points around the cuts filterPoints(outerNode, outerNode.next); filterPoints(b, b.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; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // 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 === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next;