diff --git a/dist/types/heatmap.d.ts b/dist/types/heatmap.d.ts index 6c81616..7d8e68b 100644 --- a/dist/types/heatmap.d.ts +++ b/dist/types/heatmap.d.ts @@ -16,14 +16,14 @@ export declare class HeatmapRenderer { translate: [number, number]; opacity: number; hearmapExData: HearmapExData | object; - gradShadOP: ShaderProgram; - colorShadOP: ShaderProgram; - imageShaOP: ShaderProgram; - fbTexObj: WebGLTexture; - fbo: WebGLFramebuffer; gradient: MappedGradient | null; - imageTexture: WebGLTexture | null; - pDataLength: number | undefined; + _imageTexture: WebGLTexture | null; + _pDataLength: number | undefined; + _gradShadOP: ShaderProgram; + _colorShadOP: ShaderProgram; + _imageShaOP: ShaderProgram; + _fbTexObj: WebGLTexture; + _fbo: WebGLFramebuffer; private layer; private dom; private imgWidth; @@ -32,7 +32,7 @@ export declare class HeatmapRenderer { private type; constructor(container: string | HTMLElement, config: HeatmapConfig); /** - * Invoke resize method to rerender as container resizes. + * Invoke resize method on container resize. */ resize(): void; clear(): void; @@ -55,19 +55,19 @@ export declare class HeatmapRenderer { */ setGradient(gradient: GradientElement[]): HeatmapRenderer; /** - * Set the translate transformation on the canvas + * Set the translate on the Heatmap * @param translate - Accepts array [x, y] * @returns instance */ - setTranslate(translate: Translate): this; + setTranslate(translate: Translate): HeatmapRenderer; /** - * Set the zoom transformation on the canvas + * Set the zoom transformation on the Heatmap * @param zoom - Accepts float value * @returns instance */ setZoom(zoom: number): HeatmapRenderer; /** - * Set the rotation transformation on the canvas + * Set the rotation transformation on the Heatmap * @param angle - Accepts angle in radians * @returns instance */ @@ -92,32 +92,33 @@ export declare class HeatmapRenderer { setOpacity(opacity: number): HeatmapRenderer; /** * Set the background image - * @param config - Accepts Object with { Url, height, width, x, and y} properties + * @param config - Accepts Object with { url, height, width, x, and y} properties * @returns instance */ - setBackgroundImage(config: BackgroundImageConfig): this | undefined; + setBackgroundImage(config: BackgroundImageConfig): HeatmapRenderer | undefined; /** - * Clears heatmap + * Clear heatmap */ clearData(): void; /** - * After adding data points, need to invoke .render() method to update the heatmap + * Method to append data points. This method automatically adds new data points to the existing dataset and the heatmap updates in immediately. no need to call the ".render" method separately. * @param data - The data points with 'x', 'y' and 'value' * @param transIntactFlag - Flag indicating whether to apply existing heatmap transformations on the newly added data points * @returns instance */ addData(data: Point[], transIntactFlag: boolean): HeatmapRenderer; /** + * Method to load data. This will override any existing data. * @param data - Accepts an array of data points with 'x', 'y' and 'value' * @returns instance */ renderData(data: Point[]): HeatmapRenderer; /** - * Method to re-render the heatmap. This method needs to be invoked as and when configurations get changed + * Method to update the heatmap. This method to be invoked on every change in configuration. */ render(): void; /** - * Get projected co-ordinates relative to the heatmap layer + * Get projected co-ordinates relative to the heatmap * @param data - The data point to project. * @returns projected data point. */ diff --git a/dist/visualHeatmap.esm.js b/dist/visualHeatmap.esm.js index 6650b3e..f8d2362 100644 --- a/dist/visualHeatmap.esm.js +++ b/dist/visualHeatmap.esm.js @@ -363,10 +363,10 @@ function extractData(data) { const self = this; const len = data.length; let { posVec = new Float32Array(), rVec = new Float32Array() } = (self.hearmapExData || {}); - if (self.pDataLength !== len) { + if (self._pDataLength !== len) { posVec = new Float32Array(new ArrayBuffer(len * 8)); rVec = new Float32Array(new ArrayBuffer(len * 4)); - self.pDataLength = len; + self._pDataLength = len; } const dataMinMaxValue = { min: Infinity, @@ -417,13 +417,13 @@ function renderExec() { return; } ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT); - ctx.bindTexture(ctx.TEXTURE_2D, this.fbTexObj); + ctx.bindTexture(ctx.TEXTURE_2D, this._fbTexObj); ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, this.width * this.ratio, this.height * this.ratio, 0, ctx.RGBA, ctx.UNSIGNED_BYTE, null); ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE); ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE); ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR); - ctx.bindFramebuffer(ctx.FRAMEBUFFER, this.fbo); - ctx.framebufferTexture2D(ctx.FRAMEBUFFER, ctx.COLOR_ATTACHMENT0, ctx.TEXTURE_2D, this.fbTexObj, 0); + ctx.bindFramebuffer(ctx.FRAMEBUFFER, this._fbo); + ctx.framebufferTexture2D(ctx.FRAMEBUFFER, ctx.COLOR_ATTACHMENT0, ctx.TEXTURE_2D, this._fbTexObj, 0); if (this.hearmapExData) { renderHeatGrad.call(this, ctx, this.hearmapExData); } @@ -435,23 +435,24 @@ function renderExec() { } function renderHeatGrad(ctx, exData) { var _a, _b, _c, _d; - ctx.useProgram(this.gradShadOP.program); + ctx.useProgram(this._gradShadOP.program); + const { u_resolution, u_translate, u_zoom, u_angle, u_density, u_max, u_min, u_size, u_intensity } = this._gradShadOP.uniform; this.min = this.configMin !== null ? this.configMin : (_b = (_a = exData === null || exData === void 0 ? void 0 : exData.minMax) === null || _a === void 0 ? void 0 : _a.min) !== null && _b !== void 0 ? _b : 0; this.max = this.configMax !== null ? this.configMax : (_d = (_c = exData === null || exData === void 0 ? void 0 : exData.minMax) === null || _c === void 0 ? void 0 : _c.max) !== null && _d !== void 0 ? _d : 0; - this.gradShadOP.attr[0].data = exData.posVec || []; - this.gradShadOP.attr[1].data = exData.rVec || []; - ctx.uniform2fv(this.gradShadOP.uniform.u_resolution, new Float32Array([this.width * this.ratio, this.height * this.ratio])); - ctx.uniform2fv(this.gradShadOP.uniform.u_translate, new Float32Array([this.translate[0], this.translate[1]])); - ctx.uniform1f(this.gradShadOP.uniform.u_zoom, this.zoom ? this.zoom : 0.01); - ctx.uniform1f(this.gradShadOP.uniform.u_angle, this.angle); - ctx.uniform1f(this.gradShadOP.uniform.u_density, this.ratio); - ctx.uniform1f(this.gradShadOP.uniform.u_max, this.max); - ctx.uniform1f(this.gradShadOP.uniform.u_min, this.min); - ctx.uniform1f(this.gradShadOP.uniform.u_size, this.size); - ctx.uniform1f(this.gradShadOP.uniform.u_intensity, this.intensity); - this.gradShadOP.attr.forEach(function (d) { + this._gradShadOP.attr[0].data = exData.posVec || []; + this._gradShadOP.attr[1].data = exData.rVec || []; + ctx.uniform2fv(u_resolution, new Float32Array([this.width * this.ratio, this.height * this.ratio])); + ctx.uniform2fv(u_translate, new Float32Array([this.translate[0], this.translate[1]])); + ctx.uniform1f(u_zoom, this.zoom ? this.zoom : 0.01); + ctx.uniform1f(u_angle, this.angle); + ctx.uniform1f(u_density, this.ratio); + ctx.uniform1f(u_max, this.max); + ctx.uniform1f(u_min, this.min); + ctx.uniform1f(u_size, this.size); + ctx.uniform1f(u_intensity, this.intensity); + this._gradShadOP.attr.forEach(function (d) { ctx.bindBuffer(d.bufferType, d.buffer); ctx.bufferData(d.bufferType, d.data, d.drawType); ctx.enableVertexAttribArray(d.attribute); @@ -461,13 +462,14 @@ function renderHeatGrad(ctx, exData) { } function renderImage(ctx, imageConfig) { const { x = 0, y = 0, width = 0, height = 0 } = imageConfig; - ctx.useProgram(this.imageShaOP.program); - ctx.uniform2fv(this.imageShaOP.uniform.u_resolution, new Float32Array([this.width * this.ratio, this.height * this.ratio])); - ctx.uniform2fv(this.imageShaOP.uniform.u_translate, new Float32Array([this.translate[0], this.translate[1]])); - ctx.uniform1f(this.imageShaOP.uniform.u_zoom, this.zoom ? this.zoom : 0.01); - ctx.uniform1f(this.imageShaOP.uniform.u_angle, this.angle); - ctx.uniform1f(this.imageShaOP.uniform.u_density, this.ratio); - this.imageShaOP.attr[0].data = new Float32Array([ + const { u_resolution, u_translate, u_zoom, u_angle, u_density, u_image } = this._imageShaOP.uniform; + ctx.useProgram(this._imageShaOP.program); + ctx.uniform2fv(u_resolution, new Float32Array([this.width * this.ratio, this.height * this.ratio])); + ctx.uniform2fv(u_translate, new Float32Array([this.translate[0], this.translate[1]])); + ctx.uniform1f(u_zoom, this.zoom ? this.zoom : 0.01); + ctx.uniform1f(u_angle, this.angle); + ctx.uniform1f(u_density, this.ratio); + this._imageShaOP.attr[0].data = new Float32Array([ x, y, x + width, @@ -481,32 +483,33 @@ function renderImage(ctx, imageConfig) { x + width, y + height, ]); - this.imageShaOP.attr.forEach(function (d) { + this._imageShaOP.attr.forEach(function (d) { ctx.bindBuffer(d.bufferType, d.buffer); ctx.bufferData(d.bufferType, d.data, d.drawType); ctx.enableVertexAttribArray(d.attribute); ctx.vertexAttribPointer(d.attribute, d.size, d.valueType, true, 0, 0); }); - ctx.uniform1i(this.imageShaOP.uniform.u_image, 0); + ctx.uniform1i(u_image, 0); ctx.activeTexture(this.ctx.TEXTURE0); - ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture); + ctx.bindTexture(this.ctx.TEXTURE_2D, this._imageTexture); ctx.drawArrays(ctx.TRIANGLES, 0, 6); } function renderColorGradiant(ctx) { - ctx.useProgram(this.colorShadOP.program); - ctx.uniform4fv(this.colorShadOP.uniform.u_colorArr, this.gradient.value); - ctx.uniform1f(this.colorShadOP.uniform.u_colorCount, this.gradient.length); - ctx.uniform1fv(this.colorShadOP.uniform.u_offset, new Float32Array(this.gradient.offset)); - ctx.uniform1f(this.colorShadOP.uniform.u_opacity, this.opacity); - this.colorShadOP.attr.forEach(function (d) { + const { u_colorArr, u_colorCount, u_offset, u_opacity, u_framebuffer } = this._colorShadOP.uniform; + ctx.useProgram(this._colorShadOP.program); + ctx.uniform4fv(u_colorArr, this.gradient.value); + ctx.uniform1f(u_colorCount, this.gradient.length); + ctx.uniform1fv(u_offset, new Float32Array(this.gradient.offset)); + ctx.uniform1f(u_opacity, this.opacity); + this._colorShadOP.attr.forEach(function (d) { ctx.bindBuffer(d.bufferType, d.buffer); ctx.bufferData(d.bufferType, d.data, d.drawType); ctx.enableVertexAttribArray(d.attribute); ctx.vertexAttribPointer(d.attribute, d.size, d.valueType, true, 0, 0); }); - ctx.uniform1i(this.colorShadOP.uniform.u_framebuffer, 0); + ctx.uniform1i(u_framebuffer, 0); ctx.activeTexture(ctx.TEXTURE0); - ctx.bindTexture(ctx.TEXTURE_2D, this.fbTexObj); + ctx.bindTexture(ctx.TEXTURE_2D, this._fbTexObj); ctx.drawArrays(ctx.TRIANGLES, 0, 6); } function imageInstance(url, onLoad, onError) { @@ -536,8 +539,8 @@ class HeatmapRenderer { this.opacity = 0; this.hearmapExData = {}; this.gradient = null; - this.imageTexture = null; - this.pDataLength = undefined; + this._imageTexture = null; + this._pDataLength = undefined; this.imgWidth = 0; this.imgHeight = 0; this.heatmapData = []; @@ -581,11 +584,11 @@ class HeatmapRenderer { this.hearmapExData = {}; this.layer = layer; this.dom = res; - this.gradShadOP = createGradiantShader(this.ctx, GradShader); - this.colorShadOP = createColorShader(this.ctx, ColorShader); - this.imageShaOP = createImageShader(this.ctx, ImageShader); - this.fbTexObj = ctx.createTexture(); - this.fbo = ctx.createFramebuffer(); + this._gradShadOP = createGradiantShader(this.ctx, GradShader); + this._colorShadOP = createColorShader(this.ctx, ColorShader); + this._imageShaOP = createImageShader(this.ctx, ImageShader); + this._fbTexObj = ctx.createTexture(); + this._fbo = ctx.createFramebuffer(); if (!isNullUndefined(config.size)) { this.setSize(config.size); } @@ -646,7 +649,7 @@ class HeatmapRenderer { } } /** - * Invoke resize method to rerender as container resizes. + * Invoke resize method on container resize. */ resize() { const height = this.dom.clientHeight; @@ -698,7 +701,7 @@ class HeatmapRenderer { return this; } /** - * Set the translate transformation on the canvas + * Set the translate on the Heatmap * @param translate - Accepts array [x, y] * @returns instance */ @@ -713,7 +716,7 @@ class HeatmapRenderer { return this; } /** - * Set the zoom transformation on the canvas + * Set the zoom transformation on the Heatmap * @param zoom - Accepts float value * @returns instance */ @@ -725,7 +728,7 @@ class HeatmapRenderer { return this; } /** - * Set the rotation transformation on the canvas + * Set the rotation transformation on the Heatmap * @param angle - Accepts angle in radians * @returns instance */ @@ -782,7 +785,7 @@ class HeatmapRenderer { } /** * Set the background image - * @param config - Accepts Object with { Url, height, width, x, and y} properties + * @param config - Accepts Object with { url, height, width, x, and y} properties * @returns instance */ setBackgroundImage(config) { @@ -792,7 +795,7 @@ class HeatmapRenderer { return; } const maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE); - this.imageTexture = this.ctx.createTexture(); + this._imageTexture = this.ctx.createTexture(); this.type = "TEXTURE_2D"; this.imageConfig = null; this.imgWidth = config.width || this.width; @@ -803,7 +806,7 @@ class HeatmapRenderer { this.imgHeight > maxTextureSize ? maxTextureSize : this.imgHeight; imageInstance(config.url, function onUpdateCallBack() { self.ctx.activeTexture(self.ctx.TEXTURE0); - self.ctx.bindTexture(self.ctx.TEXTURE_2D, self.imageTexture); + self.ctx.bindTexture(self.ctx.TEXTURE_2D, self._imageTexture); self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_WRAP_S, self.ctx.CLAMP_TO_EDGE); self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_WRAP_T, self.ctx.CLAMP_TO_EDGE); self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_MIN_FILTER, self.ctx.LINEAR); @@ -823,7 +826,7 @@ class HeatmapRenderer { return this; } /** - * Clears heatmap + * Clear heatmap */ clearData() { this.heatmapData = []; @@ -831,7 +834,7 @@ class HeatmapRenderer { this.render(); } /** - * After adding data points, need to invoke .render() method to update the heatmap + * Method to append data points. This method automatically adds new data points to the existing dataset and the heatmap updates in immediately. no need to call the ".render" method separately. * @param data - The data points with 'x', 'y' and 'value' * @param transIntactFlag - Flag indicating whether to apply existing heatmap transformations on the newly added data points * @returns instance @@ -849,6 +852,7 @@ class HeatmapRenderer { return this; } /** + * Method to load data. This will override any existing data. * @param data - Accepts an array of data points with 'x', 'y' and 'value' * @returns instance */ @@ -862,13 +866,13 @@ class HeatmapRenderer { return this; } /** - * Method to re-render the heatmap. This method needs to be invoked as and when configurations get changed + * Method to update the heatmap. This method to be invoked on every change in configuration. */ render() { renderExec.call(this); } /** - * Get projected co-ordinates relative to the heatmap layer + * Get projected co-ordinates relative to the heatmap * @param data - The data point to project. * @returns projected data point. */ diff --git a/dist/visualHeatmap.esm.min.js b/dist/visualHeatmap.esm.min.js index 5893457..6d51215 100644 --- a/dist/visualHeatmap.esm.min.js +++ b/dist/visualHeatmap.esm.min.js @@ -3,4 +3,4 @@ * (c) 2024 Narayana Swamy (narayanaswamy14@gmail.com) * @license BSD-3-Clause */ -const t={vertex:"#version 300 es\n\t\t\t\tin vec2 a_position;\n\t\t\t\tin float a_intensity;\n\t\t\t\tuniform float u_size;\n\t\t\t\tuniform vec2 u_resolution;\n\t\t\t\tuniform vec2 u_translate; \n\t\t\t\tuniform float u_zoom; \n\t\t\t\tuniform float u_angle; \n\t\t\t\tuniform float u_density;\n\t\t\t\tout float v_i;\n\n\t\t\t\tvec2 rotation(vec2 v, float a, float aspect) {\n\t\t\t\t\tfloat s = sin(a); float c = cos(a); mat2 rotationMat = mat2(c, -s, s, c); \n\t\t\t\t\tmat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\tmat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\treturn scaleMatInv * rotationMat * scaleMat * v;\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\t\t\t\t\tvec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);\n\t\t\t\t\tvec2 zeroToTwo = zeroToOne * 2.0 - 1.0;\n\t\t\t\t\tfloat zoomFactor = max(u_zoom, 0.1);\n\t\t\t\t\tzeroToTwo = zeroToTwo / zoomFactor;\n\t\t\t\t\tif (u_angle != 0.0) {\n\t\t\t\t\t\tzeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);\n\t\t\t\t\t}\n\t\t\t\t\tgl_Position = vec4(zeroToTwo , 0, 1);\n\t\t\t\t\tgl_PointSize = u_size * u_density;\n\t\t\t\t\tv_i = a_intensity;\n\t\t\t\t}",fragment:"#version 300 es\n\t\t\t\tprecision mediump float;\n\t\t\t\tuniform float u_max;\n\t\t\t\tuniform float u_min;\n\t\t\t\tuniform float u_intensity;\n\t\t\t\tin float v_i;\n\t\t\t\tout vec4 fragColor;\n\t\t\t\tvoid main() {\n\t\t\t\t\tfloat r = 0.0; \n\t\t\t\t\tvec2 cxy = 2.0 * gl_PointCoord - 1.0;\n\t\t\t\t\tr = dot(cxy, cxy);\n\t\t\t\t\tfloat deno = max(u_max - u_min, 1.0);\n\t\t\t\t\tif(r <= 1.0) {\n\t\t\t\t\t\tfragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));\n\t\t\t\t\t}\n\t\t\t\t}"},e={vertex:"#version 300 es\n\t\t\t\tprecision highp float;\n\t\t\t\tin vec2 a_texCoord;\n\t\t\t\tout vec2 v_texCoord;\n\t\t\t\tvoid main() {\n\t\t\t\t\tvec2 clipSpace = a_texCoord * 2.0 - 1.0;\n\t\t\t\t\tgl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);\n\t\t\t\t\tv_texCoord = a_texCoord;\n\t\t\t\t}\n\t",fragment:"#version 300 es\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t\tin vec2 v_texCoord;\n\t\t\t\t\tout vec4 fragColor;\n\t\t\t\t\tuniform sampler2D u_framebuffer;\n\t\t\t\t\tuniform vec4 u_colorArr[20];\n\t\t\t\t\tuniform float u_colorCount;\n\t\t\t\t\tuniform float u_opacity;\n\t\t\t\t\tuniform float u_offset[20];\n\n\t\t\t\t\tfloat remap ( float minval, float maxval, float curval ) {\n\t\t\t\t\t\treturn ( curval - minval ) / ( maxval - minval );\n\t\t\t\t\t}\n\n\t\t\t\t\tvoid main() {\n\t\t\t\t\t\tfloat alpha = texture(u_framebuffer, v_texCoord.xy).a;\n\t\t\t\t\t\tif (alpha > 0.0 && alpha <= 1.0) {\n\t\t\t\t\t\t\tvec4 color_;\n\n\t\t\t\t\t\t\tif (alpha <= u_offset[0]) {\n\t\t\t\t\t\t\t\tcolor_ = u_colorArr[0];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfor (int i = 1; i <= 20; ++i) {\n\t\t\t\t\t\t\t\t\tif (alpha <= u_offset[i]) {\n\t\t\t\t\t\t\t\t\t\tcolor_ = mix( u_colorArr[i - 1], u_colorArr[i], remap( u_offset[i - 1], u_offset[i], alpha ) );\n\t\t\t\t\t\t\t\t\t\tcolor_ = color_ * mix( u_colorArr[i - 1][3], u_colorArr[i][3], remap( u_offset[i - 1], u_offset[i], alpha ));\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcolor_ = color_ * u_opacity;\n\t\t\t\t\t\t\tif (color_.a < 0.0) {\n\t\t\t\t\t\t\t\tcolor_.a = 0.0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfragColor = color_;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfragColor = vec4(0.0, 0.0, 0.0, 0.0);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t"},i={vertex:"#version 300 es\n precision highp float;\n in vec2 a_position;\n in vec2 a_texCoord;\n uniform vec2 u_resolution;\n\t\t\t\t\tuniform vec2 u_translate; \n\t\t\t\t\tuniform float u_zoom; \n\t\t\t\t\tuniform float u_angle; \n\t\t\t\t\tuniform float u_density;\n out vec2 v_texCoord;\n\n vec2 rotation(vec2 v, float a, float aspect) {\n\t\t\t\t\t\tfloat s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c);\n\t\t\t\t\t\tmat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\tmat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\treturn scaleMatInv * m * scaleMat * v;\n\t\t\t\t\t}\n\n void main() {\n \tvec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);\n \tzeroToOne.y = 1.0 - zeroToOne.y;\n\t\t\t\t\t\tvec2 zeroToTwo = zeroToOne * 2.0 - 1.0;\n\t\t\t\t\t\tfloat zoomFactor = u_zoom;\n\t\t\t\t\t\tif (zoomFactor == 0.0) {\n\t\t\t\t\t\t\tzoomFactor = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tzeroToTwo = zeroToTwo / zoomFactor;\n\t\t\t\t\t\tif (u_angle != 0.0) {\n\t\t\t\t\t\t\tzeroToTwo = rotation(zeroToTwo, u_angle * -1.0, u_resolution.x / u_resolution.y);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl_Position = vec4(zeroToTwo , 0, 1);\n\t\t\t\t\t\tv_texCoord = a_texCoord;\n }\n \t\t",fragment:"#version 300 es\n precision mediump float;\n uniform sampler2D u_image;\n in vec2 v_texCoord;\n out vec4 fragColor;\n void main() {\n fragColor = texture(u_image, v_texCoord);\n }\n "};function r(t,e,i){const r=t.createShader(t[e]);if(!r)throw new Error("Failed to create shader.");t.shaderSource(r,i),t.compileShader(r);if(!t.getShaderParameter(r,t.COMPILE_STATUS)){const e=t.getShaderInfoLog(r);throw t.deleteShader(r),new Error("*** Error compiling shader '"+r+"':"+e)}return r}function o(t,e){const i=r(t,"VERTEX_SHADER",e.vertex),o=r(t,"FRAGMENT_SHADER",e.fragment),a=t.createProgram();if(!a)throw new Error("Failed to create program.");t.attachShader(a,i),t.attachShader(a,o),t.linkProgram(a);if(t.getProgramParameter(a,t.LINK_STATUS))return a;{const e=t.getProgramInfoLog(a);throw t.deleteProgram(a),new Error("Error in program linking:"+e)}}function a(t){return null==t}function n(t){return"number"!=typeof t}function s(t){if(!Array.isArray(t)||t.length<2)throw new Error("Invalid gradient: Expected an array with at least 2 elements.");if(!function(t){for(let e=0;et[e].value&&(a.min=t[e].value),a.max1||t<0)throw this.intensity=t>1?1:0,new Error("Invalid Intensity value "+t);return this.intensity=t,this}setOpacity(t){if(a(t)||n(t))throw new Error("Invalid Opacity: Expected Number");if(t>1||t<0)throw new Error("Invalid Opacity value "+t);return this.opacity=t,this}setBackgroundImage(t){const e=this;if(!t.url)return;const i=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE);return this.imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",this.imageConfig=null,this.imgWidth=t.width||this.width,this.imgHeight=t.height||this.height,this.imgWidth=this.imgWidth>i?i:this.imgWidth,this.imgHeight=this.imgHeight>i?i:this.imgHeight,function(t,e,i){const r=new Image;r.crossOrigin="anonymous",r.onload=e,r.onerror=i,r.src=t}(t.url,(function(){e.ctx.activeTexture(e.ctx.TEXTURE0),e.ctx.bindTexture(e.ctx.TEXTURE_2D,e.imageTexture),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_S,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_T,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MIN_FILTER,e.ctx.LINEAR),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MAG_FILTER,e.ctx.LINEAR),e.ctx.texImage2D(e.ctx.TEXTURE_2D,0,e.ctx.RGBA,this.naturalWidth,this.naturalHeight,0,e.ctx.RGBA,e.ctx.UNSIGNED_BYTE,this),e.imageConfig={x:t.x||0,y:t.y||0,height:e.imgHeight,width:e.imgWidth,image:this},e.render()}),(function(t){throw new Error(`Image Load Error, ${t}`)})),this}clearData(){this.heatmapData=[],this.hearmapExData={},this.render()}addData(t,e){const i=this;for(let r=0;r 0.0 && alpha <= 1.0) {\n\t\t\t\t\t\t\tvec4 color_;\n\n\t\t\t\t\t\t\tif (alpha <= u_offset[0]) {\n\t\t\t\t\t\t\t\tcolor_ = u_colorArr[0];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfor (int i = 1; i <= 20; ++i) {\n\t\t\t\t\t\t\t\t\tif (alpha <= u_offset[i]) {\n\t\t\t\t\t\t\t\t\t\tcolor_ = mix( u_colorArr[i - 1], u_colorArr[i], remap( u_offset[i - 1], u_offset[i], alpha ) );\n\t\t\t\t\t\t\t\t\t\tcolor_ = color_ * mix( u_colorArr[i - 1][3], u_colorArr[i][3], remap( u_offset[i - 1], u_offset[i], alpha ));\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcolor_ = color_ * u_opacity;\n\t\t\t\t\t\t\tif (color_.a < 0.0) {\n\t\t\t\t\t\t\t\tcolor_.a = 0.0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfragColor = color_;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfragColor = vec4(0.0, 0.0, 0.0, 0.0);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t"},i={vertex:"#version 300 es\n precision highp float;\n in vec2 a_position;\n in vec2 a_texCoord;\n uniform vec2 u_resolution;\n\t\t\t\t\tuniform vec2 u_translate; \n\t\t\t\t\tuniform float u_zoom; \n\t\t\t\t\tuniform float u_angle; \n\t\t\t\t\tuniform float u_density;\n out vec2 v_texCoord;\n\n vec2 rotation(vec2 v, float a, float aspect) {\n\t\t\t\t\t\tfloat s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c);\n\t\t\t\t\t\tmat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\tmat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\treturn scaleMatInv * m * scaleMat * v;\n\t\t\t\t\t}\n\n void main() {\n \tvec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);\n \tzeroToOne.y = 1.0 - zeroToOne.y;\n\t\t\t\t\t\tvec2 zeroToTwo = zeroToOne * 2.0 - 1.0;\n\t\t\t\t\t\tfloat zoomFactor = u_zoom;\n\t\t\t\t\t\tif (zoomFactor == 0.0) {\n\t\t\t\t\t\t\tzoomFactor = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tzeroToTwo = zeroToTwo / zoomFactor;\n\t\t\t\t\t\tif (u_angle != 0.0) {\n\t\t\t\t\t\t\tzeroToTwo = rotation(zeroToTwo, u_angle * -1.0, u_resolution.x / u_resolution.y);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl_Position = vec4(zeroToTwo , 0, 1);\n\t\t\t\t\t\tv_texCoord = a_texCoord;\n }\n \t\t",fragment:"#version 300 es\n precision mediump float;\n uniform sampler2D u_image;\n in vec2 v_texCoord;\n out vec4 fragColor;\n void main() {\n fragColor = texture(u_image, v_texCoord);\n }\n "};function r(t,e,i){const r=t.createShader(t[e]);if(!r)throw new Error("Failed to create shader.");t.shaderSource(r,i),t.compileShader(r);if(!t.getShaderParameter(r,t.COMPILE_STATUS)){const e=t.getShaderInfoLog(r);throw t.deleteShader(r),new Error("*** Error compiling shader '"+r+"':"+e)}return r}function o(t,e){const i=r(t,"VERTEX_SHADER",e.vertex),o=r(t,"FRAGMENT_SHADER",e.fragment),n=t.createProgram();if(!n)throw new Error("Failed to create program.");t.attachShader(n,i),t.attachShader(n,o),t.linkProgram(n);if(t.getProgramParameter(n,t.LINK_STATUS))return n;{const e=t.getProgramInfoLog(n);throw t.deleteProgram(n),new Error("Error in program linking:"+e)}}function n(t){return null==t}function a(t){return"number"!=typeof t}function s(t){if(!Array.isArray(t)||t.length<2)throw new Error("Invalid gradient: Expected an array with at least 2 elements.");if(!function(t){for(let e=0;et[e].value&&(n.min=t[e].value),n.max1||t<0)throw this.intensity=t>1?1:0,new Error("Invalid Intensity value "+t);return this.intensity=t,this}setOpacity(t){if(n(t)||a(t))throw new Error("Invalid Opacity: Expected Number");if(t>1||t<0)throw new Error("Invalid Opacity value "+t);return this.opacity=t,this}setBackgroundImage(t){const e=this;if(!t.url)return;const i=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE);return this._imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",this.imageConfig=null,this.imgWidth=t.width||this.width,this.imgHeight=t.height||this.height,this.imgWidth=this.imgWidth>i?i:this.imgWidth,this.imgHeight=this.imgHeight>i?i:this.imgHeight,function(t,e,i){const r=new Image;r.crossOrigin="anonymous",r.onload=e,r.onerror=i,r.src=t}(t.url,(function(){e.ctx.activeTexture(e.ctx.TEXTURE0),e.ctx.bindTexture(e.ctx.TEXTURE_2D,e._imageTexture),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_S,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_T,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MIN_FILTER,e.ctx.LINEAR),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MAG_FILTER,e.ctx.LINEAR),e.ctx.texImage2D(e.ctx.TEXTURE_2D,0,e.ctx.RGBA,this.naturalWidth,this.naturalHeight,0,e.ctx.RGBA,e.ctx.UNSIGNED_BYTE,this),e.imageConfig={x:t.x||0,y:t.y||0,height:e.imgHeight,width:e.imgWidth,image:this},e.render()}),(function(t){throw new Error(`Image Load Error, ${t}`)})),this}clearData(){this.heatmapData=[],this.hearmapExData={},this.render()}addData(t,e){const i=this;for(let r=0;r maxTextureSize ? maxTextureSize : this.imgHeight; imageInstance(config.url, function onUpdateCallBack() { self.ctx.activeTexture(self.ctx.TEXTURE0); - self.ctx.bindTexture(self.ctx.TEXTURE_2D, self.imageTexture); + self.ctx.bindTexture(self.ctx.TEXTURE_2D, self._imageTexture); self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_WRAP_S, self.ctx.CLAMP_TO_EDGE); self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_WRAP_T, self.ctx.CLAMP_TO_EDGE); self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_MIN_FILTER, self.ctx.LINEAR); @@ -829,7 +832,7 @@ return this; } /** - * Clears heatmap + * Clear heatmap */ clearData() { this.heatmapData = []; @@ -837,7 +840,7 @@ this.render(); } /** - * After adding data points, need to invoke .render() method to update the heatmap + * Method to append data points. This method automatically adds new data points to the existing dataset and the heatmap updates in immediately. no need to call the ".render" method separately. * @param data - The data points with 'x', 'y' and 'value' * @param transIntactFlag - Flag indicating whether to apply existing heatmap transformations on the newly added data points * @returns instance @@ -855,6 +858,7 @@ return this; } /** + * Method to load data. This will override any existing data. * @param data - Accepts an array of data points with 'x', 'y' and 'value' * @returns instance */ @@ -868,13 +872,13 @@ return this; } /** - * Method to re-render the heatmap. This method needs to be invoked as and when configurations get changed + * Method to update the heatmap. This method to be invoked on every change in configuration. */ render() { renderExec.call(this); } /** - * Get projected co-ordinates relative to the heatmap layer + * Get projected co-ordinates relative to the heatmap * @param data - The data point to project. * @returns projected data point. */ diff --git a/dist/visualHeatmap.min.js b/dist/visualHeatmap.min.js index aa1a8fe..ec70724 100644 --- a/dist/visualHeatmap.min.js +++ b/dist/visualHeatmap.min.js @@ -3,4 +3,4 @@ * (c) 2024 Narayana Swamy (narayanaswamy14@gmail.com) * @license BSD-3-Clause */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).visualHeatmap=e()}(this,(function(){"use strict";const t={vertex:"#version 300 es\n\t\t\t\tin vec2 a_position;\n\t\t\t\tin float a_intensity;\n\t\t\t\tuniform float u_size;\n\t\t\t\tuniform vec2 u_resolution;\n\t\t\t\tuniform vec2 u_translate; \n\t\t\t\tuniform float u_zoom; \n\t\t\t\tuniform float u_angle; \n\t\t\t\tuniform float u_density;\n\t\t\t\tout float v_i;\n\n\t\t\t\tvec2 rotation(vec2 v, float a, float aspect) {\n\t\t\t\t\tfloat s = sin(a); float c = cos(a); mat2 rotationMat = mat2(c, -s, s, c); \n\t\t\t\t\tmat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\tmat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\treturn scaleMatInv * rotationMat * scaleMat * v;\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\t\t\t\t\tvec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);\n\t\t\t\t\tvec2 zeroToTwo = zeroToOne * 2.0 - 1.0;\n\t\t\t\t\tfloat zoomFactor = max(u_zoom, 0.1);\n\t\t\t\t\tzeroToTwo = zeroToTwo / zoomFactor;\n\t\t\t\t\tif (u_angle != 0.0) {\n\t\t\t\t\t\tzeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);\n\t\t\t\t\t}\n\t\t\t\t\tgl_Position = vec4(zeroToTwo , 0, 1);\n\t\t\t\t\tgl_PointSize = u_size * u_density;\n\t\t\t\t\tv_i = a_intensity;\n\t\t\t\t}",fragment:"#version 300 es\n\t\t\t\tprecision mediump float;\n\t\t\t\tuniform float u_max;\n\t\t\t\tuniform float u_min;\n\t\t\t\tuniform float u_intensity;\n\t\t\t\tin float v_i;\n\t\t\t\tout vec4 fragColor;\n\t\t\t\tvoid main() {\n\t\t\t\t\tfloat r = 0.0; \n\t\t\t\t\tvec2 cxy = 2.0 * gl_PointCoord - 1.0;\n\t\t\t\t\tr = dot(cxy, cxy);\n\t\t\t\t\tfloat deno = max(u_max - u_min, 1.0);\n\t\t\t\t\tif(r <= 1.0) {\n\t\t\t\t\t\tfragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));\n\t\t\t\t\t}\n\t\t\t\t}"},e={vertex:"#version 300 es\n\t\t\t\tprecision highp float;\n\t\t\t\tin vec2 a_texCoord;\n\t\t\t\tout vec2 v_texCoord;\n\t\t\t\tvoid main() {\n\t\t\t\t\tvec2 clipSpace = a_texCoord * 2.0 - 1.0;\n\t\t\t\t\tgl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);\n\t\t\t\t\tv_texCoord = a_texCoord;\n\t\t\t\t}\n\t",fragment:"#version 300 es\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t\tin vec2 v_texCoord;\n\t\t\t\t\tout vec4 fragColor;\n\t\t\t\t\tuniform sampler2D u_framebuffer;\n\t\t\t\t\tuniform vec4 u_colorArr[20];\n\t\t\t\t\tuniform float u_colorCount;\n\t\t\t\t\tuniform float u_opacity;\n\t\t\t\t\tuniform float u_offset[20];\n\n\t\t\t\t\tfloat remap ( float minval, float maxval, float curval ) {\n\t\t\t\t\t\treturn ( curval - minval ) / ( maxval - minval );\n\t\t\t\t\t}\n\n\t\t\t\t\tvoid main() {\n\t\t\t\t\t\tfloat alpha = texture(u_framebuffer, v_texCoord.xy).a;\n\t\t\t\t\t\tif (alpha > 0.0 && alpha <= 1.0) {\n\t\t\t\t\t\t\tvec4 color_;\n\n\t\t\t\t\t\t\tif (alpha <= u_offset[0]) {\n\t\t\t\t\t\t\t\tcolor_ = u_colorArr[0];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfor (int i = 1; i <= 20; ++i) {\n\t\t\t\t\t\t\t\t\tif (alpha <= u_offset[i]) {\n\t\t\t\t\t\t\t\t\t\tcolor_ = mix( u_colorArr[i - 1], u_colorArr[i], remap( u_offset[i - 1], u_offset[i], alpha ) );\n\t\t\t\t\t\t\t\t\t\tcolor_ = color_ * mix( u_colorArr[i - 1][3], u_colorArr[i][3], remap( u_offset[i - 1], u_offset[i], alpha ));\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcolor_ = color_ * u_opacity;\n\t\t\t\t\t\t\tif (color_.a < 0.0) {\n\t\t\t\t\t\t\t\tcolor_.a = 0.0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfragColor = color_;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfragColor = vec4(0.0, 0.0, 0.0, 0.0);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t"},i={vertex:"#version 300 es\n precision highp float;\n in vec2 a_position;\n in vec2 a_texCoord;\n uniform vec2 u_resolution;\n\t\t\t\t\tuniform vec2 u_translate; \n\t\t\t\t\tuniform float u_zoom; \n\t\t\t\t\tuniform float u_angle; \n\t\t\t\t\tuniform float u_density;\n out vec2 v_texCoord;\n\n vec2 rotation(vec2 v, float a, float aspect) {\n\t\t\t\t\t\tfloat s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c);\n\t\t\t\t\t\tmat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\tmat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\treturn scaleMatInv * m * scaleMat * v;\n\t\t\t\t\t}\n\n void main() {\n \tvec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);\n \tzeroToOne.y = 1.0 - zeroToOne.y;\n\t\t\t\t\t\tvec2 zeroToTwo = zeroToOne * 2.0 - 1.0;\n\t\t\t\t\t\tfloat zoomFactor = u_zoom;\n\t\t\t\t\t\tif (zoomFactor == 0.0) {\n\t\t\t\t\t\t\tzoomFactor = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tzeroToTwo = zeroToTwo / zoomFactor;\n\t\t\t\t\t\tif (u_angle != 0.0) {\n\t\t\t\t\t\t\tzeroToTwo = rotation(zeroToTwo, u_angle * -1.0, u_resolution.x / u_resolution.y);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl_Position = vec4(zeroToTwo , 0, 1);\n\t\t\t\t\t\tv_texCoord = a_texCoord;\n }\n \t\t",fragment:"#version 300 es\n precision mediump float;\n uniform sampler2D u_image;\n in vec2 v_texCoord;\n out vec4 fragColor;\n void main() {\n fragColor = texture(u_image, v_texCoord);\n }\n "};function r(t,e,i){const r=t.createShader(t[e]);if(!r)throw new Error("Failed to create shader.");t.shaderSource(r,i),t.compileShader(r);if(!t.getShaderParameter(r,t.COMPILE_STATUS)){const e=t.getShaderInfoLog(r);throw t.deleteShader(r),new Error("*** Error compiling shader '"+r+"':"+e)}return r}function o(t,e){const i=r(t,"VERTEX_SHADER",e.vertex),o=r(t,"FRAGMENT_SHADER",e.fragment),n=t.createProgram();if(!n)throw new Error("Failed to create program.");t.attachShader(n,i),t.attachShader(n,o),t.linkProgram(n);if(t.getProgramParameter(n,t.LINK_STATUS))return n;{const e=t.getProgramInfoLog(n);throw t.deleteProgram(n),new Error("Error in program linking:"+e)}}function n(t){return null==t}function a(t){return"number"!=typeof t}function s(t){if(!Array.isArray(t)||t.length<2)throw new Error("Invalid gradient: Expected an array with at least 2 elements.");if(!function(t){for(let e=0;et[e].value&&(n.min=t[e].value),n.max1||t<0)throw this.intensity=t>1?1:0,new Error("Invalid Intensity value "+t);return this.intensity=t,this}setOpacity(t){if(n(t)||a(t))throw new Error("Invalid Opacity: Expected Number");if(t>1||t<0)throw new Error("Invalid Opacity value "+t);return this.opacity=t,this}setBackgroundImage(t){const e=this;if(!t.url)return;const i=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE);return this.imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",this.imageConfig=null,this.imgWidth=t.width||this.width,this.imgHeight=t.height||this.height,this.imgWidth=this.imgWidth>i?i:this.imgWidth,this.imgHeight=this.imgHeight>i?i:this.imgHeight,function(t,e,i){const r=new Image;r.crossOrigin="anonymous",r.onload=e,r.onerror=i,r.src=t}(t.url,(function(){e.ctx.activeTexture(e.ctx.TEXTURE0),e.ctx.bindTexture(e.ctx.TEXTURE_2D,e.imageTexture),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_S,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_T,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MIN_FILTER,e.ctx.LINEAR),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MAG_FILTER,e.ctx.LINEAR),e.ctx.texImage2D(e.ctx.TEXTURE_2D,0,e.ctx.RGBA,this.naturalWidth,this.naturalHeight,0,e.ctx.RGBA,e.ctx.UNSIGNED_BYTE,this),e.imageConfig={x:t.x||0,y:t.y||0,height:e.imgHeight,width:e.imgWidth,image:this},e.render()}),(function(t){throw new Error(`Image Load Error, ${t}`)})),this}clearData(){this.heatmapData=[],this.hearmapExData={},this.render()}addData(t,e){const i=this;for(let r=0;r 0.0 && alpha <= 1.0) {\n\t\t\t\t\t\t\tvec4 color_;\n\n\t\t\t\t\t\t\tif (alpha <= u_offset[0]) {\n\t\t\t\t\t\t\t\tcolor_ = u_colorArr[0];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfor (int i = 1; i <= 20; ++i) {\n\t\t\t\t\t\t\t\t\tif (alpha <= u_offset[i]) {\n\t\t\t\t\t\t\t\t\t\tcolor_ = mix( u_colorArr[i - 1], u_colorArr[i], remap( u_offset[i - 1], u_offset[i], alpha ) );\n\t\t\t\t\t\t\t\t\t\tcolor_ = color_ * mix( u_colorArr[i - 1][3], u_colorArr[i][3], remap( u_offset[i - 1], u_offset[i], alpha ));\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcolor_ = color_ * u_opacity;\n\t\t\t\t\t\t\tif (color_.a < 0.0) {\n\t\t\t\t\t\t\t\tcolor_.a = 0.0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfragColor = color_;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfragColor = vec4(0.0, 0.0, 0.0, 0.0);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t"},i={vertex:"#version 300 es\n precision highp float;\n in vec2 a_position;\n in vec2 a_texCoord;\n uniform vec2 u_resolution;\n\t\t\t\t\tuniform vec2 u_translate; \n\t\t\t\t\tuniform float u_zoom; \n\t\t\t\t\tuniform float u_angle; \n\t\t\t\t\tuniform float u_density;\n out vec2 v_texCoord;\n\n vec2 rotation(vec2 v, float a, float aspect) {\n\t\t\t\t\t\tfloat s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c);\n\t\t\t\t\t\tmat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\tmat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);\n\t\t\t\t\t\treturn scaleMatInv * m * scaleMat * v;\n\t\t\t\t\t}\n\n void main() {\n \tvec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);\n \tzeroToOne.y = 1.0 - zeroToOne.y;\n\t\t\t\t\t\tvec2 zeroToTwo = zeroToOne * 2.0 - 1.0;\n\t\t\t\t\t\tfloat zoomFactor = u_zoom;\n\t\t\t\t\t\tif (zoomFactor == 0.0) {\n\t\t\t\t\t\t\tzoomFactor = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tzeroToTwo = zeroToTwo / zoomFactor;\n\t\t\t\t\t\tif (u_angle != 0.0) {\n\t\t\t\t\t\t\tzeroToTwo = rotation(zeroToTwo, u_angle * -1.0, u_resolution.x / u_resolution.y);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl_Position = vec4(zeroToTwo , 0, 1);\n\t\t\t\t\t\tv_texCoord = a_texCoord;\n }\n \t\t",fragment:"#version 300 es\n precision mediump float;\n uniform sampler2D u_image;\n in vec2 v_texCoord;\n out vec4 fragColor;\n void main() {\n fragColor = texture(u_image, v_texCoord);\n }\n "};function r(t,e,i){const r=t.createShader(t[e]);if(!r)throw new Error("Failed to create shader.");t.shaderSource(r,i),t.compileShader(r);if(!t.getShaderParameter(r,t.COMPILE_STATUS)){const e=t.getShaderInfoLog(r);throw t.deleteShader(r),new Error("*** Error compiling shader '"+r+"':"+e)}return r}function o(t,e){const i=r(t,"VERTEX_SHADER",e.vertex),o=r(t,"FRAGMENT_SHADER",e.fragment),n=t.createProgram();if(!n)throw new Error("Failed to create program.");t.attachShader(n,i),t.attachShader(n,o),t.linkProgram(n);if(t.getProgramParameter(n,t.LINK_STATUS))return n;{const e=t.getProgramInfoLog(n);throw t.deleteProgram(n),new Error("Error in program linking:"+e)}}function n(t){return null==t}function a(t){return"number"!=typeof t}function s(t){if(!Array.isArray(t)||t.length<2)throw new Error("Invalid gradient: Expected an array with at least 2 elements.");if(!function(t){for(let e=0;et[e].value&&(n.min=t[e].value),n.max1||t<0)throw this.intensity=t>1?1:0,new Error("Invalid Intensity value "+t);return this.intensity=t,this}setOpacity(t){if(n(t)||a(t))throw new Error("Invalid Opacity: Expected Number");if(t>1||t<0)throw new Error("Invalid Opacity value "+t);return this.opacity=t,this}setBackgroundImage(t){const e=this;if(!t.url)return;const i=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE);return this._imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",this.imageConfig=null,this.imgWidth=t.width||this.width,this.imgHeight=t.height||this.height,this.imgWidth=this.imgWidth>i?i:this.imgWidth,this.imgHeight=this.imgHeight>i?i:this.imgHeight,function(t,e,i){const r=new Image;r.crossOrigin="anonymous",r.onload=e,r.onerror=i,r.src=t}(t.url,(function(){e.ctx.activeTexture(e.ctx.TEXTURE0),e.ctx.bindTexture(e.ctx.TEXTURE_2D,e._imageTexture),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_S,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_WRAP_T,e.ctx.CLAMP_TO_EDGE),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MIN_FILTER,e.ctx.LINEAR),e.ctx.texParameteri(e.ctx.TEXTURE_2D,e.ctx.TEXTURE_MAG_FILTER,e.ctx.LINEAR),e.ctx.texImage2D(e.ctx.TEXTURE_2D,0,e.ctx.RGBA,this.naturalWidth,this.naturalHeight,0,e.ctx.RGBA,e.ctx.UNSIGNED_BYTE,this),e.imageConfig={x:t.x||0,y:t.y||0,height:e.imgHeight,width:e.imgWidth,image:this},e.render()}),(function(t){throw new Error(`Image Load Error, ${t}`)})),this}clearData(){this.heatmapData=[],this.hearmapExData={},this.render()}addData(t,e){const i=this;for(let r=0;r