ol-owm
Version:
Weather layer for OpenLayers and Leaflet using OpenWeatherMap
18,558 lines • 608 kB
JavaScript
(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<t.length;n++){var e=t[n][0](r);if(e)return [e,t[n][1]]}return [null,void 0]},x=function(r){return "string"==typeof r?N(r.trim(),y.string):"object"==typeof r&&null!==r?N(r,y.object):[null,void 0]},M=function(r,t){var n=c(r);return {h:n.h,s:e(n.s+100*t,0,100),l:n.l,a:n.a}},H=function(r){return (299*r.r+587*r.g+114*r.b)/1e3/255},$=function(r,t){var n=c(r);return {h:n.h,s:n.s,l:e(n.l+100*t,0,100),a:n.a}},j=function(){function r(r){this.parsed=x(r)[0],this.rgba=this.parsed||{r:0,g:0,b:0,a:1};}return r.prototype.isValid=function(){return null!==this.parsed},r.prototype.brightness=function(){return n(H(this.rgba),2)},r.prototype.isDark=function(){return H(this.rgba)<.5},r.prototype.isLight=function(){return H(this.rgba)>=.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<t&&(t=u,b=g);}return b}};f.string.push([function(f){var r=f.toLowerCase(),d="transparent"===r?"#0000":a[r];return d?new e(d).toRgb():null},"name"]);}
k([namesPlugin]);
const normalize = (a, min, max) => {
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 </g, "#glsl_include <");
return this.injectShaderModule(module, modules);
};
return shader.replace(regExp, replacement);
}
createShader(source = this.source) {
let s = source.replace(/#include </g, "#glsl_include <");
s = this.injectShaderModule(s, this.#includes || {}).replace(/\n\n+/gm, "\n\n");
this.gl.shaderSource(this.handle, s);
this.gl.compileShader(this.handle);
if (!this.gl.getShaderParameter(this.handle, this.gl.COMPILE_STATUS)) {
const log = this.gl.getShaderInfoLog(this.handle) || "";
this.gl.deleteShader(this.handle);
throw new Error(`${this.toString()}
${log}
${addLineNumbers(s)}`);
}
}
get source() {
return this.sourceCode;
}
get shaderType() {
return this.#shaderType;
}
getSource() {
return this.gl.getShaderSource(this.handle);
}
setSource(source) {
const name = getShaderName(source);
if (name) {
this.name = genShaderName(name);
}
this.createShader(source);
}
removeStats() {
}
deleteHandle() {
this.gl.deleteShader(this.handle);
}
toString() {
return `${getTypeName(this.gl, this.shaderType)}:${this.id}`;
}
}
class VertexShader extends Shader {
constructor(renderer, sourceCode, includes) {
super(renderer, sourceCode, "vertex", includes);
}
createHandle() {
return this.gl.createShader(this.gl.VERTEX_SHADER);
}
}
class FragmentShader extends Shader {
constructor(renderer, sourceCode, includes) {
super(renderer, sourceCode, "fragment", includes);
}
createHandle() {
return this.gl.createShader(this.gl.FRAGMENT_SHADER);
}
}
const getDefines = (t) => {
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: "<cancel>",
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 === "<cancel>") {
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 === "<response>") {
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: "<response>",
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 <decodeFloat>\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(av<FLOAT_MIN){return vec4(0.0,0.0,0.0,0.0);}else if(v>FLOAT_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 <decodeFloat>\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 <random>\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.x<x&&x<bbox.z&&bbox.y<pos.y&&pos.y<bbox.w);}void main(){vec2 pos=v_particle_pos;if(!containsXY(pos.xy,u_data_bbox)||!containsXY(pos.xy,u_bbox)||calcTexture(pos).a==0.0){discard;}vec2 velocity=bilinear(pos);float value=length(velocity);float value_t=(value-u_colorRange.x)/(u_colorRange.y-u_colorRange.x);vec2 ramp_pos=vec2(value_t,0.5);vec4 color=texture2D(u_colorRamp,ramp_pos);float distance=length(2.0*gl_PointCoord-1.0);if(distance>1.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;
}));