See More

(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('leaflet')) : typeof define === 'function' && define.amd ? define(['exports', 'leaflet'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.leafletWind = {}, global.L)); })(this, (function (exports, L) { 'use strict'; function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var L__namespace = /*#__PURE__*/_interopNamespaceDefault(L); const hasOwnProperty = Object.prototype.hasOwnProperty; const symToStringTag = typeof Symbol !== "undefined" ? Symbol.toStringTag : void 0; function baseGetTag(value) { if (value === null) { return value === void 0 ? "[object Undefined]" : "[object Null]"; } if (!(symToStringTag && symToStringTag in Object(value))) { return toString.call(value); } const isOwn = hasOwnProperty.call(value, symToStringTag); const tag = value[symToStringTag]; let unmasked = false; try { value[symToStringTag] = void 0; unmasked = true; } catch (e) { } const result = Object.prototype.toString.call(value); if (unmasked) { if (isOwn) { value[symToStringTag] = tag; } else { delete value[symToStringTag]; } } return result; } function isFunction$1(value) { if (!isObject$1(value)) { return false; } const tag = baseGetTag(value); return tag === "[object Function]" || tag === "[object AsyncFunction]" || tag === "[object GeneratorFunction]" || tag === "[object Proxy]"; } function isObject$1(value) { const type = typeof value; return value !== null && (type === "object" || type === "function"); } function isString$1(value) { if (value == null) { return false; } return typeof value === "string" || value.constructor !== null && value.constructor === String; } function isNumber$1(value) { return Object.prototype.toString.call(value) === "[object Number]" && !isNaN(value); } function isArray(arr) { return Array.isArray(arr); } function assign(target, ...sources) { return Object.assign(target, ...sources); } function warnLog(msg, n) { console.warn(`${n || "wind-layer"}: ${msg}`); } const warnings = {}; function warnOnce(namespaces, msg) { if (!warnings[msg]) { warnLog(msg, namespaces); warnings[msg] = true; } } function floorMod(a, n) { return a - n * Math.floor(a / n); } function isValide(val) { return val !== void 0 && val !== null && !isNaN(val); } function formatData(data, options = {}) { let uComp = void 0; let vComp = void 0; data.forEach(function(record) { switch (record.header.parameterCategory + "," + record.header.parameterNumber) { case "1,2": case "2,2": uComp = record; break; case "1,3": case "2,3": vComp = record; break; } }); if (!vComp || !uComp) { return void 0; } const header = uComp.header; const vectorField = new Field({ xmin: header.lo1, // 一般格点数据是按照矩形范围来切割,所以定义其经纬度范围 ymin: header.la1, xmax: header.lo2, ymax: header.la2, deltaX: header.dx, // x(经度)增量 deltaY: header.dy, // y(维度)增量 cols: header.nx, // 列(可由 `(xmax - xmin) / deltaX` 得到) rows: header.ny, // 行 us: uComp.data, // U分量 vs: vComp.data, // V分量 ...options }); return vectorField; } function createCanvas(width, height, retina, Canvas) { if (typeof document !== "undefined") { const canvas = document.createElement("canvas"); canvas.width = width * retina; canvas.height = height * retina; return canvas; } else { return new Canvas(width * retina, height * retina); } } let Vector$1 = class Vector { constructor(u, v) { this.u = u; this.v = v; this.m = this.magnitude(); } /** * 向量值(这里指风速) * @returns {Number} */ magnitude() { return Math.sqrt(this.u ** 2 + this.v ** 2); } /** * 流体方向 (这里指风向,范围为0-360º) * N is 0º and E is 90º * @returns {Number} */ directionTo() { const verticalAngle = Math.atan2(this.u, this.v); let inDegrees = verticalAngle * (180 / Math.PI); if (inDegrees < 0) { inDegrees += 360; } return inDegrees; } /** * Angle in degrees (0 to 360º) From x--> * N is 0º and E is 90º * @returns {Number} */ directionFrom() { const a = this.directionTo(); return (a + 180) % 360; } }; class Field { constructor(params) { this.grid = []; this.xmin = params.xmin; this.xmax = params.xmax; this.ymin = params.ymin; this.ymax = params.ymax; this.cols = params.cols; this.rows = params.rows; this.us = params.us; this.vs = params.vs; this.deltaX = params.deltaX; this.deltaY = params.deltaY; this.flipY = Boolean(params.flipY); this.ymin = Math.min(params.ymax, params.ymin); this.ymax = Math.max(params.ymax, params.ymin); if (!(this.deltaY < 0 && this.ymin < this.ymax)) { if (params.flipY === void 0) { this.flipY = true; } console.warn("[wind-core]: The data is flipY"); } this.isFields = true; const cols = Math.ceil((this.xmax - this.xmin) / params.deltaX); const rows = Math.ceil((this.ymax - this.ymin) / params.deltaY); if (cols !== this.cols || rows !== this.rows) { console.warn("[wind-core]: The data grid not equal"); } this.isContinuous = Math.floor(this.cols * params.deltaX) >= 360; this.translateX = "translateX" in params ? params.translateX : this.xmax > 180; if ("wrappedX" in params) { warnOnce("[wind-core]: ", "`wrappedX` namespace will deprecated please use `translateX` instead\uFF01"); } this.wrapX = Boolean(params.wrapX); this.grid = this.buildGrid(); this.range = this.calculateRange(); } // from https://github.com/sakitam-fdd/wind-layer/blob/95368f9433/src/windy/windy.js#L110 buildGrid() { const grid = []; let p = 0; const { rows, cols, us, vs } = this; for (let j = 0; j < rows; j++) { const row = []; for (let i = 0; i < cols; i++, p++) { const u = us[p]; const v = vs[p]; const valid = this.isValid(u) && this.isValid(v); row[i] = valid ? new Vector$1(u, v) : null; } if (this.isContinuous) { row.push(row[0]); } grid[j] = row; } return grid; } /** * release data */ release() { this.grid = []; } /** * grib data extent * 格点数据范围 */ extent() { return [this.xmin, this.ymin, this.xmax, this.ymax]; } /** * Bilinear interpolation for Vector * 针对向量进行双线性插值 * https://en.wikipedia.org/wiki/Bilinear_interpolation * @param {Number} x * @param {Number} y * @param {Number[]} g00 * @param {Number[]} g10 * @param {Number[]} g01 * @param {Number[]} g11 * @returns {Vector} */ bilinearInterpolateVector(x, y, g00, g10, g01, g11) { const rx = 1 - x; const ry = 1 - y; const a = rx * ry; const b = x * ry; const c = rx * y; const d = x * y; const u = g00.u * a + g10.u * b + g01.u * c + g11.u * d; const v = g00.v * a + g10.v * b + g01.v * c + g11.v * d; return new Vector$1(u, v); } /** * calculate vector value range */ calculateRange() { if (!this.grid || !this.grid[0]) return; const rows = this.grid.length; const cols = this.grid[0].length; let min; let max; for (let j = 0; j < rows; j++) { for (let i = 0; i < cols; i++) { const vec = this.grid[j][i]; if (vec !== null) { const val = vec.m || vec.magnitude(); if (min === void 0) { min = val; } else if (max === void 0) { max = val; min = Math.min(min, max); max = Math.max(min, max); } else { min = Math.min(val, min); max = Math.max(val, max); } } } } return [min, max]; } /** * 检查 uv是否合法 * @param x * @private */ isValid(x) { return x !== null && x !== void 0; } getWrappedLongitudes() { let xmin = this.xmin; let xmax = this.xmax; if (this.translateX) { if (this.isContinuous) { xmin = -180; xmax = 180; } else { xmax = this.xmax - 360; xmin = this.xmin - 360; } } return [xmin, xmax]; } contains(lon, lat) { const [xmin, xmax] = this.getWrappedLongitudes(); if (xmax > 180 && lon >= -180 && lon <= xmax - 360) { lon += 360; } else if (xmin < -180 && lon <= 180 && lon >= xmin + 360) { lon -= 360; } const longitudeIn = lon >= xmin && lon <= xmax; let latitudeIn; if (this.deltaY >= 0) { latitudeIn = lat >= this.ymin && lat <= this.ymax; } else { latitudeIn = lat >= this.ymax && lat <= this.ymin; } return longitudeIn && latitudeIn; } /** * 获取经纬度所在的位置索引 * @param lon * @param lat */ getDecimalIndexes(lon, lat) { const i = floorMod(lon - this.xmin, 360) / this.deltaX; if (this.flipY) { const j = (this.ymax - lat) / this.deltaY; return [i, j]; } else { const j = (this.ymin + lat) / this.deltaY; return [i, j]; } } /** * Nearest value at lon-lat coordinates * 线性插值 * @param lon * @param lat */ valueAt(lon, lat) { let flag = false; if (this.wrapX) { flag = true; } else if (this.contains(lon, lat)) { flag = true; } if (!flag) return null; const indexes = this.getDecimalIndexes(lon, lat); const ii = Math.floor(indexes[0]); const jj = Math.floor(indexes[1]); const ci = this.clampColumnIndex(ii); const cj = this.clampRowIndex(jj); return this.valueAtIndexes(ci, cj); } /** * Get interpolated grid value lon-lat coordinates * 双线性插值 * @param lon * @param lat */ interpolatedValueAt(lon, lat) { let flag = false; if (this.wrapX) { flag = true; } else if (this.contains(lon, lat)) { flag = true; } if (!flag) return null; const [i, j] = this.getDecimalIndexes(lon, lat); return this.interpolatePoint(i, j); } hasValueAt(lon, lat) { const value = this.valueAt(lon, lat); return value !== null; } /** * 基于向量的双线性插值 * @param i * @param j */ interpolatePoint(i, j) { const indexes = this.getFourSurroundingIndexes(i, j); const [fi, ci, fj, cj] = indexes; const values = this.getFourSurroundingValues(fi, ci, fj, cj); if (values) { const [g00, g10, g01, g11] = values; return this.bilinearInterpolateVector(i - fi, j - fj, g00, g10, g01, g11); } return null; } /** * Check the column index is inside the field, * adjusting to min or max when needed * @private * @param {Number} ii - index * @returns {Number} i - inside the allowed indexes */ clampColumnIndex(ii) { let i = ii; if (ii < 0) { i = 0; } const maxCol = this.cols - 1; if (ii > maxCol) { i = maxCol; } return i; } /** * Check the row index is inside the field, * adjusting to min or max when needed * @private * @param {Number} jj index * @returns {Number} j - inside the allowed indexes */ clampRowIndex(jj) { let j = jj; if (jj < 0) { j = 0; } const maxRow = this.rows - 1; if (jj > maxRow) { j = maxRow; } return j; } /** * 计算索引位置周围的数据 * @private * @param {Number} i - decimal index * @param {Number} j - decimal index * @returns {Array} [fi, ci, fj, cj] */ getFourSurroundingIndexes(i, j) { const fi = Math.floor(i); let ci = fi + 1; if (this.isContinuous && ci >= this.cols) { ci = 0; } ci = this.clampColumnIndex(ci); const fj = this.clampRowIndex(Math.floor(j)); const cj = this.clampRowIndex(fj + 1); return [fi, ci, fj, cj]; } /** * Get four surrounding values or null if not available, * from 4 integer indexes * @private * @param {Number} fi * @param {Number} ci * @param {Number} fj * @param {Number} cj * @returns {Array} */ getFourSurroundingValues(fi, ci, fj, cj) { let row; if (row = this.grid[fj]) { const g00 = row[fi]; const g10 = row[ci]; if (this.isValid(g00) && this.isValid(g10) && (row = this.grid[cj])) { const g01 = row[fi]; const g11 = row[ci]; if (this.isValid(g01) && this.isValid(g11)) { return [g00, g10, g01, g11]; } } } return null; } /** * Value for grid indexes * @param {Number} i - column index (integer) * @param {Number} j - row index (integer) * @returns {Vector|Number} */ valueAtIndexes(i, j) { return this.grid[j][i]; } /** * Lon-Lat for grid indexes * @param {Number} i - column index (integer) * @param {Number} j - row index (integer) * @returns {Number[]} [lon, lat] */ lonLatAtIndexes(i, j) { const lon = this.longitudeAtX(i); const lat = this.latitudeAtY(j); return [lon, lat]; } /** * Longitude for grid-index * @param {Number} i - column index (integer) * @returns {Number} longitude at the center of the cell */ longitudeAtX(i) { const halfXPixel = this.deltaX / 2; let lon = this.xmin + halfXPixel + i * this.deltaX; if (this.translateX) { lon = lon > 180 ? lon - 360 : lon; } return lon; } /** * Latitude for grid-index * @param {Number} j - row index (integer) * @returns {Number} latitude at the center of the cell */ latitudeAtY(j) { const halfYPixel = this.deltaY / 2; return this.ymax - halfYPixel - j * this.deltaY; } /** * 生成粒子位置 * @param o * @param width * @param height * @param unproject * @return IPosition */ randomize(o = {}, width, height, unproject) { const i = Math.random() * (width || this.cols) | 0; const j = Math.random() * (height || this.rows) | 0; const coords = unproject([i, j]); if (coords !== null) { o.x = coords[0]; o.y = coords[1]; } else { o.x = this.longitudeAtX(i); o.y = this.latitudeAtY(j); } return o; } /** * 判断是否是 `Field` 的实例 * @return boolean */ checkFields() { return this.isFields; } } const defaultOptions$2 = { globalAlpha: 0.9, // 全局透明度 lineWidth: 1, // 线条宽度 colorScale: "#fff", velocityScale: 1 / 25, // particleAge: 90, maxAge: 90, // alias for particleAge // particleMultiplier: 1 / 300, // TODO: PATHS = Math.round(width * height * particleMultiplier); paths: 800, frameRate: 20, useCoordsDraw: true }; function indexFor(m, min, max, colorScale) { return Math.max(0, Math.min(colorScale.length - 1, Math.round((m - min) / (max - min) * (colorScale.length - 1)))); } class WindCore { constructor(ctx, options, field) { this.particles = []; this.generated = false; this.ctx = ctx; if (!this.ctx) { throw new Error("ctx error"); } this.animate = this.animate.bind(this); this.setOptions(options); if (field) { this.updateData(field); } } static { this.Field = Field; } /** * 设置配置项 * @param options */ setOptions(options) { this.options = { ...defaultOptions$2, ...options }; const { width, height } = this.ctx.canvas; if ("particleAge" in options && !("maxAge" in options) && isNumber$1(this.options.particleAge)) { this.options.maxAge = this.options.particleAge; } if ("particleMultiplier" in options && !("paths" in options) && isNumber$1(this.options.particleMultiplier)) { this.options.paths = Math.round(width * height * this.options.particleMultiplier); } this.prerender(); } /** * 获取配置项 */ getOptions() { return this.options; } /** * 更新数据 * @param field */ updateData(field) { this.field = field; if (!this.generated) { return; } this.particles = this.prepareParticlePaths(); } // @ts-ignore project(...args) { throw new Error("project must be overriden"); } // @ts-ignore unproject(...args) { throw new Error("unproject must be overriden"); } /** * 判断位置是否在当前视窗内 * @param coordinates */ intersectsCoordinate(coordinates) { throw new Error("must be overriden"); } /** * 清空当前画布 */ clearCanvas() { this.stop(); this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); this.forceStop = false; } isStop() { return !this.starting; } /** * 启动粒子动画 */ start() { this.starting = true; this.forceStop = false; this.then = Date.now(); this.animate(); } /** * 停止粒子动画 */ stop() { cancelAnimationFrame(this.animationLoop); this.starting = false; this.forceStop = true; } animate() { if (this.animationLoop) { cancelAnimationFrame(this.animationLoop); } this.animationLoop = requestAnimationFrame(this.animate); const now = Date.now(); const delta = now - this.then; if (delta > this.options.frameRate) { this.then = now - delta % this.options.frameRate; this.render(); } } /** * 渲染前处理 */ prerender() { this.generated = false; if (!this.field) { return; } this.particles = this.prepareParticlePaths(); this.generated = true; if (!this.starting && !this.forceStop) { this.starting = true; this.then = Date.now(); this.animate(); } } /** * 开始渲染 */ render() { this.moveParticles(); this.drawParticles(); this.postrender(); } /** * each frame render end */ postrender() { } moveParticles() { const { width, height } = this.ctx.canvas; const particles = this.particles; const maxAge = this.options.maxAge; const velocityScale = isFunction$1(this.options.velocityScale) ? this.options.velocityScale() : this.options.velocityScale; let i = 0; const len = particles.length; for (; i < len; i++) { const particle = particles[i]; if (particle.age > maxAge) { particle.age = 0; this.field.randomize(particle, width, height, this.unproject); } const x = particle.x; const y = particle.y; const vector = this.field.interpolatedValueAt(x, y); if (vector === null) { particle.age = maxAge; } else { const xt = x + vector.u * velocityScale; const yt = y + vector.v * velocityScale; if (this.field.hasValueAt(xt, yt)) { particle.xt = xt; particle.yt = yt; particle.m = vector.m; } else { particle.x = xt; particle.y = yt; particle.age = maxAge; } } particle.age++; } } fadeIn() { const prev = this.ctx.globalCompositeOperation; this.ctx.globalCompositeOperation = "destination-in"; this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); this.ctx.globalCompositeOperation = prev; } drawParticles() { const particles = this.particles; this.fadeIn(); this.ctx.globalAlpha = this.options.globalAlpha; this.ctx.fillStyle = `rgba(0, 0, 0, ${this.options.globalAlpha})`; this.ctx.lineWidth = isNumber$1(this.options.lineWidth) ? this.options.lineWidth : 1; this.ctx.strokeStyle = isString$1(this.options.colorScale) ? this.options.colorScale : "#fff"; let i = 0; const len = particles.length; if (this.field && len > 0) { let min; let max; if (isValide(this.options.minVelocity) && isValide(this.options.maxVelocity)) { min = this.options.minVelocity; max = this.options.maxVelocity; } else { [min, max] = this.field.range; } for (; i < len; i++) { this[this.options.useCoordsDraw ? "drawCoordsParticle" : "drawPixelParticle"](particles[i], min, max); } } } /** * 用于绘制像素粒子 * @param particle * @param min * @param max */ drawPixelParticle(particle, min, max) { const pointPrev = [particle.x, particle.y]; const pointNext = [particle.xt, particle.yt]; if (pointNext && pointPrev && isValide(pointNext[0]) && isValide(pointNext[1]) && isValide(pointPrev[0]) && isValide(pointPrev[1]) && particle.age <= this.options.maxAge) { this.ctx.beginPath(); this.ctx.moveTo(pointPrev[0], pointPrev[1]); this.ctx.lineTo(pointNext[0], pointNext[1]); if (isFunction$1(this.options.colorScale)) { this.ctx.strokeStyle = this.options.colorScale(particle.m); } else if (Array.isArray(this.options.colorScale)) { const colorIdx = indexFor(particle.m, min, max, this.options.colorScale); this.ctx.strokeStyle = this.options.colorScale[colorIdx]; } if (isFunction$1(this.options.lineWidth)) { this.ctx.lineWidth = this.options.lineWidth(particle.m); } particle.x = particle.xt; particle.y = particle.yt; this.ctx.stroke(); } } /** * 用于绘制坐标粒子 * @param particle * @param min * @param max */ drawCoordsParticle(particle, min, max) { const source = [particle.x, particle.y]; const target = [particle.xt, particle.yt]; if (target && source && isValide(target[0]) && isValide(target[1]) && isValide(source[0]) && isValide(source[1]) && this.intersectsCoordinate(target) && particle.age <= this.options.maxAge) { const pointPrev = this.project(source); const pointNext = this.project(target); if (pointPrev && pointNext) { this.ctx.beginPath(); this.ctx.moveTo(pointPrev[0], pointPrev[1]); this.ctx.lineTo(pointNext[0], pointNext[1]); particle.x = particle.xt; particle.y = particle.yt; if (isFunction$1(this.options.colorScale)) { this.ctx.strokeStyle = this.options.colorScale(particle.m); } else if (Array.isArray(this.options.colorScale)) { const colorIdx = indexFor(particle.m, min, max, this.options.colorScale); this.ctx.strokeStyle = this.options.colorScale[colorIdx]; } if (isFunction$1(this.options.lineWidth)) { this.ctx.lineWidth = this.options.lineWidth(particle.m); } this.ctx.stroke(); } } } prepareParticlePaths() { const { width, height } = this.ctx.canvas; const particleCount = typeof this.options.paths === "function" ? this.options.paths(this) : this.options.paths; const particles = []; if (!this.field) { return []; } let i = 0; for (; i < particleCount; i++) { particles.push( this.field.randomize( { age: this.randomize() }, width, height, this.unproject ) ); } return particles; } randomize() { return Math.floor(Math.random() * this.options.maxAge); } } /** * Common utilities * @module glMatrix */ // Configuration Constants var EPSILON = 0.000001; var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array; /** * Sets the type of array used when creating new vectors and matrices * * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array */ function setMatrixArrayType(type) { ARRAY_TYPE = type; } if (!Math.hypot) Math.hypot = function () { var y = 0, i = arguments.length; while (i--) { y += arguments[i] * arguments[i]; } return Math.sqrt(y); }; /** * 3x3 Matrix * @module mat3 */ /** * Creates a new identity mat3 * * @returns {mat3} a new 3x3 matrix */ function create$4() { var out = new ARRAY_TYPE(9); if (ARRAY_TYPE != Float32Array) { out[1] = 0; out[2] = 0; out[3] = 0; out[5] = 0; out[6] = 0; out[7] = 0; } out[0] = 1; out[4] = 1; out[8] = 1; return out; } /** * Copies the upper-left 3x3 values into the given mat3. * * @param {mat3} out the receiving 3x3 matrix * @param {ReadonlyMat4} a the source 4x4 matrix * @returns {mat3} out */ function fromMat4(out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[4]; out[4] = a[5]; out[5] = a[6]; out[6] = a[8]; out[7] = a[9]; out[8] = a[10]; return out; } /** * Copy the values from one mat3 to another * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the source matrix * @returns {mat3} out */ function copy$3(out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; out[4] = a[4]; out[5] = a[5]; out[6] = a[6]; out[7] = a[7]; out[8] = a[8]; return out; } /** * Set the components of a mat3 to the given values * * @param {mat3} out the receiving matrix * @param {Number} m00 Component in column 0, row 0 position (index 0) * @param {Number} m01 Component in column 0, row 1 position (index 1) * @param {Number} m02 Component in column 0, row 2 position (index 2) * @param {Number} m10 Component in column 1, row 0 position (index 3) * @param {Number} m11 Component in column 1, row 1 position (index 4) * @param {Number} m12 Component in column 1, row 2 position (index 5) * @param {Number} m20 Component in column 2, row 0 position (index 6) * @param {Number} m21 Component in column 2, row 1 position (index 7) * @param {Number} m22 Component in column 2, row 2 position (index 8) * @returns {mat3} out */ function set$4(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) { out[0] = m00; out[1] = m01; out[2] = m02; out[3] = m10; out[4] = m11; out[5] = m12; out[6] = m20; out[7] = m21; out[8] = m22; return out; } /** * Set a mat3 to the identity matrix * * @param {mat3} out the receiving matrix * @returns {mat3} out */ function identity$1(out) { out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 1; out[5] = 0; out[6] = 0; out[7] = 0; out[8] = 1; return out; } /** * Transpose the values of a mat3 * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the source matrix * @returns {mat3} out */ function transpose$1(out, a) { // If we are transposing ourselves we can skip a few steps but have to cache some values if (out === a) { var a01 = a[1], a02 = a[2], a12 = a[5]; out[1] = a[3]; out[2] = a[6]; out[3] = a01; out[5] = a[7]; out[6] = a02; out[7] = a12; } else { out[0] = a[0]; out[1] = a[3]; out[2] = a[6]; out[3] = a[1]; out[4] = a[4]; out[5] = a[7]; out[6] = a[2]; out[7] = a[5]; out[8] = a[8]; } return out; } /** * Inverts a mat3 * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the source matrix * @returns {mat3} out */ function invert$2(out, a) { var a00 = a[0], a01 = a[1], a02 = a[2]; var a10 = a[3], a11 = a[4], a12 = a[5]; var a20 = a[6], a21 = a[7], a22 = a[8]; var b01 = a22 * a11 - a12 * a21; var b11 = -a22 * a10 + a12 * a20; var b21 = a21 * a10 - a11 * a20; // Calculate the determinant var det = a00 * b01 + a01 * b11 + a02 * b21; if (!det) { return null; } det = 1.0 / det; out[0] = b01 * det; out[1] = (-a22 * a01 + a02 * a21) * det; out[2] = (a12 * a01 - a02 * a11) * det; out[3] = b11 * det; out[4] = (a22 * a00 - a02 * a20) * det; out[5] = (-a12 * a00 + a02 * a10) * det; out[6] = b21 * det; out[7] = (-a21 * a00 + a01 * a20) * det; out[8] = (a11 * a00 - a01 * a10) * det; return out; } /** * Calculates the adjugate of a mat3 * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the source matrix * @returns {mat3} out */ function adjoint$1(out, a) { var a00 = a[0], a01 = a[1], a02 = a[2]; var a10 = a[3], a11 = a[4], a12 = a[5]; var a20 = a[6], a21 = a[7], a22 = a[8]; out[0] = a11 * a22 - a12 * a21; out[1] = a02 * a21 - a01 * a22; out[2] = a01 * a12 - a02 * a11; out[3] = a12 * a20 - a10 * a22; out[4] = a00 * a22 - a02 * a20; out[5] = a02 * a10 - a00 * a12; out[6] = a10 * a21 - a11 * a20; out[7] = a01 * a20 - a00 * a21; out[8] = a00 * a11 - a01 * a10; return out; } /** * Calculates the determinant of a mat3 * * @param {ReadonlyMat3} a the source matrix * @returns {Number} determinant of a */ function determinant$1(a) { var a00 = a[0], a01 = a[1], a02 = a[2]; var a10 = a[3], a11 = a[4], a12 = a[5]; var a20 = a[6], a21 = a[7], a22 = a[8]; return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); } /** * Multiplies two mat3's * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the first operand * @param {ReadonlyMat3} b the second operand * @returns {mat3} out */ function multiply$5(out, a, b) { var a00 = a[0], a01 = a[1], a02 = a[2]; var a10 = a[3], a11 = a[4], a12 = a[5]; var a20 = a[6], a21 = a[7], a22 = a[8]; var b00 = b[0], b01 = b[1], b02 = b[2]; var b10 = b[3], b11 = b[4], b12 = b[5]; var b20 = b[6], b21 = b[7], b22 = b[8]; out[0] = b00 * a00 + b01 * a10 + b02 * a20; out[1] = b00 * a01 + b01 * a11 + b02 * a21; out[2] = b00 * a02 + b01 * a12 + b02 * a22; out[3] = b10 * a00 + b11 * a10 + b12 * a20; out[4] = b10 * a01 + b11 * a11 + b12 * a21; out[5] = b10 * a02 + b11 * a12 + b12 * a22; out[6] = b20 * a00 + b21 * a10 + b22 * a20; out[7] = b20 * a01 + b21 * a11 + b22 * a21; out[8] = b20 * a02 + b21 * a12 + b22 * a22; return out; } /** * Translate a mat3 by the given vector * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the matrix to translate * @param {ReadonlyVec2} v vector to translate by * @returns {mat3} out */ function translate$1(out, a, v) { var a00 = a[0], a01 = a[1], a02 = a[2], a10 = a[3], a11 = a[4], a12 = a[5], a20 = a[6], a21 = a[7], a22 = a[8], x = v[0], y = v[1]; out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a10; out[4] = a11; out[5] = a12; out[6] = x * a00 + y * a10 + a20; out[7] = x * a01 + y * a11 + a21; out[8] = x * a02 + y * a12 + a22; return out; } /** * Rotates a mat3 by the given angle * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the matrix to rotate * @param {Number} rad the angle to rotate the matrix by * @returns {mat3} out */ function rotate$1(out, a, rad) { var a00 = a[0], a01 = a[1], a02 = a[2], a10 = a[3], a11 = a[4], a12 = a[5], a20 = a[6], a21 = a[7], a22 = a[8], s = Math.sin(rad), c = Math.cos(rad); out[0] = c * a00 + s * a10; out[1] = c * a01 + s * a11; out[2] = c * a02 + s * a12; out[3] = c * a10 - s * a00; out[4] = c * a11 - s * a01; out[5] = c * a12 - s * a02; out[6] = a20; out[7] = a21; out[8] = a22; return out; } /** * Scales the mat3 by the dimensions in the given vec2 * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the matrix to rotate * @param {ReadonlyVec2} v the vec2 to scale the matrix by * @returns {mat3} out **/ function scale$4(out, a, v) { var x = v[0], y = v[1]; out[0] = x * a[0]; out[1] = x * a[1]; out[2] = x * a[2]; out[3] = y * a[3]; out[4] = y * a[4]; out[5] = y * a[5]; out[6] = a[6]; out[7] = a[7]; out[8] = a[8]; return out; } /** * Creates a matrix from a vector translation * This is equivalent to (but much faster than): * * mat3.identity(dest); * mat3.translate(dest, dest, vec); * * @param {mat3} out mat3 receiving operation result * @param {ReadonlyVec2} v Translation vector * @returns {mat3} out */ function fromTranslation$1(out, v) { out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 1; out[5] = 0; out[6] = v[0]; out[7] = v[1]; out[8] = 1; return out; } /** * Creates a matrix from a given angle * This is equivalent to (but much faster than): * * mat3.identity(dest); * mat3.rotate(dest, dest, rad); * * @param {mat3} out mat3 receiving operation result * @param {Number} rad the angle to rotate the matrix by * @returns {mat3} out */ function fromRotation$1(out, rad) { var s = Math.sin(rad), c = Math.cos(rad); out[0] = c; out[1] = s; out[2] = 0; out[3] = -s; out[4] = c; out[5] = 0; out[6] = 0; out[7] = 0; out[8] = 1; return out; } /** * Creates a matrix from a vector scaling * This is equivalent to (but much faster than): * * mat3.identity(dest); * mat3.scale(dest, dest, vec); * * @param {mat3} out mat3 receiving operation result * @param {ReadonlyVec2} v Scaling vector * @returns {mat3} out */ function fromScaling$1(out, v) { out[0] = v[0]; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = v[1]; out[5] = 0; out[6] = 0; out[7] = 0; out[8] = 1; return out; } /** * Calculates a 3x3 matrix from the given quaternion * * @param {mat3} out mat3 receiving operation result * @param {ReadonlyQuat} q Quaternion to create matrix from * * @returns {mat3} out */ function fromQuat$1(out, q) { var x = q[0], y = q[1], z = q[2], w = q[3]; var x2 = x + x; var y2 = y + y; var z2 = z + z; var xx = x * x2; var yx = y * x2; var yy = y * y2; var zx = z * x2; var zy = z * y2; var zz = z * z2; var wx = w * x2; var wy = w * y2; var wz = w * z2; out[0] = 1 - yy - zz; out[3] = yx - wz; out[6] = zx + wy; out[1] = yx + wz; out[4] = 1 - xx - zz; out[7] = zy - wx; out[2] = zx - wy; out[5] = zy + wx; out[8] = 1 - xx - yy; return out; } /** * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix * * @param {mat3} out mat3 receiving operation result * @param {ReadonlyMat4} a Mat4 to derive the normal matrix from * * @returns {mat3} out */ function normalFromMat4(out, a) { var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; var b00 = a00 * a11 - a01 * a10; var b01 = a00 * a12 - a02 * a10; var b02 = a00 * a13 - a03 * a10; var b03 = a01 * a12 - a02 * a11; var b04 = a01 * a13 - a03 * a11; var b05 = a02 * a13 - a03 * a12; var b06 = a20 * a31 - a21 * a30; var b07 = a20 * a32 - a22 * a30; var b08 = a20 * a33 - a23 * a30; var b09 = a21 * a32 - a22 * a31; var b10 = a21 * a33 - a23 * a31; var b11 = a22 * a33 - a23 * a32; // Calculate the determinant var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1.0 / det; out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; return out; } /** * Returns Frobenius norm of a mat3 * * @param {ReadonlyMat3} a the matrix to calculate Frobenius norm of * @returns {Number} Frobenius norm */ function frob(a) { return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]); } /** * Adds two mat3's * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the first operand * @param {ReadonlyMat3} b the second operand * @returns {mat3} out */ function add$4(out, a, b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; out[2] = a[2] + b[2]; out[3] = a[3] + b[3]; out[4] = a[4] + b[4]; out[5] = a[5] + b[5]; out[6] = a[6] + b[6]; out[7] = a[7] + b[7]; out[8] = a[8] + b[8]; return out; } /** * Subtracts matrix b from matrix a * * @param {mat3} out the receiving matrix * @param {ReadonlyMat3} a the first operand * @param {ReadonlyMat3} b the second operand * @returns {mat3} out */ function subtract$4(out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; out[3] = a[3] - b[3]; out[4] = a[4] - b[4]; out[5] = a[5] - b[5]; out[6] = a[6] - b[6]; out[7] = a[7] - b[7]; out[8] = a[8] - b[8]; return out; } /** * Returns whether or not the matrices have approximately the same elements in the same position. * * @param {ReadonlyMat3} a The first matrix. * @param {ReadonlyMat3} b The second matrix. * @returns {Boolean} True if the matrices are equal, false otherwise. */ function equals$5(a, b) { var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7], a8 = a[8]; var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8]; return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8)); } /** * Copy the values from one mat4 to another * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the source matrix * @returns {mat4} out */ function copy$2(out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; out[4] = a[4]; out[5] = a[5]; out[6] = a[6]; out[7] = a[7]; out[8] = a[8]; out[9] = a[9]; out[10] = a[10]; out[11] = a[11]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; return out; } /** * Set the components of a mat4 to the given values * * @param {mat4} out the receiving matrix * @param {Number} m00 Component in column 0, row 0 position (index 0) * @param {Number} m01 Component in column 0, row 1 position (index 1) * @param {Number} m02 Component in column 0, row 2 position (index 2) * @param {Number} m03 Component in column 0, row 3 position (index 3) * @param {Number} m10 Component in column 1, row 0 position (index 4) * @param {Number} m11 Component in column 1, row 1 position (index 5) * @param {Number} m12 Component in column 1, row 2 position (index 6) * @param {Number} m13 Component in column 1, row 3 position (index 7) * @param {Number} m20 Component in column 2, row 0 position (index 8) * @param {Number} m21 Component in column 2, row 1 position (index 9) * @param {Number} m22 Component in column 2, row 2 position (index 10) * @param {Number} m23 Component in column 2, row 3 position (index 11) * @param {Number} m30 Component in column 3, row 0 position (index 12) * @param {Number} m31 Component in column 3, row 1 position (index 13) * @param {Number} m32 Component in column 3, row 2 position (index 14) * @param {Number} m33 Component in column 3, row 3 position (index 15) * @returns {mat4} out */ function set$3(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { out[0] = m00; out[1] = m01; out[2] = m02; out[3] = m03; out[4] = m10; out[5] = m11; out[6] = m12; out[7] = m13; out[8] = m20; out[9] = m21; out[10] = m22; out[11] = m23; out[12] = m30; out[13] = m31; out[14] = m32; out[15] = m33; return out; } /** * Set a mat4 to the identity matrix * * @param {mat4} out the receiving matrix * @returns {mat4} out */ function identity(out) { out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = 1; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 1; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } /** * Transpose the values of a mat4 * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the source matrix * @returns {mat4} out */ function transpose(out, a) { // If we are transposing ourselves we can skip a few steps but have to cache some values if (out === a) { var a01 = a[1], a02 = a[2], a03 = a[3]; var a12 = a[6], a13 = a[7]; var a23 = a[11]; out[1] = a[4]; out[2] = a[8]; out[3] = a[12]; out[4] = a01; out[6] = a[9]; out[7] = a[13]; out[8] = a02; out[9] = a12; out[11] = a[14]; out[12] = a03; out[13] = a13; out[14] = a23; } else { out[0] = a[0]; out[1] = a[4]; out[2] = a[8]; out[3] = a[12]; out[4] = a[1]; out[5] = a[5]; out[6] = a[9]; out[7] = a[13]; out[8] = a[2]; out[9] = a[6]; out[10] = a[10]; out[11] = a[14]; out[12] = a[3]; out[13] = a[7]; out[14] = a[11]; out[15] = a[15]; } return out; } /** * Inverts a mat4 * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the source matrix * @returns {mat4} out */ function invert$1(out, a) { var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; var b00 = a00 * a11 - a01 * a10; var b01 = a00 * a12 - a02 * a10; var b02 = a00 * a13 - a03 * a10; var b03 = a01 * a12 - a02 * a11; var b04 = a01 * a13 - a03 * a11; var b05 = a02 * a13 - a03 * a12; var b06 = a20 * a31 - a21 * a30; var b07 = a20 * a32 - a22 * a30; var b08 = a20 * a33 - a23 * a30; var b09 = a21 * a32 - a22 * a31; var b10 = a21 * a33 - a23 * a31; var b11 = a22 * a33 - a23 * a32; // Calculate the determinant var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1.0 / det; out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; return out; } /** * Calculates the adjugate of a mat4 * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the source matrix * @returns {mat4} out */ function adjoint(out, a) { var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; out[0] = a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22); out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); out[2] = a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12); out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); out[5] = a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22); out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); out[7] = a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12); out[8] = a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21); out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); out[10] = a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11); out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); out[13] = a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21); out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); out[15] = a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11); return out; } /** * Calculates the determinant of a mat4 * * @param {ReadonlyMat4} a the source matrix * @returns {Number} determinant of a */ function determinant(a) { var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; var b00 = a00 * a11 - a01 * a10; var b01 = a00 * a12 - a02 * a10; var b02 = a00 * a13 - a03 * a10; var b03 = a01 * a12 - a02 * a11; var b04 = a01 * a13 - a03 * a11; var b05 = a02 * a13 - a03 * a12; var b06 = a20 * a31 - a21 * a30; var b07 = a20 * a32 - a22 * a30; var b08 = a20 * a33 - a23 * a30; var b09 = a21 * a32 - a22 * a31; var b10 = a21 * a33 - a23 * a31; var b11 = a22 * a33 - a23 * a32; // Calculate the determinant return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; } /** * Multiplies two mat4s * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the first operand * @param {ReadonlyMat4} b the second operand * @returns {mat4} out */ function multiply$4(out, a, b) { var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; // Cache only the current line of the second matrix var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; return out; } /** * Translate a mat4 by the given vector * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the matrix to translate * @param {ReadonlyVec3} v vector to translate by * @returns {mat4} out */ function translate(out, a, v) { var x = v[0], y = v[1], z = v[2]; var a00, a01, a02, a03; var a10, a11, a12, a13; var a20, a21, a22, a23; if (a === out) { out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; } else { a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; out[12] = a00 * x + a10 * y + a20 * z + a[12]; out[13] = a01 * x + a11 * y + a21 * z + a[13]; out[14] = a02 * x + a12 * y + a22 * z + a[14]; out[15] = a03 * x + a13 * y + a23 * z + a[15]; } return out; } /** * Scales the mat4 by the dimensions in the given vec3 not using vectorization * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the matrix to scale * @param {ReadonlyVec3} v the vec3 to scale the matrix by * @returns {mat4} out **/ function scale$3(out, a, v) { var x = v[0], y = v[1], z = v[2]; out[0] = a[0] * x; out[1] = a[1] * x; out[2] = a[2] * x; out[3] = a[3] * x; out[4] = a[4] * y; out[5] = a[5] * y; out[6] = a[6] * y; out[7] = a[7] * y; out[8] = a[8] * z; out[9] = a[9] * z; out[10] = a[10] * z; out[11] = a[11] * z; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; return out; } /** * Rotates a mat4 by the given angle around the given axis * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the matrix to rotate * @param {Number} rad the angle to rotate the matrix by * @param {ReadonlyVec3} axis the axis to rotate around * @returns {mat4} out */ function rotate(out, a, rad, axis) { var x = axis[0], y = axis[1], z = axis[2]; var len = Math.hypot(x, y, z); var s, c, t; var a00, a01, a02, a03; var a10, a11, a12, a13; var a20, a21, a22, a23; var b00, b01, b02; var b10, b11, b12; var b20, b21, b22; if (len < EPSILON) { return null; } len = 1 / len; x *= len; y *= len; z *= len; s = Math.sin(rad); c = Math.cos(rad); t = 1 - c; a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; // Construct the elements of the rotation matrix b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; // Perform rotation-specific matrix multiplication out[0] = a00 * b00 + a10 * b01 + a20 * b02; out[1] = a01 * b00 + a11 * b01 + a21 * b02; out[2] = a02 * b00 + a12 * b01 + a22 * b02; out[3] = a03 * b00 + a13 * b01 + a23 * b02; out[4] = a00 * b10 + a10 * b11 + a20 * b12; out[5] = a01 * b10 + a11 * b11 + a21 * b12; out[6] = a02 * b10 + a12 * b11 + a22 * b12; out[7] = a03 * b10 + a13 * b11 + a23 * b12; out[8] = a00 * b20 + a10 * b21 + a20 * b22; out[9] = a01 * b20 + a11 * b21 + a21 * b22; out[10] = a02 * b20 + a12 * b21 + a22 * b22; out[11] = a03 * b20 + a13 * b21 + a23 * b22; if (a !== out) { // If the source and destination differ, copy the unchanged last row out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } return out; } /** * Rotates a matrix by the given angle around the X axis * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the matrix to rotate * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ function rotateX(out, a, rad) { var s = Math.sin(rad); var c = Math.cos(rad); var a10 = a[4]; var a11 = a[5]; var a12 = a[6]; var a13 = a[7]; var a20 = a[8]; var a21 = a[9]; var a22 = a[10]; var a23 = a[11]; if (a !== out) { // If the source and destination differ, copy the unchanged rows out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } // Perform axis-specific matrix multiplication out[4] = a10 * c + a20 * s; out[5] = a11 * c + a21 * s; out[6] = a12 * c + a22 * s; out[7] = a13 * c + a23 * s; out[8] = a20 * c - a10 * s; out[9] = a21 * c - a11 * s; out[10] = a22 * c - a12 * s; out[11] = a23 * c - a13 * s; return out; } /** * Rotates a matrix by the given angle around the Y axis * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the matrix to rotate * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ function rotateY(out, a, rad) { var s = Math.sin(rad); var c = Math.cos(rad); var a00 = a[0]; var a01 = a[1]; var a02 = a[2]; var a03 = a[3]; var a20 = a[8]; var a21 = a[9]; var a22 = a[10]; var a23 = a[11]; if (a !== out) { // If the source and destination differ, copy the unchanged rows out[4] = a[4]; out[5] = a[5]; out[6] = a[6]; out[7] = a[7]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } // Perform axis-specific matrix multiplication out[0] = a00 * c - a20 * s; out[1] = a01 * c - a21 * s; out[2] = a02 * c - a22 * s; out[3] = a03 * c - a23 * s; out[8] = a00 * s + a20 * c; out[9] = a01 * s + a21 * c; out[10] = a02 * s + a22 * c; out[11] = a03 * s + a23 * c; return out; } /** * Rotates a matrix by the given angle around the Z axis * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the matrix to rotate * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ function rotateZ(out, a, rad) { var s = Math.sin(rad); var c = Math.cos(rad); var a00 = a[0]; var a01 = a[1]; var a02 = a[2]; var a03 = a[3]; var a10 = a[4]; var a11 = a[5]; var a12 = a[6]; var a13 = a[7]; if (a !== out) { // If the source and destination differ, copy the unchanged last row out[8] = a[8]; out[9] = a[9]; out[10] = a[10]; out[11] = a[11]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } // Perform axis-specific matrix multiplication out[0] = a00 * c + a10 * s; out[1] = a01 * c + a11 * s; out[2] = a02 * c + a12 * s; out[3] = a03 * c + a13 * s; out[4] = a10 * c - a00 * s; out[5] = a11 * c - a01 * s; out[6] = a12 * c - a02 * s; out[7] = a13 * c - a03 * s; return out; } /** * Creates a matrix from a vector translation * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.translate(dest, dest, vec); * * @param {mat4} out mat4 receiving operation result * @param {ReadonlyVec3} v Translation vector * @returns {mat4} out */ function fromTranslation(out, v) { out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = 1; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 1; out[11] = 0; out[12] = v[0]; out[13] = v[1]; out[14] = v[2]; out[15] = 1; return out; } /** * Creates a matrix from a vector scaling * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.scale(dest, dest, vec); * * @param {mat4} out mat4 receiving operation result * @param {ReadonlyVec3} v Scaling vector * @returns {mat4} out */ function fromScaling(out, v) { out[0] = v[0]; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = v[1]; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = v[2]; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } /** * Creates a matrix from a given angle around a given axis * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.rotate(dest, dest, rad, axis); * * @param {mat4} out mat4 receiving operation result * @param {Number} rad the angle to rotate the matrix by * @param {ReadonlyVec3} axis the axis to rotate around * @returns {mat4} out */ function fromRotation(out, rad, axis) { var x = axis[0], y = axis[1], z = axis[2]; var len = Math.hypot(x, y, z); var s, c, t; if (len < EPSILON) { return null; } len = 1 / len; x *= len; y *= len; z *= len; s = Math.sin(rad); c = Math.cos(rad); t = 1 - c; // Perform rotation-specific matrix multiplication out[0] = x * x * t + c; out[1] = y * x * t + z * s; out[2] = z * x * t - y * s; out[3] = 0; out[4] = x * y * t - z * s; out[5] = y * y * t + c; out[6] = z * y * t + x * s; out[7] = 0; out[8] = x * z * t + y * s; out[9] = y * z * t - x * s; out[10] = z * z * t + c; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } /** * Creates a matrix from the given angle around the X axis * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.rotateX(dest, dest, rad); * * @param {mat4} out mat4 receiving operation result * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ function fromXRotation(out, rad) { var s = Math.sin(rad); var c = Math.cos(rad); // Perform axis-specific matrix multiplication out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = c; out[6] = s; out[7] = 0; out[8] = 0; out[9] = -s; out[10] = c; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } /** * Creates a matrix from the given angle around the Y axis * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.rotateY(dest, dest, rad); * * @param {mat4} out mat4 receiving operation result * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ function fromYRotation(out, rad) { var s = Math.sin(rad); var c = Math.cos(rad); // Perform axis-specific matrix multiplication out[0] = c; out[1] = 0; out[2] = -s; out[3] = 0; out[4] = 0; out[5] = 1; out[6] = 0; out[7] = 0; out[8] = s; out[9] = 0; out[10] = c; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } /** * Creates a matrix from the given angle around the Z axis * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.rotateZ(dest, dest, rad); * * @param {mat4} out mat4 receiving operation result * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ function fromZRotation(out, rad) { var s = Math.sin(rad); var c = Math.cos(rad); // Perform axis-specific matrix multiplication out[0] = c; out[1] = s; out[2] = 0; out[3] = 0; out[4] = -s; out[5] = c; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 1; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } /** * Creates a matrix from a quaternion rotation and vector translation * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.translate(dest, vec); * let quatMat = mat4.create(); * quat4.toMat4(quat, quatMat); * mat4.multiply(dest, quatMat); * * @param {mat4} out mat4 receiving operation result * @param {quat4} q Rotation quaternion * @param {ReadonlyVec3} v Translation vector * @returns {mat4} out */ function fromRotationTranslation(out, q, v) { // Quaternion math var x = q[0], y = q[1], z = q[2], w = q[3]; var x2 = x + x; var y2 = y + y; var z2 = z + z; var xx = x * x2; var xy = x * y2; var xz = x * z2; var yy = y * y2; var yz = y * z2; var zz = z * z2; var wx = w * x2; var wy = w * y2; var wz = w * z2; out[0] = 1 - (yy + zz); out[1] = xy + wz; out[2] = xz - wy; out[3] = 0; out[4] = xy - wz; out[5] = 1 - (xx + zz); out[6] = yz + wx; out[7] = 0; out[8] = xz + wy; out[9] = yz - wx; out[10] = 1 - (xx + yy); out[11] = 0; out[12] = v[0]; out[13] = v[1]; out[14] = v[2]; out[15] = 1; return out; } /** * Returns the translation vector component of a transformation * matrix. If a matrix is built with fromRotationTranslation, * the returned vector will be the same as the translation vector * originally supplied. * @param {vec3} out Vector to receive translation component * @param {ReadonlyMat4} mat Matrix to be decomposed (input) * @return {vec3} out */ function getTranslation(out, mat) { out[0] = mat[12]; out[1] = mat[13]; out[2] = mat[14]; return out; } /** * Returns the scaling factor component of a transformation * matrix. If a matrix is built with fromRotationTranslationScale * with a normalized Quaternion paramter, the returned vector will be * the same as the scaling vector * originally supplied. * @param {vec3} out Vector to receive scaling factor component * @param {ReadonlyMat4} mat Matrix to be decomposed (input) * @return {vec3} out */ function getScaling(out, mat) { var m11 = mat[0]; var m12 = mat[1]; var m13 = mat[2]; var m21 = mat[4]; var m22 = mat[5]; var m23 = mat[6]; var m31 = mat[8]; var m32 = mat[9]; var m33 = mat[10]; out[0] = Math.hypot(m11, m12, m13); out[1] = Math.hypot(m21, m22, m23); out[2] = Math.hypot(m31, m32, m33); return out; } /** * Returns a quaternion representing the rotational component * of a transformation matrix. If a matrix is built with * fromRotationTranslation, the returned quaternion will be the * same as the quaternion originally supplied. * @param {quat} out Quaternion to receive the rotation component * @param {ReadonlyMat4} mat Matrix to be decomposed (input) * @return {quat} out */ function getRotation(out, mat) { var scaling = new ARRAY_TYPE(3); getScaling(scaling, mat); var is1 = 1 / scaling[0]; var is2 = 1 / scaling[1]; var is3 = 1 / scaling[2]; var sm11 = mat[0] * is1; var sm12 = mat[1] * is2; var sm13 = mat[2] * is3; var sm21 = mat[4] * is1; var sm22 = mat[5] * is2; var sm23 = mat[6] * is3; var sm31 = mat[8] * is1; var sm32 = mat[9] * is2; var sm33 = mat[10] * is3; var trace = sm11 + sm22 + sm33; var S = 0; if (trace > 0) { S = Math.sqrt(trace + 1.0) * 2; out[3] = 0.25 * S; out[0] = (sm23 - sm32) / S; out[1] = (sm31 - sm13) / S; out[2] = (sm12 - sm21) / S; } else if (sm11 > sm22 && sm11 > sm33) { S = Math.sqrt(1.0 + sm11 - sm22 - sm33) * 2; out[3] = (sm23 - sm32) / S; out[0] = 0.25 * S; out[1] = (sm12 + sm21) / S; out[2] = (sm31 + sm13) / S; } else if (sm22 > sm33) { S = Math.sqrt(1.0 + sm22 - sm11 - sm33) * 2; out[3] = (sm31 - sm13) / S; out[0] = (sm12 + sm21) / S; out[1] = 0.25 * S; out[2] = (sm23 + sm32) / S; } else { S = Math.sqrt(1.0 + sm33 - sm11 - sm22) * 2; out[3] = (sm12 - sm21) / S; out[0] = (sm31 + sm13) / S; out[1] = (sm23 + sm32) / S; out[2] = 0.25 * S; } return out; } /** * Creates a matrix from a quaternion rotation, vector translation and vector scale * This is equivalent to (but much faster than): * * mat4.identity(dest); * mat4.translate(dest, vec); * let quatMat = mat4.create(); * quat4.toMat4(quat, quatMat); * mat4.multiply(dest, quatMat); * mat4.scale(dest, scale) * * @param {mat4} out mat4 receiving operation result * @param {quat4} q Rotation quaternion * @param {ReadonlyVec3} v Translation vector * @param {ReadonlyVec3} s Scaling vector * @returns {mat4} out */ function fromRotationTranslationScale(out, q, v, s) { // Quaternion math var x = q[0], y = q[1], z = q[2], w = q[3]; var x2 = x + x; var y2 = y + y; var z2 = z + z; var xx = x * x2; var xy = x * y2; var xz = x * z2; var yy = y * y2; var yz = y * z2; var zz = z * z2; var wx = w * x2; var wy = w * y2; var wz = w * z2; var sx = s[0]; var sy = s[1]; var sz = s[2]; out[0] = (1 - (yy + zz)) * sx; out[1] = (xy + wz) * sx; out[2] = (xz - wy) * sx; out[3] = 0; out[4] = (xy - wz) * sy; out[5] = (1 - (xx + zz)) * sy; out[6] = (yz + wx) * sy; out[7] = 0; out[8] = (xz + wy) * sz; out[9] = (yz - wx) * sz; out[10] = (1 - (xx + yy)) * sz; out[11] = 0; out[12] = v[0]; out[13] = v[1]; out[14] = v[2]; out[15] = 1; return out; } /** * Calculates a 4x4 matrix from the given quaternion * * @param {mat4} out mat4 receiving operation result * @param {ReadonlyQuat} q Quaternion to create matrix from * * @returns {mat4} out */ function fromQuat(out, q) { var x = q[0], y = q[1], z = q[2], w = q[3]; var x2 = x + x; var y2 = y + y; var z2 = z + z; var xx = x * x2; var yx = y * x2; var yy = y * y2; var zx = z * x2; var zy = z * y2; var zz = z * z2; var wx = w * x2; var wy = w * y2; var wz = w * z2; out[0] = 1 - yy - zz; out[1] = yx + wz; out[2] = zx - wy; out[3] = 0; out[4] = yx - wz; out[5] = 1 - xx - zz; out[6] = zy + wx; out[7] = 0; out[8] = zx + wy; out[9] = zy - wx; out[10] = 1 - xx - yy; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } /** * Generates a frustum matrix with the given bounds * * @param {mat4} out mat4 frustum matrix will be written into * @param {Number} left Left bound of the frustum * @param {Number} right Right bound of the frustum * @param {Number} bottom Bottom bound of the frustum * @param {Number} top Top bound of the frustum * @param {Number} near Near bound of the frustum * @param {Number} far Far bound of the frustum * @returns {mat4} out */ function frustum(out, left, right, bottom, top, near, far) { var rl = 1 / (right - left); var tb = 1 / (top - bottom); var nf = 1 / (near - far); out[0] = near * 2 * rl; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = near * 2 * tb; out[6] = 0; out[7] = 0; out[8] = (right + left) * rl; out[9] = (top + bottom) * tb; out[10] = (far + near) * nf; out[11] = -1; out[12] = 0; out[13] = 0; out[14] = far * near * 2 * nf; out[15] = 0; return out; } /** * Generates a perspective projection matrix with the given bounds. * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1], * which matches WebGL/OpenGL's clip volume. * Passing null/undefined/no value for far will generate infinite projection matrix. * * @param {mat4} out mat4 frustum matrix will be written into * @param {number} fovy Vertical field of view in radians * @param {number} aspect Aspect ratio. typically viewport width/height * @param {number} near Near bound of the frustum * @param {number} far Far bound of the frustum, can be null or Infinity * @returns {mat4} out */ function perspectiveNO(out, fovy, aspect, near, far) { var f = 1.0 / Math.tan(fovy / 2), nf; out[0] = f / aspect; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = f; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[11] = -1; out[12] = 0; out[13] = 0; out[15] = 0; if (far != null && far !== Infinity) { nf = 1 / (near - far); out[10] = (far + near) * nf; out[14] = 2 * far * near * nf; } else { out[10] = -1; out[14] = -2 * near; } return out; } /** * Alias for {@link mat4.perspectiveNO} * @function */ var perspective = perspectiveNO; /** * Generates a orthogonal projection matrix with the given bounds. * The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1], * which matches WebGL/OpenGL's clip volume. * * @param {mat4} out mat4 frustum matrix will be written into * @param {number} left Left bound of the frustum * @param {number} right Right bound of the frustum * @param {number} bottom Bottom bound of the frustum * @param {number} top Top bound of the frustum * @param {number} near Near bound of the frustum * @param {number} far Far bound of the frustum * @returns {mat4} out */ function orthoNO(out, left, right, bottom, top, near, far) { var lr = 1 / (left - right); var bt = 1 / (bottom - top); var nf = 1 / (near - far); out[0] = -2 * lr; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = -2 * bt; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 2 * nf; out[11] = 0; out[12] = (left + right) * lr; out[13] = (top + bottom) * bt; out[14] = (far + near) * nf; out[15] = 1; return out; } /** * Alias for {@link mat4.orthoNO} * @function */ var ortho = orthoNO; /** * Generates a look-at matrix with the given eye position, focal point, and up axis. * If you want a matrix that actually makes an object look at another object, you should use targetTo instead. * * @param {mat4} out mat4 frustum matrix will be written into * @param {ReadonlyVec3} eye Position of the viewer * @param {ReadonlyVec3} center Point the viewer is looking at * @param {ReadonlyVec3} up vec3 pointing up * @returns {mat4} out */ function lookAt(out, eye, center, up) { var x0, x1, x2, y0, y1, y2, z0, z1, z2, len; var eyex = eye[0]; var eyey = eye[1]; var eyez = eye[2]; var upx = up[0]; var upy = up[1]; var upz = up[2]; var centerx = center[0]; var centery = center[1]; var centerz = center[2]; if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) { return identity(out); } z0 = eyex - centerx; z1 = eyey - centery; z2 = eyez - centerz; len = 1 / Math.hypot(z0, z1, z2); z0 *= len; z1 *= len; z2 *= len; x0 = upy * z2 - upz * z1; x1 = upz * z0 - upx * z2; x2 = upx * z1 - upy * z0; len = Math.hypot(x0, x1, x2); if (!len) { x0 = 0; x1 = 0; x2 = 0; } else { len = 1 / len; x0 *= len; x1 *= len; x2 *= len; } y0 = z1 * x2 - z2 * x1; y1 = z2 * x0 - z0 * x2; y2 = z0 * x1 - z1 * x0; len = Math.hypot(y0, y1, y2); if (!len) { y0 = 0; y1 = 0; y2 = 0; } else { len = 1 / len; y0 *= len; y1 *= len; y2 *= len; } out[0] = x0; out[1] = y0; out[2] = z0; out[3] = 0; out[4] = x1; out[5] = y1; out[6] = z1; out[7] = 0; out[8] = x2; out[9] = y2; out[10] = z2; out[11] = 0; out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); out[15] = 1; return out; } /** * Adds two mat4's * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the first operand * @param {ReadonlyMat4} b the second operand * @returns {mat4} out */ function add$3(out, a, b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; out[2] = a[2] + b[2]; out[3] = a[3] + b[3]; out[4] = a[4] + b[4]; out[5] = a[5] + b[5]; out[6] = a[6] + b[6]; out[7] = a[7] + b[7]; out[8] = a[8] + b[8]; out[9] = a[9] + b[9]; out[10] = a[10] + b[10]; out[11] = a[11] + b[11]; out[12] = a[12] + b[12]; out[13] = a[13] + b[13]; out[14] = a[14] + b[14]; out[15] = a[15] + b[15]; return out; } /** * Subtracts matrix b from matrix a * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the first operand * @param {ReadonlyMat4} b the second operand * @returns {mat4} out */ function subtract$3(out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; out[3] = a[3] - b[3]; out[4] = a[4] - b[4]; out[5] = a[5] - b[5]; out[6] = a[6] - b[6]; out[7] = a[7] - b[7]; out[8] = a[8] - b[8]; out[9] = a[9] - b[9]; out[10] = a[10] - b[10]; out[11] = a[11] - b[11]; out[12] = a[12] - b[12]; out[13] = a[13] - b[13]; out[14] = a[14] - b[14]; out[15] = a[15] - b[15]; return out; } /** * Multiply each element of the matrix by a scalar. * * @param {mat4} out the receiving matrix * @param {ReadonlyMat4} a the matrix to scale * @param {Number} b amount to scale the matrix's elements by * @returns {mat4} out */ function multiplyScalar(out, a, b) { out[0] = a[0] * b; out[1] = a[1] * b; out[2] = a[2] * b; out[3] = a[3] * b; out[4] = a[4] * b; out[5] = a[5] * b; out[6] = a[6] * b; out[7] = a[7] * b; out[8] = a[8] * b; out[9] = a[9] * b; out[10] = a[10] * b; out[11] = a[11] * b; out[12] = a[12] * b; out[13] = a[13] * b; out[14] = a[14] * b; out[15] = a[15] * b; return out; } /** * Returns whether or not the matrices have approximately the same elements in the same position. * * @param {ReadonlyMat4} a The first matrix. * @param {ReadonlyMat4} b The second matrix. * @returns {Boolean} True if the matrices are equal, false otherwise. */ function equals$4(a, b) { var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; var a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7]; var a8 = a[8], a9 = a[9], a10 = a[10], a11 = a[11]; var a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15]; var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; var b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7]; var b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11]; var b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8)) && Math.abs(a9 - b9) <= EPSILON * Math.max(1.0, Math.abs(a9), Math.abs(b9)) && Math.abs(a10 - b10) <= EPSILON * Math.max(1.0, Math.abs(a10), Math.abs(b10)) && Math.abs(a11 - b11) <= EPSILON * Math.max(1.0, Math.abs(a11), Math.abs(b11)) && Math.abs(a12 - b12) <= EPSILON * Math.max(1.0, Math.abs(a12), Math.abs(b12)) && Math.abs(a13 - b13) <= EPSILON * Math.max(1.0, Math.abs(a13), Math.abs(b13)) && Math.abs(a14 - b14) <= EPSILON * Math.max(1.0, Math.abs(a14), Math.abs(b14)) && Math.abs(a15 - b15) <= EPSILON * Math.max(1.0, Math.abs(a15), Math.abs(b15)); } /** * 3 Dimensional Vector * @module vec3 */ /** * Creates a new, empty vec3 * * @returns {vec3} a new 3D vector */ function create$3() { var out = new ARRAY_TYPE(3); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; out[2] = 0; } return out; } /** * Calculates the length of a vec3 * * @param {ReadonlyVec3} a vector to calculate length of * @returns {Number} length of a */ function length$3(a) { var x = a[0]; var y = a[1]; var z = a[2]; return Math.hypot(x, y, z); } /** * Creates a new vec3 initialized with the given values * * @param {Number} x X component * @param {Number} y Y component * @param {Number} z Z component * @returns {vec3} a new 3D vector */ function fromValues(x, y, z) { var out = new ARRAY_TYPE(3); out[0] = x; out[1] = y; out[2] = z; return out; } /** * Set the components of a vec3 to the given values * * @param {vec3} out the receiving vector * @param {Number} x X component * @param {Number} y Y component * @param {Number} z Z component * @returns {vec3} out */ function set$2(out, x, y, z) { out[0] = x; out[1] = y; out[2] = z; return out; } /** * Adds two vec3's * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {vec3} out */ function add$2(out, a, b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; out[2] = a[2] + b[2]; return out; } /** * Subtracts vector b from vector a * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {vec3} out */ function subtract$2(out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; return out; } /** * Multiplies two vec3's * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {vec3} out */ function multiply$3(out, a, b) { out[0] = a[0] * b[0]; out[1] = a[1] * b[1]; out[2] = a[2] * b[2]; return out; } /** * Divides two vec3's * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {vec3} out */ function divide$2(out, a, b) { out[0] = a[0] / b[0]; out[1] = a[1] / b[1]; out[2] = a[2] / b[2]; return out; } /** * Scales a vec3 by a scalar number * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the vector to scale * @param {Number} b amount to scale the vector by * @returns {vec3} out */ function scale$2(out, a, b) { out[0] = a[0] * b; out[1] = a[1] * b; out[2] = a[2] * b; return out; } /** * Adds two vec3's after scaling the second operand by a scalar value * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @param {Number} scale the amount to scale b by before adding * @returns {vec3} out */ function scaleAndAdd$1(out, a, b, scale) { out[0] = a[0] + b[0] * scale; out[1] = a[1] + b[1] * scale; out[2] = a[2] + b[2] * scale; return out; } /** * Calculates the euclidian distance between two vec3's * * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {Number} distance between a and b */ function distance$2(a, b) { var x = b[0] - a[0]; var y = b[1] - a[1]; var z = b[2] - a[2]; return Math.hypot(x, y, z); } /** * Calculates the squared euclidian distance between two vec3's * * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {Number} squared distance between a and b */ function squaredDistance$2(a, b) { var x = b[0] - a[0]; var y = b[1] - a[1]; var z = b[2] - a[2]; return x * x + y * y + z * z; } /** * Negates the components of a vec3 * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a vector to negate * @returns {vec3} out */ function negate$2(out, a) { out[0] = -a[0]; out[1] = -a[1]; out[2] = -a[2]; return out; } /** * Returns the inverse of the components of a vec3 * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a vector to invert * @returns {vec3} out */ function inverse$2(out, a) { out[0] = 1.0 / a[0]; out[1] = 1.0 / a[1]; out[2] = 1.0 / a[2]; return out; } /** * Normalize a vec3 * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a vector to normalize * @returns {vec3} out */ function normalize$4(out, a) { var x = a[0]; var y = a[1]; var z = a[2]; var len = x * x + y * y + z * z; if (len > 0) { //TODO: evaluate use of glm_invsqrt here? len = 1 / Math.sqrt(len); } out[0] = a[0] * len; out[1] = a[1] * len; out[2] = a[2] * len; return out; } /** * Calculates the dot product of two vec3's * * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {Number} dot product of a and b */ function dot$3(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } /** * Computes the cross product of two vec3's * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @returns {vec3} out */ function cross$2(out, a, b) { var ax = a[0], ay = a[1], az = a[2]; var bx = b[0], by = b[1], bz = b[2]; out[0] = ay * bz - az * by; out[1] = az * bx - ax * bz; out[2] = ax * by - ay * bx; return out; } /** * Performs a linear interpolation between two vec3's * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the first operand * @param {ReadonlyVec3} b the second operand * @param {Number} t interpolation amount, in the range [0-1], between the two inputs * @returns {vec3} out */ function lerp$2(out, a, b, t) { var ax = a[0]; var ay = a[1]; var az = a[2]; out[0] = ax + t * (b[0] - ax); out[1] = ay + t * (b[1] - ay); out[2] = az + t * (b[2] - az); return out; } /** * Transforms the vec3 with a mat4. * 4th vector component is implicitly '1' * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the vector to transform * @param {ReadonlyMat4} m matrix to transform with * @returns {vec3} out */ function transformMat4$2(out, a, m) { var x = a[0], y = a[1], z = a[2]; var w = m[3] * x + m[7] * y + m[11] * z + m[15]; w = w || 1.0; out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; return out; } /** * Transforms the vec3 with a mat3. * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the vector to transform * @param {ReadonlyMat3} m the 3x3 matrix to transform with * @returns {vec3} out */ function transformMat3$1(out, a, m) { var x = a[0], y = a[1], z = a[2]; out[0] = x * m[0] + y * m[3] + z * m[6]; out[1] = x * m[1] + y * m[4] + z * m[7]; out[2] = x * m[2] + y * m[5] + z * m[8]; return out; } /** * Transforms the vec3 with a quat * Can also be used for dual quaternions. (Multiply it with the real part) * * @param {vec3} out the receiving vector * @param {ReadonlyVec3} a the vector to transform * @param {ReadonlyQuat} q quaternion to transform with * @returns {vec3} out */ function transformQuat$1(out, a, q) { // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed var qx = q[0], qy = q[1], qz = q[2], qw = q[3]; var x = a[0], y = a[1], z = a[2]; // var qvec = [qx, qy, qz]; // var uv = vec3.cross([], qvec, a); var uvx = qy * z - qz * y, uvy = qz * x - qx * z, uvz = qx * y - qy * x; // var uuv = vec3.cross([], qvec, uv); var uuvx = qy * uvz - qz * uvy, uuvy = qz * uvx - qx * uvz, uuvz = qx * uvy - qy * uvx; // vec3.scale(uv, uv, 2 * w); var w2 = qw * 2; uvx *= w2; uvy *= w2; uvz *= w2; // vec3.scale(uuv, uuv, 2); uuvx *= 2; uuvy *= 2; uuvz *= 2; // return vec3.add(out, a, vec3.add(out, uv, uuv)); out[0] = x + uvx + uuvx; out[1] = y + uvy + uuvy; out[2] = z + uvz + uuvz; return out; } /** * Get the angle between two 3D vectors * @param {ReadonlyVec3} a The first operand * @param {ReadonlyVec3} b The second operand * @returns {Number} The angle in radians */ function angle$1(a, b) { var ax = a[0], ay = a[1], az = a[2], bx = b[0], by = b[1], bz = b[2], mag1 = Math.sqrt(ax * ax + ay * ay + az * az), mag2 = Math.sqrt(bx * bx + by * by + bz * bz), mag = mag1 * mag2, cosine = mag && dot$3(a, b) / mag; return Math.acos(Math.min(Math.max(cosine, -1), 1)); } /** * Returns whether or not the vectors have approximately the same elements in the same position. * * @param {ReadonlyVec3} a The first vector. * @param {ReadonlyVec3} b The second vector. * @returns {Boolean} True if the vectors are equal, false otherwise. */ function equals$3(a, b) { var a0 = a[0], a1 = a[1], a2 = a[2]; var b0 = b[0], b1 = b[1], b2 = b[2]; return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)); } /** * Alias for {@link vec3.length} * @function */ var len = length$3; /** * Perform some operation over an array of vec3s. * * @param {Array} a the array of vectors to iterate over * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed * @param {Number} offset Number of elements to skip at the beginning of the array * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array * @param {Function} fn Function to call for each vector in the array * @param {Object} [arg] additional argument to pass to fn * @returns {Array} a * @function */ (function () { var vec = create$3(); return function (a, stride, offset, count, fn, arg) { var i, l; if (!stride) { stride = 3; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; vec[2] = a[i + 2]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; a[i + 2] = vec[2]; } return a; }; })(); /** * 4 Dimensional Vector * @module vec4 */ /** * Creates a new, empty vec4 * * @returns {vec4} a new 4D vector */ function create$2() { var out = new ARRAY_TYPE(4); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0; } return out; } /** * Copy the values from one vec4 to another * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the source vector * @returns {vec4} out */ function copy$1(out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; return out; } /** * Set the components of a vec4 to the given values * * @param {vec4} out the receiving vector * @param {Number} x X component * @param {Number} y Y component * @param {Number} z Z component * @param {Number} w W component * @returns {vec4} out */ function set$1(out, x, y, z, w) { out[0] = x; out[1] = y; out[2] = z; out[3] = w; return out; } /** * Adds two vec4's * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @returns {vec4} out */ function add$1(out, a, b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; out[2] = a[2] + b[2]; out[3] = a[3] + b[3]; return out; } /** * Subtracts vector b from vector a * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @returns {vec4} out */ function subtract$1(out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; out[3] = a[3] - b[3]; return out; } /** * Multiplies two vec4's * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @returns {vec4} out */ function multiply$2(out, a, b) { out[0] = a[0] * b[0]; out[1] = a[1] * b[1]; out[2] = a[2] * b[2]; out[3] = a[3] * b[3]; return out; } /** * Divides two vec4's * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @returns {vec4} out */ function divide$1(out, a, b) { out[0] = a[0] / b[0]; out[1] = a[1] / b[1]; out[2] = a[2] / b[2]; out[3] = a[3] / b[3]; return out; } /** * Scales a vec4 by a scalar number * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the vector to scale * @param {Number} b amount to scale the vector by * @returns {vec4} out */ function scale$1(out, a, b) { out[0] = a[0] * b; out[1] = a[1] * b; out[2] = a[2] * b; out[3] = a[3] * b; return out; } /** * Adds two vec4's after scaling the second operand by a scalar value * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @param {Number} scale the amount to scale b by before adding * @returns {vec4} out */ function scaleAndAdd(out, a, b, scale) { out[0] = a[0] + b[0] * scale; out[1] = a[1] + b[1] * scale; out[2] = a[2] + b[2] * scale; out[3] = a[3] + b[3] * scale; return out; } /** * Calculates the euclidian distance between two vec4's * * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @returns {Number} distance between a and b */ function distance$1(a, b) { var x = b[0] - a[0]; var y = b[1] - a[1]; var z = b[2] - a[2]; var w = b[3] - a[3]; return Math.hypot(x, y, z, w); } /** * Calculates the squared euclidian distance between two vec4's * * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @returns {Number} squared distance between a and b */ function squaredDistance$1(a, b) { var x = b[0] - a[0]; var y = b[1] - a[1]; var z = b[2] - a[2]; var w = b[3] - a[3]; return x * x + y * y + z * z + w * w; } /** * Calculates the length of a vec4 * * @param {ReadonlyVec4} a vector to calculate length of * @returns {Number} length of a */ function length$2(a) { var x = a[0]; var y = a[1]; var z = a[2]; var w = a[3]; return Math.hypot(x, y, z, w); } /** * Negates the components of a vec4 * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a vector to negate * @returns {vec4} out */ function negate$1(out, a) { out[0] = -a[0]; out[1] = -a[1]; out[2] = -a[2]; out[3] = -a[3]; return out; } /** * Returns the inverse of the components of a vec4 * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a vector to invert * @returns {vec4} out */ function inverse$1(out, a) { out[0] = 1.0 / a[0]; out[1] = 1.0 / a[1]; out[2] = 1.0 / a[2]; out[3] = 1.0 / a[3]; return out; } /** * Normalize a vec4 * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a vector to normalize * @returns {vec4} out */ function normalize$3(out, a) { var x = a[0]; var y = a[1]; var z = a[2]; var w = a[3]; var len = x * x + y * y + z * z + w * w; if (len > 0) { len = 1 / Math.sqrt(len); } out[0] = x * len; out[1] = y * len; out[2] = z * len; out[3] = w * len; return out; } /** * Calculates the dot product of two vec4's * * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @returns {Number} dot product of a and b */ function dot$2(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; } /** * Returns the cross-product of three vectors in a 4-dimensional space * * @param {ReadonlyVec4} result the receiving vector * @param {ReadonlyVec4} U the first vector * @param {ReadonlyVec4} V the second vector * @param {ReadonlyVec4} W the third vector * @returns {vec4} result */ function cross$1(out, u, v, w) { var A = v[0] * w[1] - v[1] * w[0], B = v[0] * w[2] - v[2] * w[0], C = v[0] * w[3] - v[3] * w[0], D = v[1] * w[2] - v[2] * w[1], E = v[1] * w[3] - v[3] * w[1], F = v[2] * w[3] - v[3] * w[2]; var G = u[0]; var H = u[1]; var I = u[2]; var J = u[3]; out[0] = H * F - I * E + J * D; out[1] = -(G * F) + I * C - J * B; out[2] = G * E - H * C + J * A; out[3] = -(G * D) + H * B - I * A; return out; } /** * Performs a linear interpolation between two vec4's * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the first operand * @param {ReadonlyVec4} b the second operand * @param {Number} t interpolation amount, in the range [0-1], between the two inputs * @returns {vec4} out */ function lerp$1(out, a, b, t) { var ax = a[0]; var ay = a[1]; var az = a[2]; var aw = a[3]; out[0] = ax + t * (b[0] - ax); out[1] = ay + t * (b[1] - ay); out[2] = az + t * (b[2] - az); out[3] = aw + t * (b[3] - aw); return out; } /** * Transforms the vec4 with a mat4. * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the vector to transform * @param {ReadonlyMat4} m matrix to transform with * @returns {vec4} out */ function transformMat4$1(out, a, m) { var x = a[0], y = a[1], z = a[2], w = a[3]; out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; return out; } /** * Transforms the vec4 with a quat * * @param {vec4} out the receiving vector * @param {ReadonlyVec4} a the vector to transform * @param {ReadonlyQuat} q quaternion to transform with * @returns {vec4} out */ function transformQuat(out, a, q) { var x = a[0], y = a[1], z = a[2]; var qx = q[0], qy = q[1], qz = q[2], qw = q[3]; // calculate quat * vec var ix = qw * x + qy * z - qz * y; var iy = qw * y + qz * x - qx * z; var iz = qw * z + qx * y - qy * x; var iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; out[3] = a[3]; return out; } /** * Returns whether or not the vectors have approximately the same elements in the same position. * * @param {ReadonlyVec4} a The first vector. * @param {ReadonlyVec4} b The second vector. * @returns {Boolean} True if the vectors are equal, false otherwise. */ function equals$2(a, b) { var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)); } /** * Perform some operation over an array of vec4s. * * @param {Array} a the array of vectors to iterate over * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed * @param {Number} offset Number of elements to skip at the beginning of the array * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array * @param {Function} fn Function to call for each vector in the array * @param {Object} [arg] additional argument to pass to fn * @returns {Array} a * @function */ (function () { var vec = create$2(); return function (a, stride, offset, count, fn, arg) { var i, l; if (!stride) { stride = 4; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; vec[2] = a[i + 2]; vec[3] = a[i + 3]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; a[i + 2] = vec[2]; a[i + 3] = vec[3]; } return a; }; })(); /** * Quaternion * @module quat */ /** * Creates a new identity quat * * @returns {quat} a new quaternion */ function create$1() { var out = new ARRAY_TYPE(4); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; out[2] = 0; } out[3] = 1; return out; } /** * Sets a quat from the given angle and rotation axis, * then returns it. * * @param {quat} out the receiving quaternion * @param {ReadonlyVec3} axis the axis around which to rotate * @param {Number} rad the angle in radians * @returns {quat} out **/ function setAxisAngle(out, axis, rad) { rad = rad * 0.5; var s = Math.sin(rad); out[0] = s * axis[0]; out[1] = s * axis[1]; out[2] = s * axis[2]; out[3] = Math.cos(rad); return out; } /** * Gets the rotation axis and angle for a given * quaternion. If a quaternion is created with * setAxisAngle, this method will return the same * values as providied in the original parameter list * OR functionally equivalent values. * Example: The quaternion formed by axis [0, 0, 1] and * angle -90 is the same as the quaternion formed by * [0, 0, 1] and 270. This method favors the latter. * @param {vec3} out_axis Vector receiving the axis of rotation * @param {ReadonlyQuat} q Quaternion to be decomposed * @return {Number} Angle, in radians, of the rotation */ function getAxisAngle(out_axis, q) { var rad = Math.acos(q[3]) * 2.0; var s = Math.sin(rad / 2.0); if (s > EPSILON) { out_axis[0] = q[0] / s; out_axis[1] = q[1] / s; out_axis[2] = q[2] / s; } else { // If s is zero, return any axis (no rotation - axis does not matter) out_axis[0] = 1; out_axis[1] = 0; out_axis[2] = 0; } return rad; } /** * Gets the angular distance between two unit quaternions * * @param {ReadonlyQuat} a Origin unit quaternion * @param {ReadonlyQuat} b Destination unit quaternion * @return {Number} Angle, in radians, between the two quaternions */ function getAngle(a, b) { var dotproduct = dot$1(a, b); return Math.acos(2 * dotproduct * dotproduct - 1); } /** * Multiplies two quat's * * @param {quat} out the receiving quaternion * @param {ReadonlyQuat} a the first operand * @param {ReadonlyQuat} b the second operand * @returns {quat} out */ function multiply$1(out, a, b) { var ax = a[0], ay = a[1], az = a[2], aw = a[3]; var bx = b[0], by = b[1], bz = b[2], bw = b[3]; out[0] = ax * bw + aw * bx + ay * bz - az * by; out[1] = ay * bw + aw * by + az * bx - ax * bz; out[2] = az * bw + aw * bz + ax * by - ay * bx; out[3] = aw * bw - ax * bx - ay * by - az * bz; return out; } /** * Performs a spherical linear interpolation between two quat * * @param {quat} out the receiving quaternion * @param {ReadonlyQuat} a the first operand * @param {ReadonlyQuat} b the second operand * @param {Number} t interpolation amount, in the range [0-1], between the two inputs * @returns {quat} out */ function slerp(out, a, b, t) { // benchmarks: // http://jsperf.com/quaternion-slerp-implementations var ax = a[0], ay = a[1], az = a[2], aw = a[3]; var bx = b[0], by = b[1], bz = b[2], bw = b[3]; var omega, cosom, sinom, scale0, scale1; // calc cosine cosom = ax * bx + ay * by + az * bz + aw * bw; // adjust signs (if necessary) if (cosom < 0.0) { cosom = -cosom; bx = -bx; by = -by; bz = -bz; bw = -bw; } // calculate coefficients if (1.0 - cosom > EPSILON) { // standard case (slerp) omega = Math.acos(cosom); sinom = Math.sin(omega); scale0 = Math.sin((1.0 - t) * omega) / sinom; scale1 = Math.sin(t * omega) / sinom; } else { // "from" and "to" quaternions are very close // ... so we can do a linear interpolation scale0 = 1.0 - t; scale1 = t; } // calculate final values out[0] = scale0 * ax + scale1 * bx; out[1] = scale0 * ay + scale1 * by; out[2] = scale0 * az + scale1 * bz; out[3] = scale0 * aw + scale1 * bw; return out; } /** * Calculates the inverse of a quat * * @param {quat} out the receiving quaternion * @param {ReadonlyQuat} a quat to calculate inverse of * @returns {quat} out */ function invert(out, a) { var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; var dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3; var invDot = dot ? 1.0 / dot : 0; // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 out[0] = -a0 * invDot; out[1] = -a1 * invDot; out[2] = -a2 * invDot; out[3] = a3 * invDot; return out; } /** * Calculates the conjugate of a quat * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. * * @param {quat} out the receiving quaternion * @param {ReadonlyQuat} a quat to calculate conjugate of * @returns {quat} out */ function conjugate(out, a) { out[0] = -a[0]; out[1] = -a[1]; out[2] = -a[2]; out[3] = a[3]; return out; } /** * Creates a quaternion from the given 3x3 rotation matrix. * * NOTE: The resultant quaternion is not normalized, so you should be sure * to renormalize the quaternion yourself where necessary. * * @param {quat} out the receiving quaternion * @param {ReadonlyMat3} m rotation matrix * @returns {quat} out * @function */ function fromMat3(out, m) { // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes // article "Quaternion Calculus and Fast Animation". var fTrace = m[0] + m[4] + m[8]; var fRoot; if (fTrace > 0.0) { // |w| > 1/2, may as well choose w > 1/2 fRoot = Math.sqrt(fTrace + 1.0); // 2w out[3] = 0.5 * fRoot; fRoot = 0.5 / fRoot; // 1/(4w) out[0] = (m[5] - m[7]) * fRoot; out[1] = (m[6] - m[2]) * fRoot; out[2] = (m[1] - m[3]) * fRoot; } else { // |w| <= 1/2 var i = 0; if (m[4] > m[0]) i = 1; if (m[8] > m[i * 3 + i]) i = 2; var j = (i + 1) % 3; var k = (i + 2) % 3; fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0); out[i] = 0.5 * fRoot; fRoot = 0.5 / fRoot; out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot; out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot; out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot; } return out; } /** * Creates a quaternion from the given euler angle x, y, z. * * @param {quat} out the receiving quaternion * @param {x} Angle to rotate around X axis in degrees. * @param {y} Angle to rotate around Y axis in degrees. * @param {z} Angle to rotate around Z axis in degrees. * @returns {quat} out * @function */ function fromEuler(out, x, y, z) { var halfToRad = 0.5 * Math.PI / 180.0; x *= halfToRad; y *= halfToRad; z *= halfToRad; var sx = Math.sin(x); var cx = Math.cos(x); var sy = Math.sin(y); var cy = Math.cos(y); var sz = Math.sin(z); var cz = Math.cos(z); out[0] = sx * cy * cz - cx * sy * sz; out[1] = cx * sy * cz + sx * cy * sz; out[2] = cx * cy * sz - sx * sy * cz; out[3] = cx * cy * cz + sx * sy * sz; return out; } /** * Copy the values from one quat to another * * @param {quat} out the receiving quaternion * @param {ReadonlyQuat} a the source quaternion * @returns {quat} out * @function */ var copy = copy$1; /** * Set the components of a quat to the given values * * @param {quat} out the receiving quaternion * @param {Number} x X component * @param {Number} y Y component * @param {Number} z Z component * @param {Number} w W component * @returns {quat} out * @function */ var set = set$1; /** * Calculates the dot product of two quat's * * @param {ReadonlyQuat} a the first operand * @param {ReadonlyQuat} b the second operand * @returns {Number} dot product of a and b * @function */ var dot$1 = dot$2; /** * Calculates the length of a quat * * @param {ReadonlyQuat} a vector to calculate length of * @returns {Number} length of a */ var length$1 = length$2; /** * Normalize a quat * * @param {quat} out the receiving quaternion * @param {ReadonlyQuat} a quaternion to normalize * @returns {quat} out * @function */ var normalize$2 = normalize$3; /** * Sets a quaternion to represent the shortest rotation from one * vector to another. * * Both vectors are assumed to be unit length. * * @param {quat} out the receiving quaternion. * @param {ReadonlyVec3} a the initial vector * @param {ReadonlyVec3} b the destination vector * @returns {quat} out */ (function () { var tmpvec3 = create$3(); var xUnitVec3 = fromValues(1, 0, 0); var yUnitVec3 = fromValues(0, 1, 0); return function (out, a, b) { var dot = dot$3(a, b); if (dot < -0.999999) { cross$2(tmpvec3, xUnitVec3, a); if (len(tmpvec3) < 0.000001) cross$2(tmpvec3, yUnitVec3, a); normalize$4(tmpvec3, tmpvec3); setAxisAngle(out, tmpvec3, Math.PI); return out; } else if (dot > 0.999999) { out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 1; return out; } else { cross$2(tmpvec3, a, b); out[0] = tmpvec3[0]; out[1] = tmpvec3[1]; out[2] = tmpvec3[2]; out[3] = 1 + dot; return normalize$2(out, out); } }; })(); /** * Performs a spherical linear interpolation with two control points * * @param {quat} out the receiving quaternion * @param {ReadonlyQuat} a the first operand * @param {ReadonlyQuat} b the second operand * @param {ReadonlyQuat} c the third operand * @param {ReadonlyQuat} d the fourth operand * @param {Number} t interpolation amount, in the range [0-1], between the two inputs * @returns {quat} out */ (function () { var temp1 = create$1(); var temp2 = create$1(); return function (out, a, b, c, d, t) { slerp(temp1, a, d, t); slerp(temp2, b, c, t); slerp(out, temp1, temp2, 2 * t * (1 - t)); return out; }; })(); /** * Sets the specified quaternion with values corresponding to the given * axes. Each axis is a vec3 and is expected to be unit length and * perpendicular to all other specified axes. * * @param {ReadonlyVec3} view the vector representing the viewing direction * @param {ReadonlyVec3} right the vector representing the local "right" direction * @param {ReadonlyVec3} up the vector representing the local "up" direction * @returns {quat} out */ (function () { var matr = create$4(); return function (out, view, right, up) { matr[0] = right[0]; matr[3] = right[1]; matr[6] = right[2]; matr[1] = up[0]; matr[4] = up[1]; matr[7] = up[2]; matr[2] = -view[0]; matr[5] = -view[1]; matr[8] = -view[2]; return normalize$2(out, fromMat3(out, matr)); }; })(); /** * 2 Dimensional Vector * @module vec2 */ /** * Creates a new, empty vec2 * * @returns {vec2} a new 2D vector */ function create() { var out = new ARRAY_TYPE(2); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; } return out; } /** * Adds two vec2's * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {vec2} out */ function add(out, a, b) { out[0] = a[0] + b[0]; out[1] = a[1] + b[1]; return out; } /** * Subtracts vector b from vector a * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {vec2} out */ function subtract(out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; return out; } /** * Multiplies two vec2's * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {vec2} out */ function multiply(out, a, b) { out[0] = a[0] * b[0]; out[1] = a[1] * b[1]; return out; } /** * Divides two vec2's * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {vec2} out */ function divide(out, a, b) { out[0] = a[0] / b[0]; out[1] = a[1] / b[1]; return out; } /** * Scales a vec2 by a scalar number * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the vector to scale * @param {Number} b amount to scale the vector by * @returns {vec2} out */ function scale(out, a, b) { out[0] = a[0] * b; out[1] = a[1] * b; return out; } /** * Calculates the euclidian distance between two vec2's * * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {Number} distance between a and b */ function distance(a, b) { var x = b[0] - a[0], y = b[1] - a[1]; return Math.hypot(x, y); } /** * Calculates the squared euclidian distance between two vec2's * * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {Number} squared distance between a and b */ function squaredDistance(a, b) { var x = b[0] - a[0], y = b[1] - a[1]; return x * x + y * y; } /** * Calculates the length of a vec2 * * @param {ReadonlyVec2} a vector to calculate length of * @returns {Number} length of a */ function length(a) { var x = a[0], y = a[1]; return Math.hypot(x, y); } /** * Negates the components of a vec2 * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a vector to negate * @returns {vec2} out */ function negate(out, a) { out[0] = -a[0]; out[1] = -a[1]; return out; } /** * Returns the inverse of the components of a vec2 * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a vector to invert * @returns {vec2} out */ function inverse(out, a) { out[0] = 1.0 / a[0]; out[1] = 1.0 / a[1]; return out; } /** * Normalize a vec2 * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a vector to normalize * @returns {vec2} out */ function normalize$1(out, a) { var x = a[0], y = a[1]; var len = x * x + y * y; if (len > 0) { //TODO: evaluate use of glm_invsqrt here? len = 1 / Math.sqrt(len); } out[0] = a[0] * len; out[1] = a[1] * len; return out; } /** * Calculates the dot product of two vec2's * * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {Number} dot product of a and b */ function dot(a, b) { return a[0] * b[0] + a[1] * b[1]; } /** * Computes the cross product of two vec2's * Note that the cross product must by definition produce a 3D vector * * @param {vec3} out the receiving vector * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @returns {vec3} out */ function cross(out, a, b) { var z = a[0] * b[1] - a[1] * b[0]; out[0] = out[1] = 0; out[2] = z; return out; } /** * Performs a linear interpolation between two vec2's * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the first operand * @param {ReadonlyVec2} b the second operand * @param {Number} t interpolation amount, in the range [0-1], between the two inputs * @returns {vec2} out */ function lerp(out, a, b, t) { var ax = a[0], ay = a[1]; out[0] = ax + t * (b[0] - ax); out[1] = ay + t * (b[1] - ay); return out; } /** * Transforms the vec2 with a mat3 * 3rd vector component is implicitly '1' * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the vector to transform * @param {ReadonlyMat3} m matrix to transform with * @returns {vec2} out */ function transformMat3(out, a, m) { var x = a[0], y = a[1]; out[0] = m[0] * x + m[3] * y + m[6]; out[1] = m[1] * x + m[4] * y + m[7]; return out; } /** * Transforms the vec2 with a mat4 * 3rd vector component is implicitly '0' * 4th vector component is implicitly '1' * * @param {vec2} out the receiving vector * @param {ReadonlyVec2} a the vector to transform * @param {ReadonlyMat4} m matrix to transform with * @returns {vec2} out */ function transformMat4(out, a, m) { var x = a[0]; var y = a[1]; out[0] = m[0] * x + m[4] * y + m[12]; out[1] = m[1] * x + m[5] * y + m[13]; return out; } /** * Get the angle between two 2D vectors * @param {ReadonlyVec2} a The first operand * @param {ReadonlyVec2} b The second operand * @returns {Number} The angle in radians */ function angle(a, b) { var x1 = a[0], y1 = a[1], x2 = b[0], y2 = b[1], // mag is the product of the magnitudes of a and b mag = Math.sqrt(x1 * x1 + y1 * y1) * Math.sqrt(x2 * x2 + y2 * y2), // mag &&.. short circuits if mag == 0 cosine = mag && (x1 * x2 + y1 * y2) / mag; // Math.min(Math.max(cosine, -1), 1) clamps the cosine between -1 and 1 return Math.acos(Math.min(Math.max(cosine, -1), 1)); } /** * Returns whether or not the vectors have approximately the same elements in the same position. * * @param {ReadonlyVec2} a The first vector. * @param {ReadonlyVec2} b The second vector. * @returns {Boolean} True if the vectors are equal, false otherwise. */ function equals$1(a, b) { var a0 = a[0], a1 = a[1]; var b0 = b[0], b1 = b[1]; return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)); } /** * Perform some operation over an array of vec2s. * * @param {Array} a the array of vectors to iterate over * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed * @param {Number} offset Number of elements to skip at the beginning of the array * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array * @param {Function} fn Function to call for each vector in the array * @param {Object} [arg] additional argument to pass to fn * @returns {Array} a * @function */ (function () { var vec = create(); return function (a, stride, offset, count, fn, arg) { var i, l; if (!stride) { stride = 2; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; } return a; }; })(); var r={grad:.9,turn:360,rad:360/(2*Math.PI)},t=function(r){return "string"==typeof r?r.length>0:"number"==typeof r},n=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=Math.pow(10,t)),Math.round(n*r)/n+0},e=function(r,t,n){return void 0===t&&(t=0),void 0===n&&(n=1),r>n?n:r>t?r:t},u=function(r){return (r=isFinite(r)?r%360:0)>0?r:r+360},a=function(r){return {r:e(r.r,0,255),g:e(r.g,0,255),b:e(r.b,0,255),a:e(r.a)}},o=function(r){return {r:n(r.r),g:n(r.g),b:n(r.b),a:n(r.a,3)}},i=/^#([0-9a-f]{3,8})$/i,s=function(r){var t=r.toString(16);return t.length<2?"0"+t:t},h=function(r){var t=r.r,n=r.g,e=r.b,u=r.a,a=Math.max(t,n,e),o=a-Math.min(t,n,e),i=o?a===t?(n-e)/o:a===n?2+(e-t)/o:4+(t-n)/o:0;return {h:60*(i<0?i+6:i),s:a?o/a*100:0,v:a/255*100,a:u}},b=function(r){var t=r.h,n=r.s,e=r.v,u=r.a;t=t/360*6,n/=100,e/=100;var a=Math.floor(t),o=e*(1-n),i=e*(1-(t-a)*n),s=e*(1-(1-t+a)*n),h=a%6;return {r:255*[e,i,o,o,s,e][h],g:255*[s,e,e,i,o,o][h],b:255*[o,o,s,e,e,i][h],a:u}},g=function(r){return {h:u(r.h),s:e(r.s,0,100),l:e(r.l,0,100),a:e(r.a)}},d=function(r){return {h:n(r.h),s:n(r.s),l:n(r.l),a:n(r.a,3)}},f=function(r){return b((n=(t=r).s,{h:t.h,s:(n*=((e=t.l)<50?e:100-e)/100)>0?2*n/(e+n)*100:0,v:e+n,a:t.a}));var t,n,e;},c=function(r){return {h:(t=h(r)).h,s:(u=(200-(n=t.s))*(e=t.v)/100)>0&&u<200?n*e/100/(u<=100?u:200-u)*100:0,l:u/2,a:t.a};var t,n,e,u;},l=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s*,\s*([+-]?\d*\.?\d+)%\s*,\s*([+-]?\d*\.?\d+)%\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,p=/^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s+([+-]?\d*\.?\d+)%\s+([+-]?\d*\.?\d+)%\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,v=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,m=/^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i,y={string:[[function(r){var t=i.exec(r);return t?(r=t[1]).length<=4?{r:parseInt(r[0]+r[0],16),g:parseInt(r[1]+r[1],16),b:parseInt(r[2]+r[2],16),a:4===r.length?n(parseInt(r[3]+r[3],16)/255,2):1}:6===r.length||8===r.length?{r:parseInt(r.substr(0,2),16),g:parseInt(r.substr(2,2),16),b:parseInt(r.substr(4,2),16),a:8===r.length?n(parseInt(r.substr(6,2),16)/255,2):1}:null:null},"hex"],[function(r){var t=v.exec(r)||m.exec(r);return t?t[2]!==t[4]||t[4]!==t[6]?null:a({r:Number(t[1])/(t[2]?100/255:1),g:Number(t[3])/(t[4]?100/255:1),b:Number(t[5])/(t[6]?100/255:1),a:void 0===t[7]?1:Number(t[7])/(t[8]?100:1)}):null},"rgb"],[function(t){var n=l.exec(t)||p.exec(t);if(!n)return null;var e,u,a=g({h:(e=n[1],u=n[2],void 0===u&&(u="deg"),Number(e)*(r[u]||1)),s:Number(n[3]),l:Number(n[4]),a:void 0===n[5]?1:Number(n[5])/(n[6]?100:1)});return f(a)},"hsl"]],object:[[function(r){var n=r.r,e=r.g,u=r.b,o=r.a,i=void 0===o?1:o;return t(n)&&t(e)&&t(u)?a({r:Number(n),g:Number(e),b:Number(u),a:Number(i)}):null},"rgb"],[function(r){var n=r.h,e=r.s,u=r.l,a=r.a,o=void 0===a?1:a;if(!t(n)||!t(e)||!t(u))return null;var i=g({h:Number(n),s:Number(e),l:Number(u),a:Number(o)});return f(i)},"hsl"],[function(r){var n=r.h,a=r.s,o=r.v,i=r.a,s=void 0===i?1:i;if(!t(n)||!t(a)||!t(o))return null;var h=function(r){return {h:u(r.h),s:e(r.s,0,100),v:e(r.v,0,100),a:e(r.a)}}({h:Number(n),s:Number(a),v:Number(o),a:Number(s)});return b(h)},"hsv"]]},N=function(r,t){for(var n=0;n=.5},r.prototype.toHex=function(){return r=o(this.rgba),t=r.r,e=r.g,u=r.b,i=(a=r.a)<1?s(n(255*a)):"","#"+s(t)+s(e)+s(u)+i;var r,t,e,u,a,i;},r.prototype.toRgb=function(){return o(this.rgba)},r.prototype.toRgbString=function(){return r=o(this.rgba),t=r.r,n=r.g,e=r.b,(u=r.a)<1?"rgba("+t+", "+n+", "+e+", "+u+")":"rgb("+t+", "+n+", "+e+")";var r,t,n,e,u;},r.prototype.toHsl=function(){return d(c(this.rgba))},r.prototype.toHslString=function(){return r=d(c(this.rgba)),t=r.h,n=r.s,e=r.l,(u=r.a)<1?"hsla("+t+", "+n+"%, "+e+"%, "+u+")":"hsl("+t+", "+n+"%, "+e+"%)";var r,t,n,e,u;},r.prototype.toHsv=function(){return r=h(this.rgba),{h:n(r.h),s:n(r.s),v:n(r.v),a:n(r.a,3)};var r;},r.prototype.invert=function(){return w({r:255-(r=this.rgba).r,g:255-r.g,b:255-r.b,a:r.a});var r;},r.prototype.saturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,r))},r.prototype.desaturate=function(r){return void 0===r&&(r=.1),w(M(this.rgba,-r))},r.prototype.grayscale=function(){return w(M(this.rgba,-1))},r.prototype.lighten=function(r){return void 0===r&&(r=.1),w($(this.rgba,r))},r.prototype.darken=function(r){return void 0===r&&(r=.1),w($(this.rgba,-r))},r.prototype.rotate=function(r){return void 0===r&&(r=15),this.hue(this.hue()+r)},r.prototype.alpha=function(r){return "number"==typeof r?w({r:(t=this.rgba).r,g:t.g,b:t.b,a:r}):n(this.rgba.a,3);var t;},r.prototype.hue=function(r){var t=c(this.rgba);return "number"==typeof r?w({h:r,s:t.s,l:t.l,a:t.a}):n(t.h)},r.prototype.isEqual=function(r){return this.toHex()===w(r).toHex()},r}(),w=function(r){return r instanceof j?r:new j(r)},S=[],k=function(r){r.forEach(function(r){S.indexOf(r)<0&&(r(j,y),S.push(r));});}; function parseShader(shader, defines = [], includes = []) { return shader.replace(/#defines/, defines.join("\n")).replace(/#includes/, includes.join("\n")); } function defineShader(shader, defines = {}) { return Object.keys(defines).reduce((str, key) => defines[key] ? `#define ${key} ${defines[key]} ${str}` : str, shader); } function getShaderName(shader, defaultName = "unnamed") { const SHADER_NAME_REGEXP = /#define\s*SHADER_NAME\s*([A-Za-z0-9_-]+)\s*/; const match = shader.match(SHADER_NAME_REGEXP); return match ? match[1] : defaultName; } function getWireframeIndex(position, indices, numIndices, data) { const edges = /* @__PURE__ */ new Set(); if (data) { for (let j = 0, l = numIndices; j < l; j += 3) { const a = data[j]; const b = data[j + 1]; const c = data[j + 2]; const array = [a, b, b, c, c, a]; for (let i = 0; i < array.length; i += 2) { if (isUniqueEdge(array[i] * 3, array[i + 1] * 3, position, edges)) { indices.push(array[i], array[i + 1]); } } } } else { for (let j = 0, l = numIndices; j < l; j += 3) { const a = j; const b = j + 1; const c = j + 2; const array = [a, b, b, c, c, a]; for (let i = 0; i < array.length; i += 2) { if (isUniqueEdge(array[i] * 3, array[i + 1] * 3, position, edges)) { indices.push(array[i], array[i + 1]); } } } } return indices; } function isUniqueEdge(start, end, position, edges) { const hash1 = `${position[start]},${position[start + 1]},${position[start + 2]}-${position[end]},${position[end + 1]},${position[end + 2]}`; const hash2 = `${position[end]},${position[end + 1]},${position[end + 2]}-${position[start]},${position[start + 1]},${position[start + 2]}`; if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1); edges.add(hash2); return true; } } const DEG_TO_RAD = Math.PI / 180; const RAD_TO_DEG = 180 / Math.PI; function degToRad$1(deg) { return deg * DEG_TO_RAD; } function radToDeg$1(a) { return a * RAD_TO_DEG; } function clamp$1(val, min, max) { return Math.min(Math.max(val, min), max); } function isPowerOfTwo(value) { return Math.log(value) / Math.LN2 % 1 === 0; } let FloatArray = Float32Array; function highPrecision(b, notifyGlMatrix = true) { if (b) { FloatArray = Float64Array; } else { FloatArray = Float32Array; } if (notifyGlMatrix) { setMatrixArrayType(FloatArray); } } function getFloatArrayConstructor() { return FloatArray; } function isWebGL(gl) { if (typeof WebGLRenderingContext !== "undefined" && gl instanceof WebGLRenderingContext) { return true; } if (typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext) { return true; } if (gl?.gl && (gl.gl instanceof WebGLRenderingContext || gl.gl instanceof WebGL2RenderingContext)) { return true; } return Boolean(gl && Number.isFinite(gl._version)); } function isWebGL2(gl) { if (typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext) { return true; } if (gl?.gl && gl.gl instanceof WebGL2RenderingContext) { return true; } return Boolean(gl && gl._version === 2); } function getContext(canvas, glOptions = {}, requestWebGl2 = false) { const names = ["webgl2", "webgl", "experimental-webgl"]; if (!requestWebGl2) { names.shift(); } let context = null; function onContextCreationError(error) { console.error(error.statusMessage); } canvas?.addEventListener?.("webglcontextcreationerror", onContextCreationError, false); for (let ii = 0; ii < names.length; ++ii) { try { context = canvas.getContext(names[ii], glOptions); } catch (e) { } if (context) { break; } } canvas?.removeEventListener?.("webglcontextcreationerror", onContextCreationError, false); return context; } const now = () => ("undefined" == typeof performance ? Date : performance).now(); function typeOf(value) { return Object.prototype.toString.call(value).slice(8, -1).toLowerCase(); } function isString(s) { return typeOf(s) === "string"; } function isUndef(s) { return typeOf(s) === "undefined"; } function isHex(string) { return isString(string) && string.includes("%"); } function isNumber(s) { return typeOf(s) === "number"; } function isRegexp(obj) { return typeOf(obj) === "regexp"; } function isNull(value) { return value == null; } function isObject(value) { const type = typeof value; return value !== null && (type === "object" || type === "function"); } function hasValue(v, state) { if (isObject(v)) { return !isNull(v.value) && (isNull(state) || v.value === state); } else { return !isNull(v) && (isNull(state) || v === state); } } const uidCounters = {}; function uid(id = "id") { uidCounters[id] = uidCounters[id] || 1; const count = uidCounters[id]++; return `${id}-${count}`; } function omit(obj, keys = []) { return Object.keys(obj).filter((key) => keys.indexOf(key) < 0).reduce( (newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {} ); } function pick(obj, keys = []) { return Object.keys(obj).filter((key) => keys.indexOf(key) > -1).reduce( (newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {} ); } const callbacks = []; const fpsInterval = 1e3 / 60; let time = performance.now(); function requestAnimationFrameLoop() { const current = now(); const delta = current - time; if (delta >= fpsInterval) { time = current - delta % fpsInterval; const funcs = callbacks.slice(); callbacks.length = 0; for (let i = 0; i < funcs.length; i++) { funcs[i] && funcs[i](current, delta); } } else { setImmediate(requestAnimationFrameLoop); } } function raf(func) { callbacks.push(func); if (callbacks.length === 1) { setImmediate(requestAnimationFrameLoop); } return callbacks.length - 1; } function caf(id) { callbacks[id] = void 0; } function requestAnimationFrame$1(cb) { if (typeof window !== "undefined" && window.requestAnimationFrame) { return window.requestAnimationFrame(cb); } return raf(cb); } function cancelAnimationFrame$1(cb) { if (typeof window !== "undefined" && window.cancelAnimationFrame) { return window.cancelAnimationFrame(cb); } return caf(cb); } var index = /*#__PURE__*/Object.freeze({ __proto__: null, DEG_TO_RAD: DEG_TO_RAD, RAD_TO_DEG: RAD_TO_DEG, cancelAnimationFrame: cancelAnimationFrame$1, clamp: clamp$1, defineShader: defineShader, degToRad: degToRad$1, getContext: getContext, getFloatArrayConstructor: getFloatArrayConstructor, getShaderName: getShaderName, getWireframeIndex: getWireframeIndex, hasValue: hasValue, highPrecision: highPrecision, isHex: isHex, isNull: isNull, isNumber: isNumber, isObject: isObject, isPowerOfTwo: isPowerOfTwo, isRegexp: isRegexp, isString: isString, isUndef: isUndef, isUniqueEdge: isUniqueEdge, isWebGL: isWebGL, isWebGL2: isWebGL2, now: now, omit: omit, parseShader: parseShader, pick: pick, radToDeg: radToDeg$1, requestAnimationFrame: requestAnimationFrame$1, typeOf: typeOf, uid: uid }); class Clock { #lastTime = 0; #elapsedTime = 0; #start = false; running; constructor(running = true) { this.running = running; } start() { if (!this.#start) { this.reset(); this.#start = true; } } stop() { this.getElapsedTime(); this.#start = false; this.running = false; } reset() { this.#lastTime = now(); this.#elapsedTime = 0; } getElapsedTime() { this.getDelta(); return this.#elapsedTime; } getDelta() { let deltaTime = 0; if (this.running && !this.#start) { this.start(); return 0; } if (this.#start) { const time = now(); deltaTime = (time - this.#lastTime) / 1e3; this.#lastTime = time; this.#elapsedTime = this.#elapsedTime + deltaTime; } return deltaTime; } } const defaultOptions$1 = { autoStart: true }; class Raf { options; #raf; #animating; #isVisible; #clock; #callback; constructor(cb, options = {}) { this.options = { ...options, ...defaultOptions$1 }; this.#clock = new Clock(); this.reset(); this.onVisibilityChange = this.onVisibilityChange.bind(this); this.#callback = () => { const time = this.#clock.getElapsedTime(); cb && cb(time); }; if (this.options.autoStart) { this.start(); } } get visible() { return this.#isVisible; } get animating() { return this.#animating; } reset() { this.#animating = false; this.#isVisible = true; if (this.#raf !== void 0) { cancelAnimationFrame$1(this.#raf); } } get elapsedTime() { return this.#clock.getElapsedTime(); } start() { if (this.#animating) return; this.#animating = true; this.#clock.start(); this.tick(); if (typeof window !== "undefined" && window.document) { window.document.addEventListener("visibilitychange", this.onVisibilityChange, false); } } stop() { this.#clock.stop(); this.reset(); if (typeof window !== "undefined" && window.document) { window.document.removeEventListener("visibilitychange", this.onVisibilityChange, false); } } tick() { if (!this.#animating || !this.#isVisible) return; this.#raf = requestAnimationFrame$1(() => { this.tick(); }); this.#callback(); } onVisibilityChange() { if (typeof window !== "undefined" && window.document) { this.#isVisible = !window.document.hidden; } if (this.#isVisible) { this.reset(); this.start(); } } } class Event { type; constructor(type, params = {}) { this.type = type; (Object.getOwnPropertyNames(params) || []).forEach((key) => { this[key] = params[key]; }); } } class EventEmitter { fns; validateEventTypes; constructor({ validEventTypes = [/.*/] } = {}) { this.fns = /* @__PURE__ */ new Map(); this.validateEventTypes = validEventTypes; } validateEventType(type) { let vs = this.validateEventTypes; if (!Array.isArray(this.validateEventTypes)) { vs = [this.validateEventTypes]; } let isValid = true; vs.forEach((r) => { if (isRegexp(r) && !r.test(type)) { isValid = false; } }); if (!isValid) { throw new Error(`Invalid Event Type: '${type}'. Event type should be any of: ${vs}.`); } } on(type, handler, context) { this.validateEventType(type); if (isString(type)) { const names = type.split(" "); if (names.length > 1) { names.forEach((t) => { this.on(t, handler, context); }); return this; } } if (!this.has(type)) { this.fns.set(type, []); } this.fns.get(type).push(handler); return this; } once(type, handler, context) { this.validateEventType(type); if (isString(type)) { const names = type.split(" "); if (names.length > 1) { names.forEach((t) => { this.once(t, handler, context); }); return this; } } const onceHandler = (...args) => { this.off(type, onceHandler); handler.call(context || this, ...args); }; return this.on(type, onceHandler, context); } off(type, handler, context) { this.validateEventType(type); if (isString(type)) { const names = type.split(" "); if (names.length > 1) { names.forEach((t) => { this.off(t, handler, context); }); return this; } } const handlers = this.has(type); if (handlers) { if (handler) { const fns = handlers.filter((h) => h !== handler); this.fns.set(type, fns); } else { this.fns.delete(type); } } return this; } emit(type, args) { const eventObject = type instanceof Event ? type : new Event(type, args); this.validateEventType(eventObject.type); const fns = this.has(eventObject.type); if (fns) { return fns.map((fn) => fn.call(this, eventObject)); } } has(type) { return this.fns.get(type); } clear() { this.fns.clear(); return this; } } class Vector { elements = new (getFloatArrayConstructor())(2); fromArray(array, offset = 0) { let i = 0; for (; i < this.elements.length; i++) { this.elements[i] = array[offset + i]; } return this; } toArray(out = [], offset = 0) { let i = 0; for (; i < this.elements.length; i++) { out[offset + i] = this.elements[i]; } return out; } } class Vector2 extends Vector { elements = new (getFloatArrayConstructor())(2); constructor(x = 0, y = 0) { super(); const v = this.elements; v[0] = x; v[1] = y; } get x() { return this.elements[0]; } set x(x) { this.elements[0] = x; } get y() { return this.elements[1]; } set y(y) { this.elements[1] = y; } fromObject(object) { const { x, y } = object; if (x !== void 0) this.x = x; if (y !== void 0) this.y = y; return this; } toObject() { return { x: this.x, y: this.y }; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(s) { return this.set(s, s); } add(vec) { add(this.elements, this.elements, vec.elements); return this; } addScalar(v) { add(this.elements, this.elements, [v, v]); return this; } subtract(vec) { subtract(this.elements, this.elements, vec.elements); return this; } subtractScalar(v) { subtract(this.elements, this.elements, [v, v]); return this; } multiply(vec) { multiply(this.elements, this.elements, vec.elements); return this; } multiplyScalar(v) { multiply(this.elements, this.elements, [v, v]); return this; } divide(vec) { divide(this.elements, this.elements, vec.elements); return this; } divideScalar(v) { divide(this.elements, this.elements, [v, v]); return this; } scale(s) { scale(this.elements, this.elements, s); return this; } distanceTo(vec) { return distance(this.elements, vec.elements); } length() { return length(this.elements); } distanceToSquared(v) { return squaredDistance(v.elements, this.elements); } angle() { return angle(this.elements, [1, 0]); } angleTo(v) { return angle(this.elements, v.elements); } dot(vec) { return dot(this.elements, vec.elements); } equals(vec) { return equals$1(this.elements, vec.elements); } cross(vec) { cross(this.elements, this.elements, vec.elements); return this; } negate() { negate(this.elements, this.elements); return this; } inverse() { inverse(this.elements, this.elements); return this; } lerp(vec, t) { lerp(this.elements, this.elements, vec.elements, t); return this; } normalize() { normalize$1(this.elements, this.elements); return this; } applyMatrix3(matrix) { transformMat3(this.elements, this.elements, matrix.elements); return this; } applyMatrix4(matrix) { transformMat4(this.elements, this.elements, matrix.elements); return this; } copy(vec2) { this.x = vec2.x; this.y = vec2.y; return this; } clone() { return new Vector2(this.x, this.y); } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } const tempArray$1 = []; class Quaternion extends Vector { elements = new (getFloatArrayConstructor())(4); #changeCallbacks = []; constructor(x = 0, y = 0, z = 0, w = 0) { super(); const v = this.elements; v[0] = x; v[1] = y; v[2] = z; v[3] = w; } get x() { return this.elements[0]; } set x(x) { this.elements[0] = x; this.triggerChange(); } get y() { return this.elements[1]; } set y(y) { this.elements[1] = y; this.triggerChange(); } get z() { return this.elements[2]; } set z(z) { this.elements[2] = z; this.triggerChange(); } get w() { return this.elements[3]; } set w(w) { this.elements[3] = w; this.triggerChange(); } fromObject({ x, y, z, w }) { if (x !== void 0) this.x = x; if (y !== void 0) this.y = y; if (z !== void 0) this.z = z; if (w !== void 0) this.w = w; this.triggerChange(); return this; } toObject() { return { x: this.x, y: this.y, z: this.z, w: this.w }; } fromAxisAngle(axis, rad) { setAxisAngle(this.elements, axis.elements, rad); this.triggerChange(); return this; } getAxisAngle(axis = new Vector3()) { const rad = getAxisAngle(tempArray$1, this.elements); axis.set(tempArray$1[0], tempArray$1[1], tempArray$1[2]); return rad; } fromEuler(e) { fromEuler(this.elements, radToDeg$1(e.x), radToDeg$1(e.y), radToDeg$1(e.z)); this.triggerChange(); return this; } fromMat3(m) { fromMat3(this.elements, m); return this; } set(x, y, z, w) { set(this.elements, x, y, z, w); this.triggerChange(); return this; } length() { return length$1(this.elements); } multiply(a, b) { if (b) { multiply$1(this.elements, a.elements, b.elements); } else { multiply$1(this.elements, this.elements, a.elements); } this.triggerChange(); return this; } slerp(q, t) { slerp(this.elements, this.elements, q.elements, t); this.triggerChange(); return this; } invert() { invert(this.elements, this.elements); this.triggerChange(); return this; } conjugate() { conjugate(this.elements, this.elements); this.triggerChange(); return this; } normalize() { normalize$2(this.elements, this.elements); this.triggerChange(); return this; } dot(q) { return dot$1(this.elements, q.elements); } angleTo(q) { return getAngle(this.elements, q.elements); } clone() { return new Quaternion().copy(this); } copy(q) { copy(this.elements, q.elements); this.triggerChange(); return this; } equals(q) { return equals$2(this.elements, q.elements); } onChange(fn) { if (!this.#changeCallbacks.includes(fn)) { this.#changeCallbacks.push(fn); } } triggerChange() { this.#changeCallbacks.forEach((cb) => cb()); } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } class Vector3 extends Vector { elements = new (getFloatArrayConstructor())(3); constructor(x = 0, y = 0, z = 0) { super(); const v = this.elements; v[0] = x; v[1] = y; v[2] = z; } get x() { return this.elements[0]; } set x(x) { this.elements[0] = x; } get y() { return this.elements[1]; } set y(y) { this.elements[1] = y; } get z() { return this.elements[2]; } set z(z) { this.elements[2] = z; } fromObject(object) { const { x, y, z } = object; if (x !== void 0) this.x = x; if (y !== void 0) this.y = y; if (z !== void 0) this.z = z; return this; } toObject() { return { x: this.x, y: this.y, z: this.z }; } set(x, y, z) { set$2(this.elements, x, y, z); return this; } setScalar(s) { return this.set(s, s, s); } length() { return length$3(this.elements); } add(vec) { add$2(this.elements, this.elements, vec.elements); return this; } addScalar(v) { add$2(this.elements, this.elements, [v, v, v]); return this; } subtract(vec) { subtract$2(this.elements, this.elements, vec.elements); return this; } subtractScalar(v) { subtract$2(this.elements, this.elements, [v, v, v]); return this; } subVectors(a, b) { subtract$2(this.elements, a.elements, b.elements); return this; } multiply(vec) { multiply$3(this.elements, this.elements, vec.elements); return this; } multiplyScalar(v) { multiply$3(this.elements, this.elements, [v, v, v]); return this; } divide(vec) { divide$2(this.elements, this.elements, vec.elements); return this; } divideScalar(v) { divide$2(this.elements, this.elements, [v, v, v]); return this; } scale(s) { scale$2(this.elements, this.elements, s); return this; } scaleAndAdd(v, s) { scaleAndAdd$1(this.elements, this.elements, v.elements, s); return this; } distanceTo(vec) { return distance$2(this.elements, vec.elements); } distanceToSquared(vec) { return squaredDistance$2(this.elements, vec.elements); } angle(vector) { return angle$1(this.elements, [1, 0, 0]); } angleTo(vector) { return angle$1(this.elements, vector.elements); } dot(vec) { return dot$3(this.elements, vec.elements); } equals(vec) { return equals$3(this.elements, vec.elements); } cross(vec) { cross$2(this.elements, this.elements, vec.elements); return this; } negate() { negate$2(this.elements, this.elements); return this; } inverse() { inverse$2(this.elements, this.elements); return this; } lerp(vec, t) { lerp$2(this.elements, this.elements, vec.elements, t); return this; } normalize() { normalize$4(this.elements, this.elements); return this; } applyEuler(euler) { const e = new Quaternion().fromEuler(euler); return this.applyQuaternion(e); } applyMatrix3(matrix) { transformMat3$1(this.elements, this.elements, matrix.elements); return this; } applyMatrix4(matrix) { transformMat4$2(this.elements, this.elements, matrix.elements); return this; } applyQuaternion(quaternion) { transformQuat$1(this.elements, this.elements, quaternion.elements); return this; } copy(vec3) { this.x = vec3.x; this.y = vec3.y; this.z = vec3.z; return this; } clone() { return new Vector3(this.x, this.y, this.z); } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } class Vector4 extends Vector { elements = new (getFloatArrayConstructor())(4); constructor(x = 0, y = 0, z = 0, w = 0) { super(); const v = this.elements; v[0] = x; v[1] = y; v[2] = z; v[3] = w; } get x() { return this.elements[0]; } set x(x) { this.elements[0] = x; } get y() { return this.elements[1]; } set y(y) { this.elements[1] = y; } get z() { return this.elements[2]; } set z(z) { this.elements[2] = z; } get w() { return this.elements[3]; } set w(w) { this.elements[3] = w; } fromObject(object) { const { x, y, z, w } = object; if (x !== void 0) this.x = x; if (y !== void 0) this.y = y; if (z !== void 0) this.z = z; if (w !== void 0) this.w = w; return this; } toObject() { return { x: this.x, y: this.y, z: this.z, w: this.w }; } set(x, y, z, w) { set$1(this.elements, x, y, z, w); return this; } setScalar(s) { return this.set(s, s, s, s); } add(vec) { add$1(this.elements, this.elements, vec.elements); return this; } addScalar(v) { add$1(this.elements, this.elements, [v, v, v, v]); return this; } subtract(vec) { subtract$1(this.elements, this.elements, vec.elements); return this; } subtractScalar(v) { subtract$1(this.elements, this.elements, [v, v, v, v]); return this; } subVectors(a, b) { subtract$1(this.elements, a.elements, b.elements); return this; } multiply(vec) { multiply$2(this.elements, this.elements, vec.elements); return this; } multiplyScalar(v) { multiply$2(this.elements, this.elements, [v, v, v, v]); return this; } divide(vec) { divide$1(this.elements, this.elements, vec.elements); return this; } divideScalar(v) { divide$1(this.elements, this.elements, [v, v, v, v]); return this; } scale(s) { scale$1(this.elements, this.elements, s); return this; } scaleAndAdd(v, s) { scaleAndAdd(this.elements, this.elements, v.elements, s); return this; } distanceTo(vec) { return distance$1(this.elements, vec.elements); } distanceToSquared(vec) { return squaredDistance$1(this.elements, vec.elements); } length() { return length$2(this.elements); } dot(vec) { return dot$2(this.elements, vec.elements); } equals(vec) { return equals$2(this.elements, vec.elements); } cross(vec) { cross$1(this.elements, this.elements, vec.elements); return this; } negate() { negate$1(this.elements, this.elements); return this; } inverse() { inverse$1(this.elements, this.elements); return this; } lerp(vec, t) { lerp$1(this.elements, this.elements, vec.elements, t); return this; } normalize() { normalize$3(this.elements, this.elements); return this; } applyMatrix4(matrix) { transformMat4$1(this.elements, this.elements, matrix.elements); return this; } applyQuaternion(quaternion) { transformQuat(this.elements, this.elements, quaternion.elements); return this; } copy(vec4) { this.x = vec4.x; this.y = vec4.y; this.z = vec4.z; this.w = vec4.w; return this; } clone() { return new Vector4(this.x, this.y, this.z, this.w); } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } class Matrix { elements = new (getFloatArrayConstructor())(16); fromArray(array, offset = 0) { let i = 0; for (; i < this.elements.length; i++) { this.elements[i] = array[offset + i]; } return this; } toArray(out = [], offset = 0) { let i = 0; for (; i < this.elements.length; i++) { out[offset + i] = this.elements[i]; } return out; } } class Matrix3 extends Matrix { elements = new (getFloatArrayConstructor())(9); constructor(m00 = 1, m01 = 0, m02 = 0, m10 = 0, m11 = 1, m12 = 0, m20 = 0, m21 = 0, m22 = 1) { super(); const e = this.elements; e[0] = m00; e[1] = m01; e[2] = m02; e[3] = m10; e[4] = m11; e[5] = m12; e[6] = m20; e[7] = m21; e[8] = m22; } get x() { return this.elements[2]; } get y() { return this.elements[5]; } get z() { return this.elements[8]; } static get identity() { return new Matrix3().fromArray(identity$1([])); } set(m00, m01, m02, m10, m11, m12, m20, m21, m22) { set$4(this.elements, m00, m01, m02, m10, m11, m12, m20, m21, m22); return this; } transpose() { transpose$1(this.elements, this.elements); return this; } invert(m = this) { invert$2(this.elements, m.elements); return this; } adjoint(m = this) { adjoint$1(this.elements, m.elements); return this; } determinant() { return determinant$1(this.elements); } multiply(a, b) { if (b) { multiply$5(this.elements, a.elements, b.elements); } else { multiply$5(this.elements, this.elements, a.elements); } return this; } premultiply(a, b) { if (b) { multiply$5(this.elements, b.elements, a.elements); } else { multiply$5(this.elements, a.elements, this.elements); } return this; } translate(v) { translate$1(this.elements, this.elements, v.elements); return this; } rotate(rad) { rotate$1(this.elements, this.elements, rad); return this; } scale(v) { scale$4(this.elements, this.elements, v.elements); return this; } fromTranslation(v) { fromTranslation$1(this.elements, v.elements); return this; } fromRotation(rad) { fromRotation$1(this.elements, rad); return this; } fromScaling(v) { fromScaling$1(this.elements, v.elements); return this; } fromQuat(q) { fromQuat$1(this.elements, q.elements); return this; } normalFromMat4(m) { normalFromMat4(this.elements, m.elements); return this; } fromMat4(m) { fromMat4(this.elements, m.elements); return this; } frob() { return frob(this.elements); } add(a, b) { if (b) { add$4(this.elements, a.elements, b.elements); } else { add$4(this.elements, this.elements, a.elements); } return this; } subtract(a, b) { if (b) { subtract$4(this.elements, a.elements, b.elements); } else { subtract$4(this.elements, this.elements, a.elements); } return this; } equals(a, b) { if (b) { return equals$5(a.elements, b.elements); } else { return equals$5(this.elements, a.elements); } } fromRotationTranslationScale(rotation, x, y, scaleX, scaleY) { const cos = Math.cos(rotation); const sin = Math.sin(rotation); this.set(scaleX * cos, -scaleY * sin, 0, scaleX * sin, scaleY * cos, 0, x, y, 1); return this; } getNormalMatrix(m) { normalFromMat4(this.elements, m.elements); return this; } copy(m) { copy$3(this.elements, m.elements); return this; } clone() { return new Matrix3().copy(this); } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } const tempArray = []; class Matrix4 extends Matrix { elements = new (getFloatArrayConstructor())(16); constructor(m00 = 1, m01 = 0, m02 = 0, m03 = 0, m10 = 0, m11 = 1, m12 = 0, m13 = 0, m20 = 0, m21 = 0, m22 = 1, m23 = 0, m30 = 0, m31 = 0, m32 = 0, m33 = 1) { super(); const e = this.elements; e[0] = m00; e[1] = m01; e[2] = m02; e[3] = m03; e[4] = m10; e[5] = m11; e[6] = m12; e[7] = m13; e[8] = m20; e[9] = m21; e[10] = m22; e[11] = m23; e[12] = m30; e[13] = m31; e[14] = m32; e[15] = m33; } get x() { return this.elements[12]; } get y() { return this.elements[13]; } get z() { return this.elements[14]; } get w() { return this.elements[15]; } static get identity() { return new Matrix4().fromArray(identity([])); } set(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { set$3(this.elements, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33); return this; } transpose() { transpose(this.elements, this.elements); return this; } invert(m = this) { invert$1(this.elements, m.elements); return this; } adjoint(m = this) { adjoint(this.elements, m.elements); return this; } determinant() { return determinant(this.elements); } add(a, b) { if (b) { add$3(this.elements, a.elements, b.elements); } else { add$3(this.elements, this.elements, a.elements); } return this; } subtract(a, b) { if (b) { subtract$3(this.elements, a.elements, b.elements); } else { subtract$3(this.elements, this.elements, a.elements); } return this; } multiply(a, b) { if (b) { multiply$4(this.elements, a.elements, b.elements); } else { multiply$4(this.elements, this.elements, a.elements); } return this; } multiplyScalar(a = this, b) { multiplyScalar(this.elements, a.elements, b); return this; } premultiply(a, b) { if (b) { multiply$4(this.elements, b.elements, a.elements); } else { multiply$4(this.elements, a.elements, this.elements); } return this; } translate(v) { translate(this.elements, this.elements, v.elements); return this; } rotate(rad) { rotate(this.elements, this.elements, rad); return this; } scale(vec3) { scale$3(this.elements, this.elements, vec3.elements); return this; } scaleScalar(s) { scale$3(this.elements, this.elements, [s, s, s]); return this; } fromTranslation(vec) { fromTranslation(this.elements, vec.elements); return this; } fromRotation(rad, axis) { fromRotation(this.elements, rad, axis); return this; } fromRotationX(rad) { fromXRotation(this.elements, rad); return this; } fromRotationY(rad) { fromYRotation(this.elements, rad); return this; } fromRotationZ(rad) { fromZRotation(this.elements, rad); return this; } fromScale(vec) { fromScaling(this.elements, vec.elements); return this; } fromRotationTranslation(quat, v) { fromRotationTranslation(this.elements, quat.elements, v.elements); return this; } fromPerspective(fov, aspect, near, far) { perspective(this.elements, degToRad$1(fov), aspect, near, far); return this; } fromOrthogonal(left, right, bottom, top, near, far) { ortho(this.elements, left, right, bottom, top, near, far); return this; } fromQuat(q) { fromQuat(this.elements, q.elements); return this; } equals(mat4) { return equals$4(this.elements, mat4.value); } getRotation(q = new Quaternion()) { getRotation(tempArray, this.elements); q.set(tempArray[0], tempArray[1], tempArray[2], tempArray[3]); return q; } getScale(v = new Vector3()) { getScaling(tempArray, this.elements); v.set(tempArray[0], tempArray[1], tempArray[2]); return v; } getTranslation(v = new Vector3()) { getTranslation(tempArray, this.elements); v.set(tempArray[0], tempArray[1], tempArray[2]); return v; } rotateX(rad) { rotateX(this.elements, this.elements, rad); return this; } rotateY(rad) { rotateY(this.elements, this.elements, rad); return this; } rotateZ(rad) { rotateZ(this.elements, this.elements, rad); return this; } compose(v, q, s) { fromRotationTranslationScale(this.elements, q.elements, v.elements, s.elements); return this; } decompose() { return { rotation: this.getRotation(), scale: this.getScale(), translation: this.getTranslation() }; } copy(m) { copy$2(this.elements, m.elements); return this; } clone() { return new Matrix4().copy(this); } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } class Euler extends Vector { elements = new (getFloatArrayConstructor())(3); #changeCallbacks = []; #order = "xyz"; constructor(x = 0, y = 0, z = 0, order = "xyz") { super(); const v = this.elements; v[0] = x; v[1] = y; v[2] = z; this.#order = order; } get x() { return this.elements[0]; } set x(x) { this.elements[0] = x; this.triggerChange(); } get y() { return this.elements[1]; } set y(y) { this.elements[1] = y; this.triggerChange(); } get z() { return this.elements[2]; } set z(z) { this.elements[2] = z; this.triggerChange(); } get order() { return this.#order; } set order(order) { this.#order = order; this.triggerChange(); } get roll() { return this.x; } set roll(roll) { this.x = roll; } get pitch() { return this.y; } set pitch(pitch) { this.y = pitch; } get yaw() { return this.z; } set yaw(yaw) { this.z = yaw; } fromObject({ x, y, z, order }) { if (x !== void 0) { this.x = x; } if (y !== void 0) { this.y = y; } if (z !== void 0) { this.z = z; } if (order !== void 0) { this.order = order; } this.triggerChange(); return this; } toObject() { return { x: this.x, y: this.y, z: this.z, order: this.order }; } fromRotationMatrix(m, order = this.#order, update = true) { const te = m.toArray(); const m11 = te[0]; const m12 = te[4]; const m13 = te[8]; const m21 = te[1]; const m22 = te[5]; const m23 = te[9]; const m31 = te[2]; const m32 = te[6]; const m33 = te[10]; switch (order) { case "xyz": this.y = Math.asin(clamp$1(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this.x = Math.atan2(-m23, m33); this.z = Math.atan2(-m12, m11); } else { this.x = Math.atan2(m32, m22); this.z = 0; } break; case "yxz": this.x = Math.asin(-clamp$1(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this.y = Math.atan2(m13, m33); this.z = Math.atan2(m21, m22); } else { this.y = Math.atan2(-m31, m11); this.z = 0; } break; case "zxy": this.x = Math.asin(clamp$1(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this.y = Math.atan2(-m31, m33); this.z = Math.atan2(-m12, m22); } else { this.y = 0; this.z = Math.atan2(m21, m11); } break; case "zyx": this.y = Math.asin(-clamp$1(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this.x = Math.atan2(m32, m33); this.z = Math.atan2(m21, m11); } else { this.x = 0; this.z = Math.atan2(-m12, m22); } break; case "yzx": this.z = Math.asin(clamp$1(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this.x = Math.atan2(-m23, m22); this.y = Math.atan2(-m31, m11); } else { this.x = 0; this.y = Math.atan2(m13, m33); } break; case "xzy": this.z = Math.asin(-clamp$1(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this.x = Math.atan2(m32, m22); this.y = Math.atan2(m13, m11); } else { this.x = Math.atan2(-m23, m33); this.y = 0; } break; default: throw new Error("Unknown Euler angle order"); } this.#order = order; if (update) { this.triggerChange(); } return this; } fromQuaternion(q) { const [x, y, z, w] = q.elements; const a = y * y; const s = -2 * (a + z * z) + 1; const o = 2 * (x * y + w * z); let l = -2 * (x * z - w * y); const c = 2 * (y * z + w * x); const h = -2 * (x * x + a) + 1; l = l > 1 ? 1 : l; l = l < -1 ? -1 : l; const d = Math.atan2(c, h); const u = Math.asin(l); const f = Math.atan2(o, s); return new Euler(d, u, f, "zyx"); } fromVector3(vec3, order = this.#order) { return this.set(vec3.x, vec3.y, vec3.z, order); } toQuaternion() { const t = Math.cos(0.5 * this.yaw); const e = Math.sin(0.5 * this.yaw); const n = Math.cos(0.5 * this.roll); const r = Math.sin(0.5 * this.roll); const i = Math.cos(0.5 * this.pitch); const a = Math.sin(0.5 * this.pitch); return new Quaternion( t * r * i - e * n * a, t * n * a + e * r * i, e * n * i - t * r * a, t * n * i + e * r * a ); } toVector3() { return new Vector3(this.x, this.y, this.z); } set(x, y, z, order = this.#order) { this.elements[0] = x; this.elements[1] = y; this.elements[2] = z; this.#order = order; this.triggerChange(); return this; } clone() { return new Euler().copy(this); } copy(euler) { let i = 0; for (; i < this.elements.length; i++) { this.elements[i] = euler.elements[i]; } this.#order = euler.order; this.triggerChange(); return this; } equals(e) { return this.x === e.x && this.y === e.y && this.z === e.z && this.order === e.order; } onChange(cb) { if (!this.#changeCallbacks.includes(cb)) { this.#changeCallbacks.push(cb); } } triggerChange() { this.#changeCallbacks.forEach((f) => f()); } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } function namesPlugin(e,f){var a={white:"#ffffff",bisque:"#ffe4c4",blue:"#0000ff",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",antiquewhite:"#faebd7",aqua:"#00ffff",azure:"#f0ffff",whitesmoke:"#f5f5f5",papayawhip:"#ffefd5",plum:"#dda0dd",blanchedalmond:"#ffebcd",black:"#000000",gold:"#ffd700",goldenrod:"#daa520",gainsboro:"#dcdcdc",cornsilk:"#fff8dc",cornflowerblue:"#6495ed",burlywood:"#deb887",aquamarine:"#7fffd4",beige:"#f5f5dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkkhaki:"#bdb76b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",peachpuff:"#ffdab9",darkmagenta:"#8b008b",darkred:"#8b0000",darkorchid:"#9932cc",darkorange:"#ff8c00",darkslateblue:"#483d8b",gray:"#808080",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",deeppink:"#ff1493",deepskyblue:"#00bfff",wheat:"#f5deb3",firebrick:"#b22222",floralwhite:"#fffaf0",ghostwhite:"#f8f8ff",darkviolet:"#9400d3",magenta:"#ff00ff",green:"#008000",dodgerblue:"#1e90ff",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",blueviolet:"#8a2be2",forestgreen:"#228b22",lawngreen:"#7cfc00",indianred:"#cd5c5c",indigo:"#4b0082",fuchsia:"#ff00ff",brown:"#a52a2a",maroon:"#800000",mediumblue:"#0000cd",lightcoral:"#f08080",darkturquoise:"#00ced1",lightcyan:"#e0ffff",ivory:"#fffff0",lightyellow:"#ffffe0",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",linen:"#faf0e6",mediumaquamarine:"#66cdaa",lemonchiffon:"#fffacd",lime:"#00ff00",khaki:"#f0e68c",mediumseagreen:"#3cb371",limegreen:"#32cd32",mediumspringgreen:"#00fa9a",lightskyblue:"#87cefa",lightblue:"#add8e6",midnightblue:"#191970",lightpink:"#ffb6c1",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",mintcream:"#f5fffa",lightslategray:"#778899",lightslategrey:"#778899",navajowhite:"#ffdead",navy:"#000080",mediumvioletred:"#c71585",powderblue:"#b0e0e6",palegoldenrod:"#eee8aa",oldlace:"#fdf5e6",paleturquoise:"#afeeee",mediumturquoise:"#48d1cc",mediumorchid:"#ba55d3",rebeccapurple:"#663399",lightsteelblue:"#b0c4de",mediumslateblue:"#7b68ee",thistle:"#d8bfd8",tan:"#d2b48c",orchid:"#da70d6",mediumpurple:"#9370db",purple:"#800080",pink:"#ffc0cb",skyblue:"#87ceeb",springgreen:"#00ff7f",palegreen:"#98fb98",red:"#ff0000",yellow:"#ffff00",slateblue:"#6a5acd",lavenderblush:"#fff0f5",peru:"#cd853f",palevioletred:"#db7093",violet:"#ee82ee",teal:"#008080",slategray:"#708090",slategrey:"#708090",aliceblue:"#f0f8ff",darkseagreen:"#8fbc8f",darkolivegreen:"#556b2f",greenyellow:"#adff2f",seagreen:"#2e8b57",seashell:"#fff5ee",tomato:"#ff6347",silver:"#c0c0c0",sienna:"#a0522d",lavender:"#e6e6fa",lightgreen:"#90ee90",orange:"#ffa500",orangered:"#ff4500",steelblue:"#4682b4",royalblue:"#4169e1",turquoise:"#40e0d0",yellowgreen:"#9acd32",salmon:"#fa8072",saddlebrown:"#8b4513",sandybrown:"#f4a460",rosybrown:"#bc8f8f",darksalmon:"#e9967a",lightgoldenrodyellow:"#fafad2",snow:"#fffafa",lightgrey:"#d3d3d3",lightgray:"#d3d3d3",dimgray:"#696969",dimgrey:"#696969",olivedrab:"#6b8e23",olive:"#808000"},r={};for(var d in a)r[a[d]]=d;var l={};e.prototype.toName=function(f){if(!(this.rgba.a||this.rgba.r||this.rgba.g||this.rgba.b))return "transparent";var d,i,n=r[this.toHex()];if(n)return n;if(null==f?void 0:f.closest){var o=this.toRgb(),t=1/0,b="black";if(!l.length)for(var c in a)l[c]=new e(a[c]).toRgb();for(var g in a){var u=(d=o,i=l[g],Math.pow(d.r-i.r,2)+Math.pow(d.g-i.g,2)+Math.pow(d.b-i.b,2));u { const hex = isHex(a); const diff = max - min; let v = clamp$1(Number.parseFloat(`${a}`), min, max); if (hex) { v = Number.parseInt("" + a * max, 10) / 100; } return Math.abs(v - max) < 1e-6 ? 1 : a % diff / diff; }; class Color { r; g; b; a; constructor(v = 255, g, b, a = 1, isNormalized = false) { this.r = 1; this.g = 1; this.b = 1; this.a = 1; if (isUndef(g) && isUndef(b)) { if (isNumber(v) && v <= 255) { this.setRGBA(v, v, v, this.a, isNormalized); } else { const rgb = w(v).toRgb(); if (rgb) { this.setRGBA(rgb.r, rgb.g, rgb.b, rgb.a); } else { console.error("Unsupported color value {".concat(String(v), "} provided")); } } } else { this.setRGBA(v, g, b, a); } } fromColor(c) { const color = w(c).toRgb(); return this.setRGBA(color.r, color.g, color.b, color.a); } fromHSL(h, s, l, a = 1) { const color = w({ h, s, l, a }).toRgb(); return this.setRGBA(color.r, color.g, color.b, color.a); } fromHSV(h, s, v, a = 1) { const color = w({ h, s, v, a }).toRgb(); return this.setRGBA(color.r, color.g, color.b, color.a); } setRGB(r, g, b) { this.setRGBA(r, g, b, this.a); return this; } setRGBA(r, g, b, a, isNormalized) { this.r = isNormalized ? r : normalize(r, 0, 255); this.g = isNormalized ? g : normalize(g, 0, 255); this.b = isNormalized ? b : normalize(b, 0, 255); this.setAlpha(a); return this; } setAlpha(alpha) { if (alpha > 1) { this.a = normalize(alpha, 0, 255); } else { this.a = alpha; } return this; } toHex() { return w(this.toObject()).toHex(); } toHSL() { return w(this.toObject()).toHsl(); } toHSV() { return w(this.toObject()).toHsv(); } toObject(isNormalized = false) { const m = isNormalized ? 1 : 255; return { r: this.r * m, g: this.g * m, b: this.b * m, a: this.a }; } toArray() { return [this.r, this.g, this.b, this.a]; } toVector() { return new Vector4().fromArray(this.toArray()); } toVector3() { return new Vector3().fromArray(this.toArray()); } toString() { return `${this.constructor.name}(${this.r}, ${this.g}, ${this.b}, ${this.a})`; } } class ProjectionMatrix extends Matrix4 { frustum(mat4, left, right, top, bottom, near, far) { frustum(mat4.elements, left, right, bottom, top, near, far); return this; } orthographic(left, right, top, bottom, near, far) { ortho(this.elements, left, right, bottom, top, near, far); return this; } perspective(fovy, aspect, near, far) { perspective(this.elements, fovy, aspect, near, far); return this; } lookAt(eye, target = new Vector3(0, 0, 0), up = new Vector3(0, 1, 0)) { lookAt(this.elements, eye.elements, target.elements, up.elements); return this; } toString() { return `${this.constructor.name}(${this.elements.join(", ")})`; } } class Object3D { visible; localMatrix; worldMatrix; matrixAutoUpdate; position; scale; rotation; quaternion; up; children; parent; worldMatrixNeedsUpdate; constructor() { this.visible = true; this.localMatrix = new ProjectionMatrix(); this.worldMatrix = new ProjectionMatrix(); this.matrixAutoUpdate = true; this.position = new Vector3(); this.scale = new Vector3(1, 1, 1); this.rotation = new Euler(); this.quaternion = new Quaternion(); this.up = new Vector3(0, 1, 0); this.parent = null; this.children = []; this.worldMatrixNeedsUpdate = false; this.rotation.onChange(() => { this.quaternion.fromEuler(this.rotation); }); this.quaternion.onChange(() => { this.rotation.fromQuaternion(this.quaternion); }); } add(object, notifyChild = true) { if (!this.contains(object)) { this.children.push(object); } if (notifyChild) { object.setParent(this, false); } } remove(object, notifyChild = true) { if (this.contains(object)) { this.children.splice(this.children.indexOf(object), 1); } if (notifyChild) { object.setParent(null, false); } } contains(object) { return this.children.includes(object); } setParent(object, notifyParent = true) { if (this.parent && object !== this.parent) { this.parent.remove(this, false); } this.parent = object; if (notifyParent && object) { object.add(this, false); } } traverse(callback) { if (!callback(this)) { for (let i = 0, l = this.children.length; i < l; i++) { this.children[i].traverse(callback); } } } lookAt(eye, invert) { if (invert) { this.localMatrix.lookAt(this.position, eye, this.up); } else { this.localMatrix.lookAt(eye, this.position, this.up); } this.localMatrix.getRotation(this.quaternion); this.rotation.fromQuaternion(this.quaternion); } updateMatrixWorld(force) { let f = force; if (this.matrixAutoUpdate) { this.updateMatrix(); } if (this.worldMatrixNeedsUpdate || f) { if (this.parent === null) { this.worldMatrix.copy(this.localMatrix); } else { this.worldMatrix.multiply(this.parent.worldMatrix, this.localMatrix); } this.worldMatrixNeedsUpdate = false; f = true; } for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; child.updateMatrixWorld(f); } } updateMatrix() { this.localMatrix.compose(this.position, this.quaternion, this.scale); this.worldMatrixNeedsUpdate = true; } decompose() { this.localMatrix.getTranslation(this.position); this.localMatrix.getRotation(this.quaternion); this.localMatrix.getScale(this.scale); this.rotation.fromQuaternion(this.quaternion); } clone() { return new Object3D().copy(this, false); } copy(object, recursive) { this.visible = object.visible; this.position.copy(object.position); this.scale.copy(object.scale); this.rotation.copy(object.rotation); this.quaternion.copy(object.quaternion); this.up.copy(object.up); this.localMatrix.copy(object.localMatrix); this.worldMatrix.copy(object.worldMatrix); this.matrixAutoUpdate = object.matrixAutoUpdate; if (recursive) { for (let i = 0, n = object.children.length; i < n; i++) { const children = object.children[i]; this.add(children.clone()); } } return this; } } class Base { renderer; constructor(renderer) { this.renderer = renderer; } get gl() { return this.renderer.gl; } get rendererState() { return this.renderer.state; } } const getBufferType = (gl, data) => { if (data instanceof Float32Array || data instanceof Float64Array) { return gl.FLOAT; } if (data instanceof Uint16Array) { return gl.UNSIGNED_SHORT; } if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { return gl.UNSIGNED_BYTE; } if (data instanceof Uint32Array) { return gl.UNSIGNED_INT; } if (data instanceof Int8Array) { return gl.BYTE; } if (data instanceof Int16Array) { return gl.SHORT; } if (data instanceof Int32Array) { return gl.INT; } }; class BufferAttribute { id; data; type; size; instanced; stride; offset; divisor; normalized; needsUpdate; count; usage; target; buffer; constructor(renderer, attribute) { const attr = Object.assign( {}, { size: 1, normalized: true, stride: 0, offset: 0, divisor: 0, usage: renderer.gl.STATIC_DRAW }, attribute ); this.id = uid("attribute"); this.needsUpdate = false; if (!attribute.data || Array.isArray(attribute.data)) { throw new TypeError("BufferAttribute: data should be a typed array"); } this.data = attr.data; this.size = attr.size || 1; this.type = attr.type || getBufferType(renderer.gl, attr.data); this.normalized = attr.normalized || false; this.stride = attr.stride || 0; this.offset = attr.offset || 0; this.divisor = attr.divisor || 0; this.instanced = attr.divisor > 0; this.usage = attr.usage || renderer.gl.STATIC_DRAW; if (attr.target) { this.target = attr.target; } let count = attr.count; if (attr.count === void 0 || attr.count === null) { count = attr.stride ? attr.data.byteLength / attr.stride : attr.data.length / attr.size; } this.count = count; } } const tempVec3 = new Vector3(); class Geometry extends Base { #id; #attributes; #VAOs; #bounds; drawRange; instancedCount; isInstanced; drawMode; constructor(renderer, attributes = {}) { super(renderer); this.drawRange = { start: 0, count: 0 }; this.instancedCount = 0; this.isInstanced = false; this.#attributes = /* @__PURE__ */ new Map(); this.#VAOs = /* @__PURE__ */ new Map(); this.#id = uid("geometry"); this.drawMode = this.gl.TRIANGLES; renderer.bindVertexArray(null); renderer.state.setActiveGeometry(null); for (const name in attributes) { const attribute = attributes[name]; if (attribute instanceof BufferAttribute) { if (name === "index") { this.setIndex(attribute); } else { this.addAttribute(name, attribute); } } else { if (attribute.data) { const n = new BufferAttribute(this.renderer, attribute); if (name === "index") { this.setIndex(n); } else { this.addAttribute(name, n); } } } } } get id() { return this.#id; } get attributes() { return this.#attributes; } get attributesData() { const attributes = {}; const iterator = this.#attributes.entries(); for (let i = 0; i < this.#attributes.size; i++) { const entry = iterator.next().value; attributes[entry[0]] = omit(entry[1], [ "id", "buffer" ]); } return attributes; } get index() { return this.attributes.get("index"); } get bounds() { return this.#bounds; } set bounds(bounds) { this.#bounds = bounds; } addAttribute(name, attribute) { if (!attribute.target) { attribute.target = name === "index" ? this.gl.ELEMENT_ARRAY_BUFFER : this.gl.ARRAY_BUFFER; } attribute.needsUpdate = false; this.attributes.set(name, attribute); if (!attribute.buffer) { attribute.buffer = this.gl.createBuffer(); this.updateAttribute(attribute); } if (attribute.divisor) { this.isInstanced = true; if (this.instancedCount && this.instancedCount !== attribute.count * attribute.divisor) { this.instancedCount = Math.min(this.instancedCount, attribute.count * attribute.divisor); return console.warn( `Geometry has multiple instanced buffers of different length - instancedCount: ${this.instancedCount}, count: ${attribute.count}, divisor: ${attribute.divisor}, attribute: ${name}` ); } this.instancedCount = attribute.count * attribute.divisor; } else if (name === "index") { this.drawRange.count = attribute.count; } else if (!this.index) { this.drawRange.count = Math.max(this.drawRange.count, attribute.count); } } getAttribute(name) { return this.attributes.get(name); } setAttributeData(name, data) { const attribute = this.getAttribute(name); if (attribute) { attribute.data = data; attribute.needsUpdate = true; } } updateAttribute(attribute) { const createBuffer = !attribute.buffer; if (createBuffer) { attribute.buffer = this.gl.createBuffer(); } if (this.rendererState.boundBuffer !== attribute.buffer) { this.gl.bindBuffer(attribute.target, attribute.buffer); this.rendererState.boundBuffer = attribute.buffer; } this.gl.bufferData(attribute.target, attribute.data, attribute.usage); attribute.needsUpdate = false; } removeAttribute(attribute) { this.attributes.delete(attribute); } setIndex(index) { if (index instanceof BufferAttribute) { index.size = 1; this.addAttribute("index", index); } else { const buffer = new BufferAttribute(this.renderer, { data: index.length > 65535 ? new Uint32Array(index) : new Uint16Array(index), size: 1 }); this.addAttribute("index", buffer); } this.drawRange.count = this.index?.count; } setVertices(data) { const array = []; const len = data.length; for (let i = 0; i < len; i++) { const item = data[i]; array.push(item[0], item[1], item[2]); } this.addAttribute( "position", new BufferAttribute(this.renderer, { data: new Float32Array(array), size: 3 }) ); } setNormals(data) { this.addAttribute( "normal", new BufferAttribute(this.renderer, { data: new Float32Array(data), size: 2 }) ); } setUVs(data) { this.addAttribute( "uv", new BufferAttribute(this.renderer, { data: new Float32Array(data), size: 2 }) ); } setColors(colors) { const data = []; for (let i = 0; i < colors.length; i++) { let color = colors[i]; if (color && (color instanceof Vector3 || color instanceof Vector4)) { color = color.toArray(); } data.push(color[0], color[1], color[2], color[3] || 1); } this.addAttribute( "color", new BufferAttribute(this.renderer, { data: new Float32Array(data), size: 4 }) ); } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } setInstancedCount(count) { this.instancedCount = count; } createVAO(program) { const { attributeOrder } = program; const vao = this.renderer.createVertexArray(); this.renderer.bindVertexArray(vao); this.#VAOs.set(attributeOrder, vao); this.bindAttributes(program); } bindAttributes(program) { program.attributeLocations.forEach((location, { name, type }) => { const attributes = this.attributes.get(name); if (!attributes) return; this.gl.bindBuffer(attributes.target, attributes.buffer); this.rendererState.boundBuffer = attributes.buffer; let numLoc = 1; if (type === this.gl.FLOAT_MAT2) numLoc = 2; if (type === this.gl.FLOAT_MAT3) numLoc = 3; if (type === this.gl.FLOAT_MAT4) numLoc = 4; const size = attributes.size / numLoc; const stride = numLoc === 1 ? 0 : numLoc * numLoc * numLoc; const offset = numLoc === 1 ? 0 : numLoc * numLoc; for (let i = 0; i < numLoc; i++) { const attribIndex = location + i; this.gl.vertexAttribPointer( attribIndex, size, attributes.type, attributes.normalized, attributes.stride + stride, attributes.offset + offset ); this.gl.enableVertexAttribArray(attribIndex); this.renderer.vertexAttribDivisor(attribIndex, attributes.divisor); } }); const index = this.attributes.get("index"); if (index) { this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, index.buffer); } } computeBoundingBox(vertices) { const { data, offset = 0, stride, size } = this.attributes.get("position"); if (!this.#bounds) { this.#bounds = { min: new Vector3(), max: new Vector3(), center: new Vector3(), scale: new Vector3(), radius: Number.POSITIVE_INFINITY }; } this.#bounds.min.setScalar(+Number.POSITIVE_INFINITY); this.#bounds.max.setScalar(Number.NEGATIVE_INFINITY); const array = vertices || data; const dl = stride || size; for (let i = offset; i < array.length; i += dl) { const x = array[i + 0]; const y = array[i + 1]; const z = array[i + 2]; this.#bounds.min.x = Math.min(x, this.#bounds.min.x); this.#bounds.min.y = Math.min(y, this.#bounds.min.y); this.#bounds.min.z = Math.min(z, this.#bounds.min.z); this.#bounds.max.x = Math.max(x, this.#bounds.max.x); this.#bounds.max.y = Math.max(y, this.#bounds.max.y); this.#bounds.max.z = Math.max(z, this.#bounds.max.z); } this.#bounds.scale.subVectors(this.#bounds.max, this.#bounds.min); this.#bounds.center.add(this.#bounds.min).add(this.#bounds.max).divideScalar(2); return this.#bounds; } computeBoundingSphere(vertices) { const { data, offset = 0, stride, size } = this.attributes.get("position"); if (!this.#bounds) { this.computeBoundingBox(vertices); } const array = vertices || data; let len = 0; const dl = stride || size; const length = array.length; for (let j = offset; j < length; j += dl) { tempVec3.fromArray(array, j); len = Math.max(len, this.#bounds.center.distanceToSquared(tempVec3)); } this.#bounds.radius = Math.sqrt(len); } draw(program, drawMode = this.drawMode) { const { start, count } = this.drawRange; const activeGeometryId = `${this.id}_${program.attributeOrder}`; if (this.rendererState.activeGeometryId !== activeGeometryId) { const vao = this.#VAOs.get(program.attributeOrder); if (!vao) { this.createVAO(program); } this.renderer.bindVertexArray(this.#VAOs.get(program.attributeOrder)); this.rendererState.activeGeometryId = activeGeometryId; } program.attributeLocations.forEach((location, { name }) => { const attribute = this.getAttribute(name); if (attribute && attribute.needsUpdate) { this.updateAttribute(attribute); } }); if (this.isInstanced) { if (this.index) { const offset = this.index.offset + 2 * start; this.renderer.drawElementsInstanced( drawMode, count, this.index.type, offset, this.instancedCount ); } else { this.renderer.drawArraysInstanced(drawMode, start, count, this.instancedCount); } } else if (this.index) { const offset = this.index.offset + 2 * start; this.gl.drawElements(drawMode, count, this.index.type, offset); } else { this.gl.drawArrays(drawMode, start, count); } } copy(source) { const attributes = source.attributesData; for (const name in attributes) { const attribute = attributes[name]; if (attribute instanceof BufferAttribute) { if (name === "index") { this.setIndex(attribute); } else { this.addAttribute(name, attribute); } } else { if (attribute.data) { const n = new BufferAttribute(this.renderer, attribute); if (name === "index") { this.setIndex(n); } else { this.addAttribute(name, n); } } } } if (source.bounds) { this.bounds = { min: new Vector3().copy(source.bounds.min), max: new Vector3().copy(source.bounds.max), center: new Vector3().copy(source.bounds.center), scale: new Vector3().copy(source.bounds.scale), radius: source.bounds.radius }; } return this; } clone() { const geometry = new Geometry(this.renderer, {}).copy(this); geometry.drawMode = this.drawMode; return geometry; } destroy() { this.#VAOs.forEach((t) => { this.renderer.deleteVertexArray(t); }); this.#VAOs.clear(); this.#attributes.forEach((t) => { this.gl.deleteBuffer(t.buffer); }); this.#attributes.clear(); } } class Mesh extends Object3D { gl; modelViewMatrix; normalMatrix; renderOrder; zDepth; frustumCulled; mode; renderer; #id; #lastMode; #geometry; #program; #wireframe; #wireframeGeometry; constructor(renderer, options = {}) { super(); const opts = Object.assign({}, { mode: renderer.gl.TRIANGLES, frustumCulled: true, renderOrder: 0 }, options); this.renderer = renderer; this.gl = this.renderer.gl; this.modelViewMatrix = new Matrix4(); this.normalMatrix = new Matrix3(); this.renderOrder = opts.renderOrder; this.frustumCulled = opts.frustumCulled; this.zDepth = 0; this.#id = opts.id || uid("mesh"); this.#geometry = opts.geometry; this.#program = opts.program; this.#wireframe = Boolean(opts.wireframe); this.mode = opts.mode; this.#lastMode = opts.mode; if (this.#wireframe) { this.mode = this.gl.LINES; this.updateWireframeGeometry(this.#wireframe); } } get id() { return this.#id; } get geometry() { return this.#wireframe ? this.#wireframeGeometry : this.#geometry; } get program() { return this.#program; } set wireframe(wireframe) { this.mode = wireframe ? this.gl.LINES : this.#lastMode; this.#wireframe = wireframe; this.updateWireframeGeometry(this.#wireframe); } get wireframe() { return this.#wireframe; } draw(options = {}) { const { camera, target } = options; const uniforms = {}; if (camera) { Object.assign(uniforms, { projectionMatrix: camera.projectionMatrix, cameraPosition: camera.worldPosition, viewMatrix: camera.viewMatrix }); this.modelViewMatrix.multiply(camera.viewMatrix, this.worldMatrix); this.normalMatrix.getNormalMatrix(this.modelViewMatrix); } else { this.modelViewMatrix.copy(this.worldMatrix); } Object.assign(uniforms, { resolution: new Vector2( this.renderer.state?.viewport?.width || 1, this.renderer.state?.viewport?.height || 1 ), modelMatrix: this.worldMatrix, modelViewMatrix: this.modelViewMatrix, normalMatrix: this.normalMatrix }); Object.keys(uniforms).forEach((key) => { if (!Object.hasOwn(this.program.uniforms, key)) { this.program.uniforms[key] = { value: null }; } this.program.uniforms[key].value = uniforms[key]; }); if (target) target.bind(); this.program.use(); this.geometry.draw(this.program, this.mode); if (target) target.unbind(); } updateWireframeGeometry(wireframe, force = false) { if (this.#geometry && (force || !this.#wireframeGeometry)) { if (this.#wireframeGeometry) { this.#wireframeGeometry.destroy(); } const attributes = this.#geometry.attributes; const positionArray = attributes.get("position")?.data; const indexAttribute = this.#geometry.index?.data; const numIndices = indexAttribute ? indexAttribute.length : Math.floor(positionArray.length / 3); const index = []; if (this.#geometry.index) { if (indexAttribute) { getWireframeIndex(positionArray, index, numIndices, indexAttribute); } } else { getWireframeIndex(positionArray, index, numIndices); } const indices = index.length > 65536 ? new Uint32Array(index) : new Uint16Array(index); this.#wireframeGeometry = new Geometry(this.renderer, { ...this.#geometry.attributesData, index: { data: indices } }); } } updateGeometry(geometry, destroy = true) { if (destroy && this.#geometry) { this.#geometry.destroy(); } this.#geometry = geometry; this.updateWireframeGeometry(this.#wireframe, true); } updateProgram(program, destroy = true) { if (destroy && this.#program) { this.#program.destroy(); } this.#program = program; } destroy() { this.program.destroy(); this.geometry.destroy(); } clone() { return new Mesh(this.gl, { geometry: this.geometry, program: this.program, frustumCulled: this.frustumCulled, mode: this.mode, renderOrder: this.renderOrder }).copy(this); } copy(mesh, recursive = true) { super.copy(mesh, recursive); this.modelViewMatrix.copy(mesh.modelViewMatrix); this.normalMatrix.copy(mesh.normalMatrix); this.mode = mesh.mode; this.renderOrder = mesh.renderOrder; this.zDepth = mesh.zDepth; return this; } } class Scene extends Object3D { clone() { return new Scene().copy(this, false); } copy(source, recursive) { super.copy(source, recursive); this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } } var BlendType = /* @__PURE__ */ ((BlendType2) => { BlendType2[BlendType2["NoBlending"] = 0] = "NoBlending"; BlendType2[BlendType2["NormalBlending"] = 1] = "NormalBlending"; BlendType2[BlendType2["AdditiveBlending"] = 2] = "AdditiveBlending"; BlendType2[BlendType2["SubtractiveBlending"] = 3] = "SubtractiveBlending"; BlendType2[BlendType2["MultiplyBlending"] = 4] = "MultiplyBlending"; BlendType2[BlendType2["CustomBlending"] = 5] = "CustomBlending"; return BlendType2; })(BlendType || {}); class State extends Base { #state; constructor(renderer, options) { super(renderer); const { gl } = renderer; this.#state = { viewport: { x: 0, y: 0, width: 0, height: 0 } }; this.apply( options || { frontFace: gl.CCW, depthTest: false, depthWrite: true, depthMask: true, depthFunc: gl.LESS, blending: 1 /* NormalBlending */, blendFunc: { src: gl.ONE, dst: gl.ZERO }, blendEquation: { modeRGB: gl.FUNC_ADD }, premultiplyAlpha: false, unpackAlignment: 4, flipY: false, framebuffer: null, textureUnits: [], activeTextureUnit: -1, activeGeometryId: -1, currentProgramId: -1, clearAlpha: 1, clearColor: new Color(0), stencil: { func: {}, opFront: {}, opBack: {} } } ); } get state() { return this.#state; } get viewport() { return this.#state.viewport; } get textureUnits() { return this.#state.textureUnits; } get activeTextureUnit() { return this.#state.activeTextureUnit; } set activeTextureUnit(activeTextureUnit) { this.#state.activeTextureUnit = activeTextureUnit; } get currentProgramId() { return this.#state.currentProgramId; } set currentProgramId(id) { this.#state.currentProgramId = id; } get activeGeometryId() { return this.#state.activeGeometryId; } set activeGeometryId(id) { this.#state.activeGeometryId = id; } set flipY(flipY) { this.#state.flipY = flipY; } get flipY() { return this.#state.flipY; } set unpackAlignment(unpackAlignment) { this.#state.unpackAlignment = unpackAlignment; } get unpackAlignment() { return this.#state.unpackAlignment; } set premultiplyAlpha(premultiplyAlpha) { this.#state.premultiplyAlpha = premultiplyAlpha; } get premultiplyAlpha() { return this.#state.premultiplyAlpha; } set boundBuffer(boundBuffer) { this.#state.boundBuffer = boundBuffer; } get boundBuffer() { return this.#state.boundBuffer; } set anisotropy(anisotropy) { this.#state.anisotropy = anisotropy; } get anisotropy() { return this.#state.anisotropy; } apply(options) { if (options.blending !== void 0 && options.blending !== null) { this.setBlending(options.blending, options); } else { if (options.blendFunc) { const { src, dst, srcAlpha, dstAlpha } = options.blendFunc; this.setBlendFunc(src, dst, srcAlpha, dstAlpha); this.enable(this.gl.BLEND); } else { this.disable(this.gl.BLEND); } if (options.blendEquation) { const { modeRGB, modeAlpha } = options.blendEquation; this.setBlendEquation(modeRGB, modeAlpha); } } if (!isUndef(options.cullFace) && !isNull(options.cullFace)) { this.setCullFace(options.cullFace); } if (!isUndef(options.frontFace) && !isNull(options.frontFace)) { this.setFrontFace(options.frontFace); } if (options.depthTest) { this.enable(this.gl.DEPTH_TEST); } else { this.disable(this.gl.DEPTH_TEST); } if (!isUndef(options.depthMask) && !isNull(options.depthMask)) { this.setDepthMask(options.depthMask); } if (!isUndef(options.depthWrite) && !isNull(options.depthWrite)) { this.setDepthMask(options.depthWrite); } if (!isUndef(options.depthFunc) && !isNull(options.depthFunc)) { this.setDepthFunc(options.depthFunc); } if (!isUndef(options.lineWidth) && !isNull(options.lineWidth)) { this.setLineWidth(options.lineWidth); } this.#state = Object.assign(this.#state, options); } enable(id) { if (this.#state[id] !== true) { this.gl.enable(id); this.#state[id] = true; } } disable(id) { if (this.#state[id] !== false) { this.gl.disable(id); this.#state[id] = false; } } setViewport(width, height, x = 0, y = 0) { if (this.#state.viewport.width === width && this.#state.viewport.height === height) return; this.gl.viewport(x, y, width, height); this.#state.viewport = { width, height, x, y }; } setMask(colorMask) { if (this.#state.colorMask !== colorMask) { this.gl.colorMask(colorMask, colorMask, colorMask, colorMask); this.#state.colorMask = colorMask; } } setBlending(blending, options) { this.#state.blending = blending; if (blending === 0 /* NoBlending */) { this.disable(this.gl.BLEND); return; } else { this.enable(this.gl.BLEND); } if (blending === 2 /* AdditiveBlending */) { if (this.#state.premultiplyAlpha) { this.setBlendEquation(this.gl.FUNC_ADD, this.gl.FUNC_ADD); this.setBlendFunc(this.gl.ONE, this.gl.ONE, this.gl.ONE, this.gl.ONE); } else { this.setBlendEquation(this.gl.FUNC_ADD); this.setBlendFunc(this.gl.SRC_ALPHA, this.gl.ONE); } } else if (blending === 3 /* SubtractiveBlending */) { if (this.#state.premultiplyAlpha) { this.setBlendEquation(this.gl.FUNC_ADD, this.gl.FUNC_ADD); this.setBlendFunc( this.gl.ZERO, this.gl.ZERO, this.gl.ONE_MINUS_SRC_COLOR, this.gl.ONE_MINUS_SRC_ALPHA ); } else { this.setBlendEquation(this.gl.FUNC_ADD); this.setBlendFunc(this.gl.ZERO, this.gl.ONE_MINUS_SRC_COLOR); } } else if (blending === 4 /* MultiplyBlending */) { if (this.#state.premultiplyAlpha) { this.setBlendEquation(this.gl.FUNC_ADD, this.gl.FUNC_ADD); this.setBlendFunc(this.gl.ZERO, this.gl.SRC_COLOR, this.gl.ZERO, this.gl.SRC_ALPHA); } else { this.setBlendEquation(this.gl.FUNC_ADD); this.setBlendFunc(this.gl.ZERO, this.gl.SRC_COLOR); } } else if (blending === 1 /* NormalBlending */) { if (this.#state.premultiplyAlpha) { this.setBlendEquation(this.gl.FUNC_ADD, this.gl.FUNC_ADD); this.setBlendFunc( this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA ); } else { this.setBlendEquation(this.gl.FUNC_ADD, this.gl.FUNC_ADD); this.setBlendFunc( this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA ); } } else if (blending === 5 /* CustomBlending */) { if (options?.blendFunc) { const { src, dst, srcAlpha, dstAlpha } = options.blendFunc; this.setBlendFunc(src, dst, srcAlpha, dstAlpha); this.enable(this.gl.BLEND); } if (options?.blendEquation) { const { modeRGB, modeAlpha } = options.blendEquation; this.setBlendEquation(modeRGB, modeAlpha); } } else { console.error("State: Invalid blending: ", blending); } } setBlendFunc(src, dst, srcAlpha, dstAlpha) { if (src !== this.#state.blendFunc?.src || dst !== this.#state.blendFunc?.dst || srcAlpha !== this.#state.blendFunc?.srcAlpha || dstAlpha !== this.#state.blendFunc?.dstAlpha) { this.#state.blendFunc = { src, dst, srcAlpha, dstAlpha }; if (!isUndef(srcAlpha) && !isNull(srcAlpha) && !isUndef(dstAlpha) && !isNull(dstAlpha)) { this.gl.blendFuncSeparate(src, dst, srcAlpha, dstAlpha); } else { this.gl.blendFunc(src, dst); } } } setBlendEquation(modeRGB, modeAlpha) { if (modeRGB !== this.#state.blendEquation?.modeRGB || modeAlpha !== this.#state.blendEquation?.modeAlpha) { this.#state.blendEquation = { modeRGB, modeAlpha }; if (!isUndef(modeAlpha) && !isNull(modeAlpha)) { this.gl.blendEquationSeparate(modeRGB, modeAlpha); } else { this.gl.blendEquation(modeRGB); } } } setClearAlpha(alpha) { if (this.#state.clearAlpha !== alpha) { this.#state.clearAlpha = alpha; } } setClearColor(color, alpha) { if (this.#state.clearAlpha !== alpha || this.#state.clearColor !== color) { this.#state.clearColor = color; if (!isUndef(alpha) && !isNull(alpha)) { this.#state.clearAlpha = alpha; } else { this.#state.clearAlpha = color.a; } this.gl.clearColor(color.r, color.g, color.b, this.#state.clearAlpha); } } setCullFace(cullFace) { if (this.#state.cullFace !== cullFace) { if (cullFace) { this.gl.enable(this.gl.CULL_FACE); } else { this.gl.disable(this.gl.CULL_FACE); } this.#state.cullFace = cullFace; this.gl.cullFace(cullFace); } } setFrontFace(frontFace) { if (this.#state.frontFace !== frontFace) { this.#state.frontFace = frontFace; this.gl.frontFace(frontFace); } } setDepthMask(mask) { if (this.#state.depthMask !== mask) { this.#state.depthMask = mask; this.gl.depthMask(mask); } } setDepthFunc(func) { if (this.#state.depthFunc !== func) { this.#state.depthFunc = func; this.gl.depthFunc(func); } } setDepthTest(state) { if (this.#state.depthTest !== state) { this.#state.depthTest = state; if (state) { this.enable(this.gl.DEPTH_TEST); } else { this.disable(this.gl.DEPTH_TEST); } } } setStencilFunc(cmp, ref, mask, face) { if (this.#state?.stencil?.func?.cmp !== cmp || this.#state?.stencil?.func?.ref !== ref || this.#state?.stencil?.func?.mask !== mask) { if (!this.#state?.stencil) { this.#state.stencil = {}; } if (!this.#state?.stencil?.func) { this.#state.stencil.func = {}; } this.#state.stencil.func = { ref, mask, cmp }; if (face) { this.gl.stencilFuncSeparate(face, cmp, ref, mask); } else { this.gl.stencilFunc(cmp, ref, mask); } } } setStencilOp(fail, zFail, zPass, face) { if (!this.#state?.stencil) { this.#state.stencil = {}; } if (!face || face === this.gl.FRONT_AND_BACK) { return this.#state.stencil?.opFront?.fail !== fail || this.#state.stencil?.opFront?.zFail !== zFail || this.#state.stencil?.opFront?.zPass !== zPass || this.#state.stencil?.opBack?.fail !== fail || this.#state.stencil?.opBack?.zFail !== zFail || this.#state.stencil?.opBack?.zPass !== zPass; } else if (face === this.gl.FRONT) { return this.#state.stencil?.opFront?.fail !== fail || this.#state.stencil?.opFront?.zFail !== zFail || this.#state.stencil?.opFront?.zPass !== zPass; } else if (face === this.gl.BACK) { return this.#state.stencil?.opBack?.fail !== fail || this.#state.stencil?.opBack?.zFail !== zFail || this.#state.stencil?.opBack?.zPass !== zPass; } } setStencilMask(mask, face) { if (this.#state.stencil?.mask !== mask) { this.#state.stencil = { ...this.#state.stencil, mask }; if (face) { this.gl.stencilMaskSeparate(face, mask); } else { this.gl.stencilMask(mask); } } } setActiveTexture(unit) { if (this.#state.activeTextureUnit !== unit) { this.#state.activeTextureUnit = unit; this.gl.activeTexture(this.gl.TEXTURE0 + unit); } } setLineWidth(width) { if (this.#state.lineWidth !== width) { this.#state.lineWidth = width; this.gl.lineWidth(width); } } setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { this.enable(this.gl.POLYGON_OFFSET_FILL); if (this.#state.polygonOffsetFactor !== factor || this.#state.polygonOffsetUnits !== units) { this.gl.polygonOffset(factor, units); this.#state.polygonOffsetFactor = factor; this.#state.polygonOffsetUnits = units; } } else { this.disable(this.gl.POLYGON_OFFSET_FILL); } } bindFramebuffer(v = {}) { const { target = this.gl.FRAMEBUFFER, buffer = null } = v; if (this.#state.framebuffer !== buffer) { this.#state.framebuffer = buffer; this.gl.bindFramebuffer(target, buffer); } } setActiveGeometry(id) { this.#state.activeGeometryId = id; } reset(force = true) { const keys = Object.keys(this.#state); if (force) { keys.filter((key) => ["viewport", "premultiplyAlpha"].indexOf(key) < 0).forEach((key) => { delete this.#state[key]; }); this.bindFramebuffer({ buffer: null }); this.apply({ frontFace: this.gl.CCW, depthTest: false, depthWrite: true, depthMask: true, depthFunc: this.gl.LESS, blending: 1 /* NormalBlending */, blendFunc: { src: this.gl.ONE, dst: this.gl.ZERO }, blendEquation: { modeRGB: this.gl.FUNC_ADD }, premultiplyAlpha: false, unpackAlignment: 4, flipY: false, framebuffer: null, textureUnits: [], activeTextureUnit: -1, activeGeometryId: -1, currentProgramId: -1, clearAlpha: 1, clearColor: new Color(0), stencil: { func: {}, opFront: {}, opBack: {} } }); } else { keys.filter( (key) => [ "flipY", "framebuffer", "textureUnits", "activeTextureUnit", "activeGeometryId", "currentProgramId" ].indexOf(key) > -1 ).forEach((key) => { delete this.#state[key]; }); this.bindFramebuffer({ buffer: null }); this.#state.flipY = false; this.#state.activeGeometryId = -1; this.#state.activeTextureUnit = -1; this.#state.currentProgramId = -1; this.#state.textureUnits = []; this.#state.boundBuffer = null; } } } const external1ExtensionKeys = [ "WEBGL_depth_texture", "OES_texture_half_float", "OES_texture_float", "OES_standard_derivatives", "OES_element_index_uint", "EXT_frag_depth", "EXT_blend_minmax", "EXT_shader_texture_lod", "WEBGL_draw_buffers", "WEBGL_color_buffer_float" ]; const external2ExtensionKeys = [ "EXT_color_buffer_float" ]; const external12ExtensionKeys = [ "WEBGL_lose_context", "OES_texture_half_float_linear", "OES_texture_float_linear", "EXT_color_buffer_half_float", "WEBGL_debug_renderer_info", "EXT_texture_filter_anisotropic" ]; class Renderer { #gl; #state; #extensions; #autoClear; #depth; #alpha; #stencil; #antialias; #premultipliedAlpha; #preserveDrawingBuffer; #color; #dpr; #frustumCull; vertexAttribDivisor; drawArraysInstanced; drawElementsInstanced; createVertexArray; bindVertexArray; deleteVertexArray; width; height; constructor(gl, opts = {}) { const options = Object.assign( {}, { autoClear: true, depth: true, alpha: false, stencil: false, antialias: false, premultipliedAlpha: false, preserveDrawingBuffer: false, requestWebGl2: true, extensions: [] }, opts ); this.#autoClear = Boolean(options.autoClear); this.#depth = options.depth; this.#alpha = options.alpha; this.#stencil = options.stencil; this.#antialias = options.antialias; this.#premultipliedAlpha = options.premultipliedAlpha; this.#preserveDrawingBuffer = options.preserveDrawingBuffer; this.#gl = isWebGL(gl) || isWebGL2(gl) ? gl : getContext( gl, { alpha: this.#alpha, depth: this.#depth, stencil: this.#stencil, antialias: this.#antialias, powerPreference: options.powerPreference, premultipliedAlpha: this.#premultipliedAlpha, preserveDrawingBuffer: this.#preserveDrawingBuffer }, options.requestWebGl2 ); const attrs = this.#gl?.getContextAttributes(); const viewport = this.#gl?.getParameter(this.#gl.VIEWPORT); const flipY = this.#gl?.getParameter(this.#gl.UNPACK_FLIP_Y_WEBGL); this.#state = new State(this); if (attrs) { this.#depth = Boolean(attrs.depth); this.#antialias = Boolean(attrs.antialias); this.#alpha = Boolean(attrs.alpha); this.#stencil = Boolean(attrs.stencil); this.#premultipliedAlpha = Boolean(attrs.premultipliedAlpha); this.#preserveDrawingBuffer = Boolean(attrs.preserveDrawingBuffer); } this.#state.flipY = Boolean(flipY); this.#state.setViewport(viewport[2], viewport[3], viewport[0], viewport[1]); this.#state.premultiplyAlpha = this.#premultipliedAlpha; this.#color = true; this.#dpr = options.dpr || 1; this.width = this.gl.canvas.width / this.#dpr; this.height = this.gl.canvas.height / this.#dpr; this.#frustumCull = !!options.frustumCull; this.#extensions = {}; this.vertexAttribDivisor = this.getExtension( "ANGLE_instanced_arrays", "vertexAttribDivisor", "vertexAttribDivisorANGLE" ); this.drawArraysInstanced = this.getExtension( "ANGLE_instanced_arrays", "drawArraysInstanced", "drawArraysInstancedANGLE" ); this.drawElementsInstanced = this.getExtension( "ANGLE_instanced_arrays", "drawElementsInstanced", "drawElementsInstancedANGLE" ); this.createVertexArray = this.getExtension( "OES_vertex_array_object", "createVertexArray", "createVertexArrayOES" ); this.bindVertexArray = this.getExtension( "OES_vertex_array_object", "bindVertexArray", "bindVertexArrayOES" ); this.deleteVertexArray = this.getExtension( "OES_vertex_array_object", "deleteVertexArray", "deleteVertexArrayOES" ); if (options.extensions) { options.extensions.filter( (extension) => external1ExtensionKeys.findIndex((ext) => ext === extension) > -1 ).forEach((extension) => { if (!this.#extensions[extension] && !this.isWebGL2) { this.#extensions[extension] = this.gl.getExtension(extension); } }); options.extensions.filter( (extension) => external2ExtensionKeys.findIndex((ext) => ext === extension) > -1 ).forEach((extension) => { if (!this.#extensions[extension] && this.isWebGL2) { this.#extensions[extension] = this.gl.getExtension(extension); } }); options.extensions.filter( (extension) => external12ExtensionKeys.findIndex((ext) => ext === extension) > -1 ).forEach((extension) => { if (!this.#extensions[extension]) { this.#extensions[extension] = this.gl.getExtension(extension); } }); } } get gl() { return this.#gl; } get attributes() { return { dpr: this.#dpr, flipY: this.#state.flipY, depth: this.#depth, color: this.#color, antialias: this.#antialias, alpha: this.#alpha, stencil: this.#stencil, autoClear: this.#autoClear, frustumCull: this.#frustumCull, premultipliedAlpha: this.#premultipliedAlpha, preserveDrawingBuffer: this.#preserveDrawingBuffer }; } get canvas() { return this.#gl.canvas; } get isWebGL() { return isWebGL(this.gl); } get isWebGL2() { return isWebGL2(this.gl); } get extensions() { return this.#extensions; } extension(key) { return this.#extensions[key]; } get size() { return { width: "clientWidth" in this.canvas ? this.canvas.clientWidth : this.canvas.width, height: "clientHeight" in this.canvas ? this.canvas.clientHeight : this.canvas.height }; } get state() { return this.#state; } get premultipliedAlpha() { return this.#premultipliedAlpha; } setSize(width, height) { this.width = width; this.height = height; this.gl.canvas.width = width * this.#dpr; this.gl.canvas.height = height * this.#dpr; } setViewport(width, height, x = 0, y = 0) { this.#state.setViewport(width, height, x, y); } getExtension(extension, method, extFunc) { const func = this.gl[method]; if (method && func) return func.bind(this.gl); if (!this.#extensions[extension]) { this.#extensions[extension] = this.gl.getExtension(extension); } const ef = this.#extensions[extension]; return method ? ef ? ef[extFunc].bind(ef) : null : ef; } getRenderList({ scene, camera }) { const renderList = []; scene.traverse((node) => { if (!node.visible) return true; if (!node.draw) return; if (this.#frustumCull && node.frustumCulled && camera) { if (!camera.frustumIntersectsMesh(node)) return; } renderList.push(node); }); return renderList; } render(params) { const { scene, camera, target = null, update = true, clear } = params; if (target === null) { this.#state.bindFramebuffer({ buffer: null }); this.setViewport(this.width * this.#dpr, this.height * this.#dpr); } else { target.bind(); this.setViewport(target.width, target.height); } if (clear || this.#autoClear && clear !== false) { if (this.#depth && (!target || target.depth)) { this.#state.enable(this.gl.DEPTH_TEST); this.#state.setDepthMask(true); } this.clear(this.#color, this.#depth, this.#stencil); } if (update) scene.updateMatrixWorld(); if (camera) camera.updateMatrixWorld(); const renderList = this.getRenderList({ scene, camera }); let i = 0; const len = renderList.length; for (; i < len; i++) { const node = renderList[i]; node.draw({ camera }); } if (target) { target.unbind(); } } clear(color = this.#color, depth = this.#depth, stencil = this.#stencil) { let bits = 0; if (color) bits |= this.gl.COLOR_BUFFER_BIT; if (depth) bits |= this.gl.DEPTH_BUFFER_BIT; if (stencil) bits |= this.gl.STENCIL_BUFFER_BIT; this.gl.clear(bits); } resetState(force = true, vao = null) { this.#state.reset(force); this.bindVertexArray(vao); } } const ERR_RESOURCE_METHOD_UNDEFINED = "Resource subclass must define virtual methods"; class Resource extends Base { #handle; #lastHandle; id; name; userData; byteLength; options; constructor(renderer, options = {}) { super(renderer); this.id = options?.id || uid(this.constructor.name); this.name = options?.name; this.userData = options?.userData; this.#handle = options?.handle; this.options = options; if (this.#handle === void 0) { this.#handle = this.createHandle(); } this.byteLength = 0; } get handle() { return this.#handle; } swapHandle(handle) { this.#lastHandle = this.#handle; this.#handle = handle; } restoreHandle() { this.#handle = this.#lastHandle; } destroy() { this.delete(); } delete({ deleteChildren = false } = {}) { const children = this.handle && this.deleteHandle(this.handle); if (this.handle) { this.removeStats(); } this.#handle = null; if (children && deleteChildren) { children.filter(Boolean).forEach((child) => child.delete()); } return this; } bind(funcOrHandle = this.handle) { throw new Error(ERR_RESOURCE_METHOD_UNDEFINED); } unbind() { this.bind(null); } removeStats() { throw new Error(ERR_RESOURCE_METHOD_UNDEFINED); } createHandle() { throw new Error(ERR_RESOURCE_METHOD_UNDEFINED); } deleteHandle() { throw new Error(ERR_RESOURCE_METHOD_UNDEFINED); } toString() { return `${this.constructor.name}(${this.id})`; } } class RenderBuffer extends Resource { width; height; #internalFormat; constructor(renderer, options = {}) { super(renderer, { ...options, format: options.format || renderer.gl.DEPTH_COMPONENT16 }); this.#internalFormat = this.options.format; this.width = this.options.width; this.height = this.options.height; console.assert( this.width > 0 && this.height > 0, "Renderbuffer object requires valid width and height greater than zero" ); this.bind(); renderer.gl.renderbufferStorage( renderer.gl.RENDERBUFFER, this.#internalFormat, this.width, this.height ); } resize(width, height) { if (width === this.width && height === this.height) return; this.width = width; this.height = height; this.bind(); this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.#internalFormat, width, height); this.unbind(); } bind() { this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.handle); } unbind() { this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null); } removeStats() { } destroy() { this.unbind(); this.deleteHandle(); } createHandle() { return this.gl.createRenderbuffer(); } deleteHandle() { this.handle && this.gl.deleteRenderbuffer(this.handle); } } const emptyPixel = new Uint8Array(4); class Texture extends Resource { needsUpdate = false; textureUnit = 0; image; width; height; target; #state = {}; constructor(renderer, options = {}, needsUpdate = true) { const { gl } = renderer; const defaultOptions = { target: gl.TEXTURE_2D, type: gl.UNSIGNED_BYTE, format: gl.RGBA, internalFormat: options.format || gl.RGBA, wrapS: gl.CLAMP_TO_EDGE, wrapT: gl.CLAMP_TO_EDGE, generateMipmaps: true, minFilter: gl.LINEAR, magFilter: gl.LINEAR, premultiplyAlpha: false, unpackAlignment: 4, anisotropy: 0, flipY: false, level: 0 }; const opt = Object.assign({}, defaultOptions, options); super(renderer, opt); this.textureUnit = 0; this.image = this.options.image; this.width = this.options.width; this.height = this.options.height; this.target = this.options.target; this.#state.version = -1; this.needsUpdate = Boolean(needsUpdate); if (this.needsUpdate) { this.update(); } } setData(image, width = this.width, height = this.height) { this.image = image; this.width = width; this.height = height; this.needsUpdate = true; } setOptions(options) { this.options = Object.assign(this.options, options); this.width = this.options.width; this.height = this.options.height; this.needsUpdate = true; } fromSrc(url) { return new Promise((resolve, reject) => { const image = new Image(); image.onload = () => { this.setData(image, image.width, image.height); resolve(this); }; image.onerror = (e) => { reject(e); }; image.crossOrigin = "*"; image.src = url; }); } update(units = 0) { const needUpdate = !(this.image === this.#state.image && !this.needsUpdate); const checked = needUpdate || this.rendererState.textureUnits[units] !== this.id || this.rendererState.activeTextureUnit !== units; if (checked) { this.rendererState.setActiveTexture(units); this.bind(units); } if (!needUpdate) return; this.needsUpdate = false; if (this.options.wrapS !== this.#state.wrapS) { this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.options.wrapS); this.#state.wrapS = this.options.wrapS; } if (this.options.wrapT !== this.#state.wrapT) { this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.options.wrapT); this.#state.wrapT = this.options.wrapT; } if (this.options.minFilter !== this.#state.minFilter) { this.gl.texParameteri( this.target, this.gl.TEXTURE_MIN_FILTER, this.options.minFilter ); this.#state.minFilter = this.options.minFilter; } if (this.options.magFilter !== this.#state.magFilter) { this.gl.texParameteri( this.target, this.gl.TEXTURE_MAG_FILTER, this.options.magFilter ); this.#state.magFilter = this.options.magFilter; } if (this.options.flipY !== this.rendererState.flipY) { this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, this.options.flipY); this.rendererState.flipY = this.options.flipY; } if (this.options.premultiplyAlpha !== this.rendererState.premultiplyAlpha) { this.gl.pixelStorei( this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.options.premultiplyAlpha ); this.rendererState.premultiplyAlpha = this.options.premultiplyAlpha; } if (this.options.unpackAlignment !== this.rendererState.unpackAlignment) { this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, this.options.unpackAlignment); this.rendererState.unpackAlignment = this.options.unpackAlignment; } if (this.options.anisotropy && this.options.anisotropy !== this.rendererState.anisotropy) { const extTextureFilterAnisotropic = this.gl.getExtension("EXT_texture_filter_anisotropic") || this.gl.getExtension("MOZ_EXT_texture_filter_anisotropic") || this.gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"); if (extTextureFilterAnisotropic) { const max = this.gl.getParameter( extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); let v = this.options.anisotropy; if (this.options.anisotropy > max) { v = max; console.warn( `[Texture]: Texture.anisotropy option exceeded the maximum allowed value ${max} of the device` ); } this.gl.texParameterf( this.target, extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, v ); } this.rendererState.anisotropy = this.options.anisotropy; } if (this.image) { if (this.image.width) { this.width = this.image.width; this.height = this.image.height; } if (this.renderer.isWebGL2 && isNumber(this.options.offset)) { this.gl.texImage2D( this.target, this.options.level, this.options.internalFormat, this.width, this.height, 0, this.options.format, this.options.type, this.image, this.options.offset ); } else { if (ArrayBuffer.isView(this.image)) { this.gl.texImage2D( this.target, this.options.level, this.options.internalFormat, this.width, this.height, 0, this.options.format, this.options.type, this.image ); } else { this.gl.texImage2D( this.target, this.options.level, this.options.internalFormat, this.options.format, this.options.type, this.image ); } } if (this.options.generateMipmaps) { if (this.renderer.isWebGL2 || isPowerOfTwo(this.image.width) && isPowerOfTwo(this.image.height)) { this.gl.generateMipmap(this.target); } else { this.options.generateMipmaps = false; this.options.wrapS = this.gl.CLAMP_TO_EDGE; this.options.wrapT = this.options.wrapS; this.options.minFilter = this.gl.LINEAR; } } } else { if (this.renderer.isWebGL2 && isNumber(this.options.offset)) { if (this.width > 0) { this.gl.texImage2D( this.target, this.options.level, this.options.internalFormat, this.width, this.height, 0, this.options.format, this.options.type, this.options.offset ); } else { this.gl.texImage2D( this.target, 0, this.gl.RGBA, 1, 1, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, emptyPixel, this.options.offset ); } } else { if (this.width > 0) { this.gl.texImage2D( this.target, this.options.level, this.options.internalFormat, this.width, this.height, 0, this.options.format, this.options.type, null ); } else { this.gl.texImage2D( this.target, 0, this.gl.RGBA, 1, 1, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, emptyPixel ); } } } this.#state.image = this.image; this.#state.version += 1; } bind(unit = this.textureUnit) { if (this.rendererState.textureUnits[this.rendererState.activeTextureUnit] === this.id) return; this.textureUnit = unit; this.rendererState.textureUnits[this.textureUnit] = this.id; this.gl.bindTexture(this.target, this.handle); } unbind() { this.gl.activeTexture(this.gl.TEXTURE0 + this.textureUnit); this.gl.bindTexture(this.target, null); delete this.rendererState.textureUnits[this.textureUnit]; } destroy() { this.unbind(); super.destroy(); } removeStats() { this.#state = { version: -1 }; } createHandle() { return this.gl.createTexture(); } deleteHandle() { if (this.handle) { this.gl.deleteTexture(this.handle); } } toString() { return `Texture(${this.id},${this.width}x${this.height})`; } } class DataTexture extends Texture { needsUpdate = true; constructor(renderer, options = {}) { super(renderer, { ...options, image: options.data, premultiplyAlpha: true, flipY: false, unpackAlignment: 1 }); } } class RenderTarget extends Resource { #textures; #renderBuffers; depth; width; height; viewport; drawBuffersChanged; drawBuffers; #clearColors; #clearDepth; #clearStencil; constructor(renderer, options = {}) { super(renderer, { color: 1, depth: true, depthTexture: false, stencil: false, ...options }); this.#renderBuffers = /* @__PURE__ */ new Map(); this.#textures = /* @__PURE__ */ new Map(); this.depth = Boolean(options.depth); this.drawBuffers = []; this.drawBuffersChanged = false; this.width = this.options.width; this.height = this.options.height; this.viewport = new Vector4(0, 0, this.width, this.height); this.name = this.options.name; const attachments = this.options.attachments || []; if (attachments.length === 0) { for (let i = 0; i < this.options.color; i++) { const opt = { wrapS: this.gl.CLAMP_TO_EDGE, wrapT: this.gl.CLAMP_TO_EDGE, minFilter: this.gl.LINEAR, magFilter: this.gl.LINEAR, type: this.gl.UNSIGNED_BYTE, format: this.gl.RGBA, flipY: false, generateMipmaps: false, ...options }; let texture; if (opt.data) { texture = new DataTexture(renderer, opt); } else { texture = new Texture( renderer, omit(opt, [ "data", "name", "attachments", "depthTexture" ]) ); } attachments.push([this.gl.COLOR_ATTACHMENT0 + i, texture]); } if (options.depthTexture && (renderer.isWebGL2 || !renderer.isWebGL2 && renderer.gl.getExtension("WEBGL_depth_texture"))) { const texture = new Texture(renderer, { width: this.width, height: this.height, minFilter: this.gl.NEAREST, magFilter: this.gl.NEAREST, format: this.gl.DEPTH_COMPONENT, internalFormat: renderer.isWebGL2 ? this.gl.DEPTH_COMPONENT16 : this.gl.DEPTH_COMPONENT, type: this.gl.UNSIGNED_INT }); attachments.push([this.gl.DEPTH_ATTACHMENT, texture]); } else { const { depth, stencil } = options; if (depth && !stencil) { const renderBuffer = new RenderBuffer(renderer, { format: this.gl.DEPTH_COMPONENT16, width: this.width, height: this.height }); attachments.push([this.gl.DEPTH_ATTACHMENT, renderBuffer]); } else if (stencil && !depth) { const renderBuffer = new RenderBuffer(renderer, { format: this.gl.STENCIL_INDEX8, width: this.width, height: this.height }); attachments.push([this.gl.STENCIL_ATTACHMENT, renderBuffer]); } else if (depth && stencil) { const renderBuffer = new RenderBuffer(renderer, { format: this.gl.DEPTH_STENCIL, width: this.width, height: this.height }); attachments.push([this.gl.DEPTH_STENCIL_ATTACHMENT, renderBuffer]); } } } this.create(attachments); } get texture() { return this.#textures.values().next().value; } set clearColors(colors) { this.#clearColors = colors; } get clearColors() { return this.#clearColors; } set clearDepth(depth) { this.#clearDepth = depth; } get clearDepth() { return this.#clearDepth; } set clearStencil(stencil) { this.#clearStencil = stencil; } get clearStencil() { return this.#clearStencil; } create(attachments) { this.#clearColors = []; this.#clearDepth = 1; this.#clearStencil = 0; for (const attachment of attachments) { const attach = attachment[0]; const target = attachment[1]; if (target instanceof RenderBuffer) { this.#renderBuffers.set(attach, target); } else if (target instanceof Texture) { this.#textures.set(attach, target); this.drawBuffers.push(attach); } const i = attach - this.gl.COLOR_ATTACHMENT0; this.#clearColors[i] = [0, 0, 0, 0]; } if (this.options.color > 1) { if (this.renderer.isWebGL2) { this.gl.drawBuffers(this.drawBuffers); } else { const ext = this.renderer.extension("WEBGL_draw_buffers"); if (ext && ext.drawBuffersWEBGL) { ext.drawBuffersWEBGL(this.drawBuffers); } else { throw new Error( "Please open the corresponding extension [WEBGL_draw_buffers](https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_draw_buffers#browser_compatibility) and check whether the browser supports it" ); } } } this.drawBuffersChanged = true; this.bind(); this.#renderBuffers.forEach((rbo, attachment) => { this.gl.framebufferRenderbuffer( this.gl.FRAMEBUFFER, attachment, this.gl.RENDERBUFFER, rbo.handle ); }); this.#textures.forEach((texture, attachment) => { this.gl.framebufferTexture2D( this.gl.FRAMEBUFFER, attachment, this.gl.TEXTURE_2D, texture.handle, 0 ); }); this.unbind(); const status = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER); if (status !== this.gl.FRAMEBUFFER_COMPLETE) { switch (status) { case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: throw new Error( "The attachment types are mismatched or not all framebuffer attachment points are framebuffer attachment complete" ); case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: throw new Error("There is no attachment"); case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: throw new Error(" Height and width of the attachment are not the same."); case this.gl.FRAMEBUFFER_UNSUPPORTED: throw new Error( "The format of the attachment is not supported or if depth and stencil attachments are not the same renderbuffer" ); } } return this.handle; } clear() { this.bind(); let flags = 0; if (this.clearColors[0]) { const color = this.clearColors[0]; this.gl.clearColor(color[0], color[1], color[2], color[3]); flags |= this.gl.COLOR_BUFFER_BIT; } if (isNumber(this.#clearDepth)) { this.gl.clearDepth(this.#clearDepth); flags |= this.gl.DEPTH_BUFFER_BIT; } if (isNumber(this.#clearStencil)) { this.gl.clearStencil(this.#clearStencil); flags |= this.gl.STENCIL_BUFFER_BIT; } this.gl.clear(flags); this.unbind(); } getTexture(key) { return this.#textures.get(key); } resize(width, height) { if (this.width !== width || this.height !== height) { this.width = width; this.height = height; this.#textures.forEach((texture) => { if (texture.width !== width || texture.height !== height) { texture.width = width; texture.height = height; texture.needsUpdate = true; texture.update(); } }); this.#renderBuffers.forEach((rbo) => { rbo.resize(width, height); }); this.viewport.set(0, 0, width, height); } } bind(fbo = this.gl.FRAMEBUFFER) { this.gl.bindFramebuffer(fbo, this.handle); } unbind(fbo = this.gl.FRAMEBUFFER) { this.gl.bindFramebuffer(fbo, null); } removeStats() { } destroy() { this.#textures.forEach((texture) => { texture.destroy(); }); this.#renderBuffers.forEach((buffer) => { buffer.destroy(); }); this.deleteHandle(); } createHandle() { return this.gl.createFramebuffer(); } deleteHandle() { this.handle && this.gl.deleteFramebuffer(this.handle); } toString() { return `RenderTarget(${this.id},${this.width}x${this.height})`; } } const ERR_SOURCE = "Shader: GLSL source code must be a JavaScript string"; const cachedIds = {}; function genShaderName(key = "id") { cachedIds[key] = cachedIds[key] || 1; const idx = cachedIds[key]; cachedIds[key] += 1; return "".concat(key, "-").concat(idx); } const getTypeName = (ctx, shaderType) => { switch (shaderType) { case ctx.VERTEX_SHADER: return "vertex-shader"; case ctx.FRAGMENT_SHADER: return "fragment-shader"; default: return "unknown"; } }; const getShaderType = (ctx, type) => { switch (type) { case "fragment": return ctx.FRAGMENT_SHADER; case "vertex": return ctx.VERTEX_SHADER; default: return; } }; function addLineNumbers(string) { const lines = string.split("\n"); for (let i = 0; i < lines.length; i++) { lines[i] = i + 1 + ": " + lines[i]; } return lines.join("\n"); } class Shader extends Resource { #shaderType; #includes; sourceCode; constructor(renderer, sourceCode, type, includes = {}) { const shaderType = getShaderType(renderer.gl, type); super(renderer, { name: getShaderName(sourceCode) || genShaderName(getTypeName(renderer, shaderType)) }); console.assert(typeof sourceCode === "string", ERR_SOURCE); this.#includes = includes; this.#shaderType = shaderType; this.sourceCode = this.injectShaderModule(sourceCode, includes || {}).replace( /\n\n+/gm, "\n\n" ); this.createShader(this.sourceCode); } injectShaderModule(shader, modules = {}) { const regExp = /^[\t ]*#glsl_include +<([\w.]+)>/gm; const replacement = (substring, r) => { let module = modules[r]; if (module === void 0) throw new Error("Cannot resolve #include <".concat(r, ">")); module = module.replace(/#include { const defines = []; return defines.map((d) => "#define ".concat(d)); }; const arrayCacheF32 = {}; function flatten(a) { const arrayLen = a.length; const valueLen = a[0].length; if (valueLen === void 0) return a; const length = arrayLen * valueLen; let value = arrayCacheF32[length]; if (!value) arrayCacheF32[length] = value = new Float32Array(length); for (let i = 0; i < arrayLen; i++) value.set(a[i], i * valueLen); return value; } function setUniform(gl, type, location, value) { value = value.length ? flatten(value) : value; const isArray = value.length; switch (type) { case WebGLRenderingContext.FLOAT: return isArray ? gl.uniform1fv(location, value) : gl.uniform1f(location, value); case WebGLRenderingContext.FLOAT_VEC2: return gl.uniform2fv(location, value); case WebGLRenderingContext.FLOAT_VEC3: return gl.uniform3fv(location, value); case WebGLRenderingContext.FLOAT_VEC4: return gl.uniform4fv(location, value); case WebGLRenderingContext.BOOL: case WebGLRenderingContext.INT: case WebGLRenderingContext.SAMPLER_2D: case WebGLRenderingContext.SAMPLER_CUBE: return isArray ? gl.uniform1iv(location, value) : gl.uniform1i(location, value); case WebGLRenderingContext.BOOL_VEC2: case WebGLRenderingContext.INT_VEC2: return gl.uniform2iv(location, value); case WebGLRenderingContext.BOOL_VEC3: case WebGLRenderingContext.INT_VEC3: return gl.uniform3iv(location, value); case WebGLRenderingContext.BOOL_VEC4: case WebGLRenderingContext.INT_VEC4: return gl.uniform4iv(location, value); case WebGLRenderingContext.FLOAT_MAT2: return gl.uniformMatrix2fv(location, false, value); case WebGLRenderingContext.FLOAT_MAT3: return gl.uniformMatrix3fv(location, false, value); case WebGLRenderingContext.FLOAT_MAT4: return gl.uniformMatrix4fv(location, false, value); } } class Program extends Resource { attributeOrder; uniforms; #uniformLocations; #attributeLocations; #vs; #fs; #renderState; constructor(renderer, options = {}) { super(renderer, options); const { id, vertexShader, fragmentShader, uniforms = {}, transparent = false, defines = [], includes = {}, cullFace, frontFace = renderer.gl.CCW, depthTest = true, depthWrite = true, depthFunc = renderer.gl.LESS, blending = 1, blendFunc, blendEquation } = options; this.id = id || uid("program"); const defs = [ ...getDefines({ ...options, ...uniforms }), ...defines ].map((str) => !str.startsWith("#define ") ? "#define ".concat(str) : str); if (!vertexShader || !fragmentShader) { throw new Error(`Program: ${this.id}\uFF1Amust provide vertexShader and fragmentShader`); } this.#vs = typeof vertexShader === "string" ? new VertexShader(renderer, parseShader(vertexShader, defs), includes) : vertexShader; this.#fs = typeof fragmentShader === "string" ? new FragmentShader(renderer, parseShader(fragmentShader, defs), includes) : fragmentShader; this.gl.attachShader(this.handle, this.#vs.handle); this.gl.attachShader(this.handle, this.#fs.handle); this.gl.linkProgram(this.handle); this.gl.validateProgram(this.handle); if (!this.gl.getProgramParameter(this.handle, this.gl.LINK_STATUS)) { throw new Error( "Program:".concat(this.id, ": Error linking ").concat(this.gl.getProgramInfoLog(this.handle)) ); } this.uniforms = uniforms; this.#renderState = { blending, cullFace, frontFace, depthTest, depthWrite, depthFunc, blendFunc, blendEquation }; this.#uniformLocations = /* @__PURE__ */ new Map(); this.#attributeLocations = /* @__PURE__ */ new Map(); this.#assignUniforms(uniforms); this.#assignAttributes(); if (transparent && !blendFunc?.src) { if (this.renderer.premultipliedAlpha) { this.#renderState.blendFunc = { ...blendFunc, src: this.gl.ONE, dst: this.gl.ONE_MINUS_SRC_ALPHA }; } else { this.#renderState.blendFunc = { ...blendFunc, src: this.gl.SRC_ALPHA, dst: this.gl.ONE_MINUS_SRC_ALPHA }; } } } get uniformLocations() { return this.#uniformLocations; } get attributeLocations() { return this.#attributeLocations; } get vertexShader() { return this.#vs; } get fragmentShader() { return this.#fs; } use() { const programActive = this.rendererState.currentProgramId === this.id; let textureUnit = -1; if (!programActive) { this.gl.useProgram(this.handle); this.rendererState.currentProgramId = this.id; } this.#uniformLocations.forEach((location, activeUniform) => { const name = activeUniform.name; const uniform = this.uniforms[name]; if (!uniform) { console.warn("Program:".concat(this.id, ": Active uniform ").concat(name, " has not been supplied")); return; } if (uniform && (isUndef(uniform.value) || isNull(uniform.value))) { console.warn("Program:".concat(this.id, ": Uniform ").concat(name, " is missing a value parameter")); return; } let value = uniform?.value; if (value instanceof Texture) { textureUnit += 1; uniform.value.update(textureUnit); return setUniform(this.gl, activeUniform.type, location.location, textureUnit); } if (value instanceof Matrix || value instanceof Vector) { value = uniform.value.toArray(); } else if (value instanceof Color) { value = uniform.value.toArray(); } if (value && value.length > 0 && value[0] instanceof Texture) { const units = []; for (let i = 0; i < uniform.value.length; i++) { const v = value[i]; textureUnit += 1; v.update(textureUnit); units.push(textureUnit); } return setUniform(this.gl, activeUniform.type, location.location, units); } setUniform(this.gl, activeUniform.type, location.location, value); }); this.applyState(); } setStates(states, merge = true) { if (!merge) { this.#renderState = states; } else { this.#renderState = { ...this.#renderState, ...omit(states, ["blendFunc", "blendEquation"]) }; if (states.blendFunc) { this.#renderState.blendFunc = { ...this.#renderState.blendFunc, ...states.blendFunc }; } if (states.blendEquation) { this.#renderState.blendEquation = { ...this.#renderState.blendEquation, ...states.blendEquation }; } } } applyState() { this.rendererState.apply(this.#renderState); } setUniform(key, value) { if (this.uniforms[key]) { this.uniforms[key].value = value; } } bind() { this.gl.useProgram(this.handle); } unbind() { this.gl.useProgram(null); } createHandle() { return this.gl.createProgram(); } deleteHandle() { this.gl.deleteProgram(this.handle); } #assignUniforms(uniforms = {}) { const numUniforms = this.gl.getProgramParameter(this.handle, this.gl.ACTIVE_UNIFORMS); for (let i = 0; i < numUniforms; i++) { const uniformInfo = this.gl.getActiveUniform(this.handle, i); if (!uniformInfo) break; const name = uniformInfo.name; const split = name.match(/(\w+)/g); const uniformData = { location: this.gl.getUniformLocation(this.handle, name), type: uniformInfo.type, name: split[0], isStruct: false }; if (split.length === 3) { uniformData.isStructArray = true; uniformData.structIndex = Number(split[1]); uniformData.structProperty = split[2]; } else if (split.length === 2 && isNaN(Number(split[1]))) { uniformData.isStruct = true; uniformData.structProperty = split[1]; } const v = uniforms[name]?.value; if (!isUndef(v) && !isNull(v)) { uniformData.value = uniforms[name].value; } this.uniforms[name] = uniformData; this.#uniformLocations.set(uniformInfo, uniformData); } } #assignAttributes() { const numAttribs = this.gl.getProgramParameter(this.handle, this.gl.ACTIVE_ATTRIBUTES); const locations = []; for (let i = 0; i < numAttribs; i++) { const attribInfo = this.gl.getActiveAttrib(this.handle, i); if (!attribInfo) break; const location = this.gl.getAttribLocation(this.handle, attribInfo.name); locations[location] = attribInfo.name; this.#attributeLocations.set(attribInfo, location); } this.attributeOrder = locations.join(""); } destroy() { this.unbind(); this.deleteHandle(); } } const tempMat4 = new Matrix4(); const tempVec3a = new Vector3(); const tempVec3b = new Vector3(); const ERR_CAMERA_METHOD_UNDEFINED = "Camera subclass must define virtual methods"; class Camera extends Object3D { cameraType; projectionMatrix; viewMatrix; projectionViewMatrix; worldPosition; #near; #far; #fov; #aspect; #zoom; #bounds; frustum; constructor({ near = 0.1, far = 100, fov = 45, aspect = 1, bounds, zoom = 1 } = {}) { super(); this.cameraType = "perspective"; this.projectionMatrix = new ProjectionMatrix(); this.viewMatrix = new Matrix4(); this.projectionViewMatrix = new ProjectionMatrix(); this.worldPosition = new Vector3(); this.frustum = new Matrix4(); this.#near = near; this.#far = far; this.#fov = fov; this.#aspect = aspect; this.#bounds = bounds; this.#zoom = zoom; const { left, right, top, bottom } = bounds || {}; this.cameraType = left || right ? "orthographic" : "perspective"; if (this.cameraType === "orthographic") { this.orthographic(left, right, top, bottom, near, far, zoom); } else { this.perspective(fov, aspect, near, far); } } get near() { return this.#near; } set near(n) { this.#near = n; this.updateProjectionMatrix(); } get far() { return this.#far; } set far(f) { this.#far = f; this.updateProjectionMatrix(); } get fov() { return this.#fov; } set fov(f) { this.#fov = f; this.updateProjectionMatrix(); } get aspect() { return this.#aspect; } set aspect(aspect) { this.#aspect = aspect; this.updateProjectionMatrix(); } get zoom() { return this.#zoom; } set zoom(zoom) { this.#zoom = zoom; this.updateProjectionMatrix(); } get bounds() { return this.#bounds; } set bounds(bounds) { this.#bounds = bounds; this.updateProjectionMatrix(); } perspective(fov = this.fov, aspect = this.aspect, near = this.near, far = this.far) { this.#fov = fov; this.#aspect = aspect; this.#near = near; this.#far = far; this.projectionMatrix.fromPerspective(fov, aspect, near, far); this.cameraType = "perspective"; } orthographic(left, right, top, bottom, near = this.near, far = this.far, zoom = 1) { this.#bounds = { left, right, top, bottom }; this.near = near; this.far = far; this.projectionMatrix.orthographic( left / zoom, right / zoom, top / zoom, bottom / zoom, near, far ); this.cameraType = "orthographic"; this.projectionMatrix.frustum(this.frustum, this.#bounds.left, this.#bounds.right, this.#bounds.top, this.#bounds.bottom, this.#near, this.#far); } lookAt(t) { super.lookAt(t, true); return this; } updateMatrixWorld() { super.updateMatrixWorld(); this.viewMatrix.invert(this.worldMatrix); this.worldMatrix.getTranslation(this.worldPosition); this.projectionViewMatrix.multiply(this.projectionMatrix, this.viewMatrix); return this; } frustumIntersectsMesh(node, worldMatrix = node.worldMatrix) { if (!node.geometry.attributes.position) return true; if (!node.geometry.bounds || node.geometry.bounds.radius === Infinity) node.geometry.computeBoundingSphere(); if (!node.geometry.bounds) return true; const center = tempVec3a; center.copy(node.geometry.bounds.center); center.applyMatrix4(worldMatrix); const radius = node.geometry.bounds.radius * worldMatrix.getMaxScaleOnAxis(); return this.frustumIntersectsSphere(center, radius); } frustumIntersectsSphere(center, radius) { const normal = tempVec3b; for (let i = 0; i < 6; i++) { const plane = this.frustum[i]; const distance = normal.copy(plane).dot(center) + plane.constant; if (distance < -radius) return false; } return true; } project(v) { v.applyMatrix4(this.viewMatrix); v.applyMatrix4(this.projectionMatrix); return this; } unproject(v) { v.applyMatrix4(tempMat4.invert(this.projectionMatrix)); v.applyMatrix4(this.worldMatrix); return this; } updateProjectionMatrix() { throw new Error(ERR_CAMERA_METHOD_UNDEFINED); } } class PerspectiveCamera extends Camera { constructor(fov, aspect, near, far) { super({ fov, aspect, near, far }); } updateProjectionMatrix() { this.projectionMatrix.fromPerspective(this.fov, this.aspect, this.near, this.far); } } class OrthographicCamera extends Camera { constructor(left, right, top, bottom, near, far, zoom = 1) { super({ bounds: { left, right, top, bottom }, near, far, zoom }); } updateProjectionMatrix() { const { left, right, top, bottom } = this.bounds; const { zoom } = this; this.projectionMatrix.orthographic( left / zoom, right / zoom, top / zoom, bottom / zoom, this.near, this.far ); } } function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var earcut$2 = {exports: {}}; earcut$2.exports = earcut; earcut$2.exports.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$1(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$1(p1, q1, p2, q2) { var o1 = sign(area(p1, q1, p2)); var o2 = sign(area(p1, q1, q2)); var o3 = sign(area(p2, q2, p1)); var o4 = sign(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(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$1(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; }; var earcutExports = earcut$2.exports; var earcut$1 = /*@__PURE__*/getDefaultExportFromCjs(earcutExports); var shared, worker, wgw; function define(_, chunk) { if (!shared) { shared = chunk; } else if (!worker) { worker = chunk; } else { var workerBundleString = "var sharedChunk = {}; (" + shared + ")(sharedChunk); (" + worker + ")(sharedChunk);"; var sharedChunk = {}; shared(sharedChunk); wgw = chunk(sharedChunk); if (typeof window !== "undefined") { wgw.setWorkerUrl(window.URL.createObjectURL(new Blob([workerBundleString], { type: "text/javascript" }))); } } } define(["exports"], function(exports) { function asyncAll(array, fn, callback) { if (!array.length) { return callback(null, []); } let remaining = array.length; const results = new Array(array.length); let error = null; array.forEach((item, i2) => { fn(item, (err, result) => { if (err) { error = err; } results[i2] = result; if (--remaining === 0) callback(error, results); }); }); } function isWorker() { return typeof WorkerGlobalScope !== "undefined" && typeof self !== "undefined" && self instanceof WorkerGlobalScope; } const warnOnceHistory = {}; function warnOnce(message) { if (!warnOnceHistory[message]) { if (typeof console !== "undefined") console.warn(message); warnOnceHistory[message] = true; } } function isImageBitmap2(image) { return typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap; } function isArrayBuffer(value) { return value && typeof ArrayBuffer !== "undefined" && (value instanceof ArrayBuffer || value.constructor && value.constructor.name === "ArrayBuffer"); } let _isSafari = null; function isSafari(scope) { if (_isSafari == null) { const userAgent = scope.navigator ? scope.navigator.userAgent : null; _isSafari = !!scope.safari || !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || !!userAgent.match("Safari") && !userAgent.match("Chrome"))); } return _isSafari; } function nullFunction() { } const uidCounters = {}; function uid(id = "id") { uidCounters[id] = uidCounters[id] ?? 0; const count = uidCounters[id]++; return `${id}-${count}`; } function typeOf(value) { return Object.prototype.toString.call(value).slice(8, -1).toLowerCase(); } function isFunction2(v2) { return typeOf(v2) === "function"; } const getReferrer = isWorker() ? () => self.worker && self.worker.referrer : () => (window.location.protocol === "blob:" ? window.parent : window).location.href; function arrayBufferToImageBitmap(data, callback) { const blob = new Blob([new Uint8Array(data)], { type: "image/png" }); createImageBitmap(blob).then((imgBitmap) => { callback(null, imgBitmap); }).catch((e2) => { callback( new Error( `Could not load image because of ${e2.message}. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.` ) ); }); } const transparentPngUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII="; function arrayBufferToImage(data, callback) { const img = new Image(); img.onload = () => { callback(null, img); URL.revokeObjectURL(img.src); img.onload = null; window.requestAnimationFrame(() => { img.src = transparentPngUrl; }); }; img.onerror = () => callback( new Error( "Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported." ) ); const blob = new Blob([new Uint8Array(data)], { type: "image/png" }); img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl; } function unflatten(valuesInOneDimension, size) { const { height, width } = size; const valuesInTwoDimensions = []; for (let y2 = 0; y2 < height; y2++) { const start = y2 * width; const end = start + width; valuesInTwoDimensions.push(valuesInOneDimension.slice(start, end)); } return valuesInTwoDimensions; } function parseMetedata(str) { const array = str.split(","); const res = array.map((item) => { const kv = item.split(":"); return { [kv[0]]: isNaN(parseFloat(kv[1])) ? kv[1] : parseFloat(kv[1]) }; }); return res.reduce((pre, cur) => Object.assign({}, pre, cur), {}); } var utils2 = /* @__PURE__ */ Object.freeze({ __proto__: null, arrayBufferToImage, arrayBufferToImageBitmap, asyncAll, getReferrer, isArrayBuffer, isFunction: isFunction2, isImageBitmap: isImageBitmap2, isSafari, isWorker, nullFunction, parseMetedata, typeOf, uid, unflatten, warnOnce }); function e(e2, t2, i2) { return t2 in e2 ? Object.defineProperty(e2, t2, { value: i2, enumerable: true, configurable: true, writable: true }) : e2[t2] = i2, e2; } var t = "undefined" != typeof self ? self : global; const i = "undefined" != typeof navigator, s = i && "undefined" == typeof HTMLImageElement, n = !("undefined" == typeof global || "undefined" == typeof process || !process.versions || !process.versions.node), r = t.Buffer, a = t.BigInt, o = !!r, h = (e2) => f(e2) ? void 0 : e2, l = (e2) => void 0 !== e2; function f(e2) { return void 0 === e2 || (e2 instanceof Map ? 0 === e2.size : 0 === Object.values(e2).filter(l).length); } function u(e2) { let t2 = new Error(e2); throw delete t2.stack, t2; } function d(e2) { return "" === (e2 = function(e3) { for (; e3.endsWith("\0"); ) e3 = e3.slice(0, -1); return e3; }(e2).trim()) ? void 0 : e2; } function c(e2) { let t2 = function(e3) { let t3 = 0; return e3.ifd0.enabled && (t3 += 1024), e3.exif.enabled && (t3 += 2048), e3.makerNote && (t3 += 2048), e3.userComment && (t3 += 1024), e3.gps.enabled && (t3 += 512), e3.interop.enabled && (t3 += 100), e3.ifd1.enabled && (t3 += 1024), t3 + 2048; }(e2); return e2.jfif.enabled && (t2 += 50), e2.xmp.enabled && (t2 += 2e4), e2.iptc.enabled && (t2 += 14e3), e2.icc.enabled && (t2 += 6e3), t2; } const g = (e2) => String.fromCharCode.apply(null, e2), p = "undefined" != typeof TextDecoder ? new TextDecoder("utf-8") : void 0; function m(e2) { return p ? p.decode(e2) : o ? Buffer.from(e2).toString("utf8") : decodeURIComponent(escape(g(e2))); } class y { static from(e2, t2) { return e2 instanceof this && e2.le === t2 ? e2 : new y(e2, void 0, void 0, t2); } constructor(e2, t2 = 0, i2, s2) { if ("boolean" == typeof s2 && (this.le = s2), Array.isArray(e2) && (e2 = new Uint8Array(e2)), 0 === e2) this.byteOffset = 0, this.byteLength = 0; else if (e2 instanceof ArrayBuffer) { void 0 === i2 && (i2 = e2.byteLength - t2); let s3 = new DataView(e2, t2, i2); this._swapDataView(s3); } else if (e2 instanceof Uint8Array || e2 instanceof DataView || e2 instanceof y) { void 0 === i2 && (i2 = e2.byteLength - t2), (t2 += e2.byteOffset) + i2 > e2.byteOffset + e2.byteLength && u("Creating view outside of available memory in ArrayBuffer"); let s3 = new DataView(e2.buffer, t2, i2); this._swapDataView(s3); } else if ("number" == typeof e2) { let t3 = new DataView(new ArrayBuffer(e2)); this._swapDataView(t3); } else u("Invalid input argument for BufferView: " + e2); } _swapArrayBuffer(e2) { this._swapDataView(new DataView(e2)); } _swapBuffer(e2) { this._swapDataView(new DataView(e2.buffer, e2.byteOffset, e2.byteLength)); } _swapDataView(e2) { this.dataView = e2, this.buffer = e2.buffer, this.byteOffset = e2.byteOffset, this.byteLength = e2.byteLength; } _lengthToEnd(e2) { return this.byteLength - e2; } set(e2, t2, i2 = y) { return e2 instanceof DataView || e2 instanceof y ? e2 = new Uint8Array(e2.buffer, e2.byteOffset, e2.byteLength) : e2 instanceof ArrayBuffer && (e2 = new Uint8Array(e2)), e2 instanceof Uint8Array || u("BufferView.set(): Invalid data argument."), this.toUint8().set(e2, t2), new i2(this, t2, e2.byteLength); } subarray(e2, t2) { return t2 = t2 || this._lengthToEnd(e2), new y(this, e2, t2); } toUint8() { return new Uint8Array(this.buffer, this.byteOffset, this.byteLength); } getUint8Array(e2, t2) { return new Uint8Array(this.buffer, this.byteOffset + e2, t2); } getString(e2 = 0, t2 = this.byteLength) { return m(this.getUint8Array(e2, t2)); } getLatin1String(e2 = 0, t2 = this.byteLength) { let i2 = this.getUint8Array(e2, t2); return g(i2); } getUnicodeString(e2 = 0, t2 = this.byteLength) { const i2 = []; for (let s2 = 0; s2 < t2 && e2 + s2 < this.byteLength; s2 += 2) i2.push(this.getUint16(e2 + s2)); return g(i2); } getInt8(e2) { return this.dataView.getInt8(e2); } getUint8(e2) { return this.dataView.getUint8(e2); } getInt16(e2, t2 = this.le) { return this.dataView.getInt16(e2, t2); } getInt32(e2, t2 = this.le) { return this.dataView.getInt32(e2, t2); } getUint16(e2, t2 = this.le) { return this.dataView.getUint16(e2, t2); } getUint32(e2, t2 = this.le) { return this.dataView.getUint32(e2, t2); } getFloat32(e2, t2 = this.le) { return this.dataView.getFloat32(e2, t2); } getFloat64(e2, t2 = this.le) { return this.dataView.getFloat64(e2, t2); } getFloat(e2, t2 = this.le) { return this.dataView.getFloat32(e2, t2); } getDouble(e2, t2 = this.le) { return this.dataView.getFloat64(e2, t2); } getUintBytes(e2, t2, i2) { switch (t2) { case 1: return this.getUint8(e2, i2); case 2: return this.getUint16(e2, i2); case 4: return this.getUint32(e2, i2); case 8: return this.getUint64 && this.getUint64(e2, i2); } } getUint(e2, t2, i2) { switch (t2) { case 8: return this.getUint8(e2, i2); case 16: return this.getUint16(e2, i2); case 32: return this.getUint32(e2, i2); case 64: return this.getUint64 && this.getUint64(e2, i2); } } toString(e2) { return this.dataView.toString(e2, this.constructor.name); } ensureChunk() { } } function b(e2, t2) { u(`${e2} '${t2}' was not loaded, try using full build of exifr.`); } class w extends Map { constructor(e2) { super(), this.kind = e2; } get(e2, t2) { return this.has(e2) || b(this.kind, e2), t2 && (e2 in t2 || function(e3, t3) { u(`Unknown ${e3} '${t3}'.`); }(this.kind, e2), t2[e2].enabled || b(this.kind, e2)), super.get(e2); } keyList() { return Array.from(this.keys()); } } var S = new w("file parser"), k = new w("segment parser"), v = new w("file reader"); let O = t.fetch; function x(e2, t2) { return (s2 = e2).startsWith("data:") || s2.length > 1e4 ? P(e2, t2, "base64") : n && e2.includes("://") ? C(e2, t2, "url", A) : n ? P(e2, t2, "fs") : i ? C(e2, t2, "url", A) : void u("Invalid input argument"); var s2; } async function C(e2, t2, i2, s2) { return v.has(i2) ? P(e2, t2, i2) : s2 ? async function(e3, t3) { let i3 = await t3(e3); return new y(i3); }(e2, s2) : void u(`Parser ${i2} is not loaded`); } async function P(e2, t2, i2) { let s2 = new (v.get(i2))(e2, t2); return await s2.read(), s2; } const A = (e2) => O(e2).then((e3) => e3.arrayBuffer()), U = (e2) => new Promise((t2, i2) => { let s2 = new FileReader(); s2.onloadend = () => t2(s2.result || new ArrayBuffer()), s2.onerror = i2, s2.readAsArrayBuffer(e2); }); class I extends Map { get tagKeys() { return this.allKeys || (this.allKeys = Array.from(this.keys())), this.allKeys; } get tagValues() { return this.allValues || (this.allValues = Array.from(this.values())), this.allValues; } } function B(e2, t2, i2) { let s2 = new I(); for (let [e3, t3] of i2) s2.set(e3, t3); if (Array.isArray(t2)) for (let i3 of t2) e2.set(i3, s2); else e2.set(t2, s2); return s2; } const L = /* @__PURE__ */ new Map(), D = /* @__PURE__ */ new Map(), T = /* @__PURE__ */ new Map(), z = ["chunked", "firstChunkSize", "firstChunkSizeNode", "firstChunkSizeBrowser", "chunkSize", "chunkLimit"], N = ["jfif", "xmp", "icc", "iptc", "ihdr"], V = ["tiff", ...N], M = ["ifd0", "ifd1", "exif", "gps", "interop"], E = [...V, ...M], R = ["makerNote", "userComment"], j = ["translateKeys", "translateValues", "reviveValues", "multiSegment"], G = [...j, "sanitize", "mergeOutput", "silentErrors"]; class H { get translate() { return this.translateKeys || this.translateValues || this.reviveValues; } } class _ extends H { get needed() { return this.enabled || this.deps.size > 0; } constructor(t2, i2, s2, n2) { if (super(), e(this, "enabled", false), e(this, "skip", /* @__PURE__ */ new Set()), e(this, "pick", /* @__PURE__ */ new Set()), e(this, "deps", /* @__PURE__ */ new Set()), e(this, "translateKeys", false), e(this, "translateValues", false), e(this, "reviveValues", false), this.key = t2, this.enabled = i2, this.parse = this.enabled, this.applyInheritables(n2), this.canBeFiltered = M.includes(t2), this.canBeFiltered && (this.dict = L.get(t2)), void 0 !== s2) if (Array.isArray(s2)) this.parse = this.enabled = true, this.canBeFiltered && s2.length > 0 && this.translateTagSet(s2, this.pick); else if ("object" == typeof s2) { if (this.enabled = true, this.parse = false !== s2.parse, this.canBeFiltered) { let { pick: e2, skip: t3 } = s2; e2 && e2.length > 0 && this.translateTagSet(e2, this.pick), t3 && t3.length > 0 && this.translateTagSet(t3, this.skip); } this.applyInheritables(s2); } else true === s2 || false === s2 ? this.parse = this.enabled = s2 : u(`Invalid options argument: ${s2}`); } applyInheritables(e2) { let t2, i2; for (t2 of j) i2 = e2[t2], void 0 !== i2 && (this[t2] = i2); } translateTagSet(e2, t2) { if (this.dict) { let i2, s2, { tagKeys: n2, tagValues: r2 } = this.dict; for (i2 of e2) "string" == typeof i2 ? (s2 = r2.indexOf(i2), -1 === s2 && (s2 = n2.indexOf(Number(i2))), -1 !== s2 && t2.add(Number(n2[s2]))) : t2.add(i2); } else for (let i2 of e2) t2.add(i2); } finalizeFilters() { !this.enabled && this.deps.size > 0 ? (this.enabled = true, q(this.pick, this.deps)) : this.enabled && this.pick.size > 0 && q(this.pick, this.deps); } } var W = { jfif: false, tiff: true, xmp: false, icc: false, iptc: false, ifd0: true, ifd1: false, exif: true, gps: true, interop: false, ihdr: void 0, makerNote: false, userComment: false, multiSegment: false, skip: [], pick: [], translateKeys: true, translateValues: true, reviveValues: true, sanitize: true, mergeOutput: true, silentErrors: true, chunked: true, firstChunkSize: void 0, firstChunkSizeNode: 512, firstChunkSizeBrowser: 65536, chunkSize: 65536, chunkLimit: 5 }, $ = /* @__PURE__ */ new Map(); class K extends H { static useCached(e2) { let t2 = $.get(e2); return void 0 !== t2 || (t2 = new this(e2), $.set(e2, t2)), t2; } constructor(e2) { super(), true === e2 ? this.setupFromTrue() : void 0 === e2 ? this.setupFromUndefined() : Array.isArray(e2) ? this.setupFromArray(e2) : "object" == typeof e2 ? this.setupFromObject(e2) : u(`Invalid options argument ${e2}`), void 0 === this.firstChunkSize && (this.firstChunkSize = i ? this.firstChunkSizeBrowser : this.firstChunkSizeNode), this.mergeOutput && (this.ifd1.enabled = false), this.filterNestedSegmentTags(), this.traverseTiffDependencyTree(), this.checkLoadedPlugins(); } setupFromUndefined() { let e2; for (e2 of z) this[e2] = W[e2]; for (e2 of G) this[e2] = W[e2]; for (e2 of R) this[e2] = W[e2]; for (e2 of E) this[e2] = new _(e2, W[e2], void 0, this); } setupFromTrue() { let e2; for (e2 of z) this[e2] = W[e2]; for (e2 of G) this[e2] = W[e2]; for (e2 of R) this[e2] = true; for (e2 of E) this[e2] = new _(e2, true, void 0, this); } setupFromArray(e2) { let t2; for (t2 of z) this[t2] = W[t2]; for (t2 of G) this[t2] = W[t2]; for (t2 of R) this[t2] = W[t2]; for (t2 of E) this[t2] = new _(t2, false, void 0, this); this.setupGlobalFilters(e2, void 0, M); } setupFromObject(e2) { let t2; for (t2 of (M.ifd0 = M.ifd0 || M.image, M.ifd1 = M.ifd1 || M.thumbnail, Object.assign(this, e2), z)) this[t2] = Y(e2[t2], W[t2]); for (t2 of G) this[t2] = Y(e2[t2], W[t2]); for (t2 of R) this[t2] = Y(e2[t2], W[t2]); for (t2 of V) this[t2] = new _(t2, W[t2], e2[t2], this); for (t2 of M) this[t2] = new _(t2, W[t2], e2[t2], this.tiff); this.setupGlobalFilters(e2.pick, e2.skip, M, E), true === e2.tiff ? this.batchEnableWithBool(M, true) : false === e2.tiff ? this.batchEnableWithUserValue(M, e2) : Array.isArray(e2.tiff) ? this.setupGlobalFilters(e2.tiff, void 0, M) : "object" == typeof e2.tiff && this.setupGlobalFilters(e2.tiff.pick, e2.tiff.skip, M); } batchEnableWithBool(e2, t2) { for (let i2 of e2) this[i2].enabled = t2; } batchEnableWithUserValue(e2, t2) { for (let i2 of e2) { let e3 = t2[i2]; this[i2].enabled = false !== e3 && void 0 !== e3; } } setupGlobalFilters(e2, t2, i2, s2 = i2) { if (e2 && e2.length) { for (let e3 of s2) this[e3].enabled = false; let t3 = X(e2, i2); for (let [e3, i3] of t3) q(this[e3].pick, i3), this[e3].enabled = true; } else if (t2 && t2.length) { let e3 = X(t2, i2); for (let [t3, i3] of e3) q(this[t3].skip, i3); } } filterNestedSegmentTags() { let { ifd0: e2, exif: t2, xmp: i2, iptc: s2, icc: n2 } = this; this.makerNote ? t2.deps.add(37500) : t2.skip.add(37500), this.userComment ? t2.deps.add(37510) : t2.skip.add(37510), i2.enabled || e2.skip.add(700), s2.enabled || e2.skip.add(33723), n2.enabled || e2.skip.add(34675); } traverseTiffDependencyTree() { let { ifd0: e2, exif: t2, gps: i2, interop: s2 } = this; s2.needed && (t2.deps.add(40965), e2.deps.add(40965)), t2.needed && e2.deps.add(34665), i2.needed && e2.deps.add(34853), this.tiff.enabled = M.some((e3) => true === this[e3].enabled) || this.makerNote || this.userComment; for (let e3 of M) this[e3].finalizeFilters(); } get onlyTiff() { return !N.map((e2) => this[e2].enabled).some((e2) => true === e2) && this.tiff.enabled; } checkLoadedPlugins() { for (let e2 of V) this[e2].enabled && !k.has(e2) && b("segment parser", e2); } } function X(e2, t2) { let i2, s2, n2, r2, a2 = []; for (n2 of t2) { for (r2 of (i2 = L.get(n2), s2 = [], i2)) (e2.includes(r2[0]) || e2.includes(r2[1])) && s2.push(r2[0]); s2.length && a2.push([n2, s2]); } return a2; } function Y(e2, t2) { return void 0 !== e2 ? e2 : void 0 !== t2 ? t2 : void 0; } function q(e2, t2) { for (let i2 of t2) e2.add(i2); } e(K, "default", W); class J { constructor(t2) { e(this, "parsers", {}), e(this, "output", {}), e(this, "errors", []), e(this, "pushToErrors", (e2) => this.errors.push(e2)), this.options = K.useCached(t2); } async read(e2) { this.file = await function(e3, t2) { return "string" == typeof e3 ? x(e3, t2) : i && !s && e3 instanceof HTMLImageElement ? x(e3.src, t2) : e3 instanceof Uint8Array || e3 instanceof ArrayBuffer || e3 instanceof DataView ? new y(e3) : i && e3 instanceof Blob ? C(e3, t2, "blob", U) : void u("Invalid input argument"); }(e2, this.options); } setup() { if (this.fileParser) return; let { file: e2 } = this, t2 = e2.getUint16(0); for (let [i2, s2] of S) if (s2.canHandle(e2, t2)) return this.fileParser = new s2(this.options, this.file, this.parsers), e2[i2] = true; this.file.close && this.file.close(), u("Unknown file format"); } async parse() { let { output: e2, errors: t2 } = this; return this.setup(), this.options.silentErrors ? (await this.executeParsers().catch(this.pushToErrors), t2.push(...this.fileParser.errors)) : await this.executeParsers(), this.file.close && this.file.close(), this.options.silentErrors && t2.length > 0 && (e2.errors = t2), h(e2); } async executeParsers() { let { output: e2 } = this; await this.fileParser.parse(); let t2 = Object.values(this.parsers).map(async (t3) => { let i2 = await t3.parse(); t3.assignToOutput(e2, i2); }); this.options.silentErrors && (t2 = t2.map((e3) => e3.catch(this.pushToErrors))), await Promise.all(t2); } async extractThumbnail() { this.setup(); let { options: e2, file: t2 } = this, i2 = k.get("tiff", e2); var s2; if (t2.tiff ? s2 = { start: 0, type: "tiff" } : t2.jpeg && (s2 = await this.fileParser.getOrFindSegment("tiff")), void 0 === s2) return; let n2 = await this.fileParser.ensureSegmentChunk(s2), r2 = this.parsers.tiff = new i2(n2, e2, t2), a2 = await r2.extractThumbnail(); return t2.close && t2.close(), a2; } } async function Z(e2, t2) { let i2 = new J(t2); return await i2.read(e2), i2.parse(); } class ee { constructor(t2, i2, s2) { e(this, "errors", []), e(this, "ensureSegmentChunk", async (e2) => { let t3 = e2.start, i3 = e2.size || 65536; if (this.file.chunked) if (this.file.available(t3, i3)) e2.chunk = this.file.subarray(t3, i3); else try { e2.chunk = await this.file.readChunk(t3, i3); } catch (t4) { u(`Couldn't read segment: ${JSON.stringify(e2)}. ${t4.message}`); } else this.file.byteLength > t3 + i3 ? e2.chunk = this.file.subarray(t3, i3) : void 0 === e2.size ? e2.chunk = this.file.subarray(t3) : u("Segment unreachable: " + JSON.stringify(e2)); return e2.chunk; }), this.extendOptions && this.extendOptions(t2), this.options = t2, this.file = i2, this.parsers = s2; } injectSegment(e2, t2) { this.options[e2].enabled && this.createParser(e2, t2); } createParser(e2, t2) { let i2 = new (k.get(e2))(t2, this.options, this.file); return this.parsers[e2] = i2; } createParsers(e2) { for (let t2 of e2) { let { type: e3, chunk: i2 } = t2, s2 = this.options[e3]; if (s2 && s2.enabled) { let t3 = this.parsers[e3]; t3 && t3.append || t3 || this.createParser(e3, i2); } } } async readSegments(e2) { let t2 = e2.map(this.ensureSegmentChunk); await Promise.all(t2); } } class te { static findPosition(e2, t2) { let i2 = e2.getUint16(t2 + 2) + 2, s2 = "function" == typeof this.headerLength ? this.headerLength(e2, t2, i2) : this.headerLength, n2 = t2 + s2, r2 = i2 - s2; return { offset: t2, length: i2, headerLength: s2, start: n2, size: r2, end: n2 + r2 }; } static parse(e2, t2 = {}) { return new this(e2, new K({ [this.type]: t2 }), e2).parse(); } normalizeInput(e2) { return e2 instanceof y ? e2 : new y(e2); } constructor(t2, i2 = {}, s2) { e(this, "errors", []), e(this, "raw", /* @__PURE__ */ new Map()), e(this, "handleError", (e2) => { if (!this.options.silentErrors) throw e2; this.errors.push(e2.message); }), this.chunk = this.normalizeInput(t2), this.file = s2, this.type = this.constructor.type, this.globalOptions = this.options = i2, this.localOptions = i2[this.type], this.canTranslate = this.localOptions && this.localOptions.translate; } translate() { this.canTranslate && (this.translated = this.translateBlock(this.raw, this.type)); } get output() { return this.translated ? this.translated : this.raw ? Object.fromEntries(this.raw) : void 0; } translateBlock(e2, t2) { let i2 = T.get(t2), s2 = D.get(t2), n2 = L.get(t2), r2 = this.options[t2], a2 = r2.reviveValues && !!i2, o2 = r2.translateValues && !!s2, h2 = r2.translateKeys && !!n2, l2 = {}; for (let [t3, r3] of e2) a2 && i2.has(t3) ? r3 = i2.get(t3)(r3) : o2 && s2.has(t3) && (r3 = this.translateValue(r3, s2.get(t3))), h2 && n2.has(t3) && (t3 = n2.get(t3) || t3), l2[t3] = r3; return l2; } translateValue(e2, t2) { return t2[e2] || t2.DEFAULT || e2; } assignToOutput(e2, t2) { this.assignObjectToOutput(e2, this.constructor.type, t2); } assignObjectToOutput(e2, t2, i2) { if (this.globalOptions.mergeOutput) return Object.assign(e2, i2); e2[t2] ? Object.assign(e2[t2], i2) : e2[t2] = i2; } } e(te, "headerLength", 4), e(te, "type", void 0), e(te, "multiSegment", false), e(te, "canHandle", () => false); function ie(e2) { return 192 === e2 || 194 === e2 || 196 === e2 || 219 === e2 || 221 === e2 || 218 === e2 || 254 === e2; } function se(e2) { return e2 >= 224 && e2 <= 239; } function ne(e2, t2, i2) { for (let [s2, n2] of k) if (n2.canHandle(e2, t2, i2)) return s2; } class re extends ee { constructor(...t2) { super(...t2), e(this, "appSegments", []), e(this, "jpegSegments", []), e(this, "unknownSegments", []); } static canHandle(e2, t2) { return 65496 === t2; } async parse() { await this.findAppSegments(), await this.readSegments(this.appSegments), this.mergeMultiSegments(), this.createParsers(this.mergedAppSegments || this.appSegments); } setupSegmentFinderArgs(e2) { true === e2 ? (this.findAll = true, this.wanted = new Set(k.keyList())) : (e2 = void 0 === e2 ? k.keyList().filter((e3) => this.options[e3].enabled) : e2.filter((e3) => this.options[e3].enabled && k.has(e3)), this.findAll = false, this.remaining = new Set(e2), this.wanted = new Set(e2)), this.unfinishedMultiSegment = false; } async findAppSegments(e2 = 0, t2) { this.setupSegmentFinderArgs(t2); let { file: i2, findAll: s2, wanted: n2, remaining: r2 } = this; if (!s2 && this.file.chunked && (s2 = Array.from(n2).some((e3) => { let t3 = k.get(e3), i3 = this.options[e3]; return t3.multiSegment && i3.multiSegment; }), s2 && await this.file.readWhole()), e2 = this.findAppSegmentsInRange(e2, i2.byteLength), !this.options.onlyTiff && i2.chunked) { let t3 = false; for (; r2.size > 0 && !t3 && (i2.canReadNextChunk || this.unfinishedMultiSegment); ) { let { nextChunkOffset: s3 } = i2, n3 = this.appSegments.some((e3) => !this.file.available(e3.offset || e3.start, e3.length || e3.size)); if (t3 = e2 > s3 && !n3 ? !await i2.readNextChunk(e2) : !await i2.readNextChunk(s3), void 0 === (e2 = this.findAppSegmentsInRange(e2, i2.byteLength))) return; } } } findAppSegmentsInRange(e2, t2) { t2 -= 2; let i2, s2, n2, r2, a2, o2, { file: h2, findAll: l2, wanted: f2, remaining: u2, options: d2 } = this; for (; e2 < t2; e2++) if (255 === h2.getUint8(e2)) { if (i2 = h2.getUint8(e2 + 1), se(i2)) { if (s2 = h2.getUint16(e2 + 2), n2 = ne(h2, e2, s2), n2 && f2.has(n2) && (r2 = k.get(n2), a2 = r2.findPosition(h2, e2), o2 = d2[n2], a2.type = n2, this.appSegments.push(a2), !l2 && (r2.multiSegment && o2.multiSegment ? (this.unfinishedMultiSegment = a2.chunkNumber < a2.chunkCount, this.unfinishedMultiSegment || u2.delete(n2)) : u2.delete(n2), 0 === u2.size))) break; d2.recordUnknownSegments && (a2 = te.findPosition(h2, e2), a2.marker = i2, this.unknownSegments.push(a2)), e2 += s2 + 1; } else if (ie(i2)) { if (s2 = h2.getUint16(e2 + 2), 218 === i2 && false !== d2.stopAfterSos) return; d2.recordJpegSegments && this.jpegSegments.push({ offset: e2, length: s2, marker: i2 }), e2 += s2 + 1; } } return e2; } mergeMultiSegments() { if (!this.appSegments.some((e3) => e3.multiSegment)) return; let e2 = function(e3, t2) { let i2, s2, n2, r2 = /* @__PURE__ */ new Map(); for (let a2 = 0; a2 < e3.length; a2++) i2 = e3[a2], s2 = i2[t2], r2.has(s2) ? n2 = r2.get(s2) : r2.set(s2, n2 = []), n2.push(i2); return Array.from(r2); }(this.appSegments, "type"); this.mergedAppSegments = e2.map(([e3, t2]) => { let i2 = k.get(e3, this.options); if (i2.handleMultiSegments) { return { type: e3, chunk: i2.handleMultiSegments(t2) }; } return t2[0]; }); } getSegment(e2) { return this.appSegments.find((t2) => t2.type === e2); } async getOrFindSegment(e2) { let t2 = this.getSegment(e2); return void 0 === t2 && (await this.findAppSegments(0, [e2]), t2 = this.getSegment(e2)), t2; } } e(re, "type", "jpeg"), S.set("jpeg", re); const ae = [void 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4]; class oe extends te { parseHeader() { var e2 = this.chunk.getUint16(); 18761 === e2 ? this.le = true : 19789 === e2 && (this.le = false), this.chunk.le = this.le, this.headerParsed = true; } parseTags(e2, t2, i2 = /* @__PURE__ */ new Map()) { let { pick: s2, skip: n2 } = this.options[t2]; s2 = new Set(s2); let r2 = s2.size > 0, a2 = 0 === n2.size, o2 = this.chunk.getUint16(e2); e2 += 2; for (let h2 = 0; h2 < o2; h2++) { let o3 = this.chunk.getUint16(e2); if (r2) { if (s2.has(o3) && (i2.set(o3, this.parseTag(e2, o3, t2)), s2.delete(o3), 0 === s2.size)) break; } else !a2 && n2.has(o3) || i2.set(o3, this.parseTag(e2, o3, t2)); e2 += 12; } return i2; } parseTag(e2, t2, i2) { let { chunk: s2 } = this, n2 = s2.getUint16(e2 + 2), r2 = s2.getUint32(e2 + 4), a2 = ae[n2]; if (a2 * r2 <= 4 ? e2 += 8 : e2 = s2.getUint32(e2 + 8), (n2 < 1 || n2 > 13) && u(`Invalid TIFF value type. block: ${i2.toUpperCase()}, tag: ${t2.toString(16)}, type: ${n2}, offset ${e2}`), e2 > s2.byteLength && u(`Invalid TIFF value offset. block: ${i2.toUpperCase()}, tag: ${t2.toString(16)}, type: ${n2}, offset ${e2} is outside of chunk size ${s2.byteLength}`), 1 === n2) return s2.getUint8Array(e2, r2); if (2 === n2) return d(s2.getString(e2, r2)); if (7 === n2) return s2.getUint8Array(e2, r2); if (1 === r2) return this.parseTagValue(n2, e2); { let t3 = new (function(e3) { switch (e3) { case 1: return Uint8Array; case 3: return Uint16Array; case 4: return Uint32Array; case 5: return Array; case 6: return Int8Array; case 8: return Int16Array; case 9: return Int32Array; case 10: return Array; case 11: return Float32Array; case 12: return Float64Array; default: return Array; } }(n2))(r2), i3 = a2; for (let s3 = 0; s3 < r2; s3++) t3[s3] = this.parseTagValue(n2, e2), e2 += i3; return t3; } } parseTagValue(e2, t2) { let { chunk: i2 } = this; switch (e2) { case 1: return i2.getUint8(t2); case 3: return i2.getUint16(t2); case 4: return i2.getUint32(t2); case 5: return i2.getUint32(t2) / i2.getUint32(t2 + 4); case 6: return i2.getInt8(t2); case 8: return i2.getInt16(t2); case 9: return i2.getInt32(t2); case 10: return i2.getInt32(t2) / i2.getInt32(t2 + 4); case 11: return i2.getFloat(t2); case 12: return i2.getDouble(t2); case 13: return i2.getUint32(t2); default: u(`Invalid tiff type ${e2}`); } } } class he extends oe { static canHandle(e2, t2) { return 225 === e2.getUint8(t2 + 1) && 1165519206 === e2.getUint32(t2 + 4) && 0 === e2.getUint16(t2 + 8); } async parse() { this.parseHeader(); let { options: e2 } = this; return e2.ifd0.enabled && await this.parseIfd0Block(), e2.exif.enabled && await this.safeParse("parseExifBlock"), e2.gps.enabled && await this.safeParse("parseGpsBlock"), e2.interop.enabled && await this.safeParse("parseInteropBlock"), e2.ifd1.enabled && await this.safeParse("parseThumbnailBlock"), this.createOutput(); } safeParse(e2) { let t2 = this[e2](); return void 0 !== t2.catch && (t2 = t2.catch(this.handleError)), t2; } findIfd0Offset() { void 0 === this.ifd0Offset && (this.ifd0Offset = this.chunk.getUint32(4)); } findIfd1Offset() { if (void 0 === this.ifd1Offset) { this.findIfd0Offset(); let e2 = this.chunk.getUint16(this.ifd0Offset), t2 = this.ifd0Offset + 2 + 12 * e2; this.ifd1Offset = this.chunk.getUint32(t2); } } parseBlock(e2, t2) { let i2 = /* @__PURE__ */ new Map(); return this[t2] = i2, this.parseTags(e2, t2, i2), i2; } async parseIfd0Block() { if (this.ifd0) return; let { file: e2 } = this; this.findIfd0Offset(), this.ifd0Offset < 8 && u("Malformed EXIF data"), !e2.chunked && this.ifd0Offset > e2.byteLength && u(`IFD0 offset points to outside of file. this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e2.byteLength}`), e2.tiff && await e2.ensureChunk(this.ifd0Offset, c(this.options)); let t2 = this.parseBlock(this.ifd0Offset, "ifd0"); return 0 !== t2.size ? (this.exifOffset = t2.get(34665), this.interopOffset = t2.get(40965), this.gpsOffset = t2.get(34853), this.xmp = t2.get(700), this.iptc = t2.get(33723), this.icc = t2.get(34675), this.options.sanitize && (t2.delete(34665), t2.delete(40965), t2.delete(34853), t2.delete(700), t2.delete(33723), t2.delete(34675)), t2) : void 0; } async parseExifBlock() { if (this.exif) return; if (this.ifd0 || await this.parseIfd0Block(), void 0 === this.exifOffset) return; this.file.tiff && await this.file.ensureChunk(this.exifOffset, c(this.options)); let e2 = this.parseBlock(this.exifOffset, "exif"); return this.interopOffset || (this.interopOffset = e2.get(40965)), this.makerNote = e2.get(37500), this.userComment = e2.get(37510), this.options.sanitize && (e2.delete(40965), e2.delete(37500), e2.delete(37510)), this.unpack(e2, 41728), this.unpack(e2, 41729), e2; } unpack(e2, t2) { let i2 = e2.get(t2); i2 && 1 === i2.length && e2.set(t2, i2[0]); } async parseGpsBlock() { if (this.gps) return; if (this.ifd0 || await this.parseIfd0Block(), void 0 === this.gpsOffset) return; let e2 = this.parseBlock(this.gpsOffset, "gps"); return e2 && e2.has(2) && e2.has(4) && (e2.set("latitude", le(...e2.get(2), e2.get(1))), e2.set("longitude", le(...e2.get(4), e2.get(3)))), e2; } async parseInteropBlock() { if (!this.interop && (this.ifd0 || await this.parseIfd0Block(), void 0 !== this.interopOffset || this.exif || await this.parseExifBlock(), void 0 !== this.interopOffset)) return this.parseBlock(this.interopOffset, "interop"); } async parseThumbnailBlock(e2 = false) { if (!this.ifd1 && !this.ifd1Parsed && (!this.options.mergeOutput || e2)) return this.findIfd1Offset(), this.ifd1Offset > 0 && (this.parseBlock(this.ifd1Offset, "ifd1"), this.ifd1Parsed = true), this.ifd1; } async extractThumbnail() { if (this.headerParsed || this.parseHeader(), this.ifd1Parsed || await this.parseThumbnailBlock(true), void 0 === this.ifd1) return; let e2 = this.ifd1.get(513), t2 = this.ifd1.get(514); return this.chunk.getUint8Array(e2, t2); } get image() { return this.ifd0; } get thumbnail() { return this.ifd1; } createOutput() { let e2, t2, i2, s2 = {}; for (t2 of M) if (e2 = this[t2], !f(e2)) if (i2 = this.canTranslate ? this.translateBlock(e2, t2) : Object.fromEntries(e2), this.options.mergeOutput) { if ("ifd1" === t2) continue; Object.assign(s2, i2); } else s2[t2] = i2; return this.makerNote && (s2.makerNote = this.makerNote), this.userComment && (s2.userComment = this.userComment), s2; } assignToOutput(e2, t2) { if (this.globalOptions.mergeOutput) Object.assign(e2, t2); else for (let [i2, s2] of Object.entries(t2)) this.assignObjectToOutput(e2, i2, s2); } } function le(e2, t2, i2, s2) { var n2 = e2 + t2 / 60 + i2 / 3600; return "S" !== s2 && "W" !== s2 || (n2 *= -1), n2; } e(he, "type", "tiff"), e(he, "headerLength", 10), k.set("tiff", he); const ue = { ifd0: false, ifd1: false, exif: false, gps: false, interop: false, sanitize: false, reviveValues: true, translateKeys: false, translateValues: false, mergeOutput: false }; Object.assign({}, ue, { firstChunkSize: 4e4, gps: [1, 2, 3, 4] }); Object.assign({}, ue, { tiff: false, ifd1: true, mergeOutput: false }); Object.assign({}, ue, { firstChunkSize: 4e4, ifd0: [274] }); if ("object" == typeof navigator) { let e2 = navigator.userAgent; if (e2.includes("iPad") || e2.includes("iPhone")) { e2.match(/OS (\d+)_(\d+)/); } else if (e2.includes("OS X 10")) { e2.match(/OS X 10[_.](\d+)/); } if (e2.includes("Chrome/")) { e2.match(/Chrome\/(\d+)/); } else if (e2.includes("Firefox/")) { e2.match(/Firefox\/(\d+)/); } } class Oe extends y { constructor(...t2) { super(...t2), e(this, "ranges", new xe()), 0 !== this.byteLength && this.ranges.add(0, this.byteLength); } _tryExtend(e2, t2, i2) { if (0 === e2 && 0 === this.byteLength && i2) { let e3 = new DataView(i2.buffer || i2, i2.byteOffset, i2.byteLength); this._swapDataView(e3); } else { let i3 = e2 + t2; if (i3 > this.byteLength) { let { dataView: e3 } = this._extend(i3); this._swapDataView(e3); } } } _extend(e2) { let t2; t2 = o ? r.allocUnsafe(e2) : new Uint8Array(e2); let i2 = new DataView(t2.buffer, t2.byteOffset, t2.byteLength); return t2.set(new Uint8Array(this.buffer, this.byteOffset, this.byteLength), 0), { uintView: t2, dataView: i2 }; } subarray(e2, t2, i2 = false) { return t2 = t2 || this._lengthToEnd(e2), i2 && this._tryExtend(e2, t2), this.ranges.add(e2, t2), super.subarray(e2, t2); } set(e2, t2, i2 = false) { i2 && this._tryExtend(t2, e2.byteLength, e2); let s2 = super.set(e2, t2); return this.ranges.add(t2, s2.byteLength), s2; } async ensureChunk(e2, t2) { this.chunked && (this.ranges.available(e2, t2) || await this.readChunk(e2, t2)); } available(e2, t2) { return this.ranges.available(e2, t2); } } class xe { constructor() { e(this, "list", []); } get length() { return this.list.length; } add(e2, t2, i2 = 0) { let s2 = e2 + t2, n2 = this.list.filter((t3) => Ce(e2, t3.offset, s2) || Ce(e2, t3.end, s2)); if (n2.length > 0) { e2 = Math.min(e2, ...n2.map((e3) => e3.offset)), s2 = Math.max(s2, ...n2.map((e3) => e3.end)), t2 = s2 - e2; let i3 = n2.shift(); i3.offset = e2, i3.length = t2, i3.end = s2, this.list = this.list.filter((e3) => !n2.includes(e3)); } else this.list.push({ offset: e2, length: t2, end: s2 }); } available(e2, t2) { let i2 = e2 + t2; return this.list.some((t3) => t3.offset <= e2 && i2 <= t3.end); } } function Ce(e2, t2, i2) { return e2 <= t2 && t2 <= i2; } class Pe extends Oe { constructor(t2, i2) { super(0), e(this, "chunksRead", 0), this.input = t2, this.options = i2; } async readWhole() { this.chunked = false, await this.readChunk(this.nextChunkOffset); } async readChunked() { this.chunked = true, await this.readChunk(0, this.options.firstChunkSize); } async readNextChunk(e2 = this.nextChunkOffset) { if (this.fullyRead) return this.chunksRead++, false; let t2 = this.options.chunkSize, i2 = await this.readChunk(e2, t2); return !!i2 && i2.byteLength === t2; } async readChunk(e2, t2) { if (this.chunksRead++, 0 !== (t2 = this.safeWrapAddress(e2, t2))) return this._readChunk(e2, t2); } safeWrapAddress(e2, t2) { return void 0 !== this.size && e2 + t2 > this.size ? Math.max(0, this.size - e2) : t2; } get nextChunkOffset() { if (0 !== this.ranges.list.length) return this.ranges.list[0].length; } get canReadNextChunk() { return this.chunksRead < this.options.chunkLimit; } get fullyRead() { return void 0 !== this.size && this.nextChunkOffset === this.size; } read() { return this.options.chunked ? this.readChunked() : this.readWhole(); } close() { } } v.set("blob", class extends Pe { async readWhole() { this.chunked = false; let e2 = await U(this.input); this._swapArrayBuffer(e2); } readChunked() { return this.chunked = true, this.size = this.input.size, super.readChunked(); } async _readChunk(e2, t2) { let i2 = t2 ? e2 + t2 : void 0, s2 = this.input.slice(e2, i2), n2 = await U(s2); return this.set(n2, e2, true); } }); v.set("url", class extends Pe { async readWhole() { this.chunked = false; let e2 = await A(this.input); e2 instanceof ArrayBuffer ? this._swapArrayBuffer(e2) : e2 instanceof Uint8Array && this._swapBuffer(e2); } async _readChunk(e2, t2) { let i2 = t2 ? e2 + t2 - 1 : void 0, s2 = this.options.httpHeaders || {}; (e2 || i2) && (s2.range = `bytes=${[e2, i2].join("-")}`); let n2 = await O(this.input, { headers: s2 }), r2 = await n2.arrayBuffer(), a2 = r2.byteLength; if (416 !== n2.status) return a2 !== t2 && (this.size = e2 + a2), this.set(r2, e2, true); } }); y.prototype.getUint64 = function(e2) { let t2 = this.getUint32(e2), i2 = this.getUint32(e2 + 4); return t2 < 1048575 ? t2 << 32 | i2 : void 0 !== typeof a ? (console.warn("Using BigInt because of type 64uint but JS can only handle 53b numbers."), a(t2) << a(32) | a(i2)) : void u("Trying to read 64b value but JS can only handle 53b numbers."); }; class Ue extends ee { parseBoxes(e2 = 0) { let t2 = []; for (; e2 < this.file.byteLength - 4; ) { let i2 = this.parseBoxHead(e2); if (t2.push(i2), 0 === i2.length) break; e2 += i2.length; } return t2; } parseSubBoxes(e2) { e2.boxes = this.parseBoxes(e2.start); } findBox(e2, t2) { return void 0 === e2.boxes && this.parseSubBoxes(e2), e2.boxes.find((e3) => e3.kind === t2); } parseBoxHead(e2) { let t2 = this.file.getUint32(e2), i2 = this.file.getString(e2 + 4, 4), s2 = e2 + 8; return 1 === t2 && (t2 = this.file.getUint64(e2 + 8), s2 += 8), { offset: e2, length: t2, kind: i2, start: s2 }; } parseBoxFullHead(e2) { if (void 0 !== e2.version) return; let t2 = this.file.getUint32(e2.start); e2.version = t2 >> 24, e2.start += 4; } } class Ie extends Ue { static canHandle(e2, t2) { if (0 !== t2) return false; let i2 = e2.getUint16(2); if (i2 > 50) return false; let s2 = 16, n2 = []; for (; s2 < i2; ) n2.push(e2.getString(s2, 4)), s2 += 4; return n2.includes(this.type); } async parse() { let e2 = this.file.getUint32(0), t2 = this.parseBoxHead(e2); for (; "meta" !== t2.kind; ) e2 += t2.length, await this.file.ensureChunk(e2, 16), t2 = this.parseBoxHead(e2); await this.file.ensureChunk(t2.offset, t2.length), this.parseBoxFullHead(t2), this.parseSubBoxes(t2), this.options.icc.enabled && await this.findIcc(t2), this.options.tiff.enabled && await this.findExif(t2); } async registerSegment(e2, t2, i2) { await this.file.ensureChunk(t2, i2); let s2 = this.file.subarray(t2, i2); this.createParser(e2, s2); } async findIcc(e2) { let t2 = this.findBox(e2, "iprp"); if (void 0 === t2) return; let i2 = this.findBox(t2, "ipco"); if (void 0 === i2) return; let s2 = this.findBox(i2, "colr"); void 0 !== s2 && await this.registerSegment("icc", s2.offset + 12, s2.length); } async findExif(e2) { let t2 = this.findBox(e2, "iinf"); if (void 0 === t2) return; let i2 = this.findBox(e2, "iloc"); if (void 0 === i2) return; let s2 = this.findExifLocIdInIinf(t2), n2 = this.findExtentInIloc(i2, s2); if (void 0 === n2) return; let [r2, a2] = n2; await this.file.ensureChunk(r2, a2); let o2 = 4 + this.file.getUint32(r2); r2 += o2, a2 -= o2, await this.registerSegment("tiff", r2, a2); } findExifLocIdInIinf(e2) { this.parseBoxFullHead(e2); let t2, i2, s2, n2, r2 = e2.start, a2 = this.file.getUint16(r2); for (r2 += 2; a2--; ) { if (t2 = this.parseBoxHead(r2), this.parseBoxFullHead(t2), i2 = t2.start, t2.version >= 2 && (s2 = 3 === t2.version ? 4 : 2, n2 = this.file.getString(i2 + s2 + 2, 4), "Exif" === n2)) return this.file.getUintBytes(i2, s2); r2 += t2.length; } } get8bits(e2) { let t2 = this.file.getUint8(e2); return [t2 >> 4, 15 & t2]; } findExtentInIloc(e2, t2) { this.parseBoxFullHead(e2); let i2 = e2.start, [s2, n2] = this.get8bits(i2++), [r2, a2] = this.get8bits(i2++), o2 = 2 === e2.version ? 4 : 2, h2 = 1 === e2.version || 2 === e2.version ? 2 : 0, l2 = a2 + s2 + n2, f2 = 2 === e2.version ? 4 : 2, u2 = this.file.getUintBytes(i2, f2); for (i2 += f2; u2--; ) { let e3 = this.file.getUintBytes(i2, o2); i2 += o2 + h2 + 2 + r2; let f3 = this.file.getUint16(i2); if (i2 += 2, e3 === t2) return f3 > 1 && console.warn("ILOC box has more than one extent but we're only processing one\nPlease create an issue at https://github.com/MikeKovarik/exifr with this file"), [this.file.getUintBytes(i2 + a2, s2), this.file.getUintBytes(i2 + a2 + s2, n2)]; i2 += f3 * l2; } } } class Be extends Ie { } e(Be, "type", "heic"); class Fe extends Ie { } e(Fe, "type", "avif"), S.set("heic", Be), S.set("avif", Fe), B(L, ["ifd0", "ifd1"], [[256, "ImageWidth"], [257, "ImageHeight"], [258, "BitsPerSample"], [259, "Compression"], [262, "PhotometricInterpretation"], [270, "ImageDescription"], [271, "Make"], [272, "Model"], [273, "StripOffsets"], [274, "Orientation"], [277, "SamplesPerPixel"], [278, "RowsPerStrip"], [279, "StripByteCounts"], [282, "XResolution"], [283, "YResolution"], [284, "PlanarConfiguration"], [296, "ResolutionUnit"], [301, "TransferFunction"], [305, "Software"], [306, "ModifyDate"], [315, "Artist"], [316, "HostComputer"], [317, "Predictor"], [318, "WhitePoint"], [319, "PrimaryChromaticities"], [513, "ThumbnailOffset"], [514, "ThumbnailLength"], [529, "YCbCrCoefficients"], [530, "YCbCrSubSampling"], [531, "YCbCrPositioning"], [532, "ReferenceBlackWhite"], [700, "ApplicationNotes"], [33432, "Copyright"], [33723, "IPTC"], [34665, "ExifIFD"], [34675, "ICC"], [34853, "GpsIFD"], [330, "SubIFD"], [40965, "InteropIFD"], [40091, "XPTitle"], [40092, "XPComment"], [40093, "XPAuthor"], [40094, "XPKeywords"], [40095, "XPSubject"]]), B(L, "exif", [[33434, "ExposureTime"], [33437, "FNumber"], [34850, "ExposureProgram"], [34852, "SpectralSensitivity"], [34855, "ISO"], [34858, "TimeZoneOffset"], [34859, "SelfTimerMode"], [34864, "SensitivityType"], [34865, "StandardOutputSensitivity"], [34866, "RecommendedExposureIndex"], [34867, "ISOSpeed"], [34868, "ISOSpeedLatitudeyyy"], [34869, "ISOSpeedLatitudezzz"], [36864, "ExifVersion"], [36867, "DateTimeOriginal"], [36868, "CreateDate"], [36873, "GooglePlusUploadCode"], [36880, "OffsetTime"], [36881, "OffsetTimeOriginal"], [36882, "OffsetTimeDigitized"], [37121, "ComponentsConfiguration"], [37122, "CompressedBitsPerPixel"], [37377, "ShutterSpeedValue"], [37378, "ApertureValue"], [37379, "BrightnessValue"], [37380, "ExposureCompensation"], [37381, "MaxApertureValue"], [37382, "SubjectDistance"], [37383, "MeteringMode"], [37384, "LightSource"], [37385, "Flash"], [37386, "FocalLength"], [37393, "ImageNumber"], [37394, "SecurityClassification"], [37395, "ImageHistory"], [37396, "SubjectArea"], [37500, "MakerNote"], [37510, "UserComment"], [37520, "SubSecTime"], [37521, "SubSecTimeOriginal"], [37522, "SubSecTimeDigitized"], [37888, "AmbientTemperature"], [37889, "Humidity"], [37890, "Pressure"], [37891, "WaterDepth"], [37892, "Acceleration"], [37893, "CameraElevationAngle"], [40960, "FlashpixVersion"], [40961, "ColorSpace"], [40962, "ExifImageWidth"], [40963, "ExifImageHeight"], [40964, "RelatedSoundFile"], [41483, "FlashEnergy"], [41486, "FocalPlaneXResolution"], [41487, "FocalPlaneYResolution"], [41488, "FocalPlaneResolutionUnit"], [41492, "SubjectLocation"], [41493, "ExposureIndex"], [41495, "SensingMethod"], [41728, "FileSource"], [41729, "SceneType"], [41730, "CFAPattern"], [41985, "CustomRendered"], [41986, "ExposureMode"], [41987, "WhiteBalance"], [41988, "DigitalZoomRatio"], [41989, "FocalLengthIn35mmFormat"], [41990, "SceneCaptureType"], [41991, "GainControl"], [41992, "Contrast"], [41993, "Saturation"], [41994, "Sharpness"], [41996, "SubjectDistanceRange"], [42016, "ImageUniqueID"], [42032, "OwnerName"], [42033, "SerialNumber"], [42034, "LensInfo"], [42035, "LensMake"], [42036, "LensModel"], [42037, "LensSerialNumber"], [42080, "CompositeImage"], [42081, "CompositeImageCount"], [42082, "CompositeImageExposureTimes"], [42240, "Gamma"], [59932, "Padding"], [59933, "OffsetSchema"], [65e3, "OwnerName"], [65001, "SerialNumber"], [65002, "Lens"], [65100, "RawFile"], [65101, "Converter"], [65102, "WhiteBalance"], [65105, "Exposure"], [65106, "Shadows"], [65107, "Brightness"], [65108, "Contrast"], [65109, "Saturation"], [65110, "Sharpness"], [65111, "Smoothness"], [65112, "MoireFilter"], [40965, "InteropIFD"]]), B(L, "gps", [[0, "GPSVersionID"], [1, "GPSLatitudeRef"], [2, "GPSLatitude"], [3, "GPSLongitudeRef"], [4, "GPSLongitude"], [5, "GPSAltitudeRef"], [6, "GPSAltitude"], [7, "GPSTimeStamp"], [8, "GPSSatellites"], [9, "GPSStatus"], [10, "GPSMeasureMode"], [11, "GPSDOP"], [12, "GPSSpeedRef"], [13, "GPSSpeed"], [14, "GPSTrackRef"], [15, "GPSTrack"], [16, "GPSImgDirectionRef"], [17, "GPSImgDirection"], [18, "GPSMapDatum"], [19, "GPSDestLatitudeRef"], [20, "GPSDestLatitude"], [21, "GPSDestLongitudeRef"], [22, "GPSDestLongitude"], [23, "GPSDestBearingRef"], [24, "GPSDestBearing"], [25, "GPSDestDistanceRef"], [26, "GPSDestDistance"], [27, "GPSProcessingMethod"], [28, "GPSAreaInformation"], [29, "GPSDateStamp"], [30, "GPSDifferential"], [31, "GPSHPositioningError"]]), B(D, ["ifd0", "ifd1"], [[274, { 1: "Horizontal (normal)", 2: "Mirror horizontal", 3: "Rotate 180", 4: "Mirror vertical", 5: "Mirror horizontal and rotate 270 CW", 6: "Rotate 90 CW", 7: "Mirror horizontal and rotate 90 CW", 8: "Rotate 270 CW" }], [296, { 1: "None", 2: "inches", 3: "cm" }]]); let Le = B(D, "exif", [[34850, { 0: "Not defined", 1: "Manual", 2: "Normal program", 3: "Aperture priority", 4: "Shutter priority", 5: "Creative program", 6: "Action program", 7: "Portrait mode", 8: "Landscape mode" }], [37121, { 0: "-", 1: "Y", 2: "Cb", 3: "Cr", 4: "R", 5: "G", 6: "B" }], [37383, { 0: "Unknown", 1: "Average", 2: "CenterWeightedAverage", 3: "Spot", 4: "MultiSpot", 5: "Pattern", 6: "Partial", 255: "Other" }], [37384, { 0: "Unknown", 1: "Daylight", 2: "Fluorescent", 3: "Tungsten (incandescent light)", 4: "Flash", 9: "Fine weather", 10: "Cloudy weather", 11: "Shade", 12: "Daylight fluorescent (D 5700 - 7100K)", 13: "Day white fluorescent (N 4600 - 5400K)", 14: "Cool white fluorescent (W 3900 - 4500K)", 15: "White fluorescent (WW 3200 - 3700K)", 17: "Standard light A", 18: "Standard light B", 19: "Standard light C", 20: "D55", 21: "D65", 22: "D75", 23: "D50", 24: "ISO studio tungsten", 255: "Other" }], [37385, { 0: "Flash did not fire", 1: "Flash fired", 5: "Strobe return light not detected", 7: "Strobe return light detected", 9: "Flash fired, compulsory flash mode", 13: "Flash fired, compulsory flash mode, return light not detected", 15: "Flash fired, compulsory flash mode, return light detected", 16: "Flash did not fire, compulsory flash mode", 24: "Flash did not fire, auto mode", 25: "Flash fired, auto mode", 29: "Flash fired, auto mode, return light not detected", 31: "Flash fired, auto mode, return light detected", 32: "No flash function", 65: "Flash fired, red-eye reduction mode", 69: "Flash fired, red-eye reduction mode, return light not detected", 71: "Flash fired, red-eye reduction mode, return light detected", 73: "Flash fired, compulsory flash mode, red-eye reduction mode", 77: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", 79: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", 89: "Flash fired, auto mode, red-eye reduction mode", 93: "Flash fired, auto mode, return light not detected, red-eye reduction mode", 95: "Flash fired, auto mode, return light detected, red-eye reduction mode" }], [41495, { 1: "Not defined", 2: "One-chip color area sensor", 3: "Two-chip color area sensor", 4: "Three-chip color area sensor", 5: "Color sequential area sensor", 7: "Trilinear sensor", 8: "Color sequential linear sensor" }], [41728, { 1: "Film Scanner", 2: "Reflection Print Scanner", 3: "Digital Camera" }], [41729, { 1: "Directly photographed" }], [41985, { 0: "Normal", 1: "Custom", 2: "HDR (no original saved)", 3: "HDR (original saved)", 4: "Original (for HDR)", 6: "Panorama", 7: "Portrait HDR", 8: "Portrait" }], [41986, { 0: "Auto", 1: "Manual", 2: "Auto bracket" }], [41987, { 0: "Auto", 1: "Manual" }], [41990, { 0: "Standard", 1: "Landscape", 2: "Portrait", 3: "Night", 4: "Other" }], [41991, { 0: "None", 1: "Low gain up", 2: "High gain up", 3: "Low gain down", 4: "High gain down" }], [41996, { 0: "Unknown", 1: "Macro", 2: "Close", 3: "Distant" }], [42080, { 0: "Unknown", 1: "Not a Composite Image", 2: "General Composite Image", 3: "Composite Image Captured While Shooting" }]]); const De = { 1: "No absolute unit of measurement", 2: "Inch", 3: "Centimeter" }; Le.set(37392, De), Le.set(41488, De); const Te = { 0: "Normal", 1: "Low", 2: "High" }; function ze(e2) { return "object" == typeof e2 && void 0 !== e2.length ? e2[0] : e2; } function Ne(e2) { let t2 = Array.from(e2).slice(1); return t2[1] > 15 && (t2 = t2.map((e3) => String.fromCharCode(e3))), "0" !== t2[2] && 0 !== t2[2] || t2.pop(), t2.join("."); } function Ve(e2) { if ("string" == typeof e2) { var [t2, i2, s2, n2, r2, a2] = e2.trim().split(/[-: ]/g).map(Number), o2 = new Date(t2, i2 - 1, s2); return Number.isNaN(n2) || Number.isNaN(r2) || Number.isNaN(a2) || (o2.setHours(n2), o2.setMinutes(r2), o2.setSeconds(a2)), Number.isNaN(+o2) ? e2 : o2; } } function Me(e2) { if ("string" == typeof e2) return e2; let t2 = []; if (0 === e2[1] && 0 === e2[e2.length - 1]) for (let i2 = 0; i2 < e2.length; i2 += 2) t2.push(Ee(e2[i2 + 1], e2[i2])); else for (let i2 = 0; i2 < e2.length; i2 += 2) t2.push(Ee(e2[i2], e2[i2 + 1])); return d(String.fromCodePoint(...t2)); } function Ee(e2, t2) { return e2 << 8 | t2; } Le.set(41992, Te), Le.set(41993, Te), Le.set(41994, Te), B(T, ["ifd0", "ifd1"], [[50827, function(e2) { return "string" != typeof e2 ? m(e2) : e2; }], [306, Ve], [40091, Me], [40092, Me], [40093, Me], [40094, Me], [40095, Me]]), B(T, "exif", [[40960, Ne], [36864, Ne], [36867, Ve], [36868, Ve], [40962, ze], [40963, ze]]), B(T, "gps", [[0, (e2) => Array.from(e2).join(".")], [7, (e2) => Array.from(e2).join(":")]]); class Re extends te { static canHandle(e2, t2) { return 225 === e2.getUint8(t2 + 1) && 1752462448 === e2.getUint32(t2 + 4) && "http://ns.adobe.com/" === e2.getString(t2 + 4, "http://ns.adobe.com/".length); } static headerLength(e2, t2) { return "http://ns.adobe.com/xmp/extension/" === e2.getString(t2 + 4, "http://ns.adobe.com/xmp/extension/".length) ? 79 : 4 + "http://ns.adobe.com/xap/1.0/".length + 1; } static findPosition(e2, t2) { let i2 = super.findPosition(e2, t2); return i2.multiSegment = i2.extended = 79 === i2.headerLength, i2.multiSegment ? (i2.chunkCount = e2.getUint8(t2 + 72), i2.chunkNumber = e2.getUint8(t2 + 76), 0 !== e2.getUint8(t2 + 77) && i2.chunkNumber++) : (i2.chunkCount = 1 / 0, i2.chunkNumber = -1), i2; } static handleMultiSegments(e2) { return e2.map((e3) => e3.chunk.getString()).join(""); } normalizeInput(e2) { return "string" == typeof e2 ? e2 : y.from(e2).getString(); } parse(e2 = this.chunk) { if (!this.localOptions.parse) return e2; e2 = function(e3) { let t3 = {}, i3 = {}; for (let e4 of Ye) t3[e4] = [], i3[e4] = 0; return e3.replace(qe, (e4, s3, n2) => { if ("<" === s3) { let s4 = ++i3[n2]; return t3[n2].push(s4), `${e4}#${s4}`; } return `${e4}#${t3[n2].pop()}`; }); }(e2); let t2 = Ge.findAll(e2, "rdf", "Description"); 0 === t2.length && t2.push(new Ge("rdf", "Description", void 0, e2)); let i2, s2 = {}; for (let e3 of t2) for (let t3 of e3.properties) i2 = $e(t3.ns, s2), He(t3, i2); return function(e3) { let t3; for (let i3 in e3) t3 = e3[i3] = h(e3[i3]), void 0 === t3 && delete e3[i3]; return h(e3); }(s2); } assignToOutput(e2, t2) { if (this.localOptions.parse) for (let [i2, s2] of Object.entries(t2)) switch (i2) { case "tiff": this.assignObjectToOutput(e2, "ifd0", s2); break; case "exif": this.assignObjectToOutput(e2, "exif", s2); break; case "xmlns": break; default: this.assignObjectToOutput(e2, i2, s2); } else e2.xmp = t2; } } e(Re, "type", "xmp"), e(Re, "multiSegment", true), k.set("xmp", Re); class je { static findAll(e2) { return Ke(e2, /([a-zA-Z0-9-]+):([a-zA-Z0-9-]+)=("[^"]*"|'[^']*')/gm).map(je.unpackMatch); } static unpackMatch(e2) { let t2 = e2[1], i2 = e2[2], s2 = e2[3].slice(1, -1); return s2 = Xe(s2), new je(t2, i2, s2); } constructor(e2, t2, i2) { this.ns = e2, this.name = t2, this.value = i2; } serialize() { return this.value; } } class Ge { static findAll(e2, t2, i2) { if (void 0 !== t2 || void 0 !== i2) { t2 = t2 || "[\\w\\d-]+", i2 = i2 || "[\\w\\d-]+"; var s2 = new RegExp(`<(${t2}):(${i2})(#\\d+)?((\\s+?[\\w\\d-:]+=("[^"]*"|'[^']*'))*\\s*)(\\/>|>([\\s\\S]*?)<\\/\\1:\\2\\3>)`, "gm"); } else s2 = /<([\w\d-]+):([\w\d-]+)(#\d+)?((\s+?[\w\d-:]+=("[^"]*"|'[^']*'))*\s*)(\/>|>([\s\S]*?)<\/\1:\2\3>)/gm; return Ke(e2, s2).map(Ge.unpackMatch); } static unpackMatch(e2) { let t2 = e2[1], i2 = e2[2], s2 = e2[4], n2 = e2[8]; return new Ge(t2, i2, s2, n2); } constructor(e2, t2, i2, s2) { this.ns = e2, this.name = t2, this.attrString = i2, this.innerXml = s2, this.attrs = je.findAll(i2), this.children = Ge.findAll(s2), this.value = 0 === this.children.length ? Xe(s2) : void 0, this.properties = [...this.attrs, ...this.children]; } get isPrimitive() { return void 0 !== this.value && 0 === this.attrs.length && 0 === this.children.length; } get isListContainer() { return 1 === this.children.length && this.children[0].isList; } get isList() { let { ns: e2, name: t2 } = this; return "rdf" === e2 && ("Seq" === t2 || "Bag" === t2 || "Alt" === t2); } get isListItem() { return "rdf" === this.ns && "li" === this.name; } serialize() { if (0 === this.properties.length && void 0 === this.value) return; if (this.isPrimitive) return this.value; if (this.isListContainer) return this.children[0].serialize(); if (this.isList) return We(this.children.map(_e)); if (this.isListItem && 1 === this.children.length && 0 === this.attrs.length) return this.children[0].serialize(); let e2 = {}; for (let t2 of this.properties) He(t2, e2); return void 0 !== this.value && (e2.value = this.value), h(e2); } } function He(e2, t2) { let i2 = e2.serialize(); void 0 !== i2 && (t2[e2.name] = i2); } var _e = (e2) => e2.serialize(), We = (e2) => 1 === e2.length ? e2[0] : e2, $e = (e2, t2) => t2[e2] ? t2[e2] : t2[e2] = {}; function Ke(e2, t2) { let i2, s2 = []; if (!e2) return s2; for (; null !== (i2 = t2.exec(e2)); ) s2.push(i2); return s2; } function Xe(e2) { if (function(e3) { return null == e3 || "null" === e3 || "undefined" === e3 || "" === e3 || "" === e3.trim(); }(e2)) return; let t2 = Number(e2); if (!Number.isNaN(t2)) return t2; let i2 = e2.toLowerCase(); return "true" === i2 || "false" !== i2 && e2.trim(); } const Ye = ["rdf:li", "rdf:Seq", "rdf:Bag", "rdf:Alt", "rdf:Description"], qe = new RegExp(`(<|\\/)(${Ye.join("|")})`, "g"); const defaultOptions2 = { maxRequests: 6 }; class RequestScheduler { constructor(options) { this.options = { ...defaultOptions2, ...options }; this.requestQueue = []; this.executing = /* @__PURE__ */ new Set(); } remove(p2) { this.executing.delete(p2); if (!p2.cancelled) { p2.completed = true; this.enqueue(); } } enqueue() { for (let numImageRequests = this.executing.size; numImageRequests < this.options.maxRequests && this.requestQueue.length > 0; numImageRequests++) { const q2 = this.requestQueue.shift(); if (q2.cancelled) { this.remove(q2); continue; } const p2 = Promise.resolve().then(() => { const request2 = q2.ref(); q2.request = request2; return request2; }); this.executing.add(q2); p2.then(() => { this.remove(q2); }).catch(() => { this.remove(q2); }); } } scheduleRequest(fn) { const request2 = { ref: fn, cancelled: false, completed: false, request: null, cancel: () => { if (!request2.completed && !request2.cancelled) { request2.cancelled = true; if (request2.request) { request2.request.cancel(); } this.enqueue(); } } }; this.requestQueue.push(request2); this.enqueue(); return request2; } } var _bin = { nextZero: function(data, p2) { while (data[p2] != 0) p2++; return p2; }, readUshort: function(buff, p2) { return buff[p2] << 8 | buff[p2 + 1]; }, writeUshort: function(buff, p2, n2) { buff[p2] = n2 >> 8 & 255; buff[p2 + 1] = n2 & 255; }, readUint: function(buff, p2) { return buff[p2] * (256 * 256 * 256) + (buff[p2 + 1] << 16 | buff[p2 + 2] << 8 | buff[p2 + 3]); }, writeUint: function(buff, p2, n2) { buff[p2] = n2 >> 24 & 255; buff[p2 + 1] = n2 >> 16 & 255; buff[p2 + 2] = n2 >> 8 & 255; buff[p2 + 3] = n2 & 255; }, readASCII: function(buff, p2, l2) { var s2 = ""; for (var i2 = 0; i2 < l2; i2++) s2 += String.fromCharCode(buff[p2 + i2]); return s2; }, writeASCII: function(data, p2, s2) { for (var i2 = 0; i2 < s2.length; i2++) data[p2 + i2] = s2.charCodeAt(i2); }, readBytes: function(buff, p2, l2) { var arr = []; for (var i2 = 0; i2 < l2; i2++) arr.push(buff[p2 + i2]); return arr; }, pad: function(n2) { return n2.length < 2 ? "0" + n2 : n2; }, readUTF8: function(buff, p2, l2) { var s2 = "", ns; for (var i2 = 0; i2 < l2; i2++) s2 += "%" + _bin.pad(buff[p2 + i2].toString(16)); try { ns = decodeURIComponent(s2); } catch (e2) { return _bin.readASCII(buff, p2, l2); } return ns; } }; function toRGBA8(out) { var w2 = out.width, h2 = out.height; if (out.tabs.acTL == null) return [decodeImage(out.data, w2, h2, out).buffer]; var frms = []; if (out.frames[0].data == null) out.frames[0].data = out.data; var len = w2 * h2 * 4, img = new Uint8Array(len), empty = new Uint8Array(len), prev = new Uint8Array(len); for (var i2 = 0; i2 < out.frames.length; i2++) { var frm = out.frames[i2]; var fx = frm.rect.x, fy = frm.rect.y, fw = frm.rect.width, fh = frm.rect.height; var fdata = decodeImage(frm.data, fw, fh, out); if (i2 != 0) for (var j2 = 0; j2 < len; j2++) prev[j2] = img[j2]; if (frm.blend == 0) _copyTile(fdata, fw, fh, img, w2, h2, fx, fy, 0); else if (frm.blend == 1) _copyTile(fdata, fw, fh, img, w2, h2, fx, fy, 1); frms.push(img.buffer.slice(0)); if (frm.dispose == 0) ; else if (frm.dispose == 1) _copyTile(empty, fw, fh, img, w2, h2, fx, fy, 0); else if (frm.dispose == 2) for (var j2 = 0; j2 < len; j2++) img[j2] = prev[j2]; } return frms; } function decodeImage(data, w2, h2, out) { var area = w2 * h2, bpp = _getBPP(out); var bpl = Math.ceil(w2 * bpp / 8); var bf = new Uint8Array(area * 4), bf32 = new Uint32Array(bf.buffer); var ctype = out.ctype, depth = out.depth; var rs = _bin.readUshort; if (ctype == 6) { var qarea = area << 2; if (depth == 8) for (var i2 = 0; i2 < qarea; i2 += 4) { bf[i2] = data[i2]; bf[i2 + 1] = data[i2 + 1]; bf[i2 + 2] = data[i2 + 2]; bf[i2 + 3] = data[i2 + 3]; } if (depth == 16) for (var i2 = 0; i2 < qarea; i2++) { bf[i2] = data[i2 << 1]; } } else if (ctype == 2) { var ts = out.tabs["tRNS"]; if (ts == null) { if (depth == 8) for (var i2 = 0; i2 < area; i2++) { var ti = i2 * 3; bf32[i2] = 255 << 24 | data[ti + 2] << 16 | data[ti + 1] << 8 | data[ti]; } if (depth == 16) for (var i2 = 0; i2 < area; i2++) { var ti = i2 * 6; bf32[i2] = 255 << 24 | data[ti + 4] << 16 | data[ti + 2] << 8 | data[ti]; } } else { var tr = ts[0], tg = ts[1], tb = ts[2]; if (depth == 8) for (var i2 = 0; i2 < area; i2++) { var qi = i2 << 2, ti = i2 * 3; bf32[i2] = 255 << 24 | data[ti + 2] << 16 | data[ti + 1] << 8 | data[ti]; if (data[ti] == tr && data[ti + 1] == tg && data[ti + 2] == tb) bf[qi + 3] = 0; } if (depth == 16) for (var i2 = 0; i2 < area; i2++) { var qi = i2 << 2, ti = i2 * 6; bf32[i2] = 255 << 24 | data[ti + 4] << 16 | data[ti + 2] << 8 | data[ti]; if (rs(data, ti) == tr && rs(data, ti + 2) == tg && rs(data, ti + 4) == tb) bf[qi + 3] = 0; } } } else if (ctype == 3) { var p2 = out.tabs["PLTE"], ap = out.tabs["tRNS"], tl = ap ? ap.length : 0; if (depth == 1) for (var y2 = 0; y2 < h2; y2++) { var s0 = y2 * bpl, t0 = y2 * w2; for (var i2 = 0; i2 < w2; i2++) { var qi = t0 + i2 << 2, j2 = data[s0 + (i2 >> 3)] >> 7 - ((i2 & 7) << 0) & 1, cj = 3 * j2; bf[qi] = p2[cj]; bf[qi + 1] = p2[cj + 1]; bf[qi + 2] = p2[cj + 2]; bf[qi + 3] = j2 < tl ? ap[j2] : 255; } } if (depth == 2) for (var y2 = 0; y2 < h2; y2++) { var s0 = y2 * bpl, t0 = y2 * w2; for (var i2 = 0; i2 < w2; i2++) { var qi = t0 + i2 << 2, j2 = data[s0 + (i2 >> 2)] >> 6 - ((i2 & 3) << 1) & 3, cj = 3 * j2; bf[qi] = p2[cj]; bf[qi + 1] = p2[cj + 1]; bf[qi + 2] = p2[cj + 2]; bf[qi + 3] = j2 < tl ? ap[j2] : 255; } } if (depth == 4) for (var y2 = 0; y2 < h2; y2++) { var s0 = y2 * bpl, t0 = y2 * w2; for (var i2 = 0; i2 < w2; i2++) { var qi = t0 + i2 << 2, j2 = data[s0 + (i2 >> 1)] >> 4 - ((i2 & 1) << 2) & 15, cj = 3 * j2; bf[qi] = p2[cj]; bf[qi + 1] = p2[cj + 1]; bf[qi + 2] = p2[cj + 2]; bf[qi + 3] = j2 < tl ? ap[j2] : 255; } } if (depth == 8) for (var i2 = 0; i2 < area; i2++) { var qi = i2 << 2, j2 = data[i2], cj = 3 * j2; bf[qi] = p2[cj]; bf[qi + 1] = p2[cj + 1]; bf[qi + 2] = p2[cj + 2]; bf[qi + 3] = j2 < tl ? ap[j2] : 255; } } else if (ctype == 4) { if (depth == 8) for (var i2 = 0; i2 < area; i2++) { var qi = i2 << 2, di = i2 << 1, gr = data[di]; bf[qi] = gr; bf[qi + 1] = gr; bf[qi + 2] = gr; bf[qi + 3] = data[di + 1]; } if (depth == 16) for (var i2 = 0; i2 < area; i2++) { var qi = i2 << 2, di = i2 << 2, gr = data[di]; bf[qi] = gr; bf[qi + 1] = gr; bf[qi + 2] = gr; bf[qi + 3] = data[di + 2]; } } else if (ctype == 0) { var tr = out.tabs["tRNS"] ? out.tabs["tRNS"] : -1; for (var y2 = 0; y2 < h2; y2++) { var off = y2 * bpl, to = y2 * w2; if (depth == 1) for (var x2 = 0; x2 < w2; x2++) { var gr = 255 * (data[off + (x2 >>> 3)] >>> 7 - (x2 & 7) & 1), al = gr == tr * 255 ? 0 : 255; bf32[to + x2] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 2) for (var x2 = 0; x2 < w2; x2++) { var gr = 85 * (data[off + (x2 >>> 2)] >>> 6 - ((x2 & 3) << 1) & 3), al = gr == tr * 85 ? 0 : 255; bf32[to + x2] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 4) for (var x2 = 0; x2 < w2; x2++) { var gr = 17 * (data[off + (x2 >>> 1)] >>> 4 - ((x2 & 1) << 2) & 15), al = gr == tr * 17 ? 0 : 255; bf32[to + x2] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 8) for (var x2 = 0; x2 < w2; x2++) { var gr = data[off + x2], al = gr == tr ? 0 : 255; bf32[to + x2] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 16) for (var x2 = 0; x2 < w2; x2++) { var gr = data[off + (x2 << 1)], al = rs(data, off + (x2 << 1)) == tr ? 0 : 255; bf32[to + x2] = al << 24 | gr << 16 | gr << 8 | gr; } } } return bf; } function decode2(buff) { var data = new Uint8Array(buff), offset = 8, bin = _bin, rUs = bin.readUshort, rUi = bin.readUint; var out = { tabs: {}, frames: [] }; var dd = new Uint8Array(data.length), doff = 0; var fd, foff = 0; var mgck = [137, 80, 78, 71, 13, 10, 26, 10]; for (var i2 = 0; i2 < 8; i2++) if (data[i2] != mgck[i2]) throw "The input is not a PNG file!"; while (offset < data.length) { var len = bin.readUint(data, offset); offset += 4; var type = bin.readASCII(data, offset, 4); offset += 4; if (type == "IHDR") { _IHDR(data, offset, out); } else if (type == "iCCP") { var off = offset; while (data[off] != 0) off++; bin.readASCII(data, offset, off - offset); data[off + 1]; var fil = data.slice(off + 2, offset + len); var res = null; try { res = _inflate(fil); } catch (e2) { res = inflateRaw(fil); } out.tabs[type] = res; } else if (type == "CgBI") { out.tabs[type] = data.slice(offset, offset + 4); } else if (type == "IDAT") { for (var i2 = 0; i2 < len; i2++) dd[doff + i2] = data[offset + i2]; doff += len; } else if (type == "acTL") { out.tabs[type] = { num_frames: rUi(data, offset), num_plays: rUi(data, offset + 4) }; fd = new Uint8Array(data.length); } else if (type == "fcTL") { if (foff != 0) { var fr = out.frames[out.frames.length - 1]; fr.data = _decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect.height); foff = 0; } var rct = { x: rUi(data, offset + 12), y: rUi(data, offset + 16), width: rUi(data, offset + 4), height: rUi(data, offset + 8) }; var del = rUs(data, offset + 22); del = rUs(data, offset + 20) / (del == 0 ? 100 : del); var frm = { rect: rct, delay: Math.round(del * 1e3), dispose: data[offset + 24], blend: data[offset + 25] }; out.frames.push(frm); } else if (type == "fdAT") { for (var i2 = 0; i2 < len - 4; i2++) fd[foff + i2] = data[offset + i2 + 4]; foff += len - 4; } else if (type == "pHYs") { out.tabs[type] = [bin.readUint(data, offset), bin.readUint(data, offset + 4), data[offset + 8]]; } else if (type == "cHRM") { out.tabs[type] = []; for (var i2 = 0; i2 < 8; i2++) out.tabs[type].push(bin.readUint(data, offset + i2 * 4)); } else if (type == "tEXt" || type == "zTXt") { if (out.tabs[type] == null) out.tabs[type] = {}; var nz = bin.nextZero(data, offset); var keyw = bin.readASCII(data, offset, nz - offset); var text, tl = offset + len - nz - 1; if (type == "tEXt") text = bin.readASCII(data, nz + 1, tl); else { var bfr = _inflate(data.slice(nz + 2, nz + 2 + tl)); text = bin.readUTF8(bfr, 0, bfr.length); } out.tabs[type][keyw] = text; } else if (type == "iTXt") { if (out.tabs[type] == null) out.tabs[type] = {}; var nz = 0, off = offset; nz = bin.nextZero(data, off); var keyw = bin.readASCII(data, off, nz - off); off = nz + 1; var cflag = data[off]; data[off + 1]; off += 2; nz = bin.nextZero(data, off); bin.readASCII(data, off, nz - off); off = nz + 1; nz = bin.nextZero(data, off); bin.readUTF8(data, off, nz - off); off = nz + 1; var text, tl = len - (off - offset); if (cflag == 0) text = bin.readUTF8(data, off, tl); else { var bfr = _inflate(data.slice(off, off + tl)); text = bin.readUTF8(bfr, 0, bfr.length); } out.tabs[type][keyw] = text; } else if (type == "PLTE") { out.tabs[type] = bin.readBytes(data, offset, len); } else if (type == "hIST") { var pl = out.tabs["PLTE"].length / 3; out.tabs[type] = []; for (var i2 = 0; i2 < pl; i2++) out.tabs[type].push(rUs(data, offset + i2 * 2)); } else if (type == "tRNS") { if (out.ctype == 3) out.tabs[type] = bin.readBytes(data, offset, len); else if (out.ctype == 0) out.tabs[type] = rUs(data, offset); else if (out.ctype == 2) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)]; } else if (type == "gAMA") out.tabs[type] = bin.readUint(data, offset) / 1e5; else if (type == "sRGB") out.tabs[type] = data[offset]; else if (type == "bKGD") { if (out.ctype == 0 || out.ctype == 4) out.tabs[type] = [rUs(data, offset)]; else if (out.ctype == 2 || out.ctype == 6) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)]; else if (out.ctype == 3) out.tabs[type] = data[offset]; } else if (type == "IEND") { break; } offset += len; bin.readUint(data, offset); offset += 4; } if (foff != 0) { var fr = out.frames[out.frames.length - 1]; fr.data = _decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect.height); } out.data = _decompress(out, dd, out.width, out.height); delete out.compress; delete out.interlace; delete out.filter; return out; } function _decompress(out, dd, w2, h2) { var bpp = _getBPP(out), bpl = Math.ceil(w2 * bpp / 8), buff = new Uint8Array((bpl + 1 + out.interlace) * h2); if (out.tabs["CgBI"]) dd = inflateRaw(dd, buff); else dd = _inflate(dd, buff); if (out.interlace == 0) dd = _filterZero(dd, out, 0, w2, h2); else if (out.interlace == 1) dd = _readInterlace(dd, out); return dd; } function _inflate(data, buff) { var out = inflateRaw(new Uint8Array(data.buffer, 2, data.length - 6), buff); return out; } var inflateRaw = function() { var H2 = {}; H2.H = {}; H2.H.N = function(N2, W2) { var R2 = Uint8Array, i2 = 0, m2 = 0, J2 = 0, h2 = 0, Q2 = 0, X2 = 0, u2 = 0, w2 = 0, d2 = 0, v2, C2; if (N2[0] == 3 && N2[1] == 0) return W2 ? W2 : new R2(0); var V2 = H2.H, n2 = V2.b, A2 = V2.e, l2 = V2.R, M2 = V2.n, I2 = V2.A, e2 = V2.Z, b2 = V2.m, Z2 = W2 == null; if (Z2) W2 = new R2(N2.length >>> 2 << 5); while (i2 == 0) { i2 = n2(N2, d2, 1); m2 = n2(N2, d2 + 1, 2); d2 += 3; if (m2 == 0) { if ((d2 & 7) != 0) d2 += 8 - (d2 & 7); var D2 = (d2 >>> 3) + 4, q2 = N2[D2 - 4] | N2[D2 - 3] << 8; if (Z2) W2 = H2.H.W(W2, w2 + q2); W2.set(new R2(N2.buffer, N2.byteOffset + D2, q2), w2); d2 = D2 + q2 << 3; w2 += q2; continue; } if (Z2) W2 = H2.H.W(W2, w2 + (1 << 17)); if (m2 == 1) { v2 = b2.J; C2 = b2.h; X2 = (1 << 9) - 1; u2 = (1 << 5) - 1; } if (m2 == 2) { J2 = A2(N2, d2, 5) + 257; h2 = A2(N2, d2 + 5, 5) + 1; Q2 = A2(N2, d2 + 10, 4) + 4; d2 += 14; var j2 = 1; for (var c2 = 0; c2 < 38; c2 += 2) { b2.Q[c2] = 0; b2.Q[c2 + 1] = 0; } for (var c2 = 0; c2 < Q2; c2++) { var K2 = A2(N2, d2 + c2 * 3, 3); b2.Q[(b2.X[c2] << 1) + 1] = K2; if (K2 > j2) j2 = K2; } d2 += 3 * Q2; M2(b2.Q, j2); I2(b2.Q, j2, b2.u); v2 = b2.w; C2 = b2.d; d2 = l2(b2.u, (1 << j2) - 1, J2 + h2, N2, d2, b2.v); var r2 = V2.V(b2.v, 0, J2, b2.C); X2 = (1 << r2) - 1; var S2 = V2.V(b2.v, J2, h2, b2.D); u2 = (1 << S2) - 1; M2(b2.C, r2); I2(b2.C, r2, v2); M2(b2.D, S2); I2(b2.D, S2, C2); } while (true) { var T2 = v2[e2(N2, d2) & X2]; d2 += T2 & 15; var p2 = T2 >>> 4; if (p2 >>> 8 == 0) { W2[w2++] = p2; } else if (p2 == 256) { break; } else { var z2 = w2 + p2 - 254; if (p2 > 264) { var _2 = b2.q[p2 - 257]; z2 = w2 + (_2 >>> 3) + A2(N2, d2, _2 & 7); d2 += _2 & 7; } var $2 = C2[e2(N2, d2) & u2]; d2 += $2 & 15; var s2 = $2 >>> 4, Y2 = b2.c[s2], a2 = (Y2 >>> 4) + n2(N2, d2, Y2 & 15); d2 += Y2 & 15; while (w2 < z2) { W2[w2] = W2[w2++ - a2]; W2[w2] = W2[w2++ - a2]; W2[w2] = W2[w2++ - a2]; W2[w2] = W2[w2++ - a2]; } w2 = z2; } } } return W2.length == w2 ? W2 : W2.slice(0, w2); }; H2.H.W = function(N2, W2) { var R2 = N2.length; if (W2 <= R2) return N2; var V2 = new Uint8Array(R2 << 1); V2.set(N2, 0); return V2; }; H2.H.R = function(N2, W2, R2, V2, n2, A2) { var l2 = H2.H.e, M2 = H2.H.Z, I2 = 0; while (I2 < R2) { var e2 = N2[M2(V2, n2) & W2]; n2 += e2 & 15; var b2 = e2 >>> 4; if (b2 <= 15) { A2[I2] = b2; I2++; } else { var Z2 = 0, m2 = 0; if (b2 == 16) { m2 = 3 + l2(V2, n2, 2); n2 += 2; Z2 = A2[I2 - 1]; } else if (b2 == 17) { m2 = 3 + l2(V2, n2, 3); n2 += 3; } else if (b2 == 18) { m2 = 11 + l2(V2, n2, 7); n2 += 7; } var J2 = I2 + m2; while (I2 < J2) { A2[I2] = Z2; I2++; } } } return n2; }; H2.H.V = function(N2, W2, R2, V2) { var n2 = 0, A2 = 0, l2 = V2.length >>> 1; while (A2 < R2) { var M2 = N2[A2 + W2]; V2[A2 << 1] = 0; V2[(A2 << 1) + 1] = M2; if (M2 > n2) n2 = M2; A2++; } while (A2 < l2) { V2[A2 << 1] = 0; V2[(A2 << 1) + 1] = 0; A2++; } return n2; }; H2.H.n = function(N2, W2) { var R2 = H2.H.m, V2 = N2.length, n2, A2, l2, M2, I2, e2 = R2.j; for (var M2 = 0; M2 <= W2; M2++) e2[M2] = 0; for (M2 = 1; M2 < V2; M2 += 2) e2[N2[M2]]++; var b2 = R2.K; n2 = 0; e2[0] = 0; for (A2 = 1; A2 <= W2; A2++) { n2 = n2 + e2[A2 - 1] << 1; b2[A2] = n2; } for (l2 = 0; l2 < V2; l2 += 2) { I2 = N2[l2 + 1]; if (I2 != 0) { N2[l2] = b2[I2]; b2[I2]++; } } }; H2.H.A = function(N2, W2, R2) { var V2 = N2.length, n2 = H2.H.m, A2 = n2.r; for (var l2 = 0; l2 < V2; l2 += 2) if (N2[l2 + 1] != 0) { var M2 = l2 >> 1, I2 = N2[l2 + 1], e2 = M2 << 4 | I2, b2 = W2 - I2, Z2 = N2[l2] << b2, m2 = Z2 + (1 << b2); while (Z2 != m2) { var J2 = A2[Z2] >>> 15 - W2; R2[J2] = e2; Z2++; } } }; H2.H.l = function(N2, W2) { var R2 = H2.H.m.r, V2 = 15 - W2; for (var n2 = 0; n2 < N2.length; n2 += 2) { var A2 = N2[n2] << W2 - N2[n2 + 1]; N2[n2] = R2[A2] >>> V2; } }; H2.H.M = function(N2, W2, R2) { R2 = R2 << (W2 & 7); var V2 = W2 >>> 3; N2[V2] |= R2; N2[V2 + 1] |= R2 >>> 8; }; H2.H.I = function(N2, W2, R2) { R2 = R2 << (W2 & 7); var V2 = W2 >>> 3; N2[V2] |= R2; N2[V2 + 1] |= R2 >>> 8; N2[V2 + 2] |= R2 >>> 16; }; H2.H.e = function(N2, W2, R2) { return (N2[W2 >>> 3] | N2[(W2 >>> 3) + 1] << 8) >>> (W2 & 7) & (1 << R2) - 1; }; H2.H.b = function(N2, W2, R2) { return (N2[W2 >>> 3] | N2[(W2 >>> 3) + 1] << 8 | N2[(W2 >>> 3) + 2] << 16) >>> (W2 & 7) & (1 << R2) - 1; }; H2.H.Z = function(N2, W2) { return (N2[W2 >>> 3] | N2[(W2 >>> 3) + 1] << 8 | N2[(W2 >>> 3) + 2] << 16) >>> (W2 & 7); }; H2.H.i = function(N2, W2) { return (N2[W2 >>> 3] | N2[(W2 >>> 3) + 1] << 8 | N2[(W2 >>> 3) + 2] << 16 | N2[(W2 >>> 3) + 3] << 24) >>> (W2 & 7); }; H2.H.m = function() { var N2 = Uint16Array, W2 = Uint32Array; return { K: new N2(16), j: new N2(16), X: [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15], S: [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 999, 999, 999], T: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0], q: new N2(32), p: [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 65535, 65535], z: [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0], c: new W2(32), J: new N2(512), _: [], h: new N2(32), $: [], w: new N2(32768), C: [], v: [], d: new N2(32768), D: [], u: new N2(512), Q: [], r: new N2(1 << 15), s: new W2(286), Y: new W2(30), a: new W2(19), t: new W2(15e3), k: new N2(1 << 16), g: new N2(1 << 15) }; }(); (function() { var N2 = H2.H.m, W2 = 1 << 15; for (var R2 = 0; R2 < W2; R2++) { var V2 = R2; V2 = (V2 & 2863311530) >>> 1 | (V2 & 1431655765) << 1; V2 = (V2 & 3435973836) >>> 2 | (V2 & 858993459) << 2; V2 = (V2 & 4042322160) >>> 4 | (V2 & 252645135) << 4; V2 = (V2 & 4278255360) >>> 8 | (V2 & 16711935) << 8; N2.r[R2] = (V2 >>> 16 | V2 << 16) >>> 17; } function n2(A2, l2, M2) { while (l2-- != 0) A2.push(0, M2); } for (var R2 = 0; R2 < 32; R2++) { N2.q[R2] = N2.S[R2] << 3 | N2.T[R2]; N2.c[R2] = N2.p[R2] << 4 | N2.z[R2]; } n2(N2._, 144, 8); n2(N2._, 255 - 143, 9); n2(N2._, 279 - 255, 7); n2(N2._, 287 - 279, 8); H2.H.n(N2._, 9); H2.H.A(N2._, 9, N2.J); H2.H.l(N2._, 9); n2(N2.$, 32, 5); H2.H.n(N2.$, 5); H2.H.A(N2.$, 5, N2.h); H2.H.l(N2.$, 5); n2(N2.Q, 19, 0); n2(N2.C, 286, 0); n2(N2.D, 30, 0); n2(N2.v, 320, 0); })(); return H2.H.N; }(); function _readInterlace(data, out) { var w2 = out.width, h2 = out.height; var bpp = _getBPP(out), cbpp = bpp >> 3, bpl = Math.ceil(w2 * bpp / 8); var img = new Uint8Array(h2 * bpl); var di = 0; var starting_row = [0, 0, 4, 0, 2, 0, 1]; var starting_col = [0, 4, 0, 2, 0, 1, 0]; var row_increment = [8, 8, 8, 4, 4, 2, 2]; var col_increment = [8, 8, 4, 4, 2, 2, 1]; var pass = 0; while (pass < 7) { var ri = row_increment[pass], ci = col_increment[pass]; var sw = 0, sh = 0; var cr = starting_row[pass]; while (cr < h2) { cr += ri; sh++; } var cc = starting_col[pass]; while (cc < w2) { cc += ci; sw++; } var bpll = Math.ceil(sw * bpp / 8); _filterZero(data, out, di, sw, sh); var y2 = 0, row = starting_row[pass]; while (row < h2) { var col = starting_col[pass]; var cdi = di + y2 * bpll << 3; while (col < w2) { if (bpp == 1) { var val = data[cdi >> 3]; val = val >> 7 - (cdi & 7) & 1; img[row * bpl + (col >> 3)] |= val << 7 - ((col & 7) << 0); } if (bpp == 2) { var val = data[cdi >> 3]; val = val >> 6 - (cdi & 7) & 3; img[row * bpl + (col >> 2)] |= val << 6 - ((col & 3) << 1); } if (bpp == 4) { var val = data[cdi >> 3]; val = val >> 4 - (cdi & 7) & 15; img[row * bpl + (col >> 1)] |= val << 4 - ((col & 1) << 2); } if (bpp >= 8) { var ii = row * bpl + col * cbpp; for (var j2 = 0; j2 < cbpp; j2++) img[ii + j2] = data[(cdi >> 3) + j2]; } cdi += bpp; col += ci; } y2++; row += ri; } if (sw * sh != 0) di += sh * (1 + bpll); pass = pass + 1; } return img; } function _getBPP(out) { var noc = [1, null, 3, 1, 2, null, 4][out.ctype]; return noc * out.depth; } function _filterZero(data, out, off, w2, h2) { var bpp = _getBPP(out), bpl = Math.ceil(w2 * bpp / 8); bpp = Math.ceil(bpp / 8); var i2, di, type = data[off], x2 = 0; if (type > 1) data[off] = [0, 0, 1][type - 2]; if (type == 3) for (x2 = bpp; x2 < bpl; x2++) data[x2 + 1] = data[x2 + 1] + (data[x2 + 1 - bpp] >>> 1) & 255; for (var y2 = 0; y2 < h2; y2++) { i2 = off + y2 * bpl; di = i2 + y2 + 1; type = data[di - 1]; x2 = 0; if (type == 0) for (; x2 < bpl; x2++) data[i2 + x2] = data[di + x2]; else if (type == 1) { for (; x2 < bpp; x2++) data[i2 + x2] = data[di + x2]; for (; x2 < bpl; x2++) data[i2 + x2] = data[di + x2] + data[i2 + x2 - bpp]; } else if (type == 2) { for (; x2 < bpl; x2++) data[i2 + x2] = data[di + x2] + data[i2 + x2 - bpl]; } else if (type == 3) { for (; x2 < bpp; x2++) data[i2 + x2] = data[di + x2] + (data[i2 + x2 - bpl] >>> 1); for (; x2 < bpl; x2++) data[i2 + x2] = data[di + x2] + (data[i2 + x2 - bpl] + data[i2 + x2 - bpp] >>> 1); } else { for (; x2 < bpp; x2++) data[i2 + x2] = data[di + x2] + _paeth(0, data[i2 + x2 - bpl], 0); for (; x2 < bpl; x2++) data[i2 + x2] = data[di + x2] + _paeth(data[i2 + x2 - bpp], data[i2 + x2 - bpl], data[i2 + x2 - bpp - bpl]); } } return data; } function _paeth(a2, b2, c2) { var p2 = a2 + b2 - c2, pa = p2 - a2, pb = p2 - b2, pc = p2 - c2; if (pa * pa <= pb * pb && pa * pa <= pc * pc) return a2; else if (pb * pb <= pc * pc) return b2; return c2; } function _IHDR(data, offset, out) { out.width = _bin.readUint(data, offset); offset += 4; out.height = _bin.readUint(data, offset); offset += 4; out.depth = data[offset]; offset++; out.ctype = data[offset]; offset++; out.compress = data[offset]; offset++; out.filter = data[offset]; offset++; out.interlace = data[offset]; offset++; } function _copyTile(sb, sw, sh, tb, tw, th, xoff, yoff, mode) { var w2 = Math.min(sw, tw), h2 = Math.min(sh, th); var si = 0, ti = 0; for (var y2 = 0; y2 < h2; y2++) for (var x2 = 0; x2 < w2; x2++) { if (xoff >= 0 && yoff >= 0) { si = y2 * sw + x2 << 2; ti = (yoff + y2) * tw + xoff + x2 << 2; } else { si = (-yoff + y2) * sw - xoff + x2 << 2; ti = y2 * tw + x2 << 2; } if (mode == 0) { tb[ti] = sb[si]; tb[ti + 1] = sb[si + 1]; tb[ti + 2] = sb[si + 2]; tb[ti + 3] = sb[si + 3]; } else if (mode == 1) { var fa = sb[si + 3] * (1 / 255), fr = sb[si] * fa, fg = sb[si + 1] * fa, fb = sb[si + 2] * fa; var ba = tb[ti + 3] * (1 / 255), br = tb[ti] * ba, bg = tb[ti + 1] * ba, bb = tb[ti + 2] * ba; var ifa = 1 - fa, oa = fa + ba * ifa, ioa = oa == 0 ? 0 : 1 / oa; tb[ti + 3] = 255 * oa; tb[ti + 0] = (fr + br * ifa) * ioa; tb[ti + 1] = (fg + bg * ifa) * ioa; tb[ti + 2] = (fb + bb * ifa) * ioa; } else if (mode == 2) { var fa = sb[si + 3], fr = sb[si], fg = sb[si + 1], fb = sb[si + 2]; var ba = tb[ti + 3], br = tb[ti], bg = tb[ti + 1], bb = tb[ti + 2]; if (fa == ba && fr == br && fg == bg && fb == bb) { tb[ti] = 0; tb[ti + 1] = 0; tb[ti + 2] = 0; tb[ti + 3] = 0; } else { tb[ti] = fr; tb[ti + 1] = fg; tb[ti + 2] = fb; tb[ti + 3] = fa; } } else if (mode == 3) { var fa = sb[si + 3], fr = sb[si], fg = sb[si + 1], fb = sb[si + 2]; var ba = tb[ti + 3], br = tb[ti], bg = tb[ti + 1], bb = tb[ti + 2]; if (fa == ba && fr == br && fg == bg && fb == bb) continue; if (fa < 220 && ba > 20) return false; } } return true; } class AJAXError extends Error { constructor(status, statusText, url, body) { super(`AJAXError: ${statusText} (${status}): ${url}`); this.status = status; this.statusText = statusText; this.url = url; this.body = body; } } const isFileURL = (url) => /^file:/.test(url) || /^file:/.test(getReferrer()) && !/^\w+:/.test(url); function makeFetchRequest(requestParameters, callback) { const controller = new AbortController(); const request2 = new Request(requestParameters.url, { method: requestParameters.method || "GET", body: requestParameters.body, credentials: requestParameters.credentials, headers: requestParameters.headers, referrer: getReferrer(), signal: controller.signal }); let complete = false; let aborted = false; if (requestParameters.type === "json") { request2.headers.set("Accept", "application/json"); } const validateOrFetch = (err, cachedResponse, responseIsFresh) => { if (aborted) return; if (err) { if (err.message !== "SecurityError") { warnOnce(err); } } if (cachedResponse && responseIsFresh) { return finishRequest(cachedResponse); } fetch(request2).then((response) => { if (response.ok) { return finishRequest(response); } else { return response.blob().then((body) => callback(new AJAXError(response.status, response.statusText, requestParameters.url, body))); } }).catch((error) => { if (error.code === 20) ; callback(new Error(error.message)); }); }; const finishRequest = (response) => { (requestParameters.type === "arrayBuffer" ? response.arrayBuffer() : requestParameters.type === "json" ? response.json() : response.text()).then((result) => { if (aborted) return; complete = true; callback(null, result, response.headers.get("Cache-Control"), response.headers.get("Expires")); }).catch((err) => { if (!aborted) callback(new Error(err.message)); }); }; validateOrFetch(null, null); return { cancel: () => { aborted = true; if (!complete) controller.abort(); } }; } function makeXMLHttpRequest(requestParameters, callback) { const xhr = new XMLHttpRequest(); xhr.open(requestParameters.method || "GET", requestParameters.url, true); if (requestParameters.type === "arrayBuffer") { xhr.responseType = "arraybuffer"; } for (const k2 in requestParameters.headers) { xhr.setRequestHeader(k2, requestParameters.headers[k2]); } if (requestParameters.type === "json") { xhr.responseType = "text"; xhr.setRequestHeader("Accept", "application/json"); } xhr.withCredentials = requestParameters.credentials === "include"; xhr.onerror = () => { callback(new Error(xhr.statusText)); }; xhr.onload = () => { if ((xhr.status >= 200 && xhr.status < 300 || xhr.status === 0) && xhr.response !== null) { let data = xhr.response; if (requestParameters.type === "json") { try { data = JSON.parse(xhr.response); } catch (err) { return callback(err); } } callback(null, data, xhr.getResponseHeader("Cache-Control"), xhr.getResponseHeader("Expires")); } else { const body = new Blob([xhr.response], { type: xhr.getResponseHeader("Content-Type") }); callback(new AJAXError(xhr.status, xhr.statusText, requestParameters.url, body)); } }; xhr.send(requestParameters.body); return { cancel: () => xhr.abort() }; } const makeRequest = function(requestParameters, callback) { if (/:\/\//.test(requestParameters.url) && !/^https?:|^file:/.test(requestParameters.url)) { if (isWorker() && self.worker && self.worker.actor) { return self.worker.actor.send("getResource", requestParameters, callback); } if (!isWorker()) { return makeFetchRequest(requestParameters, callback); } } if (!isFileURL(requestParameters.url)) { if ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore fetch && Request && AbortController && Object.prototype.hasOwnProperty.call(Request.prototype, "signal") ) { return makeFetchRequest(requestParameters, callback); } if (isWorker() && self.worker && self.worker.actor) { const queueOnMainThread = true; return self.worker.actor.send("getResource", requestParameters, callback, void 0, queueOnMainThread); } } return makeXMLHttpRequest(requestParameters, callback); }; let pool; function getPool() { if (!pool) { pool = new self.GeoTIFF.Pool(); } return pool; } class RequestAdapter { constructor(options) { this.requestScheduler = new RequestScheduler(options); } getResource(mapId, params, callback) { return makeRequest(params, callback); } /** * arrayBuffer 转 Unit8 * @param data * @param callback */ arrayBuffer2unit8(data, callback) { const pngImage = decode2(data); const pixels = toRGBA8(pngImage); callback(null, { data: new Uint8Array(pixels[0]), width: pngImage.width, height: pngImage.height }); } /** * arrayBuffer 转图像 * 1. 如果支持 ImageBitmap 则生成 `ImageBitmap` 除了极少数浏览器不支持外兼容性尚可 * 2. 在 safari 和移动浏览器下配合 rgba2float 有精度问题,不建议使用 * @param data * @param callback */ arrayBuffer2Image(data, callback) { const imageBitmapSupported = typeof createImageBitmap === "function"; if (imageBitmapSupported) { arrayBufferToImageBitmap(data, callback); } else { this.arrayBuffer2unit8(data, callback); } } /** * geotiff 解析 * @param data * @param callback */ arrayBuffer2tiff(data, callback) { if (!self.GeoTIFF) { throw new Error("Must config [geotiff](https://github.com/geotiffjs/geotiff.js) dep use `configDeps`"); } self.GeoTIFF.fromArrayBuffer(data).then((geotiff) => { geotiff.getImage().then((image) => { const result = {}; const fileDirectory = image.fileDirectory; const { GeographicTypeGeoKey, ProjectedCSTypeGeoKey } = image.getGeoKeys(); result.projection = ProjectedCSTypeGeoKey || GeographicTypeGeoKey; const height = image.getHeight(); result.height = height; const width = image.getWidth(); result.width = width; const [resolutionX, resolutionY] = image.getResolution(); result.pixelHeight = Math.abs(resolutionY); result.pixelWidth = Math.abs(resolutionX); const [originX, originY] = image.getOrigin(); result.xmin = originX; result.xmax = result.xmin + width * result.pixelWidth; result.ymax = originY; result.ymin = result.ymax - height * result.pixelHeight; result.noDataValue = fileDirectory.GDAL_NODATA ? parseFloat(fileDirectory.GDAL_NODATA) : null; result.numberOfRasters = fileDirectory.SamplesPerPixel; image.readRasters({ pool: getPool() }).then((rasters) => { result.rasters = rasters; const r2 = rasters[0]; if (r2) { let i2 = 0; const bands = rasters.length; const d2 = new r2.constructor(r2.length * bands); for (; i2 < r2.length; i2++) { for (let j2 = 0; j2 < bands; j2++) { d2[i2 + j2] = rasters[j2][i2]; } } result.data = d2; } result.metadata = image.getGDALMetadata(); const metadata = parseMetedata(fileDirectory.ImageDescription || ""); result.min = metadata.min; result.max = metadata.max; result.isTiff = true; callback(null, result); }).catch((err) => { callback(err); }); }).catch((err) => { callback(err); }); }).catch((err) => { callback(err); }); } /** * 解析 exif 信息 * @param data * @param callback */ parseExif(data, callback) { Z(data).then((res) => { this.arrayBuffer2Image(data, (error, image) => { if (error) { callback(error); } else { callback(null, { data: isImageBitmap2(image) ? image : image.data, width: image.width, height: image.height, exif: res, withExif: true }); } }); }).catch((err) => { callback(err); }); } fetch(params, callback) { let aborted = false; const r2 = this.requestScheduler.scheduleRequest(() => { const p2 = new Promise((resolve) => { const request2 = makeRequest(params, (...args) => { if (aborted) { resolve(false); return; } callback(...args); resolve(args); }); p2.cancel = () => { request2.cancel(); }; }); return p2; }); return { cancel: () => { aborted = true; r2.cancel(); } }; } } let request = null; function getRequest(options = {}, force = false) { if (!request || force) { request = new RequestAdapter(options); } return request; } const registry = {}; function register(name, klass, options = {}) { if (registry[name]) throw new Error(`${name} is already registered.`); Object.defineProperty(klass, "_classRegistryKey", { value: name, writeable: false }); registry[name] = { klass, omit: options.omit || [], shallow: options.shallow || [] }; } register("Object", Object); register("Error", Error); register("AJAXError", AJAXError); function serialize(input, transferables) { if (input === null || input === void 0 || typeof input === "boolean" || typeof input === "number" || typeof input === "string" || input instanceof Boolean || input instanceof Number || input instanceof String || input instanceof Date || input instanceof RegExp || input instanceof Blob) { return input; } if (isArrayBuffer(input)) { if (transferables) { transferables.push(input); } return input; } if (isImageBitmap2(input)) { if (transferables) { transferables.push(input); } return input; } if (ArrayBuffer.isView(input)) { const view = input; if (transferables) { transferables.push(view.buffer); } return view; } if (input instanceof ImageData) { if (transferables) { transferables.push(input.data.buffer); } return input; } if (Array.isArray(input)) { const serialized = []; for (const item of input) { serialized.push(serialize(item, transferables)); } return serialized; } if (typeof input === "object") { const klass = input.constructor; const name = klass._classRegistryKey; if (!name) { throw new Error("can't serialize object of unregistered class"); } if (!registry[name]) throw new Error(`${name} is not registered.`); const properties = klass.serialize ? ( // (Temporary workaround) allow a class to provide static // `serialize()` and `deserialize()` methods to bypass the generic // approach. // This temporary workaround lets us use the generic serialization // approach for objects whose members include instances of dynamic // StructArray types. Once we refactor StructArray to be static, // we can remove this complexity. klass.serialize(input, transferables) ) : {}; if (!klass.serialize) { for (const key in input) { if (!input.hasOwnProperty(key)) continue; if (registry[name].omit.indexOf(key) >= 0) continue; const property = input[key]; properties[key] = registry[name].shallow.indexOf(key) >= 0 ? property : serialize(property, transferables); } if (input instanceof Error) { properties.message = input.message; } } else if (transferables && properties === transferables[transferables.length - 1]) { throw new Error("statically serialized object won't survive transfer of $name property"); } if (properties.$name) { throw new Error("$name property is reserved for worker serialization logic."); } if (name !== "Object") { properties.$name = name; } return properties; } throw new Error(`can't serialize object of type ${typeof input}`); } function deserialize(input) { if (input === null || input === void 0 || typeof input === "boolean" || typeof input === "number" || typeof input === "string" || input instanceof Boolean || input instanceof Number || input instanceof String || input instanceof Date || input instanceof RegExp || input instanceof Blob || isArrayBuffer(input) || isImageBitmap2(input) || ArrayBuffer.isView(input) || input instanceof ImageData) { return input; } if (Array.isArray(input)) { return input.map(deserialize); } if (typeof input === "object") { const name = input.$name || "Object"; if (!registry[name]) { throw new Error(`can't deserialize unregistered class ${name}`); } const { klass } = registry[name]; if (!klass) { throw new Error(`can't deserialize unregistered class ${name}`); } if (klass.deserialize) { return klass.deserialize(input); } const result = Object.create(klass.prototype); for (const key of Object.keys(input)) { if (key === "$name") continue; const value = input[key]; result[key] = registry[name].shallow.indexOf(key) >= 0 ? value : deserialize(value); } return result; } throw new Error(`can't deserialize object of type ${typeof input}`); } class ThrottledInvoker { constructor(callback) { this.callback = callback; this.triggered = false; if (typeof MessageChannel !== "undefined") { this.channel = new MessageChannel(); this.channel.port2.onmessage = () => { this.triggered = false; this.callback(); }; } } trigger() { if (!this.triggered) { this.triggered = true; if (this.channel) { this.channel.port1.postMessage(true); } else { setTimeout(() => { this.triggered = false; this.callback(); }, 0); } } } remove() { this.channel = null; this.callback = nullFunction; } } class Actor { constructor(target, parent, dispatcherId) { this.target = target; this.parent = parent; this.id = uid("actor"); this.dispatcherId = dispatcherId; this.callbacks = {}; this.tasks = {}; this.taskQueue = []; this.cancelCallbacks = {}; this.receive = this.receive.bind(this); this.process = this.process.bind(this); this.invoker = new ThrottledInvoker(this.process); this.target.addEventListener("message", this.receive, false); this.globalScope = isWorker() ? target : window; } /** * Sends a message from a main-thread map to a Worker or from a Worker back to * a main-thread map instance. * * @param type The name of the target method to invoke or '[source-type].[source-name].name' for a method on a WorkerSource. * @param data * @param callback * @param targetId A particular mapId to which to send this message. * @param mustQueue * @private */ send(type, data, callback, targetId, mustQueue = false) { const id = Math.round(Math.random() * 1e18).toString(36).substring(0, 10); if (callback) { this.callbacks[id] = callback; } const buffers = isSafari(this.globalScope) ? void 0 : []; this.target.postMessage( { id, type, hasCallback: !!callback, targetId, mustQueue, dispatcherId: this.dispatcherId, data: serialize(data, buffers) }, buffers ); return { cancel: () => { if (callback) { delete this.callbacks[id]; } this.target.postMessage({ id, type: "", targetId, dispatcherId: this.dispatcherId }); } }; } receive(message) { const { data } = message; const { id } = data; if (!id) { return; } if (data.targetId && this.dispatcherId !== data.targetId) { return; } if (data.type === "") { delete this.tasks[id]; const cancel = this.cancelCallbacks[id]; delete this.cancelCallbacks[id]; if (cancel) { cancel(); } } else if (isWorker() || data.mustQueue) { this.tasks[id] = data; this.taskQueue.push(id); this.invoker.trigger(); } else { this.processTask(id, data); } } process() { if (!this.taskQueue.length) { return; } const id = this.taskQueue.shift(); if (id === void 0) return; const task = this.tasks[id]; delete this.tasks[id]; if (this.taskQueue.length) { this.invoker.trigger(); } if (!task) { return; } this.processTask(id, task); } processTask(id, task) { if (task.type === "") { const callback = this.callbacks[id]; delete this.callbacks[id]; if (callback) { if (task.error) { callback(deserialize(task.error)); } else { callback(null, deserialize(task.data)); } } } else { let completed = false; const buffers = isSafari(this.globalScope) ? void 0 : []; const done = task.hasCallback ? (err, data) => { completed = true; delete this.cancelCallbacks[id]; this.target.postMessage( { id, type: "", dispatcherId: this.dispatcherId, error: err ? serialize(err) : null, data: serialize(data, buffers) }, buffers ); } : () => { completed = true; }; let callback = null; const params = deserialize(task.data); if (this.parent[task.type]) { callback = this.parent[task.type]?.(task.dispatcherId, params, done); } else { done(new Error(`Could not find function ${task.type}`)); } if (!completed && callback && callback.cancel) { this.cancelCallbacks[id] = callback.cancel; } } } remove() { this.invoker.remove(); this.target.removeEventListener("message", this.receive, false); } } exports.Actor = Actor; exports.RequestScheduler = RequestScheduler; exports.ThrottledInvoker = ThrottledInvoker; exports.asyncAll = asyncAll; exports.getReferrer = getReferrer; exports.getRequest = getRequest; exports.isWorker = isWorker; exports.nullFunction = nullFunction; exports.register = register; exports.uid = uid; exports.utils = utils2; }); define(["./shared"], function(Actor) { class Worker2 { constructor(self2) { this.cancelMap = /* @__PURE__ */ new Map(); this.self = self2; this.actor = new Actor.Actor(self2, this); this.request = Actor.getRequest(); } setReferrer(dispatcherId, referrer) { this.referrer = referrer; } configDeps(dispatcherId, deps, callback) { if (deps && Array.isArray(deps) && deps.length > 0) { try { self.importScripts(...deps); callback(null, true); } catch (e) { Actor.asyncAll( deps, (d, done) => { this.request.fetch( { url: d, type: "arrayBuffer" }, (err, data) => { if (err) { done(err, false); return console.error(err); } const url = URL.createObjectURL(new Blob([data], { type: "application/javascript" })); self.importScripts(url); setTimeout(() => { URL.revokeObjectURL(url); }); done(null, true); } ); }, callback ); } } else { callback(null, true); } } loadData(dispatcherId, params, callback) { const cancelId = params?.cancelId; const { cancel } = this.request.fetch(params, (err, data) => { this.cancelMap.delete(cancelId); if (err) { callback(err); } else { if (params?.decodeType === 0) { this.request.arrayBuffer2Image(data, callback); } else if (params?.decodeType === 1) { this.request.arrayBuffer2unit8(data, callback); } else if (params?.decodeType === 2) { this.request.arrayBuffer2tiff(data, callback); } else if (params?.decodeType === 3) { this.request.parseExif(data, callback); } } }); this.cancelMap.set(cancelId, cancel); } cancel(dispatcherId, params, callback) { const cancelId = params?.cancelId; const c = this.cancelMap.get(cancelId); if (c) { c(); callback(null, true); } else { callback(new Error("\u65E0\u76F8\u5173\u7684\u53EF\u53D6\u6D88\u8BF7\u6C42\uFF01")); } } } if (Actor.isWorker()) { self.worker = new Worker2(self); } return Worker2; }); define(["./shared"], function(Actor) { let u = ""; function setWorkerUrl(url) { u = url; } function getWorkerUrl() { return u; } let deps = []; function configDeps2(d) { deps = d; } function getConfigDeps() { return deps; } function workerFactory() { return new Worker(getWorkerUrl()); } const PRELOAD_POOL_ID = "__wind_layer_preloaded_worker_pool__"; class WorkerPool { constructor() { this.active = {}; } /** * 获取 `Worker` 实例 * @param id */ acquire(id) { if (!this.workers) { this.workers = []; for (let i = 0; i < WorkerPool.workerCount; i++) { const worker2 = workerFactory(); if (worker2) { this.workers.push(worker2); } } } this.active[id] = true; return this.workers.slice(); } /** * 释放所有 `Worker` * @param id */ release(id) { delete this.active[id]; if (this.numActive() === 0 && this.workers) { this.workers.forEach((w) => { w.terminate(); }); this.workers = null; } } isPreloaded() { return !!this.active[PRELOAD_POOL_ID]; } /** * 获取激活的`Worker` 数量 */ numActive() { return Object.keys(this.active).length; } } const hardwareConcurrency = typeof navigator !== "undefined" && navigator.hardwareConcurrency || 4; const availableLogicalProcessors = Math.floor(hardwareConcurrency / 2); WorkerPool.workerCount = Math.max(Math.min(availableLogicalProcessors, 6), 1); class Dispatcher { constructor(workerPool, parent, dispatcherId) { this.workerPool = workerPool; this.actors = []; this.currentActor = 0; this.id = Actor.uid("dispatcher"); this.dispatcherId = dispatcherId; const workers = this.workerPool.acquire(this.dispatcherId); for (let i = 0; i < workers.length; i++) { const worker2 = workers[i]; const actor = new Actor.Actor(worker2, parent, this.dispatcherId); actor.name = `Worker ${i}`; this.actors.push(actor); } if (!this.actors.length) throw new Error("No actors found"); } /** * 广播到所有 Actor * @param type * @param data * @param cb */ broadcast(type, data, cb) { cb = cb || Actor.nullFunction; Actor.asyncAll( this.actors, (actor, done) => { actor.send(type, data, done); }, cb ); } send(type, data, cb, id) { const actor = this.getActor(id); if (actor) { actor.send(type, data, cb); } } /** * 获取要发送消息的 `Actor` * TIP: 是否需要实现 `Actor` 是否占用判断 */ getActor(id) { if (id !== void 0) { const index = this.actors.findIndex((a) => a.id === id); if (index > -1) { this.currentActor = index; } else { this.currentActor = (this.currentActor + 1) % this.actors.length; } } else { this.currentActor = (this.currentActor + 1) % this.actors.length; } return this.actors[this.currentActor]; } remove(removed = true) { this.actors.forEach((actor) => { actor.remove(); }); this.actors = []; if (removed) this.workerPool.release(this.id); } } let globalWorkerPool; function getGlobalWorkerPool() { if (!globalWorkerPool) { globalWorkerPool = new WorkerPool(); } return globalWorkerPool; } function prewarm() { const workerPool = getGlobalWorkerPool(); workerPool.acquire(PRELOAD_POOL_ID); } const exported = { utils: Actor.utils, request: Actor.getRequest, register: Actor.register, configDeps: configDeps2, getConfigDeps, prewarm, getReferrer: Actor.getReferrer, setWorkerUrl, getGlobalWorkerPool, Actor: Actor.Actor, WorkerPool, Dispatcher, RequestScheduler: Actor.RequestScheduler, ThrottledInvoker: Actor.ThrottledInvoker }; return exported; }); var wgw$1 = wgw; class Pipelines { #passes = []; constructor(renderer) { this.enabled = true; this.renderer = renderer; } get passes() { return this.#passes; } get length() { return this.passes.length; } resize(width, height) { const len = this.#passes.length; for (let i = 0; i < len; i++) { const pass = this.#passes[i]; pass.resize?.(width, height); } } addPass(pass) { this.#passes.push(pass); } removePass(pass) { const idx = this.#passes.indexOf(pass); if (idx > -1) { this.#passes.splice(pass, 1); pass.destroy(); } } removePasses() { this.#passes.forEach((pass) => pass.destroy()); this.#passes = []; } getPass(id) { return this.#passes.find((pass) => pass.id === id); } prerender(rendererParams, rendererState) { const passes = this.#passes.filter((p) => p.enabled && p.prerender === true); if (passes.length > 0) { const len = passes.length; for (let i = 0; i < len; i++) { const pass = passes[i]; pass.render(rendererParams, rendererState); } this.renderer.resetState(); } } render(rendererParams, rendererState) { const passes = this.#passes.filter((p) => p.enabled && p.prerender !== true); if (passes.length > 0) { const len = passes.length; for (let i = 0; i < len; i++) { const pass = passes[i]; pass.render(rendererParams, rendererState); } this.renderer.resetState(); } } destroy() { this.#passes.forEach((pass) => pass.destroy()); } } const ERR_PASS_METHOD_UNDEFINED = "Pass subclass must define virtual methods"; class Pass { #enabled = true; constructor(id, renderer, options = {}) { this.id = id; this.renderer = renderer; this.options = options; this.setMaskPass(this.options.maskPass); } get enabled() { return this.#enabled; } set enabled(state) { this.#enabled = state; } setMaskPass(pass) { this.maskPass = pass; } render(rendererParams, rendererState, cb) { throw new Error(ERR_PASS_METHOD_UNDEFINED); } destroy() { throw new Error(ERR_PASS_METHOD_UNDEFINED); } } var vert$4 = "#define GLSLIFY 1\nattribute vec2 uv;attribute vec3 position;uniform vec3 cameraPosition;uniform mat4 viewMatrix;uniform mat4 modelMatrix;uniform mat4 modelViewMatrix;uniform mat4 projectionMatrix;varying vec2 vUv;void main(){vUv=vec2(uv.x,1.0-uv.y);gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}"; var frag$8 = "#defines\nprecision highp float;\n#define GLSLIFY 1\nvarying vec2 vUv;uniform sampler2D u_image0;uniform sampler2D u_image1;\n#include \n#if RENDER_TYPE == 1\nuniform vec4 dataRange;vec4 getColor(const vec2 uv){vec2 rg=texture2D(u_image0,uv).rg;vec2 data=rg*(dataRange.yw-dataRange.xz)+dataRange.xz;return vec4(data.xy,0.0,1.0);}\n#elif RENDER_TYPE == 0\nuniform vec2 dataRange;vec4 getColor(const vec2 uv){float r=texture2D(u_image0,uv).r;float rf=r*(dataRange.y-dataRange.x)+dataRange.x;return vec4(rf,0.0,0.0,1.0);}\n#elif RENDER_TYPE == 2\nvec4 getColor(const vec2 uv){vec4 rgba=texture2D(u_image0,uv).rgba;float r=decode_float(rgba,LITTLE_ENDIAN);return vec4(r,0.0,0.0,1.0);}\n#else\nvec4 getColor(const vec2 uv){return texture2D(u_image0,uv).rgba;}\n#endif\nvoid main(){gl_FragColor=getColor(vUv);}"; var random = "#define GLSLIFY 1\nhighp float rand(vec2 co){highp float a=12.9898;highp float b=78.233;highp float c=43758.5453;highp float dt=dot(co.xy,vec2(a,b));highp float sn=mod(dt,3.14);return fract(sin(sn)*c);}"; var encode = "#define GLSLIFY 1\nconst vec2 bitEnc=vec2(1.0,255.0);const vec2 bitDec=1.0/bitEnc;vec4 toRGBA(const vec2 pos){vec2 rg=bitEnc*pos.x;rg=fract(rg);rg-=rg.yy*vec2(1.0/255.0,0.0);vec2 ba=bitEnc*pos.y;ba=fract(ba);ba-=ba.yy*vec2(1.0/255.0,0.0);return vec4(rg,ba);}"; var encodeFloat = "#define GLSLIFY 1\n#define FLOAT_MAX 1.70141184e38\n#define FLOAT_MIN 1.17549435e-38\nlowp vec4 encode_float(highp float v){highp float av=abs(v);if(avFLOAT_MAX){return vec4(127.0,128.0,0.0,0.0)/255.0;}else if(v<-FLOAT_MAX){return vec4(255.0,128.0,0.0,0.0)/255.0;}highp vec4 c=vec4(0,0,0,0);highp float e=floor(log2(av));highp float m=av*pow(2.0,-e)-1.0;c[1]=floor(128.0*m);m-=c[1]/128.0;c[2]=floor(32768.0*m);m-=c[2]/32768.0;c[3]=floor(8388608.0*m);highp float ebias=e+127.0;c[0]=floor(ebias/2.0);ebias-=c[0]*2.0;c[1]+=floor(ebias)*128.0;c[0]+=128.0*step(0.0,-v);return c/255.0;}"; var decode = "#define GLSLIFY 1\nconst vec2 bitEnc=vec2(1.0,255.0);const vec2 bitDec=1.0/bitEnc;vec2 fromRGBA(const vec4 color){vec4 rounded_color=floor(color*255.0+0.5)/255.0;float x=dot(rounded_color.rg,bitDec);float y=dot(rounded_color.ba,bitDec);return vec2(x,y);}"; var decodeFloat = "#define GLSLIFY 1\nvec4 floatsToBytes(vec4 inputFloats,bool littleEndian){vec4 bytes=vec4(inputFloats*255.0);return(littleEndian? bytes.abgr: bytes);}float decode_float(vec4 v,bool littleEndian){vec4 bits=floatsToBytes(v,littleEndian);float sign=mix(-1.0,1.0,step(bits[3],128.0));float expo=floor(mod(bits[3]+0.2,128.0))*2.0+floor((bits[2]+0.2)/128.0)-127.0;float sig=bits[0]+bits[1]*256.0+floor(mod(bits[2]+0.2,128.0))*256.0*256.0;return sign*(1.0+sig/8388607.0)*pow(2.0,expo);}"; var shaderLib = /* @__PURE__ */ Object.freeze({ __proto__: null, decode, decodeFloat, encode, encodeFloat, random }); function isFunction(val) { return index.typeOf(val) === "function"; } function findStopLessThanOrEqualTo(stops, input) { const lastIndex = stops.length - 1; let lowerIndex = 0; let upperIndex = lastIndex; let currentIndex = 0; let currentValue; let nextValue; while (lowerIndex <= upperIndex) { currentIndex = Math.floor((lowerIndex + upperIndex) / 2); currentValue = stops[currentIndex]; nextValue = stops[currentIndex + 1]; if (currentValue <= input) { if (currentIndex === lastIndex || input < nextValue) { return currentIndex; } lowerIndex = currentIndex + 1; } else if (currentValue > input) { upperIndex = currentIndex - 1; } else { throw new Error("Input is not a number."); } } return 0; } let linkEl; function resolveURL(path) { if (!linkEl) linkEl = document.createElement("a"); linkEl.href = path; return linkEl.href; } const littleEndian = function machineIsLittleEndian() { const uint8Array = new Uint8Array([170, 187]); const uint16array = new Uint16Array(uint8Array.buffer); return uint16array[0] === 48042; }(); function isImageBitmap(image) { return typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap; } function parseRange(exif) { const string = exif?.ImageDescription || ""; const group = string.split(";"); const gs = group.filter((item) => item !== ""); return gs.map((item) => item.split(",").map((v) => parseFloat(v))); } function keysDifference(obj, other) { const difference = []; for (const i in obj) { if (!(i in other)) { difference.push(i); } } return difference; } function intersects(extent1, extent2) { return extent1[0] <= extent2[2] && extent1[2] >= extent2[0] && extent1[1] <= extent2[3] && extent1[3] >= extent2[1]; } function containsExtent(extent1, extent2) { return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] && extent1[1] <= extent2[1] && extent2[3] <= extent1[3]; } function containTile(a, b) { return containsExtent(a, b) || intersects(a, b); } function polygon2buffer(features) { const len = features.length; let i = 0; const geometries = []; for (; i < len; i++) { const feature = features[i]; const coordinates = feature.geometry.coordinates; const type = feature.geometry.type; if (type === "Polygon") { const polygon = earcut$1.flatten(feature.geometry.coordinates); const positions = new Float32Array(polygon.vertices); const indexData = earcut$1(polygon.vertices, polygon.holes, polygon.dimensions); geometries.push({ index: { data: indexData.length < 65536 ? new Uint16Array(indexData) : new Uint32Array(indexData) }, position: { data: positions, size: 2 } }); } else if (type === "MultiPolygon") { for (let k = 0; k < coordinates.length; k++) { const coordinate = coordinates[k]; const polygon = earcut$1.flatten(coordinate); const positions = new Float32Array(polygon.vertices); const indexData = earcut$1(polygon.vertices, polygon.holes, polygon.dimensions); geometries.push({ index: { data: indexData.length < 65536 ? new Uint16Array(indexData) : new Uint32Array(indexData) }, position: { data: positions, size: 2 } }); } } } return geometries; } let ComposePass$1 = class ComposePass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = true; this.#uid = index.uid("ColorComposePass"); this.#program = new Program(renderer, { vertexShader: vert$4, fragmentShader: frag$8, uniforms: { u_image0: { value: void 0 }, dataRange: { value: void 0 } }, defines: [`RENDER_TYPE ${this.options.bandType}`, `LITTLE_ENDIAN ${littleEndian}`], includes: shaderLib }); const opt = { width: this.renderer.width, height: this.renderer.height, minFilter: renderer.gl.NEAREST, magFilter: renderer.gl.NEAREST, type: this.renderer.gl.FLOAT, format: this.renderer.gl.RGBA, // generateMipmaps: false, internalFormat: this.renderer.isWebGL2 ? this.renderer.gl.RGBA32F : this.renderer.gl.RGBA, stencil: true }; this.#current = new RenderTarget(renderer, { ...opt, name: "currentRenderTargetTexture" }); this.#next = new RenderTarget(renderer, { ...opt, name: "nextRenderTargetTexture" }); } #program; #current; #next; #uid; resize(width, height) { this.#current?.resize(width, height); this.#next?.resize(width, height); } get renderTarget() { return { current: this.#current, next: this.#next }; } get textures() { return { current: this.#current?.texture, next: this.#next?.texture }; } renderTexture(renderTarget, rendererParams, rendererState, sourceCache) { if (renderTarget) { renderTarget.clear(); renderTarget.bind(); const attr = this.renderer.attributes; if (attr.depth && renderTarget.depth) { this.renderer.state.enable(this.renderer.gl.DEPTH_TEST); this.renderer.state.setDepthMask(true); } this.renderer.setViewport(renderTarget.width, renderTarget.height); } const { stencilConfigForOverlap } = this.options; const camera = rendererParams.cameras.camera; if (sourceCache) { const coordsAscending = sourceCache.getVisibleCoordinates(); const coordsDescending = coordsAscending.slice().reverse(); if (!coordsDescending.length) return; let stencil; if (this.maskPass) { stencil = this.maskPass.render(rendererParams, rendererState); } const [stencilModes, coords] = stencilConfigForOverlap(coordsDescending); for (let i = 0; i < coords.length; i++) { const coord = coords[i]; const tile = sourceCache.getTile(coord); if (!(tile && tile.hasData())) continue; const bbox = coord.getTileProjBounds(); if (!bbox) continue; const tileMesh = tile.createMesh(this.#uid, bbox, this.renderer, this.#program); const mesh = tileMesh.getMesh(); const dataRange = []; for (const [index, texture] of tile.textures) { if (texture.userData?.dataRange && Array.isArray(texture.userData?.dataRange)) { dataRange.push(...texture.userData.dataRange); } if (this.options.isRasterize?.() && (texture.options.minFilter !== this.renderer.gl.NEAREST || texture.options.magFilter !== this.renderer.gl.NEAREST)) { texture.setOptions({ minFilter: this.renderer.gl.NEAREST, magFilter: this.renderer.gl.NEAREST }); } mesh.program.setUniform(`u_image${index}`, texture); } if (dataRange.length > 0) { mesh.program.setUniform("dataRange", dataRange); } mesh.updateMatrix(); mesh.worldMatrixNeedsUpdate = false; mesh.worldMatrix.multiply(rendererParams.scene.worldMatrix, mesh.localMatrix); stencilModes[coord.overscaledZ]; mesh.draw({ ...rendererParams, camera }); if (this.options.isRasterize?.()) { for (const [_, texture] of tile.textures) { texture.setOptions({ minFilter: this.renderer.gl.LINEAR, magFilter: this.renderer.gl.LINEAR }); } } } this.renderer.clear(false, false, true); if (!stencil) { this.renderer.state.disable(this.renderer.gl.STENCIL_TEST); } } if (renderTarget) { renderTarget.unbind(); } } /** * 此处绘制主要是合并瓦片 * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { const { source } = this.options; const sourceCache = source.sourceCache; if (Array.isArray(sourceCache)) { if (sourceCache.length === 2) { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache[0]); this.renderTexture(this.#next, rendererParams, rendererState, sourceCache[1]); } else { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache[0]); this.renderTexture(this.#next, rendererParams, rendererState, sourceCache[0]); } } else { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache); this.renderTexture(this.#next, rendererParams, rendererState, sourceCache); } } destroy() { if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#current) { this.#current.destroy(); this.#current = null; } if (this.#next) { this.#next.destroy(); this.#next = null; } } }; var vert$3 = "#define GLSLIFY 1\n#defines\nattribute vec2 uv;attribute vec3 position;uniform vec2 resolution;uniform mat4 modelViewMatrix;uniform mat4 projectionMatrix;varying vec2 vUv;void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}"; var frag$7 = "#defines\nprecision highp float;\n#define GLSLIFY 1\nuniform sampler2D u_texture;uniform sampler2D u_textureNext;uniform sampler2D colorRampTexture;uniform float u_fade_t;uniform vec2 u_image_res;uniform vec2 colorRange;uniform bool useDisplayRange;uniform vec2 displayRange;uniform float opacity;varying vec2 vUv;\n#include \nvec4 calcTexture(const vec2 puv){vec4 color0=texture2D(u_texture,puv);vec4 color1=texture2D(u_textureNext,puv);return mix(color0,color1,u_fade_t);}\n#if RENDER_TYPE == 1\nvec2 decodeValue(const vec2 vc){vec4 rgba=calcTexture(vc);return rgba.rg;}\n#else\nfloat decodeValue(const vec2 vc){return calcTexture(vc).r;}\n#endif\n#if RENDER_TYPE == 1\nvec2 bilinear(const vec2 uv){vec2 px=1.0/u_image_res;vec2 vc=(floor(uv*u_image_res))*px;vec2 f=fract(uv*u_image_res);vec2 tl=decodeValue(vc);vec2 tr=decodeValue(vc+vec2(px.x,0));vec2 bl=decodeValue(vc+vec2(0,px.y));vec2 br=decodeValue(vc+px);return mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);}\n#else\nfloat bilinear(const vec2 uv){vec2 px=1.0/u_image_res;vec2 vc=(floor(uv*u_image_res))*px;vec2 f=fract(uv*u_image_res);float tl=decodeValue(vc);float tr=decodeValue(vc+vec2(px.x,0));float bl=decodeValue(vc+vec2(0,px.y));float br=decodeValue(vc+px);return mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);}\n#endif\n#if RENDER_TYPE == 1\nfloat getValue(const vec2 uv){vec2 rg=bilinear(uv);return length(rg);}\n#else\nfloat getValue(const vec2 uv){return bilinear(uv);}\n#endif\nvoid main(){vec2 uv=vUv;if(calcTexture(uv).a==0.0){discard;}float value=getValue(uv);float value_t=(value-colorRange.x)/(colorRange.y-colorRange.x);vec2 ramp_pos=vec2(value_t,0.5);vec4 color=texture2D(colorRampTexture,ramp_pos);bool display=true;if(useDisplayRange){display=value<=displayRange.y&&value>=displayRange.x;}if(display){gl_FragColor=vec4(floor(255.0*color*opacity)/255.0);}else{gl_FragColor=vec4(0.0,0.0,0.0,0.0);}}"; class ColorizePass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = false; this.#program = new Program(renderer, { vertexShader: vert$3, fragmentShader: frag$7, uniforms: { opacity: { value: 1 }, u_fade_t: { value: 0 }, displayRange: { value: new Vector2(-Infinity, Infinity) }, u_texture: { value: this.options.texture }, u_textureNext: { value: this.options.textureNext }, colorRampTexture: { value: null } }, defines: [`RENDER_TYPE ${this.options.bandType}`, `LITTLE_ENDIAN ${littleEndian}`], includes: shaderLib, transparent: true }); this.#geometry = new Geometry(renderer, { position: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, index: { size: 1, data: new Uint16Array([0, 1, 2, 2, 1, 3]) } }); this.#mesh = new Mesh(renderer, { mode: renderer.gl.TRIANGLES, program: this.#program, geometry: this.#geometry }); } #program; #mesh; #geometry; /** * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { const attr = this.renderer.attributes; this.renderer.setViewport(this.renderer.width * attr.dpr, this.renderer.height * attr.dpr); const camera = rendererParams.cameras.planeCamera; if (rendererState && this.#mesh) { const uniforms = index.pick(rendererState, [ "opacity", "colorRange", "dataRange", "colorRampTexture", "useDisplayRange", "displayRange" ]); Object.keys(uniforms).forEach((key) => { if (uniforms[key] !== void 0) { this.#mesh?.program.setUniform(key, uniforms[key]); } }); const fade = this.options.source?.getFadeTime?.() || 0; this.#mesh.program.setUniform( "u_image_res", new Vector2(this.options.texture.width, this.options.texture.height) ); this.#mesh.program.setUniform("u_fade_t", fade); this.#mesh.updateMatrix(); this.#mesh.worldMatrixNeedsUpdate = false; this.#mesh.worldMatrix.multiply(camera.worldMatrix, this.#mesh.localMatrix); this.#mesh.draw({ ...rendererParams, camera }); } } destroy() { if (this.#mesh) { this.#mesh.destroy(); this.#mesh = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#geometry) { this.#geometry.destroy(); this.#geometry = null; } } } var frag$6 = "precision highp float;\n#define GLSLIFY 1\nuniform sampler2D u_texture;uniform sampler2D u_textureNext;uniform float u_fade_t;uniform float opacity;varying vec2 vUv;void main(){vec2 uv=vUv;vec4 color0=texture2D(u_texture,vUv);vec4 color1=texture2D(u_textureNext,vUv);vec4 color=mix(color0,color1,u_fade_t);gl_FragColor=vec4(floor(255.0*color*opacity)/255.0);}"; class RasterPass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = false; this.#program = new Program(renderer, { vertexShader: vert$3, fragmentShader: frag$6, uniforms: { opacity: { value: 1 }, u_fade_t: { value: 0 }, u_texture: { value: this.options.texture }, u_textureNext: { value: this.options.textureNext } }, includes: shaderLib, transparent: true }); this.#geometry = new Geometry(renderer, { position: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, index: { size: 1, data: new Uint16Array([0, 1, 2, 2, 1, 3]) } }); this.#mesh = new Mesh(renderer, { mode: renderer.gl.TRIANGLES, program: this.#program, geometry: this.#geometry }); } #program; #mesh; #geometry; /** * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { const attr = this.renderer.attributes; this.renderer.setViewport(this.renderer.width * attr.dpr, this.renderer.height * attr.dpr); const camera = rendererParams.cameras.planeCamera; if (rendererState && this.#mesh) { const fade = this.options.source?.getFadeTime?.() || 0; const uniforms = index.pick(rendererState, ["opacity"]); Object.keys(uniforms).forEach((key) => { if (uniforms[key] !== void 0) { this.#mesh?.program.setUniform(key, uniforms[key]); } }); this.#mesh.program.setUniform("u_fade_t", fade); this.#mesh.updateMatrix(); this.#mesh.worldMatrixNeedsUpdate = false; this.#mesh.worldMatrix.multiply(camera.worldMatrix, this.#mesh.localMatrix); this.#mesh.draw({ ...rendererParams, camera }); } } destroy() { if (this.#mesh) { this.#mesh.destroy(); this.#mesh = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#geometry) { this.#geometry.destroy(); this.#geometry = null; } } } var frag$5 = "precision highp float;\n#define GLSLIFY 1\nvarying vec2 vUv;uniform sampler2D u_image0;vec4 getColor(const vec2 uv){return texture2D(u_image0,uv).rgba;}void main(){gl_FragColor=getColor(vUv);}"; class ComposePass2 extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = true; this.#uid = index.uid("ComposePass"); this.#program = new Program(renderer, { vertexShader: vert$4, fragmentShader: frag$5, uniforms: { u_image0: { value: void 0 } }, includes: shaderLib }); const opt = { width: this.renderer.width, height: this.renderer.height, minFilter: renderer.gl.NEAREST, magFilter: renderer.gl.NEAREST, type: this.renderer.gl.UNSIGNED_BYTE, format: this.renderer.gl.RGBA, generateMipmaps: true, internalFormat: this.renderer.gl.RGBA, stencil: true }; this.#current = new RenderTarget(renderer, { ...opt, name: "currentRenderTargetTexture" }); this.#next = new RenderTarget(renderer, { ...opt, name: "nextRenderTargetTexture" }); } #program; #current; #next; #uid; resize(width, height) { this.#current?.resize(width, height); this.#next?.resize(width, height); } get textures() { return { current: this.#current?.texture, next: this.#next?.texture }; } renderTexture(renderTarget, rendererParams, rendererState, sourceCache) { if (renderTarget) { renderTarget.clear(); renderTarget.bind(); const attr = this.renderer.attributes; if (attr.depth && renderTarget.depth) { this.renderer.state.enable(this.renderer.gl.DEPTH_TEST); this.renderer.state.setDepthMask(true); } this.renderer.setViewport(renderTarget.width, renderTarget.height); } const { stencilConfigForOverlap } = this.options; const camera = rendererParams.cameras.camera; if (sourceCache) { const coordsAscending = sourceCache.getVisibleCoordinates(); const coordsDescending = coordsAscending.slice().reverse(); if (!coordsDescending.length) return; let stencil; if (this.maskPass) { stencil = this.maskPass.render(rendererParams, rendererState); } const [stencilModes, coords] = stencilConfigForOverlap(coordsDescending); for (let i = 0; i < coords.length; i++) { const coord = coords[i]; const tile = sourceCache.getTile(coord); if (!(tile && tile.hasData())) continue; const bbox = coord.getTileProjBounds(); if (!bbox) continue; const tileMesh = tile.createMesh(this.#uid, bbox, this.renderer, this.#program); const mesh = tileMesh.getMesh(); for (const [index, texture] of tile.textures) { mesh.program.setUniform(`u_image${index}`, texture); } mesh.updateMatrix(); mesh.worldMatrixNeedsUpdate = false; mesh.worldMatrix.multiply(rendererParams.scene.worldMatrix, mesh.localMatrix); stencilModes[coord.overscaledZ]; mesh.draw({ ...rendererParams, camera }); } this.renderer.clear(false, false, true); if (!stencil) { this.renderer.state.disable(this.renderer.gl.STENCIL_TEST); } } if (renderTarget) { renderTarget.unbind(); } } /** * 此处绘制主要是合并瓦片 * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { const { source } = this.options; const sourceCache = source.sourceCache; if (Array.isArray(sourceCache)) { if (sourceCache.length === 2) { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache[0]); this.renderTexture(this.#next, rendererParams, rendererState, sourceCache[1]); } else { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache[0]); } } else { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache); } } destroy() { if (this.#current) { this.#current.destroy(); this.#current = null; } if (this.#next) { this.#next.destroy(); this.#next = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } } } const defaultSize = 256; class ParticlesComposePass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = true; this.#width = defaultSize; this.#height = defaultSize; this.#uid = options.id; this.#program = new Program(renderer, { vertexShader: vert$3, fragmentShader: frag$8, uniforms: { u_image0: { value: void 0 }, dataRange: { value: void 0 } }, defines: [`RENDER_TYPE ${this.options.bandType}`, `LITTLE_ENDIAN ${littleEndian}`], includes: shaderLib }); const opt = { width: this.#width, height: this.#height, minFilter: renderer.gl.NEAREST, magFilter: renderer.gl.NEAREST, type: this.renderer.gl.FLOAT, format: this.renderer.gl.RGBA, // generateMipmaps: false, internalFormat: this.renderer.isWebGL2 ? this.renderer.gl.RGBA32F : this.renderer.gl.RGBA, stencil: true }; this.#current = new RenderTarget(renderer, { ...opt, name: "currentRenderTargetTexture" }); this.#next = new RenderTarget(renderer, { ...opt, name: "nextRenderTargetTexture" }); } #program; #current; #next; #uid; #width; #height; resize(width, height) { if (width !== this.#width || height !== this.#height) { this.#current?.resize(width, height); this.#next?.resize(width, height); this.#width = width; this.#height = height; } } get textures() { return { current: this.#current?.texture, next: this.#next?.texture }; } renderTexture(renderTarget, rendererParams, rendererState, sourceCache) { if (!sourceCache) { return; } const { stencilConfigForOverlap } = this.options; const camera = rendererParams.cameras.planeCamera; const coordsAscending = sourceCache.getVisibleCoordinates(); const coordsDescending = coordsAscending.slice().reverse(); if (!coordsDescending.length) return; let xmin = Infinity; let ymin = Infinity; let xmax = -Infinity; let ymax = -Infinity; let zmin = Infinity; let zmax = -Infinity; for (let n = 0; n < coordsDescending.length; n++) { const tileId = coordsDescending[n]; const bounds = tileId.getTileProjBounds(); xmin = Math.min(bounds.left, xmin); xmax = Math.max(bounds.right, xmax); zmin = Math.min(tileId.z, zmin); zmax = Math.max(tileId.z, zmax); if (!rendererState.u_flip_y) { ymin = Math.min(bounds.top, ymin); ymax = Math.max(bounds.bottom, ymax); } else { ymin = Math.min(bounds.bottom, ymin); ymax = Math.max(bounds.top, ymax); } } const zz = this.options.getTileProjSize(zmax, coordsDescending); const dx = xmax - xmin; const dy = ymax - ymin; const w = dx / zz[0]; const h = dy / zz[1]; rendererState.sharedState.u_data_bbox = [xmin, ymin, xmax, ymax]; rendererState.sharedState.u_data_zooms = [zmin, zmax]; if (renderTarget) { renderTarget.clear(); renderTarget.bind(); const attr = this.renderer.attributes; if (attr.depth && renderTarget.depth) { this.renderer.state.enable(this.renderer.gl.DEPTH_TEST); this.renderer.state.setDepthMask(true); } let width = w * (this.options.source.tileSize ?? defaultSize); let height = h * (this.options.source.tileSize ?? defaultSize); rendererState.sharedState.u_tiles_size = [width, height]; const maxTextureSize = this.renderer.gl.getParameter(this.renderer.gl.MAX_TEXTURE_SIZE) * 0.5; const maxRenderBufferSize = this.renderer.gl.getParameter(this.renderer.gl.MAX_RENDERBUFFER_SIZE) * 0.5; const maxSize = Math.max(width, height); if (maxSize > maxTextureSize) { width = maxTextureSize / maxSize * width; height = maxTextureSize / maxSize * height; } else if (maxSize > maxRenderBufferSize) { width = maxRenderBufferSize / maxSize * width; height = maxRenderBufferSize / maxSize * height; } this.resize(width, height); this.renderer.setViewport(width, height); } const [stencilModes, coords] = stencilConfigForOverlap(coordsDescending); for (let k = 0; k < coords.length; k++) { const coord = coords[k]; if (coord) { const tile = sourceCache.getTile(coord); if (!(tile && tile.hasData())) continue; const tileBBox = coord.getTileProjBounds(); if (!tileBBox) continue; const tileMesh = tile.createMesh(this.#uid, tileBBox, this.renderer, this.#program); const mesh = tileMesh.planeMesh; const scale = Math.pow(2, zmax - coord.z); mesh.scale.set(1 / w * scale, 1 / h * scale, 1); if (!rendererState.u_flip_y) { mesh.position.set((tileBBox.left - xmin) / dx, (tileBBox.top - ymin) / dy, 0); } else { mesh.position.set((tileBBox.left - xmin) / dx, 1 - (tileBBox.top - ymin) / dy, 0); } const dataRange = []; for (const [index, texture] of tile.textures) { if (texture.userData?.dataRange && Array.isArray(texture.userData?.dataRange)) { dataRange.push(...texture.userData.dataRange); } mesh.program.setUniform(`u_image${index}`, texture); } if (dataRange.length > 0) { mesh.program.setUniform("dataRange", dataRange); } mesh.updateMatrix(); mesh.worldMatrixNeedsUpdate = false; mesh.worldMatrix.multiply(camera.worldMatrix, mesh.localMatrix); const stencilMode = stencilModes[coord.overscaledZ]; if (stencilMode) { if (stencilMode.stencil) { this.renderer.state.enable(this.renderer.gl.STENCIL_TEST); this.renderer.state.setStencilFunc(stencilMode.func?.cmp, stencilMode.func?.ref, stencilMode.func?.mask); this.renderer.state.setStencilOp(stencilMode.op?.fail, stencilMode.op?.zfail, stencilMode.op?.zpass); } else { this.renderer.state.disable(this.renderer.gl.STENCIL_TEST); } } mesh.draw({ ...rendererParams, camera }); } } if (renderTarget) { renderTarget.unbind(); } } /** * 此处绘制主要是合并瓦片 * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { const { source } = this.options; const sourceCache = source.sourceCache; if (Array.isArray(sourceCache)) { if (sourceCache.length === 2) { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache[0]); this.renderTexture(this.#next, rendererParams, rendererState, sourceCache[1]); } else { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache[0]); this.renderTexture(this.#next, rendererParams, rendererState, sourceCache[0]); } } else { this.renderTexture(this.#current, rendererParams, rendererState, sourceCache); this.renderTexture(this.#next, rendererParams, rendererState, sourceCache); } } destroy() { if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#current) { this.#current.destroy(); this.#current = null; } if (this.#next) { this.#next.destroy(); this.#next = null; } } } var frag$4 = "#defines\nprecision highp float;\n#define GLSLIFY 1\nuniform sampler2D u_texture;uniform sampler2D u_textureNext;uniform sampler2D u_particles;uniform float u_fade_t;uniform vec2 u_image_res;uniform vec4 u_bbox;uniform vec4 u_data_bbox;uniform float u_rand_seed;uniform float u_drop_rate;uniform float u_drop_rate_bump;uniform float u_speed_factor;uniform bool u_initialize;uniform bool u_flip_y;uniform float u_gl_scale;varying vec2 vUv;\n#include \nvec4 calcTexture(const vec2 puv){vec4 color0=texture2D(u_texture,puv);vec4 color1=texture2D(u_textureNext,puv);return mix(color0,color1,u_fade_t);}vec2 decodeValue(const vec2 vc){vec4 rgba=calcTexture(vc);return rgba.rg;}vec2 bilinear(const vec2 uv){vec2 px=1.0/u_image_res;vec2 vc=(floor(uv*u_image_res))*px;vec2 f=fract(uv*u_image_res);vec2 tl=decodeValue(vc);vec2 tr=decodeValue(vc+vec2(px.x,0));vec2 bl=decodeValue(vc+vec2(0,px.y));vec2 br=decodeValue(vc+px);return mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);}vec2 randomPosToGlobePos(vec2 pos){vec2 min_bbox=u_bbox.xy;vec2 max_bbox=u_bbox.zw;return mix(min_bbox,max_bbox,pos);}bool containsXY(vec2 pos,vec4 bbox){float x=pos.x;return(bbox.x<=x&&x<=bbox.z&&bbox.y<=pos.y&&pos.y<=bbox.w);}vec2 update(vec2 pos){vec2 uv=(pos.xy-u_data_bbox.xy)/(u_data_bbox.zw-u_data_bbox.xy);if(u_flip_y){uv=vec2(uv.x,1.0-uv.y);}vec2 velocity=bilinear(uv);float speed=length(velocity);vec2 v=vec2(velocity.x,-velocity.y);if(u_flip_y){v=vec2(velocity.x,velocity.y);}vec2 offset=v*0.0001*u_speed_factor*u_gl_scale;pos=pos+offset;vec2 seed=(pos.xy+vUv)*u_rand_seed;float drop_rate=u_drop_rate+speed*u_drop_rate_bump;float drop=step(1.0-drop_rate,rand(seed));vec2 random_pos=vec2(rand(seed+1.3),rand(seed+2.1));random_pos=randomPosToGlobePos(random_pos);if(!containsXY(pos.xy,u_data_bbox)||!containsXY(pos.xy,u_bbox)||calcTexture(uv).a==0.0){drop=1.0;}pos=mix(pos,random_pos,drop);return pos;}void main(){vec2 pos=texture2D(u_particles,vUv).xy;pos=update(pos);if(u_initialize){pos=randomPosToGlobePos(pos);for(int i=0;i<25;i++){pos=update(pos);}}gl_FragColor=vec4(pos.xy,0.0,1.0);}"; class UpdatePass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = true; this.#initialize = true; this.initializeRenderTarget(); this.#program = new Program(renderer, { vertexShader: vert$3, fragmentShader: frag$4, uniforms: { u_fade_t: { value: 0 }, displayRange: { value: new Vector2(-Infinity, Infinity) }, u_texture: { value: this.options.texture }, u_textureNext: { value: this.options.textureNext }, u_particles: { value: null } }, defines: [`RENDER_TYPE ${this.options.bandType}`, `LITTLE_ENDIAN ${littleEndian}`], includes: shaderLib, blending: BlendType.NoBlending, transparent: true }); this.#geometry = new Geometry(renderer, { position: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, index: { size: 1, data: new Uint16Array([0, 1, 2, 2, 1, 3]) } }); this.#mesh = new Mesh(renderer, { mode: renderer.gl.TRIANGLES, program: this.#program, geometry: this.#geometry }); } #program; #mesh; #geometry; #current; #next; #initialize; #particleRes; #getParticleRes() { return Math.ceil(Math.sqrt(this.options.getParticleNumber())); } resize() { const particleRes = this.#getParticleRes(); this.#current?.resize(particleRes, particleRes); this.#next?.resize(particleRes, particleRes); } get textures() { return { currentParticles: this.#current?.texture, nextParticles: this.#next?.texture }; } setInitialize(state) { this.#initialize = state; } /** * 创建 RenderTarget */ initializeRenderTarget() { const particleRes = this.#getParticleRes(); const particleState = new Float32Array(particleRes ** 2 * 4); const s = this.options.glScale; for (let i = 0; i < particleState.length; i++) { particleState[i] = Math.random() * s; } const opt = { data: particleState, width: particleRes, height: particleRes, minFilter: this.renderer.gl.NEAREST, magFilter: this.renderer.gl.NEAREST, type: this.renderer.gl.FLOAT, format: this.renderer.gl.RGBA, internalFormat: this.renderer.isWebGL2 ? this.renderer.gl.RGBA32F : this.renderer.gl.RGBA, stencil: false }; this.#current = new RenderTarget(this.renderer, { ...opt, name: "currentUpdateTexture" }); this.#next = new RenderTarget(this.renderer, { ...opt, name: "nextUpdateTexture" }); } /** * 交换 RenderTarget */ swapRenderTarget() { [this.#current, this.#next] = [this.#next, this.#current]; } /** * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { const attr = this.renderer.attributes; const camera = rendererParams.cameras.planeCamera; const particleRes = this.#getParticleRes(); if (!this.#particleRes || this.#particleRes !== particleRes) { this.#particleRes = particleRes; this.initializeRenderTarget(); } if (this.#next) { this.#next.bind(); if (attr.depth && this.#next.depth) { this.renderer.state.enable(this.renderer.gl.DEPTH_TEST); this.renderer.state.setDepthMask(true); } this.renderer.setViewport(this.#next.width, this.#next.height); } if (rendererState && this.#mesh) { const uniforms = index.pick(rendererState, [ "dataRange", "useDisplayRange", "displayRange", "u_drop_rate", "u_drop_rate_bump", "u_speed_factor", "u_flip_y", "u_gl_scale" ]); Object.keys(uniforms).forEach((key) => { if (uniforms[key] !== void 0) { this.#mesh?.program.setUniform(key, uniforms[key]); } }); const fade = this.options.source?.getFadeTime?.() || 0; this.#mesh.program.setUniform( "u_image_res", new Vector2(this.options.texture.width, this.options.texture.height) ); this.#mesh.program.setUniform("u_fade_t", fade); this.#mesh.program.setUniform("u_rand_seed", Math.random()); this.#mesh.program.setUniform("u_particles", this.#current?.texture); this.#mesh.program.setUniform("u_bbox", rendererState.extent); this.#mesh.program.setUniform("u_initialize", this.#initialize); this.#mesh.program.setUniform("u_data_bbox", rendererState.sharedState.u_data_bbox); this.#mesh.updateMatrix(); this.#mesh.worldMatrixNeedsUpdate = false; this.#mesh.worldMatrix.multiply(camera.worldMatrix, this.#mesh.localMatrix); this.#mesh.draw({ ...rendererParams, camera }); } if (this.#next) { this.#next.unbind(); } this.#initialize = false; this.swapRenderTarget(); } destroy() { if (this.#mesh) { this.#mesh.destroy(); this.#mesh = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#geometry) { this.#geometry.destroy(); this.#geometry = null; } if (this.#current) { this.#current.destroy(); this.#current = null; } if (this.#next) { this.#next.destroy(); this.#next = null; } } } var frag$3 = "precision highp float;\n#define GLSLIFY 1\nuniform sampler2D u_screen;uniform float u_opacity;uniform float u_fade;varying vec2 vUv;void main(){vec4 color=texture2D(u_screen,vUv);gl_FragColor=vec4(floor(255.0*color*u_opacity*u_fade)/255.0);}"; class ScreenPass extends Pass { #program; #mesh; #geometry; constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = Boolean(options.prerender); this.#program = new Program(renderer, { vertexShader: vert$3, fragmentShader: frag$3, uniforms: { opacity: { value: 1 }, u_fade: { value: 1 }, u_screen: { value: null } }, defines: [`RENDER_TYPE ${this.options.bandType}`, `LITTLE_ENDIAN ${littleEndian}`], includes: shaderLib, transparent: true, blending: options.enableBlend ? BlendType.CustomBlending : BlendType.NoBlending, blendFunc: { src: this.renderer.gl.ONE, dst: this.renderer.gl.ONE_MINUS_SRC_ALPHA }, blendEquation: { modeAlpha: this.renderer.gl.FUNC_ADD, modeRGB: this.renderer.gl.FUNC_ADD } }); this.#geometry = new Geometry(renderer, { position: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, index: { size: 1, data: new Uint16Array([0, 1, 2, 2, 1, 3]) } }); this.#mesh = new Mesh(renderer, { mode: renderer.gl.TRIANGLES, program: this.#program, geometry: this.#geometry }); } get renderTarget() { if (this.options.particlesPass && this.prerender) { return this.options.particlesPass.renderTarget; } } /** * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { if (this.renderTarget) { this.renderTarget.bind(); this.renderer.setViewport(this.renderTarget.width, this.renderTarget.height); } else { const attr = this.renderer.attributes; this.renderer.setViewport(this.renderer.width * attr.dpr, this.renderer.height * attr.dpr); } if (rendererState && this.#mesh) { const camera = rendererParams.cameras.planeCamera; this.#mesh.program.setUniform("u_fade", 1); this.#mesh.program.setUniform("u_opacity", this.prerender ? rendererState.fadeOpacity : rendererState.opacity); this.#mesh.program.setUniform( "u_screen", this.prerender ? this.options.particlesPass?.textures.backgroundTexture : this.options.particlesPass?.textures.screenTexture ); this.#mesh.updateMatrix(); this.#mesh.worldMatrixNeedsUpdate = false; this.#mesh.worldMatrix.multiply(camera.worldMatrix, this.#mesh.localMatrix); this.#mesh.draw({ ...rendererParams, camera }); } if (this.renderTarget) { this.renderTarget.unbind(); } if (this.options.particlesPass && !this.prerender) { this.options.particlesPass?.swapRenderTarget(); } } destroy() { if (this.#mesh) { this.#mesh.destroy(); this.#mesh = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#geometry) { this.#geometry.destroy(); this.#geometry = null; } } } var vert$2 = "#define GLSLIFY 1\nattribute vec2 reference;attribute float a_index;uniform vec2 resolution;uniform mat4 modelViewMatrix;uniform mat4 viewMatrix;uniform mat4 modelMatrix;uniform mat4 projectionMatrix;uniform sampler2D u_particles;uniform sampler2D u_particles_next;uniform float u_particleSize;uniform float u_particlesRes;varying vec2 v_particle_pos;void main(){float v_index=floor(a_index/6.0);vec2 uv=reference;vec4 color=texture2D(u_particles,uv);vec4 color1=texture2D(u_particles_next,uv);v_particle_pos=mix(color.rg,color1.rg,0.0);gl_PointSize=u_particleSize;gl_Position=projectionMatrix*modelViewMatrix*vec4(v_particle_pos,0.0,1.0);}"; var frag$2 = "#defines\nprecision highp float;\n#define GLSLIFY 1\nuniform sampler2D u_texture;uniform sampler2D u_textureNext;uniform vec2 u_colorRange;uniform sampler2D u_colorRamp;uniform vec4 u_bbox;uniform vec4 u_data_bbox;uniform float u_fade_t;uniform vec2 u_image_res;varying vec2 v_particle_pos;vec4 calcTexture(const vec2 puv){vec4 color0=texture2D(u_texture,puv);vec4 color1=texture2D(u_textureNext,puv);return mix(color0,color1,u_fade_t);}\n#if RENDER_TYPE == 1\nvec2 decodeValue(const vec2 vc){vec4 rgba=calcTexture(vc);return rgba.rg;}\n#else\nfloat decodeValue(const vec2 vc){return calcTexture(vc).r;}\n#endif\nvec2 bilinear(const vec2 uv){vec2 px=1.0/u_image_res;vec2 vc=(floor(uv*u_image_res))*px;vec2 f=fract(uv*u_image_res);vec2 tl=decodeValue(vc);vec2 tr=decodeValue(vc+vec2(px.x,0));vec2 bl=decodeValue(vc+vec2(0,px.y));vec2 br=decodeValue(vc+px);return mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);}bool containsXY(vec2 pos,vec4 bbox){float x=pos.x;return(bbox.x1.0){discard;}gl_FragColor=vec4(floor(255.0*color*color.a)/255.0);}"; class Particles extends Pass { #prerender = true; #privateNumParticles; #program; #mesh; #geometry; #screenTexture; #backgroundTexture; constructor(id, renderer, options = {}) { super(id, renderer, options); this.initializeRenderTarget(); this.#program = new Program(renderer, { vertexShader: vert$2, fragmentShader: frag$2, uniforms: { u_fade_t: { value: 0 }, displayRange: { value: new Vector2(-Infinity, Infinity) }, u_texture: { value: this.options.texture }, u_textureNext: { value: this.options.textureNext }, u_particles: { value: null }, u_particleSize: { value: 2 }, u_particlesRes: { value: 0 } }, defines: [`RENDER_TYPE ${this.options.bandType}`, `LITTLE_ENDIAN ${littleEndian}`], includes: shaderLib, transparent: true, blending: BlendType.NoBlending, blendFunc: { src: this.renderer.gl.ONE, dst: this.renderer.gl.ONE_MINUS_SRC_ALPHA }, blendEquation: { modeAlpha: this.renderer.gl.FUNC_ADD, modeRGB: this.renderer.gl.FUNC_ADD } }); const { particleIndices, particleReferences } = this.getParticleBuffer(); this.#geometry = new Geometry(renderer, { a_index: { size: 1, data: particleIndices }, reference: { size: 2, data: particleReferences } }); this.#mesh = new Mesh(renderer, { mode: renderer.gl.POINTS, program: this.#program, geometry: this.#geometry }); } get prerender() { return this.#prerender; } set prerender(prerender) { this.#prerender = prerender; } get textures() { return { screenTexture: this.#screenTexture?.texture, backgroundTexture: this.#backgroundTexture?.texture }; } get renderTarget() { return this.#prerender && this.#screenTexture; } resetParticles() { this.#screenTexture?.clear(); this.#backgroundTexture?.clear(); } getParticleBuffer() { const particleRes = Math.ceil(Math.sqrt(this.options.getParticleNumber())); this.particleStateResolution = particleRes; this.#privateNumParticles = particleRes * particleRes; const indexCount = this.#privateNumParticles; const particleIndices = new Float32Array(indexCount); const particleReferences = new Float32Array(indexCount * 2); for (let i = 0; i < indexCount; i++) { const t = i % particleRes / particleRes; const a = Math.trunc(i / particleRes) / particleRes; particleReferences.set([t, a], 2 * i); particleIndices[i] = i; } return { particleIndices, particleReferences }; } /** * 创建 RenderTarget */ initializeRenderTarget() { const opt = { width: this.renderer.width, height: this.renderer.height, minFilter: this.renderer.gl.LINEAR, magFilter: this.renderer.gl.LINEAR, type: this.renderer.gl.UNSIGNED_BYTE, format: this.renderer.gl.RGBA, stencil: true, premultipliedAlpha: false }; this.#screenTexture = new RenderTarget(this.renderer, { ...opt, name: "screenTexture" }); this.#backgroundTexture = new RenderTarget(this.renderer, { ...opt, name: "backgroundTexture" }); } /** * 交换 RenderTarget */ swapRenderTarget() { [this.#screenTexture, this.#backgroundTexture] = [this.#backgroundTexture, this.#screenTexture]; } /** * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { if (this.renderTarget) { this.renderTarget.bind(); this.renderer.setViewport(this.renderTarget.width, this.renderTarget.height); } else { const attr = this.renderer.attributes; this.renderer.setViewport(this.renderer.width * attr.dpr, this.renderer.height * attr.dpr); } const { camera } = rendererParams.cameras; let stencil; if (this.maskPass) { stencil = this.maskPass.render(rendererParams, rendererState); } if (rendererState && this.#mesh) { this.#mesh.program.setUniform( "u_image_res", new Vector2(this.options.texture.width, this.options.texture.height) ); const fade = this.options.source?.getFadeTime?.() || 0; this.#mesh.program.setUniform("u_fade_t", fade); this.#mesh.program.setUniform("u_colorRamp", rendererState.colorRampTexture); this.#mesh.program.setUniform("u_colorRange", rendererState.colorRange); const particleTextures = this.options.getParticles(); this.#mesh.program.setUniform("u_particles", particleTextures.currentParticles); this.#mesh.program.setUniform("u_particles_next", particleTextures.nextParticles); this.#mesh.program.setUniform("u_particlesRes", this.#privateNumParticles); const sharedState = rendererState.sharedState; this.#mesh.program.setUniform("u_bbox", rendererState.extent); this.#mesh.program.setUniform("u_data_bbox", sharedState.u_data_bbox); this.#mesh.program.setUniform("u_flip_y", rendererState.u_flip_y); this.#mesh.program.setUniform("u_gl_scale", rendererState.u_gl_scale); this.#mesh.updateMatrix(); this.#mesh.worldMatrixNeedsUpdate = false; this.#mesh.worldMatrix.multiply(rendererParams.scene.worldMatrix, this.#mesh.localMatrix); this.#mesh.draw({ ...rendererParams, camera }); } if (!stencil) { this.renderer.state.disable(this.renderer.gl.STENCIL_TEST); } if (this.renderTarget) { this.renderTarget.unbind(); } } destroy() { if (this.#mesh) { this.#mesh.destroy(); this.#mesh = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#geometry) { this.#geometry.destroy(); this.#geometry = null; } if (this.#screenTexture) { this.#screenTexture.destroy(); this.#screenTexture = null; } if (this.#backgroundTexture) { this.#backgroundTexture.destroy(); this.#backgroundTexture = null; } } } var vert$1 = "#define GLSLIFY 1\n#defines\nattribute vec2 uv;attribute vec3 position;uniform vec2 resolution;uniform mat4 modelViewMatrix;uniform mat4 projectionMatrix;varying vec2 vUv;void main(){vUv=vec2(uv.x,1.0-uv.y);gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}"; var frag$1 = "precision highp float;\n#define GLSLIFY 1\nuniform sampler2D u_texture;uniform sampler2D u_textureNext;uniform float u_fade_t;varying vec2 vUv;void main(){vec2 uv=vUv;vec4 color0=texture2D(u_texture,vUv);vec4 color1=texture2D(u_textureNext,vUv);vec4 color=mix(color0,color1,u_fade_t);gl_FragColor=color;}"; class PickerPass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = true; this.#program = new Program(renderer, { vertexShader: vert$1, fragmentShader: frag$1, uniforms: { u_fade_t: { value: 0 }, u_texture: { value: this.options.texture }, u_textureNext: { value: this.options.textureNext } }, includes: shaderLib, transparent: true }); this.#geometry = new Geometry(renderer, { position: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, index: { size: 1, data: new Uint16Array([0, 1, 2, 2, 1, 3]) } }); this.#mesh = new Mesh(renderer, { mode: renderer.gl.TRIANGLES, program: this.#program, geometry: this.#geometry }); const opt = { width: this.renderer.width, height: this.renderer.height, minFilter: renderer.gl.NEAREST, magFilter: renderer.gl.NEAREST, type: this.renderer.gl.UNSIGNED_BYTE, format: this.renderer.gl.RGBA, generateMipmaps: true, internalFormat: this.renderer.gl.RGBA, stencil: false }; if (options.useFloatTexture) { opt.type = this.renderer.gl.FLOAT; opt.internalFormat = this.renderer.isWebGL2 ? this.renderer.gl.RGBA32F : this.renderer.gl.RGBA; } this.#picker = new RenderTarget(renderer, { ...opt, name: "pickerRenderTargetTexture" }); } #program; #mesh; #geometry; #picker; #rendererParams; #rendererState; resize(width, height) { this.#picker?.resize(width, height); } /** * @param rendererParams * @param rendererState * @param pixel */ render(rendererParams = this.#rendererParams, rendererState = this.#rendererState, pixel) { return new Promise((resolve) => { if (!this.#picker || !this.#mesh) return resolve(null); this.#rendererParams = this.#rendererParams !== rendererParams ? rendererParams : this.#rendererParams; this.#rendererState = this.#rendererState !== rendererState ? rendererState : this.#rendererState; const camera = rendererParams.cameras.planeCamera; this.#picker.clear(); this.#picker.bind(); this.renderer.setViewport(this.#picker.width, this.#picker.height); if (rendererState) { const fade = this.options.source?.getFadeTime?.() || 0; this.#mesh.program.setUniform("u_fade_t", fade); this.#mesh.updateMatrix(); this.#mesh.worldMatrixNeedsUpdate = false; this.#mesh.worldMatrix.multiply(camera.worldMatrix, this.#mesh.localMatrix); this.#mesh.draw({ ...rendererParams, camera }); if (pixel) { const a = this.options.useFloatTexture ? new Float32Array(4) : new Uint8Array(4); this.renderer.gl.readPixels( pixel[0], pixel[1], 1, 1, this.renderer.gl.RGBA, this.options.useFloatTexture ? this.renderer.gl.FLOAT : this.renderer.gl.UNSIGNED_BYTE, a ); resolve(a); } else { resolve(null); } } else { resolve(null); } this.#picker.unbind(); }); } destroy() { if (this.#mesh) { this.#mesh.destroy(); this.#mesh = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#geometry) { this.#geometry.destroy(); this.#geometry = null; } if (this.#picker) { this.#picker.destroy(); this.#picker = null; } } } function parseColorStyle(styleAttrField) { if (Array.isArray(styleAttrField) && styleAttrField.length > 3) { const type = styleAttrField[0]; const action = styleAttrField[1]; const interpolateColor = []; for (let i = 3; i < styleAttrField.length; i += 2) { const val = styleAttrField[i]; const color = styleAttrField[i + 1]; interpolateColor.push({ key: val, value: color }); } return { operator: type, interpolation: { name: action[0], base: action[1] }, input: interpolateColor }; } else { console.warn("[wind-core]: style-parser style config invalid"); return {}; } } function parseZoomStyle(styleAttrField) { if (Array.isArray(styleAttrField) && styleAttrField.length > 3) { const type = styleAttrField[0]; const action = styleAttrField[1]; const interpolateZoom = []; for (let i = 3; i < styleAttrField.length; i += 2) { const val = styleAttrField[i]; const color = styleAttrField[i + 1]; interpolateZoom.push({ key: val, value: color }); } return { operator: type, interpolation: { name: action[0], base: action[1] }, input: interpolateZoom }; } else { console.warn("[wind-core]: style-parser style config invalid"); return {}; } } function createGradient(interpolateColor, min, max, w, h, gradient, ctx) { for (let i = 0; i < interpolateColor.length; i += 1) { const key = interpolateColor[i].key; const color = interpolateColor[i].value; gradient.addColorStop((key - min) / (max - min), color); } ctx.fillStyle = gradient; ctx.fillRect(0, 0, w, h); } function createStepGradient(interpolateColor, min, max, w, h, ctx) { for (let i = 0; i < interpolateColor.length; i += 1) { const key = interpolateColor[i].key; let keyNext = key; if (i < interpolateColor.length - 1) { keyNext = interpolateColor[i + 1].key; } else { keyNext = max; } const color = interpolateColor[i].value; const current = (key - min) / (max - min) * w; const next = (keyNext - min) / (max - min) * w; ctx.fillStyle = color; ctx.fillRect(current, 0, next - current, 1); } } function createLinearGradient(range, styleAttrField) { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d", { willReadFrequently: true }); const { input: interpolateColor, interpolation } = parseColorStyle(styleAttrField); if (ctx && interpolateColor && Array.isArray(interpolateColor)) { const keys = interpolateColor.map((d) => parseFloat(d.key)); const colorRange = [Math.min(...keys), Math.max(...keys)]; const [min, max] = [range[0] || colorRange[0], range[1] || colorRange[1]]; const w = 256; const h = 1; canvas.width = w; canvas.height = h; const gradient = ctx.createLinearGradient(0, 0, w, 0); if (interpolation?.name === "linear") { createGradient(interpolateColor, min, max, w, h, gradient, ctx); } else if (interpolation?.name === "step") { if (interpolation?.base === true || index.isNumber(interpolation?.base)) { const interval = Number(interpolation?.base); createGradient(interpolateColor, min, max, w, h, gradient, ctx); const len = Math.round((max - min) / interval); const canvas2 = document.createElement("canvas"); const ctx2 = canvas2.getContext("2d", { willReadFrequently: true }); canvas2.width = w; canvas2.height = h; for (let j = 0; j < len; j++) { let keyNext = j; if (j < len - 1) { keyNext = j + 1; } else { keyNext = len; } const current = Math.round(j / len * w); const color = ctx.getImageData(current, 0, 1, 1).data; const next = Math.round(keyNext / len * w); ctx2.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`; ctx2.fillRect(current, 0, next - current, h); } return { data: new Uint8Array(ctx2.getImageData(0, 0, w, h).data), colorRange }; } else if (interpolation?.base === false) { createStepGradient(interpolateColor, min, max, w, h, ctx); } } else { console.warn(`[wind-core]: invalid action type: ${interpolation}`); } return { data: new Uint8Array(ctx.getImageData(0, 0, w, h).data), colorRange }; } else { return {}; } } function exponentialInterpolation(input, base, lowerValue, upperValue) { const difference = upperValue - lowerValue; const progress = input - lowerValue; if (difference === 0) { return 0; } else if (base === 1) { return progress / difference; } else { return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1); } } function interpolationFactor(interpolation, input, lower, upper) { let t = 0; if (interpolation.name === "exponential") { t = exponentialInterpolation(input, interpolation.base, lower, upper); } else if (interpolation.name === "linear") { t = exponentialInterpolation(input, 1, lower, upper); } else if (interpolation.name === "cubic-bezier") { console.warn("interpolationFactor"); } return t; } function interpolateNumber(a, b, t) { return a * (1 - t) + b * t; } const cachedStyle = {}; function isRasterize(styleAttrField) { if (Array.isArray(styleAttrField) && styleAttrField.length > 3) { const type = styleAttrField[0]; return type === "rasterize"; } else { console.warn("[wind-core]: style-parser style config invalid"); return false; } } function createZoom(uid, zoom, key, styles, clearCache) { const ukey = `${uid}_${key}`; const styleAttrField = styles[key]; if (index.isNumber(styleAttrField)) { if (cachedStyle[ukey]) { delete cachedStyle[ukey]; } return styleAttrField; } if (styleAttrField && Array.isArray(styleAttrField) && (!cachedStyle[ukey] || clearCache)) { cachedStyle[ukey] = parseZoomStyle(styleAttrField); } if (cachedStyle[ukey]) { const { input: interpolateZoom, interpolation } = cachedStyle[ukey] || {}; if (interpolateZoom && Array.isArray(interpolateZoom)) { const labels = interpolateZoom.map((i) => i.key); const outputs = interpolateZoom.map((i) => i.value); if (zoom <= labels[0]) { return outputs[0]; } const stopCount = labels.length; if (zoom >= labels[stopCount - 1]) { return outputs[stopCount - 1]; } const index = findStopLessThanOrEqualTo(labels, zoom); const idx = labels.length - 1; const lower = labels[index]; const upper = labels[index >= idx ? idx : index + 1]; const t = interpolationFactor(interpolation, zoom, lower, upper); const outputLower = outputs[index]; const outputUpper = outputs[index >= idx ? idx : index + 1]; return interpolateNumber(outputLower, outputUpper, t); } else { return 1; } } return 1; } var RenderType = /* @__PURE__ */ ((RenderType2) => { RenderType2[RenderType2["image"] = 0] = "image"; RenderType2[RenderType2["colorize"] = 1] = "colorize"; RenderType2[RenderType2["particles"] = 2] = "particles"; RenderType2[RenderType2["arrow"] = 3] = "arrow"; RenderType2[RenderType2["barb"] = 4] = "barb"; RenderType2[RenderType2["wave"] = 5] = "wave"; return RenderType2; })(RenderType || {}); var RenderFrom = /* @__PURE__ */ ((RenderFrom2) => { RenderFrom2["r"] = "r"; RenderFrom2["rg"] = "rg"; RenderFrom2["rgba"] = "rgba"; RenderFrom2["float"] = "float"; return RenderFrom2; })(RenderFrom || {}); function getBandType(renderFrom) { if (renderFrom === "rg") { return 1; } if (renderFrom === "rgba") { return 2; } if (renderFrom === "float") { return 3; } return 0; } var DecodeType = /* @__PURE__ */ ((DecodeType2) => { DecodeType2[DecodeType2["image"] = 0] = "image"; DecodeType2[DecodeType2["unit8"] = 1] = "unit8"; DecodeType2[DecodeType2["tiff"] = 2] = "tiff"; DecodeType2[DecodeType2["imageWithExif"] = 3] = "imageWithExif"; return DecodeType2; })(DecodeType || {}); var LayerSourceType = /* @__PURE__ */ ((LayerSourceType2) => { LayerSourceType2["image"] = "image"; LayerSourceType2["tile"] = "tile"; LayerSourceType2["timeline"] = "timeline"; return LayerSourceType2; })(LayerSourceType || {}); var TileState = /* @__PURE__ */ ((TileState2) => { TileState2["loading"] = "0"; TileState2["loaded"] = "1"; TileState2["errored"] = "2"; TileState2["unloaded"] = "3"; TileState2["reloading"] = "4"; return TileState2; })(TileState || {}); var MaskType = /* @__PURE__ */ ((MaskType2) => { MaskType2[MaskType2["outside"] = 0] = "outside"; MaskType2[MaskType2["inside"] = 1] = "inside"; return MaskType2; })(MaskType || {}); var maskVert = "#define GLSLIFY 1\nattribute vec3 position;uniform vec3 cameraPosition;uniform mat4 viewMatrix;uniform mat4 modelMatrix;uniform mat4 modelViewMatrix;uniform mat4 projectionMatrix;uniform float u_offset;void main(){gl_Position=projectionMatrix*modelViewMatrix*vec4(position+vec3(u_offset,0.0,0.0),1.0);}"; var maskFrag = "#defines\nprecision mediump float;\n#define GLSLIFY 1\nvoid main(){gl_FragColor=vec4(0.0,0.0,0.0,0.0);}"; class MaskPass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = true; this.#program = new Program(renderer, { vertexShader: maskVert, fragmentShader: maskFrag, includes: shaderLib, transparent: true }); this.#meshes = []; this.updateGeometry(); } #program; #meshes; updateGeometry() { const { mask } = this.options; if (!mask || mask.data.length === 0) return; const len = mask.data.length; let i = 0; for (let k = 0; k < this.#meshes.length; k++) { const mesh = this.#meshes[k]; if (mesh.geometry) { mesh.geometry.destroy(); } } this.#meshes = []; for (; i < len; i++) { const attributes = mask.data[i]; this.#meshes.push( new Mesh(this.renderer, { mode: this.renderer.gl.TRIANGLES, program: this.#program, geometry: new Geometry(this.renderer, attributes) }) ); } } /** * @param rendererParams * @param rendererState */ // eslint-disable-next-line @typescript-eslint/no-unused-vars render(rendererParams, rendererState) { const attr = this.renderer.attributes; this.renderer.setViewport(this.renderer.width * attr.dpr, this.renderer.height * attr.dpr); const { worlds = [0] } = rendererParams.cameras; const stencil = this.renderer.gl.getParameter(this.renderer.gl.STENCIL_TEST); if (!stencil) { this.renderer.state.enable(this.renderer.gl.STENCIL_TEST); } this.renderer.gl.stencilFunc(this.renderer.gl.ALWAYS, 1, 255); this.renderer.gl.stencilOp(this.renderer.gl.REPLACE, this.renderer.gl.REPLACE, this.renderer.gl.REPLACE); this.renderer.gl.stencilMask(255); this.renderer.gl.clearStencil(0); this.renderer.gl.clear(this.renderer.gl.STENCIL_BUFFER_BIT); for (let k = 0; k < this.#meshes.length; k++) { const mesh = this.#meshes[k]; for (let j = 0; j < worlds.length; j++) { mesh.program.setUniform("u_offset", worlds[j]); mesh.updateMatrix(); mesh.worldMatrixNeedsUpdate = false; mesh.worldMatrix.multiply(rendererParams.scene.worldMatrix, mesh.localMatrix); mesh.draw({ ...rendererParams, camera: rendererParams.cameras.camera }); } } const ref = this.options.mask?.type === MaskType.outside ? 0 : 1; this.renderer.gl.stencilFunc(this.renderer.gl.EQUAL, ref, 255); this.renderer.gl.stencilOp(this.renderer.gl.KEEP, this.renderer.gl.KEEP, this.renderer.gl.KEEP); return stencil; } } class ArrowComposePass extends ParticlesComposePass { } var vert = "#define GLSLIFY 1\n#defines\nattribute vec2 uv;attribute vec2 position;attribute vec2 coords;uniform vec2 arrowSize;uniform float u_head;uniform vec2 resolution;uniform float u_devicePixelRatio;uniform vec2 pixelsToProjUnit;uniform vec3 cameraPosition;uniform mat4 viewMatrix;uniform mat4 modelMatrix;uniform mat4 projectionMatrix;uniform vec2 u_extrude_scale;uniform lowp float u_device_pixel_ratio;uniform highp float u_camera_to_center_distance;uniform sampler2D u_texture;uniform sampler2D u_textureNext;uniform sampler2D colorRampTexture;uniform float u_fade_t;uniform vec2 u_image_res;uniform vec2 colorRange;uniform bool useDisplayRange;uniform bool u_flip_y;uniform float u_zoomScale;uniform vec2 displayRange;uniform vec4 u_bbox;uniform vec4 u_data_bbox;uniform vec4 u_tile_bbox;varying vec2 vUv;varying float v_speed;varying float v_speed_t;varying float v_head;varying float v_body;varying float v_antialias;varying float v_linewidth;vec4 calcTexture(const vec2 puv){vec4 color0=texture2D(u_texture,puv);vec4 color1=texture2D(u_textureNext,puv);return mix(color0,color1,u_fade_t);}vec2 decodeValue(const vec2 vc){vec4 rgba=calcTexture(vc);return rgba.rg;}vec2 bilinear(const vec2 uv){vec2 px=1.0/u_image_res;vec2 vc=(floor(uv*u_image_res))*px;vec2 f=fract(uv*u_image_res);vec2 tl=decodeValue(vc);vec2 tr=decodeValue(vc+vec2(px.x,0.0));vec2 bl=decodeValue(vc+vec2(0.0,px.y));vec2 br=decodeValue(vc+px);return mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);}float getValue(vec2 rg){return length(rg);}float getAngle(vec2 rg){float angle=atan(rg.y,rg.x);return angle;}void rotate2d(inout vec2 v,float a){mat2 m=mat2(cos(a),-sin(a),sin(a),cos(a));v=m*v;}void main(){vUv=uv;vec2 pos=u_tile_bbox.xy+coords.xy*(u_tile_bbox.zw-u_tile_bbox.xy);vec2 size=arrowSize*u_zoomScale*pixelsToProjUnit*u_devicePixelRatio;vec2 halfSize=size/2.0;vec2 worldPosition=vec2(-halfSize.x,-halfSize.y);if(position.x==1.0){worldPosition.x=halfSize.x;}if(position.y==1.0){worldPosition.y=halfSize.y;}worldPosition+=halfSize*vec2(1.0,0);vec2 textureCoord=(pos.xy-u_data_bbox.xy)/(u_data_bbox.zw-u_data_bbox.xy);if(u_flip_y){textureCoord=vec2(textureCoord.x,1.0-textureCoord.y);}vec2 rg=bilinear(textureCoord);float value=getValue(rg);float angle=getAngle(rg);angle=u_flip_y ? angle*-1. : angle;rotate2d(worldPosition,angle);worldPosition+=pos;v_speed=value;v_speed_t=(value-colorRange.x)/(colorRange.y-colorRange.x);v_linewidth=mix(0.18,0.12,v_speed_t);v_head=u_head;v_antialias=1.0/min(arrowSize.x,arrowSize.y);v_body=mix(0.15,4.0,v_speed_t)*3.0;gl_Position=projectionMatrix*viewMatrix*modelMatrix*vec4(worldPosition,0.0,1.0);}"; var frag = "#defines\nprecision highp float;\n#define GLSLIFY 1\nuniform sampler2D u_texture;uniform sampler2D u_textureNext;uniform sampler2D colorRampTexture;uniform float u_fade_t;uniform vec2 u_image_res;uniform vec2 colorRange;uniform bool useDisplayRange;uniform vec2 displayRange;uniform float opacity;varying vec2 vUv;varying float v_speed;varying float v_speed_t;varying float v_head;varying float v_body;varying float v_antialias;varying float v_linewidth;vec4 calcTexture(const vec2 puv){vec4 color0=texture2D(u_texture,puv);vec4 color1=texture2D(u_textureNext,puv);return mix(color0,color1,u_fade_t);}float disc(vec2 pos,float size){return length(pos)-size/2.0;}vec4 filled(float distance,float linewidth,float antialias,vec4 fill){vec4 frag_color=vec4(0.0);float t=linewidth/2.0-antialias;float signed_distance=distance;float border_distance=abs(signed_distance)-t;float alpha=border_distance/antialias;alpha=exp(-alpha*alpha);if(border_distance<0.0){frag_color=fill;}else if(signed_distance<0.0){frag_color=fill;}return frag_color;}float line_distance(vec2 p,vec2 p1,vec2 p2){vec2 center=(p1+p2)*0.5;float len=length(p2-p1);vec2 dir=(p2-p1)/len;vec2 rel_p=p-center;return dot(rel_p,vec2(dir.y,-dir.x));}float segment_distance(vec2 p,vec2 p1,vec2 p2){vec2 center=(p1+p2)*0.5;float len=length(p2-p1);vec2 dir=(p2-p1)/len;vec2 rel_p=p-center;float dist1=abs(dot(rel_p,vec2(dir.y,-dir.x)));float dist2=abs(dot(rel_p,dir))-0.5*len;return max(dist1,dist2);}float arrow_stealth(vec2 texcoord,float body,float head,float linewidth,float antialias){float w=linewidth/2.0+antialias;vec2 start=-vec2(body/2.0,0.0);vec2 end=+vec2(body/2.0,0.0);float height=0.5;float d1=line_distance(texcoord,end-head*vec2(+1.0,-height),end);float d2=line_distance(texcoord,end-head*vec2(+1.0,-height),end-vec2(3.0*head/4.0,0.0));float d3=line_distance(texcoord,end-head*vec2(+1.0,+height),end);float d4=line_distance(texcoord,end-head*vec2(+1.0,+0.5),end-vec2(3.0*head/4.0,0.0));float d5=segment_distance(texcoord,start,end-vec2(linewidth,0.0));return min(d5,max(max(-d1,d3),-max(-d2,d4)));}void main(){vec2 uv=vUv;if(calcTexture(uv).a==0.0||v_speed<0.0){discard;}vec2 pos=vUv-vec2(0.0,0.5);vec2 ramp_pos=vec2(v_speed_t,0.5);vec4 color=texture2D(colorRampTexture,ramp_pos);bool display=true;if(useDisplayRange){display=v_speed<=displayRange.y&&v_speed>=displayRange.x;}if(display){if(v_speed>0.2){float d=arrow_stealth(pos.xy,v_body,v_head,v_linewidth,v_antialias);vec4 rc=filled(d,0.15,0.01,color);gl_FragColor=vec4(floor(255.0*rc*opacity)/255.0);}else{float d=disc(pos,0.15);vec4 rc=filled(d,0.01,0.01,color);gl_FragColor=vec4(floor(255.0*rc*opacity)/255.0);}}else{gl_FragColor=vec4(0.0,0.0,0.0,0.0);}}"; const TILE_EXTENT = 4096; class ArrowPass extends Pass { constructor(id, renderer, options = {}) { super(id, renderer, options); this.prerender = false; this.#program = new Program(renderer, { vertexShader: vert, fragmentShader: frag, uniforms: { opacity: { value: 1 }, u_fade_t: { value: 0 }, displayRange: { value: new Vector2(-Infinity, Infinity) }, u_texture: { value: this.options.texture }, u_textureNext: { value: this.options.textureNext }, colorRampTexture: { value: null } }, defines: [`RENDER_TYPE ${this.options.bandType}`, `LITTLE_ENDIAN ${littleEndian}`], includes: shaderLib, transparent: true }); this.#mesh = new Mesh(this.renderer, { mode: this.renderer.gl.TRIANGLES, program: this.#program, geometry: new Geometry(this.renderer, { index: { size: 1, // data: new Uint16Array([0, 1, 2, 2, 1, 3]), data: new Uint16Array([0, 1, 2, 0, 2, 3]) }, position: { size: 2, // data: new Float32Array([-1, 1, -1, -1, 1, 1, 1, -1]), data: new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]) }, coords: { divisor: 1, data: new Float32Array(2), offset: 0, size: 2, stride: 8 } }) }); } #mesh; #program; #geometry; #vertexArray; #lastTileSize; #lastSpace; createTileVertexArray(tileSize, space = 20) { if (!this.#vertexArray || tileSize !== this.#lastTileSize || space !== this.#lastSpace) { this.#lastTileSize = tileSize; this.#lastSpace = space; const column = Math.round(tileSize / space); const columnUnit = 1 / column; const halfUnit = columnUnit / 2; const points = []; for (let j = 0; j < column; j++) { for (let i = 0; i < column; i++) { points.push({ x: TILE_EXTENT * (halfUnit + i * columnUnit), y: TILE_EXTENT * (halfUnit + j * columnUnit) }); } } this.#vertexArray = new Float32Array(points.length * 2); for (let i = 0; i < points.length; i++) { const point = points[i]; const pos = { x: Math.round(point.x), y: Math.round(point.y) }; if (pos.x < 0 || pos.x >= TILE_EXTENT || pos.y < 0 || pos.y >= TILE_EXTENT) continue; this.#vertexArray[2 * i] = pos.x / TILE_EXTENT; this.#vertexArray[2 * i + 1] = pos.y / TILE_EXTENT; } const geometry = new Geometry(this.renderer, { index: { size: 1, data: new Uint16Array([0, 1, 2, 0, 2, 3]) }, position: { size: 2, data: new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]) }, coords: { divisor: 1, data: this.#vertexArray, offset: 0, size: 2, stride: 8 } }); if (this.#mesh) { this.#mesh.updateGeometry(geometry, true); } } return this.#vertexArray; } /** * @param rendererParams * @param rendererState */ render(rendererParams, rendererState) { const attr = this.renderer.attributes; this.renderer.setViewport(this.renderer.width * attr.dpr, this.renderer.height * attr.dpr); const camera = rendererParams.cameras.camera; const tileSize = this.options.source.tileSize ?? 256; const tiles = this.options.getGridTiles(this.options.source); let stencil; if (this.maskPass) { stencil = this.maskPass.render(rendererParams, rendererState); } if (rendererState && this.#mesh && tiles && tiles.length > 0) { const uniforms = index.pick(rendererState, [ "opacity", "colorRange", "dataRange", "colorRampTexture", "useDisplayRange", "displayRange" ]); const zoom = rendererState.zoom; const dataBounds = rendererState.sharedState.u_data_bbox; this.createTileVertexArray(tileSize, rendererState.symbolSpace); for (let i = 0; i < tiles.length; i++) { const tile = tiles[i]; const bounds = tile.getTileProjBounds(); const scaleFactor = Math.pow(2, zoom - tile.overscaledZ); const max = Math.max(bounds.right - bounds.left, bounds.bottom - bounds.top); const scale = 1 / max; const pixelToUnits = 1 / (tileSize * scaleFactor) / scale; Object.keys(uniforms).forEach((key) => { if (uniforms[key] !== void 0) { this.#mesh?.program.setUniform(key, uniforms[key]); } }); const fade = this.options.source?.getFadeTime?.() || 0; this.#mesh.program.setUniform( "u_image_res", new Vector2(this.options.texture.width, this.options.texture.height) ); this.#mesh.program.setUniform("u_fade_t", fade); this.#mesh.program.setUniform("arrowSize", rendererState.symbolSize); this.#mesh.program.setUniform("pixelsToProjUnit", new Vector2(pixelToUnits, pixelToUnits)); this.#mesh.program.setUniform("u_bbox", rendererState.extent); this.#mesh.program.setUniform("u_data_bbox", dataBounds); this.#mesh.program.setUniform( "u_tile_bbox", rendererState.u_flip_y ? [bounds.left, bounds.bottom, bounds.right, bounds.top] : [bounds.left, bounds.top, bounds.right, bounds.bottom] ); this.#mesh.program.setUniform("u_head", 0.1); this.#mesh.program.setUniform("u_devicePixelRatio", attr.dpr); this.#mesh.program.setUniform("u_texture", this.options.texture); this.#mesh.program.setUniform("u_textureNext", this.options.textureNext); this.#mesh.program.setUniform("u_flip_y", rendererState.u_flip_y); this.#mesh.program.setUniform("u_zoomScale", rendererState.u_zoomScale); this.#mesh.updateMatrix(); this.#mesh.worldMatrixNeedsUpdate = false; this.#mesh.worldMatrix.multiply(rendererParams.scene.worldMatrix, this.#mesh.localMatrix); this.#mesh.draw({ ...rendererParams, camera }); } } if (!stencil) { this.renderer.state.disable(this.renderer.gl.STENCIL_TEST); } } destroy() { if (this.#mesh) { this.#mesh.destroy(); this.#mesh = null; } if (this.#program) { this.#program.destroy(); this.#program = null; } if (this.#geometry) { this.#geometry.destroy(); this.#geometry = null; } } } const defaultOptions = { getViewTiles: () => [], getGridTiles: () => [], getTileProjSize: (z) => [256, 256], // eslint-disable-line getPixelsToUnits: () => [1, 1], getPixelsToProjUnit: () => [1, 1], renderType: RenderType.colorize, renderFrom: RenderFrom.r, styleSpec: { "fill-color": [ "interpolate", ["linear"], ["get", "value"], 0, "#3288bd", 10, "#66c2a5", 20, "#abdda4", 30, "#e6f598", 40, "#fee08b", 50, "#fdae61", 60, "#f46d43", 100, "#d53e4f" ], opacity: 1, numParticles: 65535, speedFactor: 1, fadeOpacity: 0.93, dropRate: 3e-3, dropRateBump: 2e-3, space: 20, size: [16, 16] }, displayRange: [Infinity, Infinity], widthSegments: 1, heightSegments: 1, wireframe: false, flipY: false, glScale: () => 1, zoomScale: () => 1, onInit: () => void 0 }; let registerDeps = false; let BaseLayer$1 = class BaseLayer { #opacity; #numParticles; #speedFactor; #fadeOpacity; #dropRate; #dropRateBump; #space; #size; #colorRange; #colorRampTexture; #nextStencilID; #maskPass; #isRasterize; constructor(source, rs, options) { this.renderer = rs.renderer; this.scene = rs.scene; this.source = source; if (!this.renderer) { throw new Error("initialize error"); } this.uid = index.uid("ScalarFill"); if (!options) { options = {}; } this.options = { ...defaultOptions, ...options, styleSpec: { ...defaultOptions.styleSpec, ...options.styleSpec } }; this.#opacity = 1; this.#nextStencilID = 1; this.dispatcher = new wgw$1.Dispatcher(wgw$1.getGlobalWorkerPool(), this, this.uid); if (!registerDeps) { const deps = wgw$1.getConfigDeps(); this.dispatcher.broadcast( "configDeps", deps.map((d) => resolveURL(d)), (err, data) => { this.options.onInit?.(err, data); } ); registerDeps = true; } this.update = this.update.bind(this); this.onTileLoaded = this.onTileLoaded.bind(this); this.source.prepare(this.renderer, this.dispatcher, { renderFrom: this.options.renderFrom ?? RenderFrom.r }); this.source.onAdd(this); if (Array.isArray(this.source.sourceCache)) { this.source.sourceCache.forEach((s) => { s.on("update", this.update); s.on("tileLoaded", this.onTileLoaded); }); } else { this.source.sourceCache.on("update", this.update); this.source.sourceCache.on("tileLoaded", this.onTileLoaded); } this.initialize(); } initialize() { this.updateOptions({}); this.sharedState = { u_bbox: [0, 0, 1, 1], u_data_bbox: [0, 0, 1, 1], u_scale: [1, 1] }; this.renderPipeline = new Pipelines(this.renderer); const bandType = getBandType(this.options.renderFrom ?? RenderFrom.r); if (this.options.mask) { this.#maskPass = new MaskPass("MaskPass", this.renderer, { mask: this.options.mask }); } if (this.options.renderType === RenderType.image) { const composePass = new ComposePass2("RasterComposePass", this.renderer, { bandType, source: this.source, renderFrom: this.options.renderFrom ?? RenderFrom.r, maskPass: this.#maskPass, stencilConfigForOverlap: this.stencilConfigForOverlap.bind(this) }); const rasterPass = new RasterPass("RasterPass", this.renderer, { bandType, source: this.source, texture: composePass.textures.current, textureNext: composePass.textures.next }); this.renderPipeline?.addPass(composePass); if (this.options.picking) { const pickerPass = new PickerPass("PickerPass", this.renderer, { source: this.source, texture: composePass.textures.current, textureNext: composePass.textures.next, useFloatTexture: false }); this.renderPipeline?.addPass(pickerPass); } this.renderPipeline?.addPass(rasterPass); } else if (this.options.renderType === RenderType.colorize) { const composePass = new ComposePass$1("ColorizeComposePass", this.renderer, { bandType, source: this.source, renderFrom: this.options.renderFrom ?? RenderFrom.r, maskPass: this.#maskPass, stencilConfigForOverlap: this.stencilConfigForOverlap.bind(this), isRasterize: () => this.#isRasterize }); const colorizePass = new ColorizePass("ColorizePass", this.renderer, { bandType, source: this.source, texture: composePass.textures.current, textureNext: composePass.textures.next }); this.renderPipeline?.addPass(composePass); if (this.options.picking) { const pickerPass = new PickerPass("PickerPass", this.renderer, { source: this.source, texture: composePass.textures.current, textureNext: composePass.textures.next, useFloatTexture: true }); this.renderPipeline?.addPass(pickerPass); } this.renderPipeline?.addPass(colorizePass); } else if (this.options.renderType === RenderType.particles) { const composePass = new ParticlesComposePass("ParticlesComposePass", this.renderer, { id: index.uid("ParticlesComposePass"), bandType, source: this.source, renderFrom: this.options.renderFrom ?? RenderFrom.r, stencilConfigForOverlap: this.stencilConfigForOverlap.bind(this), getTileProjSize: this.options.getTileProjSize }); this.renderPipeline?.addPass(composePass); const updatePass = new UpdatePass("UpdatePass", this.renderer, { bandType, source: this.source, texture: composePass.textures.current, textureNext: composePass.textures.next, getParticleNumber: () => this.#numParticles, glScale: this.options.glScale?.() }); this.renderPipeline?.addPass(updatePass); const particlesPass = new Particles("ParticlesPass", this.renderer, { bandType, source: this.source, texture: composePass.textures.current, textureNext: composePass.textures.next, getParticles: () => updatePass.textures, getParticleNumber: () => this.#numParticles, maskPass: this.#maskPass }); const particlesTexturePass = new ScreenPass("ParticlesTexturePass", this.renderer, { bandType, source: this.source, prerender: true, enableBlend: false, particlesPass }); this.renderPipeline?.addPass(particlesTexturePass); this.renderPipeline?.addPass(particlesPass); const screenPass = new ScreenPass("ScreenPass", this.renderer, { bandType, source: this.source, prerender: false, enableBlend: true, particlesPass }); this.renderPipeline?.addPass(screenPass); this.raf = new Raf( () => { if (this.options.triggerRepaint) { this.options.triggerRepaint(); } }, { autoStart: true } ); } else if (this.options.renderType === RenderType.arrow) { const composePass = new ArrowComposePass("ArrowComposePass", this.renderer, { id: index.uid("ArrowComposePass"), bandType, source: this.source, renderFrom: this.options.renderFrom ?? RenderFrom.r, stencilConfigForOverlap: this.stencilConfigForOverlap.bind(this), getTileProjSize: this.options.getTileProjSize }); const arrowPass = new ArrowPass("ArrowPass", this.renderer, { bandType, source: this.source, texture: composePass.textures.current, textureNext: composePass.textures.next, getPixelsToUnits: this.options.getPixelsToUnits, getGridTiles: this.options.getGridTiles, maskPass: this.#maskPass }); this.renderPipeline?.addPass(composePass); this.renderPipeline?.addPass(arrowPass); } } updateOptions(options) { this.options = { ...this.options, ...options, styleSpec: { ...this.options.styleSpec, ...options?.styleSpec } }; this.buildColorRamp(); this.parseStyleSpec(true); this.options?.triggerRepaint?.(); } resize(width, height) { if (this.renderPipeline) { this.renderPipeline.resize(width, height); } } /** * 设置填色色阶 */ setFillColor() { this.buildColorRamp(); } /** * 设置图层透明度 * @param opacity */ setOpacity(opacity) { this.#opacity = opacity; } /** * 设置粒子图层的粒子数量 * @param numParticles */ setNumParticles(numParticles) { this.#numParticles = numParticles; } /** * 设置粒子图层的粒子数量 * @param speedFactor */ setSpeedFactor(speedFactor) { this.#speedFactor = speedFactor; } /** * 设置粒子图层的粒子数量 * @param fadeOpacity */ setFadeOpacity(fadeOpacity) { this.#fadeOpacity = fadeOpacity; } /** * 设置粒子图层的粒子数量 * @param dropRate */ setDropRate(dropRate) { this.#dropRate = dropRate; } /** * 设置粒子图层的粒子数量 * @param dropRateBump */ setDropRateBump(dropRateBump) { this.#dropRateBump = dropRateBump; } /** * 设置 symbol 的间距 * @param space */ setSymbolSpace(space) { this.#space = space; } /** * 设置 symbol 的大小 * @param size */ setSymbolSize(size) { this.#size = size; } /** * 解析样式配置 * @param clear */ parseStyleSpec(clear) { if (isFunction(this.options.getZoom)) { const zoom = this.options.getZoom(); this.setOpacity(createZoom(this.uid, zoom, "opacity", this.options.styleSpec, clear)); if (this.options.renderType === RenderType.particles) { this.setNumParticles(createZoom(this.uid, zoom, "numParticles", this.options.styleSpec, clear)); this.setFadeOpacity(createZoom(this.uid, zoom, "fadeOpacity", this.options.styleSpec, clear)); this.setSpeedFactor(createZoom(this.uid, zoom, "speedFactor", this.options.styleSpec, clear)); this.setDropRate(createZoom(this.uid, zoom, "dropRate", this.options.styleSpec, clear)); this.setDropRateBump(createZoom(this.uid, zoom, "dropRateBump", this.options.styleSpec, clear)); } if (this.options.renderType === RenderType.arrow) { this.setSymbolSize(this.options.styleSpec?.size); this.setSymbolSpace(createZoom(this.uid, zoom, "space", this.options.styleSpec, clear)); } } } /** * 处理地图缩放事件 */ handleZoom() { this.parseStyleSpec(false); } /** * 构建渲染所需色带 */ buildColorRamp() { if (!this.options.styleSpec?.["fill-color"]) return; const { data, colorRange } = createLinearGradient([], this.options.styleSpec?.["fill-color"]); this.#isRasterize = isRasterize(this.options.styleSpec?.["fill-color"]); if (colorRange) { this.#colorRange = new Vector2(...colorRange); } if (data) { this.#colorRampTexture = new DataTexture(this.renderer, { data, name: "colorRampTexture", magFilter: this.renderer.gl.NEAREST, minFilter: this.renderer.gl.NEAREST, width: 255, height: 1 }); } } clearStencil() { this.#nextStencilID = 1; } stencilConfigForOverlap(tiles) { const coords = tiles.sort((a, b) => b.overscaledZ - a.overscaledZ); const minTileZ = coords[coords.length - 1].overscaledZ; const stencilValues = coords[0].overscaledZ - minTileZ + 1; if (stencilValues > 1) { if (this.#nextStencilID + stencilValues > 256) { this.clearStencil(); } const zToStencilMode = {}; for (let i = 0; i < stencilValues; i++) { zToStencilMode[i + minTileZ] = { stencil: true, mask: 255, func: { cmp: this.renderer.gl.GEQUAL, ref: i + this.#nextStencilID, mask: 255 }, op: { fail: this.renderer.gl.KEEP, zfail: this.renderer.gl.KEEP, zpass: this.renderer.gl.REPLACE } }; } this.#nextStencilID += stencilValues; return [zToStencilMode, coords]; } return [ { [minTileZ]: { // 禁止写入 stencil: false, mask: 0, func: { cmp: this.renderer.gl.ALWAYS, ref: 0, mask: 0 }, op: { fail: this.renderer.gl.KEEP, zfail: this.renderer.gl.KEEP, zpass: this.renderer.gl.KEEP } } }, coords ]; } moveStart() { if (this.renderPipeline && this.options.renderType === RenderType.particles) { const particlesPass = this.renderPipeline.getPass("ParticlesPass"); if (particlesPass) { particlesPass.resetParticles(); } this.renderPipeline.passes.forEach((pass) => { if (pass.id === "ParticlesTexturePass" || pass.id === "ScreenPass") { pass.enabled = false; } if (pass.id === "ParticlesPass") { pass.prerender = false; } }); } } moveEnd() { if (this.renderPipeline && this.options.renderType === RenderType.particles) { const updatePass = this.renderPipeline.getPass("UpdatePass"); if (updatePass) { updatePass.setInitialize(true); } this.renderPipeline.passes.forEach((pass) => { if (pass.id === "ParticlesTexturePass" || pass.id === "ScreenPass") { pass.enabled = true; } if (pass.id === "ParticlesPass") { pass.prerender = true; } }); } } /** * 更新视野内的瓦片 */ update() { const tiles = this.options.getViewTiles(this.source, this.options.renderType); if (Array.isArray(this.source.sourceCache)) { this.source.sourceCache.forEach((s) => { s?.update(tiles); }); } else { this.source.sourceCache?.update(tiles); } } onTileLoaded() { if (this.options.triggerRepaint && isFunction(this.options.triggerRepaint)) { this.options.triggerRepaint(); } } setMask(mask) { this.options.mask = mask; if (this.options.mask) { if (!this.#maskPass) { this.#maskPass = new MaskPass("MaskPass", this.renderer, { mask: this.options.mask }); const raster = this.renderPipeline?.getPass("RasterComposePass"); if (raster) { raster.setMaskPass(this.#maskPass); } const colorize = this.renderPipeline?.getPass("ColorizeComposePass"); if (colorize) { colorize.setMaskPass(this.#maskPass); } const particles = this.renderPipeline?.getPass("ParticlesPass"); if (particles) { particles.setMaskPass(this.#maskPass); } const arrow = this.renderPipeline?.getPass("ArrowPass"); if (arrow) { arrow.setMaskPass(this.#maskPass); } } this.#maskPass.updateGeometry(); this.options?.triggerRepaint?.(); } } async picker(pixel = [0, 0]) { if (!this.renderPipeline) return null; const pickerPass = this.renderPipeline.getPass("PickerPass"); if (!pickerPass) return null; return pickerPass.render(void 0, void 0, pixel); } prerender(cameras, renderTarget) { if (this.renderPipeline) { this.renderPipeline.prerender( { scene: this.scene, cameras, ...renderTarget ? { target: renderTarget } : {} }, { zoom: this.options?.getZoom?.() ?? 0, extent: this.options?.getExtent?.(), opacity: this.#opacity, fadeOpacity: this.#fadeOpacity, numParticles: this.#numParticles, colorRange: this.#colorRange, colorRampTexture: this.#colorRampTexture, sharedState: this.sharedState, u_drop_rate: this.#dropRate, u_drop_rate_bump: this.#dropRateBump, u_speed_factor: this.#speedFactor, u_flip_y: this.options.flipY, u_gl_scale: this.options.glScale?.(), u_zoomScale: this.options.zoomScale?.(), symbolSize: this.#size, symbolSpace: this.#space, pixelsToProjUnit: this.options.getPixelsToProjUnit() } ); } } render(cameras, renderTarget) { if (this.renderPipeline) { const state = { zoom: this.options?.getZoom?.() ?? 0, extent: this.options?.getExtent?.(), opacity: this.#opacity, fadeOpacity: this.#fadeOpacity, numParticles: this.#numParticles, colorRange: this.#colorRange, colorRampTexture: this.#colorRampTexture, displayRange: this.options.displayRange, useDisplayRange: Boolean(this.options.displayRange), sharedState: this.sharedState, u_drop_rate: this.#dropRate, u_drop_rate_bump: this.#dropRateBump, u_speed_factor: this.#speedFactor, u_flip_y: this.options.flipY, u_gl_scale: this.options.glScale?.(), u_zoomScale: this.options.zoomScale?.(), symbolSize: this.#size, symbolSpace: this.#space, pixelsToProjUnit: this.options.getPixelsToProjUnit() }; this.renderPipeline.render( { scene: this.scene, cameras, ...renderTarget ? { target: renderTarget } : {} }, state ); } } /** * 销毁此 Renderer */ destroy() { if (this.raf) { this.raf.stop(); } if (this.renderPipeline) { this.renderPipeline.destroy(); this.renderPipeline = null; } if (this.source) { if (Array.isArray(this.source.sourceCache)) { this.source.sourceCache.forEach((s) => { s.off("update", this.update); s.off("tileLoaded", this.onTileLoaded); }); } else { this.source.sourceCache.off("update", this.update); this.source.sourceCache.off("tileLoaded", this.onTileLoaded); } this.source.destroy(); } } }; class TileID { /** * @param overscaledZ 扩大的 z 值 * @param wrap 所处世界 * @param z 层级 * @param x 列 * @param y 行 * @param options 瓦片其他配置 */ constructor(overscaledZ, wrap = 0, z, x, y, options = {}) { this.x = x; this.y = y; this.z = z; this.wrap = wrap; this.tileKey = `${z}_${x}_${y}-${wrap}`; this.unWrappedTileKey = `${z}_${x}_${y}`; const max = Math.pow(2, this.z); this.wrapedX = max * wrap + this.x; this.wrapedY = this.y; this.overscaledZ = overscaledZ; this.options = options; this.getTileBounds(); } /** * 获取瓦片范围 */ getTileBounds(tileID = this) { if (isFunction(this.options.getTileBounds)) { this.tileBounds = this.options.getTileBounds(tileID); } else { console.error("[TileID]: projection function must be provided"); } return this.tileBounds; } /** * 获取瓦片投影后的范围 */ getTileProjBounds(tileID = this, force) { if (!this.projTileBounds || force) { this.projTileBounds = this.options.getTileProjBounds?.(tileID); } return this.projTileBounds; } overscaleFactor() { return Math.pow(2, this.overscaledZ - this.z); } /** * 缩放到目标层级 * @param targetZ */ scaledTo(targetZ) { const zDifference = this.z - targetZ; if (targetZ > this.z) { return new TileID(targetZ, this.wrap, this.z, this.x, this.y, this.options); } else { return new TileID(targetZ, this.wrap, targetZ, this.x >> zDifference, this.y >> zDifference, this.options); } } /** * 获取父级瓦片 */ parent() { if (this.z > 0) return new TileID(this.z - 1, this.wrap, this.z - 1, this.x >> 1, this.y >> 1, this.options); else return new TileID(this.z, this.wrap, this.z, this.x, this.y, this.options); } /** * 查找当前瓦片的子瓦片 * @param sourceMaxZoom */ children(sourceMaxZoom) { if (this.overscaledZ >= sourceMaxZoom) { return [new TileID(this.overscaledZ + 1, this.wrap, this.z, this.x, this.y, this.options)]; } const z = this.z + 1; const x = this.x * 2; const y = this.y * 2; return [ new TileID(z, this.wrap, z, x, y, this.options), new TileID(z, this.wrap, z, x + 1, y, this.options), new TileID(z, this.wrap, z, x, y + 1, this.options), new TileID(z, this.wrap, z, x + 1, y + 1, this.options) ]; } /** * 查找兄弟瓦片 */ siblings() { return this.z === 0 ? [] : this.parent().children(this.overscaledZ).filter((t) => !this.isEqual(t)); } /** * 查找相临瓦片 * @param hor 横向偏移 * @param ver 纵向偏移 */ neighbor(hor, ver = 0) { if (this.z === 0) { return new TileID(this.overscaledZ, this.wrap + hor, this.z, this.x, this.y, this.options); } const max = Math.pow(2, this.z); const w = this.x + hor; const dw = Math.floor(w / max); const wrap = this.wrap + dw; return new TileID( this.overscaledZ, wrap, this.z, (this.x + hor - max * dw) % max, (this.y + ver + max) % max, this.options ); } /** * 判断瓦片是否相同 * 一般我们认为只要 xyz 和所处世界 wrap 相同就确认相同(即 tileKey 相同) * @param tile */ isEqual(tile) { return tile.tileKey === this.tileKey; } /** * 判断是否是根节点 * @returns {boolean} */ isRoot() { return this.z === 0; } } class TileMesh { constructor(id, renderer, program, geometry) { this.id = id; this.program = program; this.mesh = new Mesh(renderer, { program, geometry }); this.planeMesh = new Mesh(renderer, { program, geometry: new Geometry(renderer, { position: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, uv: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) }, index: { size: 1, data: new Uint16Array([0, 1, 2, 2, 1, 3]) } }) }); } setCenter(center) { this.mesh.position.set(center[0], center[1], center[2] || 0); } getMesh() { return this.mesh; } destroy() { this.mesh.destroy(); this.planeMesh.destroy(); } } class Tile { /** * @param tileID * @param options */ constructor(tileID, options = {}) { this.errorCount = 0; this.maxErrorCount = 3; this.uses = 0; this.tileMeshs = /* @__PURE__ */ new Map(); this.geometries = /* @__PURE__ */ new Map(); this.#textures = /* @__PURE__ */ new Map(); this.tileID = tileID; this.tileSize = options.tileSize; this.request = /* @__PURE__ */ new Map(); this.state = TileState.loading; } #textures; /** * 瓦片是否已经加载到数据 */ hasData() { return this.state === TileState.loaded || this.state === TileState.reloading; } /** * 瓦片是否已经请求过 */ wasRequested() { return this.state === TileState.errored || this.state === TileState.loaded; } /** * 瓦片是否加载完成 */ isLoaded() { return this.state === TileState.loaded || this.state === TileState.reloading || this.state === TileState.errored; } getMesh(passId) { return this.tileMeshs.get(passId); } get textures() { return this.#textures; } get tileCenter() { return [(this.tileBounds.left + this.tileBounds.right) / 2, (this.tileBounds.top + this.tileBounds.bottom) / 2, 0]; } /** * 更新瓦片顶点信息 * @param passId * @param bbox * @param renderer * @param force */ updateGeometry(passId, bbox, renderer, force) { this.tileBounds = bbox; if (!this.geometries.get(passId) || force) { const position = [ this.tileBounds.left, this.tileBounds.top, 0, this.tileBounds.right, this.tileBounds.top, 0, this.tileBounds.left, this.tileBounds.bottom, 0, this.tileBounds.right, this.tileBounds.bottom, 0 ]; let i = 0; const len = position.length; for (; i < len; i += 3) { position[i] = position[i] - this.tileCenter[0]; position[i + 1] = position[i + 1] - this.tileCenter[1]; position[i + 2] = position[i + 2] - this.tileCenter[2]; } this.geometries.set( passId, new Geometry(renderer, { position: { size: 3, data: new Float32Array(position) }, normal: { size: 3, data: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]) }, uv: { size: 2, data: new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]) }, index: { data: new Uint16Array([0, 2, 1, 2, 3, 1]) } }) ); } return this.geometries.get(passId); } /** * 创建 `TileMesh` * @param passId 在多个 render pass 共享 tile 时我们可能需要针对多个 pass 创建渲染资源 * 在 mapbox 这种共享 gl 上下文的一般我们不需要重建,但是对于 maptalks 这种每个图层一个 gl * 上下文的我们需要针对每个 gl上下文绑定资源 * @param bbox * @param renderer * @param program * @param force */ createMesh(passId, bbox, renderer, program, force) { const geometry = this.updateGeometry(passId, bbox, renderer, force); if (!this.tileMeshs.get(passId) || force) { this.uses++; const uid = passId + "_" + this.tileID.tileKey; const tileMesh = new TileMesh(uid, renderer, program, geometry); tileMesh.setCenter(this.tileCenter); this.tileMeshs.set(passId, tileMesh); } return this.tileMeshs.get(passId); } /** * 创建纹理 * @param renderer * @param index * @param image * @param parseOptions * @param userData */ setTextures(renderer, index, image, parseOptions, userData) { const texture = this.#textures.get(index); const iib = isImageBitmap(image) || image instanceof Image; let dataRange; if (userData?.dataRange) { dataRange = userData?.dataRange; } else if (image.withExif) { dataRange = parseRange(image.exif); } if (texture) { if (texture.userData) { texture.userData.dataRange = dataRange; } texture.setData(iib ? image : image.data); } else { this.#textures.set( index, new Texture(renderer, { userData: dataRange ? { dataRange } : void 0, image: iib ? image : image.data, width: image.width, height: image.height, minFilter: renderer.gl.LINEAR, magFilter: renderer.gl.LINEAR, wrapS: renderer.gl.CLAMP_TO_EDGE, wrapT: renderer.gl.CLAMP_TO_EDGE, flipY: false, // 注意,对 ImageBitmap 无效 premultiplyAlpha: false, // 禁用 `Alpha` 预乘 type: parseOptions.renderFrom === RenderFrom.float ? renderer.gl.FLOAT : renderer.gl.UNSIGNED_BYTE, format: parseOptions.renderFrom === RenderFrom.float ? renderer.isWebGL2 ? renderer.gl.RED : renderer.gl.LUMINANCE : renderer.gl.RGBA, internalFormat: parseOptions.renderFrom === RenderFrom.float ? renderer.isWebGL2 ? renderer.gl.R32F : renderer.gl.LUMINANCE : renderer.gl.RGBA }) ); } } /** * 获取瓦片世界坐标系下的范围 */ getBounds() { return this.tileBounds; } copy(tile) { this.#textures = tile.textures; this.actor = tile.actor; this.state = tile.state !== TileState.errored ? TileState.loaded : TileState.errored; this.request = tile.request; this.reloadCallback = tile.reloadCallback; return this; } /** * 释放瓦片资源 */ destroy() { for (const [, value] of this.#textures) { if (value) { value?.destroy(); } } this.#textures.clear(); for (const [, value] of this.geometries) { if (value) { value?.destroy(); } } for (const [, value] of this.tileMeshs) { if (value) { value?.destroy(); } } this.tileMeshs.clear(); } } class DoubleQueueNode { constructor(key, val) { this.key = key; this.val = val; } } class LRUCache { constructor(max, onRemove) { this.max = max; this.onRemove = onRemove; this.reset(); } /** * 当前容量 */ get size() { return this.map.size; } reset() { if (this.map) { const iterator = this.map.entries(); for (let i = 0; i < this.map.size; i++) { const [, value] = iterator.next().value; this.onRemove(value.val); } } this.map = /* @__PURE__ */ new Map(); this.head = new DoubleQueueNode(0, 0); this.tail = new DoubleQueueNode(0, 0); this.head.next = this.tail; return this; } clear() { this.reset(); this.onRemove = () => void 0; } has(key) { const node = this.map.get(key); return node !== void 0; } get(key) { const node = this.map.get(key); if (node === void 0) { return null; } this.moveToHead(node); return node.val; } getAndRemove(key) { if (!this.has(key)) { return null; } return this.remove(key); } add(key, value) { let oldValue; const node = this.map.get(key); if (node === void 0) { this.eliminate(); const newNode = new DoubleQueueNode(key, value); const temp = this.head.next; this.head.next = newNode; newNode.next = temp; newNode.pre = this.head; temp.pre = newNode; this.map.set(key, newNode); oldValue = null; } else { this.moveToHead(node); oldValue = node.val; node.val = value; } return oldValue; } remove(key) { const deletedNode = this.map.get(key); if (deletedNode === void 0) { return null; } deletedNode.pre.next = deletedNode.next; deletedNode.next.pre = deletedNode.pre; this.onRemove(deletedNode.val); this.map.delete(key); return deletedNode.val; } /** * 设置最大缓存大小 * @param max */ setMaxSize(max) { this.max = max; while (this.size > this.max) { this.eliminate(); } } // 将节点插入至头部节点 moveToHead(node) { node.pre.next = node.next; node.next.pre = node.pre; const temp = this.head.next; this.head.next = node; node.next = temp; node.pre = this.head; temp.pre = node; } /** * 如果超出缓存限制,那么移除未使用的数据 * @private */ eliminate() { if (this.size < this.max) { return; } const last = this.tail.pre; this.onRemove(last.val); this.map.delete(last.key); last.pre.next = this.tail; this.tail.pre = last.pre; } } function compareTileId(a, b) { const aWrap = Math.abs(a.wrap * 2) - +(a.wrap < 0); const bWrap = Math.abs(b.wrap * 2) - +(b.wrap < 0); return a.overscaledZ - b.overscaledZ || bWrap - aWrap || b.y - a.y || b.x - a.x; } class SourceCache extends EventEmitter { #cache; static { this.maxOverzooming = 10; } static { this.maxUnderzooming = 3; } constructor(id, source) { super(); this.id = id; this.source = source; this.cacheTiles = {}; this.coveredTiles = {}; this.loadedParentTiles = {}; this.#cache = new LRUCache(0, this.unloadTile.bind(this)); } /** * 判断当前 source 瓦片是否全部加载完毕(成功加载或者加载错误) */ loaded() { if (!this.source.loaded()) { return false; } for (const t in this.cacheTiles) { const tile = this.cacheTiles[t]; if (tile.state !== TileState.loaded && tile.state !== TileState.errored) return false; } return true; } /** * 调用 `Source` 的瓦片加载方法 * 具体由各个`Source` 实现 * @param tile * @param callback */ loadTile(tile, callback) { return this.source.loadTile(tile, callback); } /** * 移除已加载的瓦片 * @param tile */ unloadTile(tile) { if (this.source.unloadTile) { return this.source.unloadTile(tile, () => void 0); } } /** * 取消正在加载中的瓦片 * @param tile */ abortTile(tile) { if (this.source.abortTile) { return this.source.abortTile(tile, () => void 0); } } /** * 获取所有的可渲染的瓦片 id 并且排序(从 0 世界向两边排序) */ getRenderableIds() { const renderables = []; for (const id in this.cacheTiles) { if (this._isIdRenderable(id)) renderables.push(this.cacheTiles[id]); } return renderables.map((tile) => tile.tileID).sort(compareTileId).map((tile) => tile.tileKey); } _isIdRenderable(id) { return this.cacheTiles[id] && this.cacheTiles[id].hasData() && !this.coveredTiles[id]; } /** * 获取已经加载的瓦片 */ getVisibleCoordinates() { return this.getRenderableIds().map((id) => this.cacheTiles[id].tileID); } /** * 瓦片加载完成回调 * @param tile * @param id * @param previousState * @param err * @param disableUpdate */ tileLoaded(tile, id, previousState, err, disableUpdate = false) { if (err) { tile.state = TileState.errored; if (err.status !== 404) ; else { this.emit("update"); } return; } tile.timeAdded = Date.now(); if (!disableUpdate) { this.emit("update"); } this.emit("tileLoaded"); if (this.loaded()) { this.emit("tilesLoadEnd"); } } _addTile(tileID) { let tile = this.cacheTiles[tileID.tileKey]; if (tile) return tile; tile = this.#cache.getAndRemove(tileID.tileKey); if (tile) { tile.tileID = tileID; } const cached = Boolean(tile); if (!cached) { tile = new Tile(tileID, { tileSize: this.source.tileSize * tileID.overscaleFactor() }); this.loadTile(tile, this.tileLoaded.bind(this, tile, tileID.tileKey, tile.state)); } if (!tile) return null; tile.uses++; this.cacheTiles[tileID.tileKey] = tile; return tile; } /** * 根据 `tileKey` 移除瓦片 * @param id */ _removeTile(id) { const tile = this.cacheTiles[id]; if (!tile) return; tile.uses--; delete this.cacheTiles[id]; if (tile.uses > 0) return; if (tile.hasData() && tile.state !== TileState.reloading) { this.#cache.add(tile.tileID.tileKey, tile); } else { tile.aborted = true; this.abortTile(tile); this.unloadTile(tile); } } /** * 根据 `TileID` 获取瓦片 * @param tileID */ getTile(tileID) { return this.cacheTiles[tileID?.tileKey]; } /** * 该策略会在内存中保留当前层级的瓦片的所有子瓦片(children),直到一直保留到最大覆盖缩放级别(maxCoveringZoom)为止。 * 简单来说,当当前地图缩放等级超过了当前图层的最大缩放级别时,Mapbox GL JS 会自动加载当前瓦片的所有子瓦片来填充当前视图的空白部分。而 retain any loaded children of ideal tiles up to maxCoveringZoom 这个选项会保留这些子瓦片的缓存,以便在缩放到更高层级时直接使用,而不需要重新加载。 * 举个例子,假设当前地图缩放等级是 10,最大缩放级别是 14,而 maxCoveringZoom 设置为 12。地图将会加载当前缩放级别为 10 的瓦片,并将其所有子瓦片缓存到内存中,包括缩放级别为 11、12、13 的所有瓦片。但是,因为 maxCoveringZoom 设置为 12,所以缩放到 13 级时,只会使用缓存中缩放级别为 11、12 的子瓦片。当缩放到 14 级时,则不再使用缓存,而是重新加载新的瓦片数据。 * 需要注意的是,这个选项可能会占用大量内存,因此在使用时需要根据实际情况进行设置。如果需要优化内存使用,可以将 maxCoveringZoom 设置为一个较小的值,以减少缓存的瓦片数量。 * @param idealTiles * @param zoom * @param maxCoveringZoom * @param retain */ retainLoadedChildren(idealTiles, zoom, maxCoveringZoom, retain) { for (const id in this.cacheTiles) { let tile = this.cacheTiles[id]; if (retain[id] || !tile.hasData() || tile.tileID.overscaledZ <= zoom || tile.tileID.overscaledZ > maxCoveringZoom) continue; let topmostLoadedID = tile.tileID; while (tile && tile.tileID.overscaledZ > zoom + 1) { const parentID = tile.tileID.scaledTo(tile.tileID.overscaledZ - 1); tile = this.cacheTiles[parentID.tileKey]; if (tile && tile.hasData()) { topmostLoadedID = parentID; } } let tileID = topmostLoadedID; while (tileID.overscaledZ > zoom) { tileID = tileID.scaledTo(tileID.overscaledZ - 1); if (idealTiles[tileID.tileKey]) { retain[topmostLoadedID.tileKey] = topmostLoadedID; break; } } } } updateLoadedParentTileCache() { this.loadedParentTiles = {}; for (const tileKey in this.cacheTiles) { const path = []; let parentTile; let currentId = this.cacheTiles[tileKey].tileID; while (currentId.overscaledZ > 0) { if (currentId.tileKey in this.loadedParentTiles) { parentTile = this.loadedParentTiles[currentId.tileKey]; break; } path.push(currentId.tileKey); const parentId = currentId.scaledTo(currentId.overscaledZ - 1); parentTile = this.getLoadedTile(parentId); if (parentTile) { break; } currentId = parentId; } for (const key of path) { this.loadedParentTiles[key] = parentTile; } } } updateRetainedTiles(wrapTiles) { const retain = {}; if (wrapTiles.length === 0) { return retain; } const checked = {}; const minZoom = wrapTiles.reduce((min, id) => Math.min(min, id.overscaledZ), Infinity); const maxZoom = wrapTiles[0].overscaledZ; console.assert(minZoom <= maxZoom); const minCoveringZoom = Math.max(maxZoom - SourceCache.maxOverzooming, this.source.minZoom); const maxCoveringZoom = Math.max(maxZoom + SourceCache.maxUnderzooming, this.source.minZoom); const missingTiles = {}; for (const tileID of wrapTiles) { const tile = this._addTile(tileID); retain[tileID.tileKey] = tileID; if (tile?.hasData()) continue; if (minZoom < this.source.maxZoom) { missingTiles[tileID.tileKey] = tileID; } } this.retainLoadedChildren(missingTiles, minZoom, maxCoveringZoom, retain); for (const tileID of wrapTiles) { let tile = this.cacheTiles[tileID.tileKey]; if (tile.hasData()) continue; if (tileID.z >= this.source.maxZoom) { const childTileLike = tileID.children(this.source.maxZoom)[0]; const childTile = this.getTile(childTileLike); if (!!childTile && childTile.hasData()) { retain[childTileLike.tileKey] = childTileLike; continue; } } else { const children = tileID.children(this.source.maxZoom); if (retain[children[0].tileKey] && retain[children[1].tileKey] && retain[children[2].tileKey] && retain[children[3].tileKey]) continue; } let parentWasRequested = tile.wasRequested(); for (let overscaledZ = tileID.overscaledZ - 1; overscaledZ >= minCoveringZoom; --overscaledZ) { const parentId = tileID.scaledTo(overscaledZ); if (checked[parentId.tileKey]) break; checked[parentId.tileKey] = true; tile = this.getTile(parentId); if (!tile && parentWasRequested) { tile = this._addTile(parentId); } if (tile) { retain[parentId.tileKey] = parentId; parentWasRequested = tile.wasRequested(); if (tile.hasData()) break; } } } return retain; } /** * 获取已经加载的缓存瓦片 * @param tileID * @return {*} */ getLoadedTile(tileID) { const tile = this.cacheTiles[tileID.tileKey]; if (tile && tile.hasData()) { return tile; } return this.#cache.get(tileID.tileKey); } /** * 查找已经加载的父级瓦片 * @param tileID * @param minCoveringZoom */ findLoadedParent(tileID, minCoveringZoom) { if (tileID.tileKey in this.loadedParentTiles) { const parent = this.loadedParentTiles[tileID.tileKey]; if (parent && parent.tileID.overscaledZ >= minCoveringZoom) { return parent; } else { return null; } } for (let z = tileID.overscaledZ - 1; z >= minCoveringZoom; z--) { const parentTileID = tileID.scaledTo(z); const tile = this.getLoadedTile(parentTileID); if (tile) { return tile; } } } /** * 更新当前的缓存大小 */ updateCacheSize() { const tileSize = this.source.tileSize; const { width, height } = this.source.renderer.size; const widthInTiles = Math.ceil((width || 4 * tileSize) / tileSize) + 1; const heightInTiles = Math.ceil((height || 4 * tileSize) / tileSize) + 1; const approxTilesInView = widthInTiles * heightInTiles; const commonZoomRange = 5; const viewDependentMaxSize = Math.floor(approxTilesInView * commonZoomRange); const maxSize = typeof this.source.options.maxTileCacheSize === "number" ? Math.max(this.source.options.maxTileCacheSize, viewDependentMaxSize) : viewDependentMaxSize; this.#cache.setMaxSize(maxSize); } update(wrapTiles) { this.coveredTiles = {}; let tiles = wrapTiles; this.updateCacheSize(); if (this.source.hasTile) { tiles = wrapTiles.filter((coord) => this.source.hasTile(coord)); } const retain = this.updateRetainedTiles(tiles); if (tiles.length !== 0) { const parentsForFading = {}; const ids = Object.keys(retain); for (const id of ids) { const tileID = retain[id]; const tile = this.cacheTiles[id]; if (!tile) continue; const parentTile = this.findLoadedParent( tileID, Math.max(tileID.overscaledZ - SourceCache.maxOverzooming, this.source.minZoom) ); if (parentTile) { this._addTile(parentTile.tileID); parentsForFading[parentTile.tileID.tileKey] = parentTile.tileID; } } for (const id in parentsForFading) { if (!retain[id]) { this.coveredTiles[id] = true; retain[id] = parentsForFading[id]; } } } this.emit("tilesLoadStart", { retain }); const remove = keysDifference(this.cacheTiles, retain); for (const tileKey of remove) { this._removeTile(tileKey); } this.updateLoadedParentTileCache(); const currentLength = Object.keys(this.cacheTiles).filter((k) => this.cacheTiles[k]?.wasRequested()).length; const retainLength = Object.keys(retain).length; if (currentLength < retainLength) { this.emit("tilesLoading", { progress: currentLength / retainLength }); } } /** * 重载当前视野内的瓦片(需要移除缓存) */ reload() { this.#cache.reset(); for (const key in this.cacheTiles) { this._reloadTile(key, TileState.reloading); } } _reloadTile(id, state) { const tile = this.cacheTiles[id]; if (!tile) return; if (tile.state !== TileState.loading) { tile.state = state; } this.loadTile(tile, this.tileLoaded.bind(this, tile, id, state)); } clearTiles() { for (const id in this.cacheTiles) { this._removeTile(id); } this.#cache.reset(); } /** * 查找覆盖 queryGeometry 的瓦片 * @param {QueryGeometry} queryGeometry * @param {boolean} [visualizeQueryGeometry=false] * @param {boolean} use3DQuery * @returns * @private */ tilesIn(queryGeometry) { const tileResults = []; for (const tileID in this.cacheTiles) { const tile = this.cacheTiles[tileID]; const tilesToCheck = [0]; for (const wrap of tilesToCheck) { const tileResult = queryGeometry.containsTile(this.source, tile, wrap); if (tileResult) { tileResults.push(tileResult); } } } return tileResults; } destroy() { for (const id in this.cacheTiles) { this._removeTile(id); } this.#cache.reset(); } } const URL_PATTERN = /\{ *([\w_]+) *\}/g; function formatUrl(url, data) { return url.replace(URL_PATTERN, (str, key) => { let value = data[key]; if (value === void 0) { throw new Error(`No value provided for variable ${str}`); } else if (typeof value === "function") { value = value(data); } return value; }); } class TileSource extends EventEmitter { constructor(id, options) { super(); this.roundZoom = false; this.#loaded = false; this.#tileWorkers = /* @__PURE__ */ new Map(); this.id = id; this.type = LayerSourceType.tile; this.minZoom = options.minZoom ?? 0; this.maxZoom = options.maxZoom ?? 22; this.roundZoom = Boolean(options.roundZoom); this.scheme = options.scheme || "xyz"; this.tileSize = options.tileSize || 512; this.tileBounds = options.tileBounds; this.wrapX = Boolean(options.wrapX); const decodeType = options.decodeType || DecodeType.image; const maxTileCacheSize = options.maxTileCacheSize; this.options = { ...options, decodeType, maxTileCacheSize, type: this.type }; this.#sourceCache = new SourceCache(this.id, this); } #loaded; #sourceCache; #tileWorkers; get sourceCache() { return this.#sourceCache; } onAdd(layer, cb) { this.layer = layer; this.load(cb); } update(data, clear = true) { this.options.url = data.url; this.reload(clear); return this; } prepare(renderer, dispatcher, parseOptions) { this.renderer = renderer; this.dispatcher = dispatcher; this.parseOptions = parseOptions; } /** * 兼容 TileJSON 加载,需要具体实现 * @param cb */ load(cb) { this.#loaded = true; this.url = this.options.url; if (cb) { cb(null); } } loaded() { return this.#loaded; } reload(clear) { this.#loaded = false; this.load(() => { if (clear) { this.#sourceCache.clearTiles(); } else { this.#sourceCache.reload(); } this.layer?.update(); }); } hasTile(coord) { return !this.tileBounds || containTile(this.tileBounds, coord.getTileBounds()); } getFadeTime() { return 0; } getUrl(x, y, z) { const { subdomains } = this.options; let domain = ""; if (subdomains && Array.isArray(subdomains) && subdomains.length > 0) { const { length } = subdomains; let s = (x + y) % length; if (s < 0) { s = 0; } domain = subdomains[s]; } const data = { x, y, z, s: domain }; if (Array.isArray(this.url)) { if (this.url.length > 2) { console.warn( `[TileSource]: Only supports up to two urls, Now there are more than two urls-${this.url.toString()}, and only the first two are selected by default` ); } return this.url.filter((item, index) => index < 2).map((u) => formatUrl(u, data)); } return formatUrl(this.url, data); } asyncActor(tile, url) { return new Promise((resolve, reject) => { const id = `${tile.tileID.tileKey}-${url}`; tile.actor.send( "loadData", { url: resolveURL(url), cancelId: id, type: "arrayBuffer", decodeType: this.options.decodeType }, (e, data) => { if (e) { return reject(e); } resolve(data); } ); tile.request.set(id, url); }); } getTileUrl(tileID) { const z = tileID.z; const x = tileID.x; const y = this.scheme === "tms" ? Math.pow(2, tileID.z) - tileID.y - 1 : tileID.y; const url = this.getUrl(x, y, z); let urls = url; if (index.isString(url)) { urls = [url]; } return urls; } loadTile(tile, callback) { try { if (!tile.actor || tile.state === TileState.reloading) { const urls = this.getTileUrl(tile.tileID); const key = urls.join(","); this.#tileWorkers.set(key, this.#tileWorkers.get(key) || this.dispatcher.getActor()); tile.actor = this.#tileWorkers.get(key); const p = []; for (let i = 0; i < urls.length; i++) { p.push(this.asyncActor(tile, urls[i])); } Promise.all(p).then((data) => { tile.request.clear(); if (tile.aborted) { tile.state = TileState.unloaded; return callback(null); } if (!data) return callback(null); data.forEach((d, index) => { tile.setTextures(this.renderer, index, d, this.parseOptions, this.options); }); tile.state = TileState.loaded; callback(null); }).catch((e) => { tile.state = TileState.errored; console.log(e); }); } else if (tile.state === TileState.loading) { tile.reloadCallback = callback; } } catch (e) { tile.state = TileState.errored; return callback(e); } } abortTile(tile, callback) { if (tile.request) { if (tile.request.size > 0 && tile.actor) { const iterator = tile.request.entries(); for (let i = 0; i < tile.request.size; i++) { const [id, url] = iterator.next().value; if (id) { tile.actor.send( "cancel", { url, cancelId: id }, (err) => { if (err) { tile.state = TileState.unloaded; } } ); } } } tile.request.clear(); } else { tile.state = TileState.unloaded; } callback(); } unloadTile(tile, callback) { if (tile.actor) ; } destroy() { this.layer = null; this.#loaded = false; this.#tileWorkers.clear(); this.#sourceCache.clear(); } } class ImageSource extends EventEmitter { constructor(id, options) { super(); this.roundZoom = false; this.#loaded = false; this.#tileWorkers = /* @__PURE__ */ new Map(); this.id = id; this.type = LayerSourceType.image; this.minZoom = 0; this.maxZoom = 22; this.roundZoom = false; this.tileSize = 512; this.coordinates = options.coordinates; this.wrapX = Boolean(options.wrapX); this.url = options.url; const decodeType = options.decodeType || DecodeType.image; this.options = { ...options, decodeType, type: this.type }; this.#sourceCache = new SourceCache(this.id, this); } #loaded; #sourceCache; #tileWorkers; get sourceCache() { return this.#sourceCache; } onAdd(layer, cb) { this.layer = layer; this.load(cb); } prepare(renderer, dispatcher, parseOptions) { this.renderer = renderer; this.dispatcher = dispatcher; this.parseOptions = parseOptions; } update(data, clear = true) { this.options.url = data.url; this.reload(clear); } updateImage(options, clear = true) { this.options = { ...this.options, ...options }; this.reload(clear); } setCoordinates(coordinates) { this.coordinates = coordinates; this.reload(false); } asyncActor(tile, url) { return new Promise((resolve, reject) => { const id = `${tile.tileID.tileKey}-${url}`; tile.actor.send( "loadData", { url: resolveURL(url), cancelId: id, type: "arrayBuffer", decodeType: this.options.decodeType }, (e, data) => { if (e) { return reject(e); } resolve(data); } ); tile.request.set(id, url); }); } /** * 兼容 TileJSON 加载,需要具体实现 * @param cb */ load(cb) { this.#loaded = true; this.url = this.options.url; if (cb) { cb(null); } } loaded() { return this.#loaded; } reload(clear) { this.#loaded = false; this.load(() => { if (clear) { this.#sourceCache.clearTiles(); } else { this.#sourceCache.reload(); } this.layer?.update(); }); } getTileUrl(tileID) { let urls = this.url; if (index.isString(this.url)) { urls = [this.url]; } return urls; } loadTile(tile, callback) { try { if (!tile.actor || tile.state === TileState.reloading) { const urls = this.getTileUrl(tile.tileID); const key = urls.join(","); this.#tileWorkers.set(key, this.#tileWorkers.get(key) || this.dispatcher.getActor()); tile.actor = this.#tileWorkers.get(key); const p = []; for (let i = 0; i < urls.length; i++) { p.push(this.asyncActor(tile, urls[i])); } Promise.all(p).then((data) => { tile.request.clear(); if (tile.aborted) { tile.state = TileState.unloaded; return callback(null); } if (!data) return callback(null); data.forEach((d, index) => { tile.setTextures(this.renderer, index, d, this.parseOptions, this.options); }); tile.state = TileState.loaded; callback(null); }).catch((e) => { tile.state = TileState.errored; console.log(e); }); } else if (tile.state === TileState.loading) { tile.reloadCallback = callback; } else { } } catch (e) { tile.state = TileState.errored; return callback(e); } } hasTile(coord) { return true; } getFadeTime() { return 0; } abortTile(tile, callback) { if (tile.request) { if (tile.request.size > 0 && tile.actor) { const iterator = tile.request.entries(); for (let i = 0; i < tile.request.size; i++) { const [id, url] = iterator.next().value; if (id) { tile.actor.send( "cancel", { url, cancelId: id }, (err) => { if (err) { tile.state = TileState.unloaded; } } ); } } } tile.request.clear(); } else { tile.state = TileState.unloaded; } callback(); } // eslint-disable-next-line unloadTile(tile, cb) { } destroy() { this.layer = null; this.#loaded = false; this.#tileWorkers.clear(); this.#sourceCache.clear(); } } class TrackManger { constructor() { this.tracks = /* @__PURE__ */ new Set(); this.run = this.run.bind(this); this.raf = new Raf(this.run); } add(track) { if (!this.tracks.has(track)) { this.tracks.add(track); this.raf.start(); } } run(time) { this.tracks.forEach((t) => { t.tick(time); }); } remove(track) { if (this.tracks.has(track)) { this.tracks.delete(track); } if (this.tracks.size === 0) { this.raf.stop(); } } } let tm = null; function getTrackManger() { if (!tm) { tm = new TrackManger(); } return tm; } const defaultTrackOptions = { duration: 1e3, autoplay: true, repeat: true, delay: 0, endDelay: 0, track: (p) => void 0 // eslint-disable-line }; const trackManger = getTrackManger(); class Track extends EventEmitter { #playing = false; #state = 0; #elapsedTime = -1; #lastTime = -1; #options; constructor(options) { super(); this.#options = { ...defaultTrackOptions, ...options }; if (this.#options.autoplay) { this.play(); } } /** * 获取当前 Track 的状态 */ get state() { return this.#state; } /** * 获取总的过渡时间 */ get totalDuration() { return this.#options.delay + this.#options.duration + this.#options.endDelay; } get elapsedTime() { return this.#elapsedTime; } get totalPosition() { return Math.max(0, Math.min(1, this.#elapsedTime / this.totalDuration)); } /** * 是否在播放 */ get isPlaying() { return this.#state === 1; } /** * 是否暂停 */ get isPaused() { return this.#state === 2; } /** * 是否处于激活状态 */ get isActive() { return this.isPlaying || this.isPaused; } /** * 获取当前 Track 的 cursor 位置 */ get position() { if (this.#elapsedTime < this.#options.delay) { return 0; } if (this.#elapsedTime >= this.#options.delay + this.#options.duration) { return 1; } return Math.max(0, Math.min(1, (this.#elapsedTime - this.#options.delay) / this.#options.duration)); } /** * 开始播放 */ play() { this.#playing = true; this.#state = 1; this.advance(0); trackManger.add(this); } /** * 暂停 */ pause() { if (this.#state === 1) { this.#state = 2; } } /** * 继续播放 */ resume() { if (this.#state === 2) { this.#state = 1; } } /** * 停止 */ stop() { this.#playing = false; this.#state = 3; trackManger.remove(this); } /** * 重新开始 */ restart() { this.#elapsedTime = 0; trackManger.add(this); } /** * 重置 */ reset() { if (this.#state === 1) { this.stop(); } else { this.advance(0); } } /** * 在播放和暂停状态切换 */ toggle() { if (this.#playing) { if (this.isPlaying) { this.pause(); } else { this.resume(); } } } /** * 步进 * @param position * @param e */ advance(position, e = true) { const p = index.clamp(position, 0, 1); this.#elapsedTime = e ? this.totalDuration * p : this.#options.delay + this.#options.duration * p; this.#options?.track?.(this.position); this.emit("track", { position: this.position }); } tick(time) { if (this.#lastTime < 0) { this.#lastTime = time; } const lastTime = this.#lastTime; this.#lastTime = time; if (this.#state !== 1) return; const delta = time - lastTime; this.#elapsedTime += delta; this.#elapsedTime = Math.min(this.#elapsedTime, this.totalDuration); if (this.totalPosition === 1) { this.advance(this.totalPosition); this.#options.repeat ? this.restart() : this.stop(); } else { this.advance(this.totalPosition); } } } const sourceImpl = { tile: TileSource, image: ImageSource }; function generateKey(url) { let urls = []; if (index.isString(url)) { urls = [url]; } return urls.join(","); } class TimelineSource extends EventEmitter { constructor(id, options) { super(); this.roundZoom = false; this.#loaded = false; this.#fadeTime = 0; this.#cache = /* @__PURE__ */ new Map(); this.id = id; this.type = LayerSourceType.timeline; this.minZoom = options.minZoom ?? 0; this.maxZoom = options.maxZoom ?? 22; this.roundZoom = Boolean(options.roundZoom); const scheme = options.scheme || "xyz"; this.tileSize = options.tileSize || 512; this.tileBounds = options.tileBounds; this.wrapX = Boolean(options.wrapX); if (options.sourceType === LayerSourceType.image && !options.coordinates) { throw new Error("ImageSource must provide `coordinates`"); } this.coordinates = options.coordinates; this.intervals = options.intervals; const decodeType = options.decodeType || DecodeType.image; const maxTileCacheSize = options.maxTileCacheSize; this.options = { ...defaultTrackOptions, ...options, decodeType, maxTileCacheSize, wrapX: this.wrapX, type: this.type }; const current = this.intervals[0]; this.#index = 0; this.animate = this.animate.bind(this); this.tilesLoadEnd = this.tilesLoadEnd.bind(this); const config = {}; if (options.sourceType === LayerSourceType.image) { Object.assign(config, { url: current.url, coordinates: this.coordinates, maxTileCacheSize: this.options.maxTileCacheSize, minZoom: this.minZoom, maxZoom: this.maxZoom, decodeType }); } else if (options.sourceType === LayerSourceType.tile) { Object.assign(config, { url: current.url, subdomains: this.options.subdomains, minZoom: this.minZoom, maxZoom: this.maxZoom, tileSize: this.tileSize, roundZoom: this.roundZoom, tileBounds: this.tileBounds, maxTileCacheSize: this.options.maxTileCacheSize, scheme, decodeType }); } else { throw new Error("\u4E0D\u652F\u6301\u7684\u6570\u636E\u6E90\u7C7B\u578B\uFF01"); } this.#current = new sourceImpl[options.sourceType](`${this.id}_current`, config); this.#next = new sourceImpl[options.sourceType](`${this.id}_next`, config); const currentLoadTile = this.#current.loadTile; const nextLoadTile = this.#next.loadTile; const that = this; function wrapCurrentLoadTile(tile, callback) { const key = `${tile.tileID.tileKey}-${generateKey(this.url)}`; const cacheTile = that.#cache.get(key); if (cacheTile) { tile.copy(cacheTile); callback(null, true); } else { currentLoadTile.call(this, tile, (err, data) => { if (!err && !that.#cache.has(key) && tile.state === TileState.loaded) { that.#cache.set(key, tile); } callback(err, data); }); } } function wrapNextLoadTile(tile, callback) { const key = `${tile.tileID.tileKey}-${generateKey(this.url)}`; const cacheTile = that.#cache.get(key); if (cacheTile) { tile.copy(cacheTile); callback(null, true); } else { nextLoadTile.call(this, tile, (err, data) => { if (!err && !that.#cache.has(key) && tile.state === TileState.loaded) { that.#cache.set(key, tile); } callback(err, data); }); } } this.#current.loadTile = wrapCurrentLoadTile; this.#next.loadTile = wrapNextLoadTile; this.#current.sourceCache.on("tilesLoadEnd", this.tilesLoadEnd); this.#next.sourceCache.on("tilesLoadEnd", this.tilesLoadEnd); } #loaded; #sourceCache; #current; #next; #index; #fadeTime; #track; #cache; get track() { return this.#track; } get privateType() { return this.options.sourceType; } get cache() { return this.#cache; } get source() { return [this.#current, this.#next]; } get sourceCache() { return [this.#current?.sourceCache, this.#next?.sourceCache].filter(Boolean); } onAdd(layer) { this.layer = layer; if (this.#current) { this.#current.onAdd(this.layer, (error) => { if (!error) { if (this.#next) { this.#next.onAdd(this.layer, (err) => { if (!err) { this.load(); } }); } } }); } } prepare(renderer, dispatcher, parseOptions) { this.renderer = renderer; this.dispatcher = dispatcher; this.parseOptions = parseOptions; if (this.#current) { this.#current.prepare(renderer, dispatcher, parseOptions); } if (this.#next) { this.#next.prepare(renderer, dispatcher, parseOptions); } } getFadeTime() { return this.#fadeTime; } tilesLoadEnd() { this.resume(); } animate({ position }) { const len = this.intervals.length; const lastIndex = this.#index; this.#index = position * index.clamp(len - 1, 0, Infinity); const diff = Math.floor(this.#index) - Math.floor(lastIndex); if (diff > 0 || diff < 0) { if (!this.#current?.sourceCache.loaded() || !this.#next?.sourceCache.loaded()) { this.pause(); } else { this.#fadeTime = 0; [this.#current, this.#next] = [this.#next, this.#current]; this.pause(); const item = this.intervals[index.clamp(Math.floor(this.#index), 0, len - 1)]; this.#next.update(item, true); } } else { this.#fadeTime = this.#index % 1; } if (this.layer) { this.layer.onTileLoaded(); } this.emit("update", { position, index: this.#index, clampIndex: index.clamp(Math.floor(this.#index), 0, len - 1) }); } play() { this.#track.play(); this.emit("play", { position: this.#track.position }); } pause() { this.#track.pause(); this.emit("pause", { position: this.#track.position }); } resume() { this.#track.resume(); this.emit("resume", { position: this.#track.position }); } stop() { this.#track.stop(); this.emit("stop", { position: this.#track.position }); } restart() { this.#track.restart(); this.emit("restart", { position: this.#track.position }); } load(cb) { this.#loaded = true; this.#track = new Track({ duration: this.options.duration * index.clamp(this.intervals.length - 1, 0, Infinity), endDelay: this.options.endDelay, repeat: this.options.repeat, autoplay: this.options.autoplay }); this.#track.on("track", this.animate); this.layer?.update(); if (cb) { cb(null); } this.emit("loaded", { position: this.#track.position }); } loaded() { return this.#loaded; } destroy() { this.layer = null; this.#loaded = false; this.#track.off("track", this.animate); if (this.#sourceCache && Array.isArray(this.#sourceCache)) { this.#sourceCache.forEach((s) => { s.clear(); }); } this.emit("destroy"); } } const configDeps = wgw$1.configDeps; class BaseLayer extends L__namespace.Layer { constructor(id, data, options) { super(id, data, options); } initialize(id, data, options) { if (!id) { throw Error("layer id must be specified"); } this._layerId = id; L__namespace.Util.setOptions(this, options); this.devicePixelRatio = this.options.devicePixelRatio || // @ts-ignore 忽略错误 (window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI); } _createCanvas(id, zIndex) { const canvas = createCanvas(this._width, this._height, this.devicePixelRatio); canvas.id = String(id); const panes = this._map.getPanes(); if (panes && panes.overlayPane) { panes.overlayPane.appendChild(canvas); } return canvas; } _reset() { const topLeft = this._map.containerPointToLayerPoint([0, 0]); L__namespace.DomUtil.setPosition(this.canvas, topLeft); this._redraw(); } _onResize(resizeEvent) { this.canvas.style.width = resizeEvent.newSize.x + "px"; this.canvas.style.height = resizeEvent.newSize.y + "px"; this._width = resizeEvent.newSize.x; this._height = resizeEvent.newSize.y; this._resizeCanvas(this.devicePixelRatio); } _zoomStart() { this._moveStart(); } _moveStart() { if (!this._updating) { this._updating = true; } } _animateZoom(event) { const scale = this._map.getZoomScale(event.zoom, this._map.getZoom()); const offset = this._map._latLngToNewLayerPoint(this._map.getBounds().getNorthWest(), event.zoom, event.center); L__namespace.DomUtil.setTransform(this.canvas, offset, scale); } _resizeCanvas(scale) { this.canvas.width = this._width * scale; this.canvas.height = this._height * scale; } _redraw() { this._render(); } _render() { } project(coordinate) { const pixel = this._map.latLngToContainerPoint(new L__namespace.LatLng(coordinate[1], coordinate[0])); return [pixel.x * this.devicePixelRatio, pixel.y * this.devicePixelRatio]; } unproject(pixel) { const coordinates = this._map.containerPointToLatLng(new L__namespace.Point(pixel[0], pixel[1])); return [coordinates.lng, coordinates.lat]; } intersectsCoordinate(coordinate) { const bounds = this._map.getBounds(); return bounds.contains(L__namespace.latLng(coordinate[1], coordinate[0])); } onAdd(map) { this._map = map; const size = map.getSize(); this._width = size.x; this._height = size.y; this.canvas = this._createCanvas(this._layerId, this.options.zIndex || 1); const animated = this._map.options.zoomAnimation && L__namespace.Browser.any3d; L__namespace.DomUtil.addClass(this.canvas, "leaflet-zoom-" + (animated ? "animated" : "hide")); this._map.on(this.getEvents(), this); this._resetView(); this._render(); return this; } _resetView(e) { } onMoveEnd() { this._reset(); } onRemove() { const panes = this._map.getPanes(); if (panes && panes.overlayPane) { panes.overlayPane.removeChild(this.canvas); } this._map.off(this.getEvents(), this); this.canvas = null; return this; } getEvents() { const events = { resize: this._onResize, viewreset: this._render, moveend: this.onMoveEnd, // movestart: this._moveStart, zoomstart: this._render, zoomend: this._render // zoomanim: undefined, }; if (this._map.options.zoomAnimation && L__namespace.Browser.any3d) { events.zoomanim = this._animateZoom; } return events; } } class WindLayer extends BaseLayer { initialize(id, data, options) { super.initialize(id, data, options); this.field = void 0; this.pickWindOptions(); if (data) { this.setData(data, options.fieldOptions); } } _redraw() { this._render(); } _render() { const opt = this.getWindOptions(); if (!this.wind && this._map) { const ctx = this.canvas.getContext("2d"); const data = this.getData(); this.wind = new WindCore(ctx, opt, data); this.wind.project = this.project.bind(this); this.wind.unproject = this.unproject.bind(this); this.wind.intersectsCoordinate = this.intersectsCoordinate.bind(this); this.wind.postrender = () => { }; } this.wind.prerender(); this.wind.render(); } onRemove() { if (this.wind) { this.wind.stop(); this.wind = null; } return super.onRemove(); } pickWindOptions() { Object.keys(defaultOptions$2).forEach((key) => { if (key in this.options) { if (this.options.windOptions === void 0) { this.options.windOptions = {}; } this.options.windOptions[key] = this.options[key]; } }); } /** * get wind layer data */ getData() { return this.field; } /** * set layer data * @param data * @param options * @returns {WindLayer} */ setData(data, options = {}) { if (data && data.checkFields && data.checkFields()) { this.field = data; } else if (isArray(data)) { this.field = formatData(data, options); } else { console.error("Illegal data"); } if (this.field) { this?.wind?.updateData(this.field); } return this; } setWindOptions(options) { const beforeOptions = this.options.windOptions || {}; this.options = assign(this.options, { windOptions: assign(beforeOptions, options || {}) }); if (this.wind) { const windOptions = this.options.windOptions; this.wind.setOptions(windOptions); this.wind.prerender(); } } getWindOptions() { return this.options.windOptions || {}; } } var geojsonRewind = rewind; function rewind(gj, outer) { var type = gj && gj.type, i; if (type === 'FeatureCollection') { for (i = 0; i < gj.features.length; i++) rewind(gj.features[i], outer); } else if (type === 'GeometryCollection') { for (i = 0; i < gj.geometries.length; i++) rewind(gj.geometries[i], outer); } else if (type === 'Feature') { rewind(gj.geometry, outer); } else if (type === 'Polygon') { rewindRings(gj.coordinates, outer); } else if (type === 'MultiPolygon') { for (i = 0; i < gj.coordinates.length; i++) rewindRings(gj.coordinates[i], outer); } return gj; } function rewindRings(rings, outer) { if (rings.length === 0) return; rewindRing(rings[0], outer); for (var i = 1; i < rings.length; i++) { rewindRing(rings[i], !outer); } } function rewindRing(ring, dir) { var area = 0, err = 0; for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) { var k = (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]); var m = area + k; err += Math.abs(area) >= Math.abs(k) ? area - m + k : k - m + area; area = m; } if (area + err >= 0 !== !!dir) ring.reverse(); } var rewind$1 = /*@__PURE__*/getDefaultExportFromCjs(geojsonRewind); const { clamp } = index; const earthRadius = 63710088e-1; const earthCircumference = 2 * Math.PI * earthRadius; function circumferenceAtLatitude(latitude) { return earthCircumference * Math.cos(latitude * Math.PI / 180); } function mercatorXfromLng(lng) { return (180 + lng) / 360; } function mercatorYfromLat(lat) { return (180 - 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360))) / 360; } function mercatorZfromAltitude(altitude, lat) { return altitude / circumferenceAtLatitude(lat); } function lngFromMercatorX(x, wrap = 0) { return x * 360 - 180 + wrap * 360; } function latFromMercatorY(y) { const y2 = 180 - y * 360; return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90; } const MAX_MERCATOR_LATITUDE = 85.051129; function fromLngLat(lngLatLike, altitude = 0) { const lat = clamp(lngLatLike.lat, -MAX_MERCATOR_LATITUDE, MAX_MERCATOR_LATITUDE); return { x: mercatorXfromLng(lngLatLike.lng), y: mercatorYfromLat(lat), z: mercatorZfromAltitude(altitude, lat) }; } function getCoordinatesCenterTileID(coords) { let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; for (const coord of coords) { minX = Math.min(minX, coord.x); minY = Math.min(minY, coord.y); maxX = Math.max(maxX, coord.x); maxY = Math.max(maxY, coord.y); } const dx = maxX - minX; const dy = maxY - minY; const dMax = Math.max(dx, dy); const zoom = Math.max(0, Math.floor(-Math.log(dMax) / Math.LN2)); const tilesAtZoom = Math.pow(2, zoom); return { z: zoom, x: Math.floor((minX + maxX) / 2 * tilesAtZoom), y: Math.floor((minY + maxY) / 2 * tilesAtZoom), extent: [minX, minY, maxX, maxY] }; } const { degToRad, radToDeg } = index; highPrecision(true); identity([]); class CameraSync { constructor(viewState, cameraType, scene) { this.worldMatrix = new Matrix4(); this.mercatorMatrix = new Matrix4(); this.labelPlaneMatrix = new Matrix4(); this.glCoordMatrix = new Matrix4(); const { width, height } = viewState; const fov = radToDeg(Math.atan(3 / 4)); const nearZ = 0.1; const farZ = 1e21; this.viewState = viewState; this.scene = scene; this.scene.matrixAutoUpdate = false; this.scene.worldMatrixNeedsUpdate = true; this.camera = cameraType === "orthographic" ? new OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, nearZ, farZ) : new PerspectiveCamera(fov, width / height, nearZ, farZ); this.camera.matrixAutoUpdate = false; this.camera.position.z = 600; this.setup(); } setup() { const { width, height, fov } = this.viewState; const maxPitch = degToRad(this.viewState.maxPitch); this.camera.aspect = width / height; this.halfFov = fov / 2; this.cameraToCenterDistance = 0.5 / Math.tan(this.halfFov) * height; this.acuteAngle = Math.PI / 2 - maxPitch; this.update(); } update() { const { width, height, elevation, _horizonShift, worldSize } = this.viewState; const center = this.viewState.getCenter(); const pitch = this.viewState.getPitch(); const pitchRad = degToRad(pitch); const bearing = this.viewState.getBearing(); const fovRad = this.viewState.getFovRad(); const cameraPosition = this.viewState.getCameraPosition(); const halfFov = fovRad / 2; const pitchAngle = Math.cos(Math.PI / 2 - pitchRad); const groundAngle = Math.PI / 2 + pitchRad; this.cameraToCenterDistance = 0.5 / Math.tan(halfFov) * height; const point = this.viewState.project(center); const rotateMap = new Matrix4().fromRotationZ(Math.PI); const scale = new Matrix4().fromScale(new Vector3(-worldSize, worldSize, worldSize)); const translateMap = new Matrix4().fromTranslation(new Vector3(-point.x, point.y, 0)); const nz = height / 50; const nearZ = Math.max(nz * pitchAngle, nz); const fovAboveCenter = fovRad * (0.5 + this.viewState.centerOffset().y / height); const pixelsPerMeter = mercatorZfromAltitude(1, center.lat) * worldSize || 1; const minElevationInPixels = elevation ? elevation.getMinElevationBelowMSL() * pixelsPerMeter : 0; const cameraToSeaLevelDistance = (cameraPosition[2] * worldSize - minElevationInPixels) / Math.cos(pitchRad); const topHalfSurfaceDistance = Math.sin(fovAboveCenter) * cameraToSeaLevelDistance / Math.sin(index.clamp(Math.PI - groundAngle - fovAboveCenter, 0.01, Math.PI - 0.01)); const furthestDistance = pitchAngle * topHalfSurfaceDistance + cameraToSeaLevelDistance; const horizonDistance = cameraToSeaLevelDistance * (1 / _horizonShift); const farZ = Math.min(furthestDistance * 1.01, horizonDistance); this.mercatorMatrix = new Matrix4().scale(new Vector3(worldSize, worldSize, worldSize / pixelsPerMeter)); const may = new Matrix4().fromTranslation(new Vector3(0, 0, this.cameraToCenterDistance)); this.labelPlaneMatrix = new Matrix4(); const m = new Matrix4(); m.scale(new Vector3(1, -1, 1)); m.translate(new Vector3(-1, -1, 0)); m.scale(new Vector3(2 / width, 2 / height, 1)); this.glCoordMatrix = m; this.camera.aspect = width / height; this.cameraTranslateZ = this.cameraToCenterDistance; if (this.camera instanceof OrthographicCamera) { this.camera.projectionMatrix.orthographic(-width / 2, width / 2, height / 2, -height / 2, nearZ, farZ); } else { this.camera.projectionMatrix.perspective(fovRad, width / height, nearZ, farZ); } const cameraWorldMatrix = new Matrix4().premultiply(may).premultiply(new Matrix4().fromRotationX(pitchRad)).premultiply(new Matrix4().fromRotationZ(-degToRad(bearing))); if (elevation) cameraWorldMatrix.elements[14] = cameraPosition[2] * worldSize; this.camera.worldMatrix.copy(cameraWorldMatrix); this.camera.updateMatrixWorld(); if (this.scene) { this.scene.localMatrix = new ProjectionMatrix().premultiply(rotateMap).premultiply(scale).premultiply(translateMap); } } } function getTileProjBounds(tileID) { const numTiles = 1 << tileID.z; return { left: tileID.wrapedX / numTiles, top: tileID.wrapedY / numTiles, right: (tileID.wrapedX + 1) / numTiles, bottom: (tileID.wrapedY + 1) / numTiles }; } function getTileBounds(tileID) { const { z, x, y } = tileID; const wrap = tileID.wrap; const numTiles = 1 << z; const leftLng = lngFromMercatorX(x / numTiles, wrap); const rightLng = lngFromMercatorX((x + 1) / numTiles, wrap); const topLat = latFromMercatorY(y / numTiles); const bottomLat = latFromMercatorY((y + 1) / numTiles); return [leftLng, bottomLat, rightLng, topLat]; } function getExtent(map) { const bounds = map?.getBounds(); const southWest = bounds.getSouthWest(); const northEast = bounds.getNorthEast(); const [xmin, ymin, xmax, ymax] = [southWest.lng, southWest.lat, northEast.lng, northEast.lat]; const minY = Math.max(ymin, -MAX_MERCATOR_LATITUDE); const maxY = Math.min(ymax, MAX_MERCATOR_LATITUDE); const p0 = fromLngLat({ lng: xmin, lat: maxY }); const p1 = fromLngLat({ lng: xmax, lat: minY }); return [p0.x, p0.y, p1.x, p1.y]; } function getClampZoom(options) { const z = options.zoom; if (void 0 !== options.minzoom && z < options.minzoom) { return options.minzoom; } if (void 0 !== options.maxzoom && options.maxzoom < z) { return options.maxzoom; } return z; } class ViewState { constructor() { this.tileSize = 512; this.maxPitch = 60; this._horizonShift = 0.1; } /** * 获取 gl 宽度 */ get width() { return this._width; } /** * 获取 gl 高度 */ get height() { return this._height; } get fov() { return this.getFovRad() / Math.PI * 180; } get worldSize() { const scale = Math.pow(2, this.zoom - 1); return this.tileSize * scale; } getCenter() { return this._center; } getPitch() { return 0; } getBearing() { return 0; } getFovRad() { return 0.6435011087932844; } getCameraPosition() { return [0, 0, 0]; } centerOffset() { return { x: 0, y: 0 }; } project(lnglat) { const lat = index.clamp(lnglat.lat, -MAX_MERCATOR_LATITUDE, MAX_MERCATOR_LATITUDE); const x = mercatorXfromLng(lnglat.lng); const y = mercatorYfromLat(lat); return { x: x * this.worldSize, y: y * this.worldSize, z: 0 }; } get pixelsPerMeter() { return mercatorZfromAltitude(1, this._center.lat) * this.worldSize; } unproject(p) { const lng = lngFromMercatorX(p[0]); const lat = latFromMercatorY(p[1]); return [lng, lat]; } update(state) { this._center = state.center; this._width = state.width; this._height = state.height; this.zoom = state.zoom; } } function wrapTile(x, range, includeMax) { const max = range[1]; const min = range[0]; const d = max - min; return { x: x === max && includeMax ? x : ((x - min) % d + d) % d + min, wrap: Math.floor(x / max) }; } class WebglLayer extends BaseLayer { initialize(id, source, options) { super.initialize(id, source, options); this.viewState = new ViewState(); this._currentTiles = []; this._unLimitTiles = []; this.source = source; } _resizeCanvas(scale) { super._resizeCanvas(scale); if (this.renderer) { this.renderer.setSize(this._width, this._height); } if (this.layer) { this.layer.resize(this._width, this._height); } this._render(); } get camera() { return this.sync.camera; } getTileSize() { const s = index.isNumber(this.source.tileSize) ? this.source.tileSize : this.source.tileSize?.[0] || 512; return new L__namespace.Point(s, s); } _redraw() { if (this._map && this.source) { const tileZoom = getClampZoom({ zoom: this._map.getZoom(), minzoom: this.source.minZoom, maxzoom: this.source.maxZoom }); if (tileZoom !== this._tileZoom) { this._tileZoom = tileZoom; } this._update(); } return this; } _render() { if (this._map && this.viewState) { this.viewState.update({ center: this._map.getCenter(), zoom: this._map.getZoom(), width: this._width, height: this._height }); } if (!this.gl) { this.gl = index.getContext( this.canvas, { preserveDrawingBuffer: false, antialias: true, // https://bugs.webkit.org/show_bug.cgi?id=237906 stencil: true }, true ); this.renderer = new Renderer(this.gl, { autoClear: false, extensions: [ "OES_texture_float", "OES_texture_float_linear", "WEBGL_color_buffer_float", "EXT_color_buffer_float" ] }); this.scene = new Scene(); this.sync = new CameraSync(this.viewState, "perspective", this.scene); this.planeCamera = new OrthographicCamera(0, 1, 1, 0, 0, 1); this.layer = new BaseLayer$1( this.source, { renderer: this.renderer, scene: this.scene }, { renderType: this.options.renderType, renderFrom: this.options.renderFrom, styleSpec: this.options.styleSpec, displayRange: this.options.displayRange, widthSegments: this.options.widthSegments, heightSegments: this.options.heightSegments, wireframe: this.options.wireframe, picking: this.options.picking, mask: this.processMask(), getZoom: () => this.viewState.zoom, triggerRepaint: () => { requestAnimationFrame(() => this._update()); }, getTileProjSize: (z) => { const w = 1 / Math.pow(2, z); return [w, w]; }, getPixelsToUnits: () => { const pixel = 1; const y = this.canvas.clientHeight / 2 - pixel / 2; const x = this.canvas.clientWidth / 2 - pixel / 2; const left = fromLngLat(this.viewState.unproject([x, y])); const right = fromLngLat(this.viewState.unproject([x + pixel, y + pixel])); return [Math.abs(right.x - left.x), Math.abs(left.y - right.y)]; }, getPixelsToProjUnit: () => [this.viewState.pixelsPerMeter, this.viewState.pixelsPerMeter], getViewTiles: (source, renderType) => { let { type } = source; type = type !== LayerSourceType.timeline ? type : source.privateType; if (!this._map) return []; const wrapTiles = []; if (type === LayerSourceType.image) { const cornerCoords = source.coordinates.map((c) => fromLngLat({ lng: c[0], lat: c[1] })); const tileID = getCoordinatesCenterTileID(cornerCoords); if (source.wrapX) { const x = tileID.x; const y = tileID.y; const z = tileID.z; const wrap = 0; wrapTiles.push( new TileID(z, wrap, z, x, y, { getTileBounds: () => [ source.coordinates[0][0], source.coordinates[2][1], source.coordinates[1][0], source.coordinates[0][1] ], getTileProjBounds: () => ({ left: tileID.extent[0] + wrap, top: tileID.extent[1], right: tileID.extent[2] + wrap, bottom: tileID.extent[3] }) }) ); } else { const x = tileID.x; const y = tileID.y; const z = tileID.z; const wrap = 0; wrapTiles.push( new TileID(z, wrap, z, x, y, { getTileBounds: () => [ source.coordinates[0][0], source.coordinates[2][1], source.coordinates[1][0], source.coordinates[0][1] ], getTileProjBounds: () => ({ left: tileID.extent[0] + wrap, top: tileID.extent[1], right: tileID.extent[2] + wrap, bottom: tileID.extent[3] }) }) ); } } else if (type === LayerSourceType.tile) { const tiles = this._currentTiles; for (let i = 0; i < tiles.length; i++) { const tile = tiles[i]; const { x, y, z, wrap } = tile; if (source.wrapX) { wrapTiles.push( new TileID(z, wrap, z, x, y, { getTileBounds, getTileProjBounds }) ); } else if (tile.wrap === 0) { wrapTiles.push( new TileID(z, wrap, z, x, y, { getTileBounds, getTileProjBounds }) ); } } } return wrapTiles; }, getExtent: () => getExtent(this._map), getGridTiles: (source) => { const wrapX = source.wrapX; if (!this._map) return []; const tiles = this._unLimitTiles; const wrapTiles = []; for (let i = 0; i < tiles.length; i++) { const tile = tiles[i]; const { x, y, z, wrap } = tile; if (wrapX) { wrapTiles.push( new TileID(z, wrap, z, x, y, { getTileBounds, getTileProjBounds }) ); } else if (tile.wrap === 0) { wrapTiles.push( new TileID(z, wrap, z, x, y, { getTileBounds, getTileProjBounds }) ); } } return wrapTiles; } } ); } if (this.sync) { this.sync.update(); } if (this.layer) { this.layer.update(); } this.glPrerender(); this.glRender(); } glPrerender() { this.scene.worldMatrixNeedsUpdate = true; this.scene.updateMatrixWorld(); this.camera.updateMatrixWorld(); const worlds = this.calcWrappedWorlds(); this.layer?.prerender({ worlds, camera: this.camera, planeCamera: this.planeCamera }); } glRender() { this.scene.worldMatrixNeedsUpdate = true; this.scene.updateMatrixWorld(); this.camera.updateMatrixWorld(); const worlds = this.calcWrappedWorlds(); this.layer?.render({ worlds, camera: this.camera, planeCamera: this.planeCamera }); } async picker(coordinates) { if (!this.options.picking) { console.warn("[Layer]: please enable picking options!"); return null; } if (!this.layer || !coordinates || !this._map) { console.warn("[Layer]: layer not initialized!"); return null; } const point = this._map.project(coordinates); return this.layer.picker([point.x, point.y]); } calcWrappedWorlds() { return [0]; } _resetView(e) { const animating = e && (e.pinch || e.flyTo); this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating); } _resetGrid() { const map = this._map; const crs = map.options.crs; const tileSize = this.getTileSize(); const tileZoom = this._tileZoom; const bounds = this._map.getPixelWorldBounds(this._tileZoom); if (bounds) { this._globalTileRange = this._pxBoundsToTileRange(bounds); } this._wrapX = crs.wrapLng && [ Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x), Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y) ]; this._wrapY = crs.wrapLat && [ Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x), Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y) ]; } _setView(center, zoom, noPrune, noUpdate) { let tileZoom = Math.round(zoom); if (this.options.maxZoom !== void 0 && tileZoom > this.options.maxZoom || this.options.minZoom !== void 0 && tileZoom < this.options.minZoom) { tileZoom = void 0; } else { tileZoom = getClampZoom({ minzoom: this.source.minZoom, maxzoom: this.source.maxZoom, zoom: tileZoom }); } const tileZoomChanged = this.options.updateWhenZooming && tileZoom !== this._tileZoom; if (!noUpdate || tileZoomChanged) { this._tileZoom = tileZoom; this._resetGrid(); if (tileZoom !== void 0) { this._update(center); } } } _tileCoordsToBounds(coords) { const bp = this._tileCoordsToNwSe(coords); let bounds = new L__namespace.LatLngBounds(bp[0], bp[1]); if (!this.source.wrapX) { bounds = this._map.wrapLatLngBounds(bounds); } return bounds; } _tileCoordsToNwSe(coords) { const map = this._map; const tileSize = this.getTileSize(); const nwPoint = coords.scaleBy(tileSize); const sePoint = nwPoint.add(tileSize); const nw = map.unproject(nwPoint, coords.z); const se = map.unproject(sePoint, coords.z); return [nw, se]; } _isValidTile(coords) { const crs = this._map.options.crs; if (!crs.infinite) { const bounds = this._globalTileRange; if (!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x) || !crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y)) { return false; } } return true; } _wrapCoords(coords) { const t = this._wrapX ? wrapTile(coords.x, this._wrapX) : { x: coords.x, wrap: 0 }; const newCoords = new L__namespace.Point( t.x, this._wrapY && !this.source.wrapX ? L__namespace.Util.wrapNum(coords.y, this._wrapY) : coords.y ); newCoords.z = coords.z; newCoords.wrap = t.wrap; return newCoords; } _update(center) { const map = this._map; if (!map || !this.source) { return; } const zoom = getClampZoom({ zoom: map.getZoom(), minzoom: this.source.minZoom, maxzoom: this.source.maxZoom }); if (center === void 0) { center = map.getCenter(); } if (this._tileZoom === void 0) { return; } const pixelBounds = this._getTiledPixelBounds(center, this._tileZoom); const tileRange = this._pxBoundsToTileRange(pixelBounds); const tileCenter = tileRange.getCenter(); const queue = []; if (!(isFinite(tileRange.min.x) && isFinite(tileRange.min.y) && isFinite(tileRange.max.x) && isFinite(tileRange.max.y))) { throw new Error("Attempted to load an infinite number of tiles"); } if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; } for (let j = tileRange.min.y; j <= tileRange.max.y; j++) { for (let i = tileRange.min.x; i <= tileRange.max.x; i++) { const coords = new L__namespace.Point(i, j); coords.z = this._tileZoom; if (!this._isValidTile(coords)) { continue; } queue.push(this._wrapCoords(coords)); } } queue.sort((a, b) => a.distanceTo(tileCenter) - b.distanceTo(tileCenter)); const z = map.getZoom(); const bounds = this._getTiledPixelBounds(center, z); if (bounds) { const unLimitTileRange = this._pxBoundsToTileRange(bounds); const tc = tileRange.getCenter(); const tileCoords = []; for (let j = unLimitTileRange.min.y; j <= unLimitTileRange.max.y; j++) { for (let i = unLimitTileRange.min.x; i <= unLimitTileRange.max.x; i++) { const coords = new L__namespace.Point(i, j); coords.z = z; if (!this._isValidTile(coords)) { continue; } tileCoords.push(this._wrapCoords(coords)); } } tileCoords.sort((a, b) => a.distanceTo(tc) - b.distanceTo(tc)); this._unLimitTiles = tileCoords; } this._currentTiles = queue; this._render(); return queue; } _getTiledPixelBounds(center, zoom) { const map = this._map; const mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(); const scale = map.getZoomScale(mapZoom, zoom); const pixelCenter = map.project(center, zoom).floor(); const halfSize = map.getSize().divideBy(scale * 2); return new L__namespace.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); } _pxBoundsToTileRange(bounds) { const tileSize = this.getTileSize(); return new L__namespace.Bounds( bounds.min.unscaleBy(tileSize).floor(), bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]) ); } handleZoom() { this._resetView(); if (this.layer) { this.layer.handleZoom(); } } onMoveEnd() { this._reset(); if (!this._map || this._map._animatingZoom) { return; } if (this.layer) { this.layer.moveEnd(); } } onMoveStart() { if (this.layer) { this.layer.moveStart(); } } _animateZoom(event) { super._animateZoom(event); this._setView(event.center, event.zoom, true, event.noUpdate); this.handleZoom(); } getEvents() { const events = { resize: this._onResize, viewreset: this._resetView, moveend: this.onMoveEnd, movestart: this.onMoveStart, zoom: this.handleZoom, zoomend: this._reset }; if (this._map.options.zoomAnimation && L__namespace.Browser.any3d) { events.zoomanim = this._animateZoom; } return events; } updateOptions(options) { this.options = { ...this.options, ...options || {} }; if (this.layer) { this.layer.updateOptions(options); } this._redraw(); } getMask() { return this.options.mask; } processMask() { if (this.options.mask) { const mask = this.options.mask; const data = mask.data; rewind$1(data, true); const tr = (coords) => { const mercatorCoordinates = []; for (let i2 = 0; i2 < coords.length; i2++) { const coord = coords[i2]; const p = fromLngLat(coord); mercatorCoordinates.push([p.x, p.y]); } return mercatorCoordinates; }; const features = data.features; const len = features.length; let i = 0; const fs = []; for (; i < len; i++) { const feature = features[i]; const coordinates = feature.geometry.coordinates; const type = feature.geometry.type; if (type === "Polygon") { fs.push({ type: "Feature", properties: {}, geometry: { type: "Polygon", coordinates: feature.geometry.coordinates.map((c) => tr(c)) } }); } else if (type === "MultiPolygon") { const css = []; for (let k = 0; k < coordinates.length; k++) { const coordinate = coordinates[k]; const cs = []; for (let n = 0; n < coordinate.length; n++) { cs.push(tr(coordinates[k][n])); } css.push(cs); } fs.push({ type: "Feature", properties: {}, geometry: { type: "MultiPolygon", coordinates: css } }); } } return { data: polygon2buffer(fs), type: mask.type }; } } setMask(mask) { this.options.mask = Object.assign({}, this.options.mask, mask); if (this.layer) { this.layer.setMask(this.processMask()); } } onRemove() { if (this.layer) { this.layer.destroy(); this.layer = null; } if (this.source) { if (Array.isArray(this.source.sourceCache)) { this.source.sourceCache?.forEach((s) => { s?.clearTiles(); }); } else { this.source.sourceCache?.clearTiles(); } } this._currentTiles = []; this._unLimitTiles = []; this.gl = null; this._tileZoom = void 0; return super.onRemove(); } } exports.DecodeType = DecodeType; exports.Field = Field; exports.ImageSource = ImageSource; exports.LayerSourceType = LayerSourceType; exports.MaskType = MaskType; exports.RenderFrom = RenderFrom; exports.RenderType = RenderType; exports.TileID = TileID; exports.TileSource = TileSource; exports.TimelineSource = TimelineSource; exports.WebglLayer = WebglLayer; exports.WindLayer = WindLayer; exports.configDeps = configDeps; })); //# sourceMappingURL=leaflet-wind.js.map