diff --git a/README.md b/README.md
index c5e87f0..cf9100f 100644
--- a/README.md
+++ b/README.md
@@ -3,41 +3,43 @@ Visual Heatmap, an open-source JavaScript module, emerges as a powerful tool des
### Examples:
-
- Click me
- Click me
- Click me
+ Click
+ Click
+ Click
+ Click
-## Installing
+# Installing
-If npm
+npm
```
npm i visual-heatmap --save
```
-Download source code from below links
+Or Download source code from below links
* [visualHeatmap.min.js](https://raw.githubusercontent.com/nswamy14/visual-heatmap/master/dist/visualHeatmap.min.js)
* [visualHeatmap.js](https://raw.githubusercontent.com/nswamy14/visual-heatmap/master/dist/visualHeatmap.js)
* [visualHeatmap.esm.js](https://raw.githubusercontent.com/nswamy14/visual-heatmap/master/dist/visualHeatmap.esm.js)
-Visual-Heatmap is written in ES6 Modules. To import use below syntax
-Importing everything into namespace
+
+# Usage
+
+### Importing
+Visual-Heatmap provides ES6 and UMD modules. Accordingly module can be embeded into applications.
+
```
import Heatmap from 'visual-heatmap'
```
-## VisualHeatmapJs - API
-
-### visualHeatmap()
-visualHeatmap provides a API to create context **WebGL**. API accepts container/containerId and config as an input. A layer will be created under the provided Div #containerId.
+### Instance Creation API
+visualHeatmap provides a API to create heatmap instance. API accepts container/containerId and config as an input. A context element will be created under the provided Div #containerId.
```Javascript
let instance = Heatmap('#containerId', {
- size: 30.0,
+ size: 30.0, //Radius of the data point, in pixels. Default: 20
max: 100, // if not set, will be derived from data
min: 0, // if not set, will be derived from data
- intensity: 1.0,
+ intensity: 1.0,
background: {
url: "urlPath",
width: 100, // if not set, viewport width of the image will be considered
@@ -63,32 +65,46 @@ let instance = Heatmap('#containerId', {
}]
});
```
-**Container/ContainerId** The container div element or a string CSS Query selector which identifies the container.
+**Container/ContainerId** : The container div element or a string CSS Query selector which identifies the container.
-**Config**
-Object with config properties.
+**Config Object** :
```
{
- size : Radius of the data point, in pixels.
- max : Max data Value for relative gradient computation.
- min : Min data Value for relative gradient computation.
- intensity : intensity factor.
- opacity : Opacity factor.
- rotationAngle : Rotation angle.
- translate : translate vector [x, y].
- zoom : Zoom Factor.
+ size : Radius of the data point, in pixels. Default: 20
+ max : Max data Value for relative gradient computation. if not set, will be derived from data.
+ min : Min data Value for relative gradient computation. if not set, will be derived from data.
+ intensity : intensity factor. Default: 1.0
+ opacity : Opacity factor. Default: 1.0
+ rotationAngle : Rotation angle. Default: 0
+ translate : translate vector [x, y]. Default: [0,0]
+ zoom : Zoom Factor. Default: 1.0
gradient : Color Gradient, array of objects with color value and offset.
- background: To set background of the heatMap
+ background: To set background of the heatMap. Value : { url: , x: , y: , height: , width: }
}
```
+## Adding Data API
### instance.renderData([])
Accepts an array of data points with 'x', 'y' and 'value'. [Demo](https://nswamy14.github.io/visual-heatmap/demo/heatmap1.html)
+```Javascript
+instance.renderData([{x: , y: , value: }])
+```
### instance.addData([], transformationIntactflag);
-Accepts an array of data points with 'x', 'y' and 'value' and a flag to specify to apply existing canvas transformations on the newly added data points.
+Accepts an array of data points with 'x', 'y' and 'value' and a boolean flag to specify to apply existing heatmap transformations on the newly added data points. After adding data points, need to invoke `.render()` method to update the heatmap.
Try [Example](https://nswamy14.github.io/visual-heatmap/demo/heatmap3.html)
+```Javascript
+instance.addData([{x: , y: , value: }],transformationIntactflag)
+```
+
+## Render API
+Method to re-render the heatmap. This method needs to be invoked as and when configurations get changed. [Example](https://nswamy14.github.io/visual-heatmap/demo/heatmap1.html)
+```Javascript
+instance.render()
+```
+
+## Configuration Setting API
### instance.setMax(number)
To set max data value, for relative gradient calculations.
diff --git a/demo/heatmap1.html b/demo/heatmap1.html
index c4ca57c..7182baa 100644
--- a/demo/heatmap1.html
+++ b/demo/heatmap1.html
@@ -61,25 +61,25 @@
instance.renderData(data);
} );
gui.add( params, 'size', 15, 100 ).onChange( function () {
- instance.setSize(params.size);
+ instance.setSize(params.size).render();
} );
gui.add( params, 'opacity', 0, 1 ).onChange( function () {
- instance.setOpacity(params.opacity);
+ instance.setOpacity(params.opacity).render();
} );
gui.add( params, 'intensity', 0, 1 ).onChange( function () {
- instance.setIntensity(params.intensity);
+ instance.setIntensity(params.intensity).render();
} );
gui.add( params, 'transalteX', -instance.width, instance.width ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
} );
gui.add( params, 'transalteY', -instance.height, instance.height ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
} );
gui.add( params, 'zoom', 0, 2 ).onChange( function () {
- instance.setZoom(params.zoom);
+ instance.setZoom(params.zoom).render();
} );
gui.add( params, 'rotationAngle', 0, Math.PI * 2 ).onChange( function () {
- instance.setRotationAngle(params.rotationAngle);
+ instance.setRotationAngle(params.rotationAngle).render();
} );
data = generateData(10000);
@@ -108,6 +108,12 @@
return num;
}
+ window.addEventListener("resize", function () {
+ if (instance && document.getElementById("canvas")) {
+ instance.resize();
+ }
+ });
+
diff --git a/demo/heatmap2.html b/demo/heatmap2.html
index 5f0b60f..2c825a4 100644
--- a/demo/heatmap2.html
+++ b/demo/heatmap2.html
@@ -57,25 +57,25 @@
var params = new ParamsCon();
gui.add( params, 'size', 5, 100 ).onChange( function () {
- instance.setSize(params.size);
+ instance.setSize(params.size).render();
} );
gui.add( params, 'opacity', 0, 1 ).onChange( function () {
- instance.setOpacity(params.opacity);
+ instance.setOpacity(params.opacity).render();
} );
gui.add( params, 'intensity', 0, 1 ).onChange( function () {
- instance.setIntensity(params.intensity);
+ instance.setIntensity(params.intensity).render();
} );
gui.add( params, 'transalteX', -instance.width, instance.width ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
} );
gui.add( params, 'transalteY', -instance.height, instance.height ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
} );
gui.add( params, 'zoom', 0, 2 ).onChange( function () {
- instance.setZoom(params.zoom);
+ instance.setZoom(params.zoom).render();
} );
gui.add( params, 'rotationAngle', 0, Math.PI * 2 ).onChange( function () {
- instance.setRotationAngle(params.rotationAngle);
+ instance.setRotationAngle(params.rotationAngle).render();
} );
diff --git a/demo/heatmap3.html b/demo/heatmap3.html
index 315c529..3825a4b 100644
--- a/demo/heatmap3.html
+++ b/demo/heatmap3.html
@@ -61,30 +61,31 @@
var params = new ParamsCon();
gui.add( params, 'size', 1, 200 ).onChange( function () {
- instance.setSize(params.size);
+ instance.setSize(params.size).render();
} );
gui.add( params, 'opacity', 0, 1 ).onChange( function () {
- instance.setOpacity(params.opacity);
+ instance.setOpacity(params.opacity).render();
} );
gui.add( params, 'intensity', 0, 1 ).onChange( function () {
- instance.setIntensity(params.intensity);
+ instance.setIntensity(params.intensity).render();
} );
gui.add( params, 'transalteX', -instance.width, instance.width ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
} );
gui.add( params, 'transalteY', -instance.height, instance.height ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
} );
gui.add( params, 'zoom', 0, 2 ).onChange( function () {
- instance.setZoom(params.zoom);
+ instance.setZoom(params.zoom).render();
} );
gui.add( params, 'rotationAngle', 0, Math.PI * 2 ).onChange( function () {
- instance.setRotationAngle(params.rotationAngle);
+ instance.setRotationAngle(params.rotationAngle).render();
} );
var dataPush = true;
document.getElementById('canvas').addEventListener("mousemove", function(e){
- if (dataPush) { instance.addData([{
+ if (dataPush) {
+ instance.addData([{
x: e.x,
y: e.y,
value: 10 +Math.random() * 50
diff --git a/demo/heatmapWithLabels.html b/demo/heatmapWithLabels.html
index 585438a..25688b2 100644
--- a/demo/heatmapWithLabels.html
+++ b/demo/heatmapWithLabels.html
@@ -59,31 +59,31 @@
var params = new ParamsCon();
gui.add( params, 'size', 50, 100 ).onChange( function () {
- instance.setSize(params.size);
+ instance.setSize(params.size).render();
params.updateLabels();
} );
gui.add( params, 'opacity', 0, 1 ).onChange( function () {
- instance.setOpacity(params.opacity);
+ instance.setOpacity(params.opacity).render();
params.updateLabels();
} );
gui.add( params, 'intensity', 0, 1 ).onChange( function () {
- instance.setIntensity(params.intensity);
+ instance.setIntensity(params.intensity).render();
params.updateLabels();
} );
gui.add( params, 'transalteX', -instance.width, instance.width ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
params.updateLabels();
} );
gui.add( params, 'transalteY', -instance.height, instance.height ).onChange( function () {
- instance.setTranslate([params.transalteX, params.transalteY]);
+ instance.setTranslate([params.transalteX, params.transalteY]).render();
params.updateLabels();
} );
gui.add( params, 'zoom', 0, 2 ).onChange( function () {
- instance.setZoom(params.zoom);
+ instance.setZoom(params.zoom).render();
params.updateLabels();
} );
gui.add( params, 'rotationAngle', 0, Math.PI * 2 ).onChange( function () {
- instance.setRotationAngle(params.rotationAngle);
+ instance.setRotationAngle(params.rotationAngle).render();
params.updateLabels();
} );
diff --git a/dist/visualHeatmap.esm.js b/dist/visualHeatmap.esm.js
index 92cdeda..dc477f0 100644
--- a/dist/visualHeatmap.esm.js
+++ b/dist/visualHeatmap.esm.js
@@ -1,5 +1,5 @@
/*!
- * Heatmap v1.0.5
+ * Heatmap v1.1.0
* (c) 2024 Narayana Swamy (narayanaswamy14@gmail.com)
* @license BSD-3-Clause
*/
@@ -10,29 +10,63 @@ function Heatmap (context, config = {}) {
let buffer2;
let rVec = [];
let pLen = 0;
- let dataMinValue = null;
- let dataMaxValue = null;
let maxTextureSize = null;
let imgWidth;
let imgHeight;
+ let hearmapExData;
+ let imageConfig;
+ let configMin = 0;
+ let configMax = 0;
+
+
+ function isNullUndefined (val) {
+ return val === null || val === undefined;
+ }
+
+ function isNotNumber (val) {
+ return typeof val !== 'number';
+ }
+
+ function isSortedAscending (arr) {
+ for (let i = 0; i < arr.length - 1; i++) {
+ if (arr[i + 1].offset - arr[i].offset < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
function gradientMapper (grad) {
- const arr = [];
+ if (grad.constructor !== Array) {
+ throw new Error('Invalid gradient: Wrong Gradient type, expected Array');
+ }
+
+ if (grad.length < 2) {
+ throw new Error('Invalid gradient: 2 or more values expected');
+ }
+
+ if (!isSortedAscending(grad)) {
+ throw new Error('Invalid gradient: Gradient is not sorted');
+ }
+
const gradLength = grad.length;
- const offSetsArray = [];
-
- grad.forEach(function (d) {
- arr.push(d.color[0] / 255);
- arr.push(d.color[1] / 255);
- arr.push(d.color[2] / 255);
- arr.push(d.color[3] === undefined ? 1.0 : d.color[3]);
- offSetsArray.push(d.offset);
+ const values = new Float32Array(gradLength * 4);
+ const offsets = new Array(gradLength);
+
+ grad.forEach(function (d, i) {
+ const baseIndex = i * 4;
+ values[baseIndex] = d.color[0] / 255;
+ values[baseIndex + 1] = d.color[1] / 255;
+ values[baseIndex + 2] = d.color[2] / 255;
+ values[baseIndex + 3] = d.color[3] !== undefined ? d.color[3] : 1.0;
+ offsets[i] = d.offset;
});
+
return {
- value: new Float32Array(arr),
+ value: values,
length: gradLength,
- offset: offSetsArray
+ offset: offsets
};
}
@@ -43,8 +77,8 @@ function Heatmap (context, config = {}) {
var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
if (!compiled) {
var lastError = ctx.getShaderInfoLog(shader);
- console.error("*** Error compiling shader '" + shader + "':" + lastError);
ctx.deleteShader(shader);
+ throw new Error("*** Error compiling shader '" + shader + "':" + lastError);
}
return shader;
}
@@ -62,9 +96,8 @@ function Heatmap (context, config = {}) {
var linked = ctx.getProgramParameter(program, ctx.LINK_STATUS);
if (!linked) {
var lastError = ctx.getProgramInfoLog(program);
- console.error('Error in program linking:' + lastError);
ctx.deleteProgram(program);
- return null;
+ throw new Error('Error in program linking:' + lastError);
} else {
return program;
}
@@ -193,68 +226,111 @@ function Heatmap (context, config = {}) {
}
function Chart (context, config) {
- let res;
- if (typeof context === 'string') {
- res = document.querySelector(context);
- } else if (context instanceof Element) {
- res = context;
- } else {
- throw new Error('Context must be either a string or an Element');
- }
- const height = res.clientHeight;
- const width = res.clientWidth;
- const layer = document.createElement('canvas');
- const ctx = layer.getContext('webgl2', {
- premultipliedAlpha: false,
- depth: false,
- antialias: true,
- alpha: true,
- preserveDrawingBuffer: false
- });
- ratio = getPixlRatio(ctx);
- ctx.clearColor(0, 0, 0, 0);
- ctx.enable(ctx.BLEND);
- ctx.blendEquation(ctx.FUNC_ADD);
- ctx.blendFunc(ctx.ONE, ctx.ONE_MINUS_SRC_ALPHA);
- ctx.depthMask(true);
- layer.setAttribute('height', height * ratio);
- layer.setAttribute('width', width * ratio);
- layer.style.height = `${height}px`;
- layer.style.width = `${width}px`;
- layer.style.position = 'absolute';
- res.appendChild(layer);
-
- this.gradient = gradientMapper(config.gradient);
- this.ctx = ctx;
- this.width = width;
- this.height = height;
- this.layer = layer;
- this.dom = res;
- this.gradShadOP = createGradiantShader(this.ctx);
- this.colorShadOP = createColorShader(this.ctx);
- this.imageShaOP = createImageShader(this.ctx);
- this.fbTexObj = ctx.createTexture();
- this.fbo = ctx.createFramebuffer();
-
- this.size = config.size ? config.size : 20.0;
- dataMaxValue = config.max ? config.max : null;
- dataMinValue = config.min ? config.min : null;
- this.intensity = config.intensity ? config.intensity : 1.0;
- this.translate = (config.translate && config.translate.length === 2) ? config.translate : [0, 0];
- this.zoom = (config.zoom ? config.zoom : 1.0);
- this.angle = (config.rotationAngle ? config.rotationAngle : 0.0);
- this.opacity = config.opacity ? config.opacity : 1.0;
- this.ratio = ratio;
-
- if (config.backgroundImage && config.backgroundImage.url) {
- this.setBackgroundImage(config.backgroundImage);
- }
+ try {
+ let res;
+ if (typeof context === 'string') {
+ res = document.querySelector(context);
+ } else if (context instanceof Element) {
+ res = context;
+ } else {
+ throw new Error('Context must be either a string or an Element');
+ }
+ const height = res.clientHeight;
+ const width = res.clientWidth;
+ const layer = document.createElement('canvas');
+ const ctx = layer.getContext('webgl2', {
+ premultipliedAlpha: false,
+ depth: false,
+ antialias: true,
+ alpha: true,
+ preserveDrawingBuffer: false
+ });
+ ratio = getPixlRatio(ctx);
+ ctx.clearColor(0, 0, 0, 0);
+ ctx.enable(ctx.BLEND);
+ ctx.blendEquation(ctx.FUNC_ADD);
+ ctx.blendFunc(ctx.ONE, ctx.ONE_MINUS_SRC_ALPHA);
+ ctx.depthMask(true);
+ layer.setAttribute('height', height * ratio);
+ layer.setAttribute('width', width * ratio);
+ layer.style.height = `${height}px`;
+ layer.style.width = `${width}px`;
+ layer.style.position = 'absolute';
+ res.appendChild(layer);
+
+ this.ctx = ctx;
+ this.width = width;
+ this.height = height;
+ this.layer = layer;
+ this.dom = res;
+ this.gradShadOP = createGradiantShader(this.ctx);
+ this.colorShadOP = createColorShader(this.ctx);
+ this.imageShaOP = createImageShader(this.ctx);
+ this.fbTexObj = ctx.createTexture();
+ this.fbo = ctx.createFramebuffer();
+
+ if (!isNullUndefined(config.size)) {
+ this.setSize(config.size);
+ } else {
+ this.size = 20.0;
+ }
- this.rawData = [];
+ if (!isNullUndefined(config.max)) {
+ this.setMax(config.max);
+ } else {
+ configMax = null;
+ }
+
+ if (!isNullUndefined(config.min)) {
+ this.setMin(config.min);
+ } else {
+ configMin = null;
+ }
- ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
+ if (!isNullUndefined(config.intensity)) {
+ this.setIntensity(config.intensity);
+ } else {
+ this.intensity = 1.0;
+ }
+
+ if (!isNullUndefined(config.translate)) {
+ this.setTranslate(config.translate);
+ } else {
+ this.translate = [0, 0];
+ }
+
+ if (!isNullUndefined(config.zoom)) {
+ this.setZoom(config.zoom);
+ } else {
+ this.zoom = 1.0;
+ }
- this.render(this.exData || {});
+ if (!isNullUndefined(config.angle)) {
+ this.setRotationAngle(config.angle);
+ } else {
+ this.angle = 0.0;
+ }
+
+ if (!isNullUndefined(config.opacity)) {
+ this.setOpacity(config.opacity);
+ } else {
+ this.opacity = 1.0;
+ }
+
+ this.gradient = gradientMapper(config.gradient);
+
+ this.ratio = ratio;
+
+ if (config.backgroundImage && config.backgroundImage.url) {
+ this.setBackgroundImage(config.backgroundImage);
+ }
+
+ this.heatmapData = [];
+
+ this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+ } catch (error) {
+ console.error(error);
+ }
}
Chart.prototype.resize = function () {
@@ -266,10 +342,9 @@ function Heatmap (context, config = {}) {
this.layer.style.width = `${width}px`;
this.width = width;
this.height = height;
- this.ctx.viewport(0, 0, this.width * ratio, this.height * ratio);
-
+ this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
/* Perform update */
- this.render(this.exData);
+ this.render(hearmapExData);
};
Chart.prototype.clear = function () {
@@ -277,43 +352,90 @@ function Heatmap (context, config = {}) {
};
Chart.prototype.setMax = function (max) {
- dataMaxValue = max;
- this.render(this.exData);
+ if (isNullUndefined(max) || isNotNumber(max)) {
+ throw new Error('Invalid max: Expected Number');
+ }
+
+ configMax = max;
+ return this;
};
Chart.prototype.setMin = function (min) {
- dataMinValue = min;
- this.render(this.exData);
+ if (isNullUndefined(min) || isNotNumber(min)) {
+ throw new Error('Invalid min: Expected Number');
+ }
+
+ configMin = min;
+ return this;
+ };
+
+ Chart.prototype.setGradient = function (gradient) {
+ this.gradient = gradientMapper(gradient);
+ return this;
};
Chart.prototype.setTranslate = function (translate) {
- this.translate = (translate.length === 2) ? translate : [0, 0];
- this.render(this.exData);
+ if (translate.constructor !== Array) {
+ throw new Error('Invalid Translate: Translate has to be of Array type');
+ }
+ if (translate.length !== 2) {
+ throw new Error('Translate has to be of length 2');
+ }
+ this.translate = translate;
+ return this;
};
Chart.prototype.setZoom = function (zoom) {
- this.zoom = zoom !== undefined ? zoom : 1.0;
- this.render(this.exData);
+ if (isNullUndefined(zoom) || isNotNumber(zoom)) {
+ throw new Error('Invalid zoom: Expected Number');
+ }
+
+ this.zoom = zoom;
+ return this;
};
Chart.prototype.setRotationAngle = function (angle) {
- this.angle = angle !== undefined ? angle : 0.0;
- this.render(this.exData);
+ if (isNullUndefined(angle) || isNotNumber(angle)) {
+ throw new Error('Invalid Angle: Expected Number');
+ }
+
+ this.angle = angle;
+ return this;
};
Chart.prototype.setSize = function (size) {
- this.size = size !== undefined ? size : 20.0;
- this.render(this.exData);
+ if (isNullUndefined(size) || isNotNumber(size)) {
+ throw new Error('Invalid Size: Expected Number');
+ }
+
+ this.size = size;
+ return this;
};
Chart.prototype.setIntensity = function (intensity) {
- this.intensity = intensity !== undefined ? intensity : 1.0;
- this.render(this.exData);
+ if (isNullUndefined(intensity) || isNotNumber(intensity)) {
+ this.intensity = 1.0; // applying default intensity
+ throw new Error('Invalid Intensity: Expected Number');
+ }
+
+ if (intensity > 1 || intensity < 0) {
+ this.intensity = intensity > 1 ? 1 : 0; // Setting bound value
+ throw new Error('Invalid Intensity value ' + intensity);
+ }
+ this.intensity = intensity;
+ return this;
};
Chart.prototype.setOpacity = function (opacity) {
- this.opacity = opacity !== undefined ? opacity : 1.0;
- this.render(this.exData);
+ if (isNullUndefined(opacity) || isNotNumber(opacity)) {
+ throw new Error('Invalid Opacity: Expected Number');
+ }
+
+ if (opacity > 1 || opacity < 0) {
+ throw new Error('Invalid Opacity value ' + opacity);
+ }
+ this.opacity = opacity;
+ return this;
};
Chart.prototype.setBackgroundImage = function (config) {
@@ -325,7 +447,7 @@ function Heatmap (context, config = {}) {
maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE);
this.imageTexture = this.ctx.createTexture();
this.type = 'TEXTURE_2D';
- this.imageConfig = null;
+ imageConfig = null;
imgWidth = config.width || this.width;
imgHeight = config.height || this.height;
@@ -353,7 +475,7 @@ function Heatmap (context, config = {}) {
this
);
- self.imageConfig = {
+ imageConfig = {
x: config.x || 0,
y: config.y || 0,
height: imgHeight,
@@ -361,10 +483,17 @@ function Heatmap (context, config = {}) {
image: this
};
- self.render(self.exData || {});
+ self.render();
}, function onErrorCallBack (error) {
- console.error('Image Load Error', error);
+ throw new Error('Image Load Error', error);
});
+ return this;
+ };
+
+ Chart.prototype.clearData = function () {
+ this.heatmapData = [];
+ hearmapExData = {};
+ this.render();
};
Chart.prototype.addData = function (data, transIntactFlag) {
@@ -373,15 +502,24 @@ function Heatmap (context, config = {}) {
if (transIntactFlag) {
transCoOr.call(self, data[i]);
}
- this.rawData.push(data[i]);
+ this.heatmapData.push(data[i]);
}
- this.renderData(this.rawData);
+ this.renderData(this.heatmapData);
+ return this;
};
Chart.prototype.renderData = function (data) {
- const exData = extractData(data);
- this.rawData = data;
- this.render(exData);
+ if (data.constructor !== Array) {
+ throw new Error('Expected Array type');
+ }
+ hearmapExData = extractData(data);
+ this.heatmapData = data;
+ this.render();
+ return this;
+ };
+
+ Chart.prototype.render = function () {
+ renderExec.call(this);
};
Chart.prototype.projection = function (data) {
@@ -418,9 +556,8 @@ function Heatmap (context, config = {}) {
return { x: posX, y: posY };
};
- Chart.prototype.render = function (exData) {
+ function renderExec () {
const ctx = this.ctx;
- this.exData = exData;
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT);
@@ -433,19 +570,20 @@ function Heatmap (context, config = {}) {
ctx.bindFramebuffer(ctx.FRAMEBUFFER, this.fbo);
ctx.framebufferTexture2D(ctx.FRAMEBUFFER, ctx.COLOR_ATTACHMENT0, ctx.TEXTURE_2D, this.fbTexObj, 0);
- renderHeatGrad.call(this, ctx, exData);
+ if (hearmapExData) {
+ renderHeatGrad.call(this, ctx, hearmapExData);
+ }
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
- if (this.imageConfig) {
- renderImage.call(this, ctx);
+ if (imageConfig) {
+ renderImage.call(this, ctx, imageConfig);
}
renderColorGradiant.call(this, ctx);
- };
-
+ }
function renderHeatGrad (ctx, exData) {
ctx.useProgram(this.gradShadOP.program);
- this.min = dataMinValue !== null ? dataMinValue : exData?.minMax?.min ?? 0;
- this.max = dataMaxValue !== null ? dataMaxValue : exData?.minMax?.max ?? 0;
+ this.min = configMin !== null ? configMin : exData?.minMax?.min ?? 0;
+ this.max = configMax !== null ? configMax : exData?.minMax?.max ?? 0;
this.gradShadOP.attr[0].data = exData.posVec || [];
this.gradShadOP.attr[1].data = exData.rVec || [];
@@ -466,11 +604,11 @@ function Heatmap (context, config = {}) {
ctx.vertexAttribPointer(d.attribute, d.size, d.valueType, true, 0, 0);
});
- ctx.drawArrays(ctx.POINTS, 0, (this.exData.posVec || []).length / 2);
+ ctx.drawArrays(ctx.POINTS, 0, (exData.posVec || []).length / 2);
}
- function renderImage (ctx) {
- const { x = 0, y = 0, width = 0, height = 0 } = this.imageConfig;
+ function renderImage (ctx, imageConfig) {
+ const { x = 0, y = 0, width = 0, height = 0 } = imageConfig;
ctx.useProgram(this.imageShaOP.program);
@@ -540,6 +678,9 @@ function Heatmap (context, config = {}) {
posX = (posX * halfWidth) + halfWidth - this.translate[0];
posY = (posY * halfHeight) + halfHeight - this.translate[1];
+ data.x = posX;
+ data.y = posY;
+
return { x: posX, y: posY };
}
@@ -569,57 +710,51 @@ function getPixlRatio (ctx) {
var GradShaders = {
vertex: `#version 300 es
- in vec2 a_position;
- in float a_intensity;
- uniform float u_size;
- uniform vec2 u_resolution;
- uniform vec2 u_translate;
- uniform float u_zoom;
- uniform float u_angle;
- uniform float u_density;
- out float v_i;
-
- vec2 rotation(vec2 v, float a, float aspect) {
- float s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c);
- mat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);
- mat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);
- return scaleMatInv * m * scaleMat * v;
- }
+ in vec2 a_position;
+ in float a_intensity;
+ uniform float u_size;
+ uniform vec2 u_resolution;
+ uniform vec2 u_translate;
+ uniform float u_zoom;
+ uniform float u_angle;
+ uniform float u_density;
+ out float v_i;
+
+ vec2 rotation(vec2 v, float a, float aspect) {
+ float s = sin(a); float c = cos(a); mat2 rotationMat = mat2(c, -s, s, c);
+ mat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);
+ mat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);
+ return scaleMatInv * rotationMat * scaleMat * v;
+ }
- void main() {
- vec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);
- vec2 zeroToTwo = zeroToOne * 2.0 - 1.0;
- float zoomFactor = u_zoom;
- if (zoomFactor == 0.0) {
- zoomFactor = 0.1;
- }
- zeroToTwo = zeroToTwo / zoomFactor;
- if (u_angle != 0.0) {
- zeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);
- }
- gl_Position = vec4(zeroToTwo , 0, 1);
- gl_PointSize = u_size * u_density;
- v_i = a_intensity;
- }`,
+ void main() {
+ vec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);
+ vec2 zeroToTwo = zeroToOne * 2.0 - 1.0;
+ float zoomFactor = max(u_zoom, 0.1);
+ zeroToTwo = zeroToTwo / zoomFactor;
+ if (u_angle != 0.0) {
+ zeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);
+ }
+ gl_Position = vec4(zeroToTwo , 0, 1);
+ gl_PointSize = u_size * u_density;
+ v_i = a_intensity;
+ }`,
fragment: `#version 300 es
- precision mediump float;
- uniform float u_max;
- uniform float u_min;
- uniform float u_intensity;
- in float v_i;
- out vec4 fragColor;
- void main() {
- float r = 0.0;
- vec2 cxy = 2.0 * gl_PointCoord - 1.0;
- r = dot(cxy, cxy);
- float deno = u_max - u_min;
- if (deno <= 0.0) {
- deno = 1.0;
- }
- if(r <= 1.0) {
- fragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));
- }
- }`
+ precision mediump float;
+ uniform float u_max;
+ uniform float u_min;
+ uniform float u_intensity;
+ in float v_i;
+ out vec4 fragColor;
+ void main() {
+ float r = 0.0;
+ vec2 cxy = 2.0 * gl_PointCoord - 1.0;
+ r = dot(cxy, cxy);
+ float deno = max(u_max - u_min, 1.0);
+ if(r <= 1.0) {
+ fragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));
+ }
+ }`
};
var ColorShader = {
@@ -655,7 +790,7 @@ var ColorShader = {
if (alpha <= u_offset[0]) {
color_ = u_colorArr[0];
} else {
- for (int i = 1; i <= 10; ++i) {
+ for (int i = 1; i <= 20; ++i) {
if (alpha <= u_offset[i]) {
color_ = mix( u_colorArr[i - 1], u_colorArr[i], remap( u_offset[i - 1], u_offset[i], alpha ) );
color_ = color_ * mix( u_colorArr[i - 1][3], u_colorArr[i][3], remap( u_offset[i - 1], u_offset[i], alpha ));
diff --git a/dist/visualHeatmap.esm.min.js b/dist/visualHeatmap.esm.min.js
index 7771ce4..563a10d 100644
--- a/dist/visualHeatmap.esm.min.js
+++ b/dist/visualHeatmap.esm.min.js
@@ -1,6 +1,6 @@
/*!
- * Heatmap v1.0.5
+ * Heatmap v1.1.0
* (c) 2024 Narayana Swamy (narayanaswamy14@gmail.com)
* @license BSD-3-Clause
*/
-function t(t,r={}){let n,a,s,u,f,h=[],c=[],l=0,m=null,_=null,d=null;function g(t,e,o){var i=t.createShader(t[e]);if(t.shaderSource(i,o),t.compileShader(i),!t.getShaderParameter(i,t.COMPILE_STATUS)){var r=t.getShaderInfoLog(i);console.error("*** Error compiling shader '"+i+"':"+r),t.deleteShader(i)}return i}function T(t,e){var o=g(t,"VERTEX_SHADER",e.vertex),i=g(t,"FRAGMENT_SHADER",e.fragment),r=t.createProgram();if(t.attachShader(r,o),t.attachShader(r,i),t.linkProgram(r),t.getProgramParameter(r,t.LINK_STATUS))return r;var n=t.getProgramInfoLog(r);return console.error("Error in program linking:"+n),t.deleteProgram(r),null}function x(t,r){let a;if("string"==typeof t)a=document.querySelector(t);else{if(!(t instanceof Element))throw new Error("Context must be either a string or an Element");a=t}const s=a.clientHeight,u=a.clientWidth,f=document.createElement("canvas"),h=f.getContext("webgl2",{premultipliedAlpha:!1,depth:!1,antialias:!0,alpha:!0,preserveDrawingBuffer:!1});n=function(t){const e=window.devicePixelRatio||1,o=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return e/o}(h),h.clearColor(0,0,0,0),h.enable(h.BLEND),h.blendEquation(h.FUNC_ADD),h.blendFunc(h.ONE,h.ONE_MINUS_SRC_ALPHA),h.depthMask(!0),f.setAttribute("height",s*n),f.setAttribute("width",u*n),f.style.height=`${s}px`,f.style.width=`${u}px`,f.style.position="absolute",a.appendChild(f),this.gradient=function(t){const e=[],o=t.length,i=[];return t.forEach((function(t){e.push(t.color[0]/255),e.push(t.color[1]/255),e.push(t.color[2]/255),e.push(void 0===t.color[3]?1:t.color[3]),i.push(t.offset)})),{value:new Float32Array(e),length:o,offset:i}}(r.gradient),this.ctx=h,this.width=u,this.height=s,this.layer=f,this.dom=a,this.gradShadOP=function(t){var o=T(t,e);return{program:o,attr:[{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(o,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:1,attribute:t.getAttribLocation(o,"a_intensity"),data:new Float32Array([])}],uniform:{u_resolution:t.getUniformLocation(o,"u_resolution"),u_max:t.getUniformLocation(o,"u_max"),u_min:t.getUniformLocation(o,"u_min"),u_size:t.getUniformLocation(o,"u_size"),u_intensity:t.getUniformLocation(o,"u_intensity"),u_translate:t.getUniformLocation(o,"u_translate"),u_zoom:t.getUniformLocation(o,"u_zoom"),u_angle:t.getUniformLocation(o,"u_angle"),u_density:t.getUniformLocation(o,"u_density")}}}(this.ctx),this.colorShadOP=function(t){var e=T(t,o);return{program:e,attr:[{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(e,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_framebuffer:t.getUniformLocation(e,"u_framebuffer"),u_colorArr:t.getUniformLocation(e,"u_colorArr"),u_colorCount:t.getUniformLocation(e,"u_colorCount"),u_opacity:t.getUniformLocation(e,"u_opacity"),u_offset:t.getUniformLocation(e,"u_offset")}}}(this.ctx),this.imageShaOP=function(t){var e=T(t,i);return{program:e,attr:[{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(e,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(e,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_resolution:t.getUniformLocation(e,"u_resolution"),u_image:t.getUniformLocation(e,"u_image"),u_translate:t.getUniformLocation(e,"u_translate"),u_zoom:t.getUniformLocation(e,"u_zoom"),u_angle:t.getUniformLocation(e,"u_angle"),u_density:t.getUniformLocation(e,"u_density")}}}(this.ctx),this.fbTexObj=h.createTexture(),this.fbo=h.createFramebuffer(),this.size=r.size?r.size:20,_=r.max?r.max:null,m=r.min?r.min:null,this.intensity=r.intensity?r.intensity:1,this.translate=r.translate&&2===r.translate.length?r.translate:[0,0],this.zoom=r.zoom?r.zoom:1,this.angle=r.rotationAngle?r.rotationAngle:0,this.opacity=r.opacity?r.opacity:1,this.ratio=n,r.backgroundImage&&r.backgroundImage.url&&this.setBackgroundImage(r.backgroundImage),this.rawData=[],h.viewport(0,0,h.canvas.width,h.canvas.height),this.render(this.exData||{})}function p(t,e){t.useProgram(this.gradShadOP.program),this.min=null!==m?m:e?.minMax?.min??0,this.max=null!==_?_:e?.minMax?.max??0,this.gradShadOP.attr[0].data=e.posVec||[],this.gradShadOP.attr[1].data=e.rVec||[],t.uniform2fv(this.gradShadOP.uniform.u_resolution,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(this.gradShadOP.uniform.u_translate,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(this.gradShadOP.uniform.u_zoom,this.zoom?this.zoom:.01),t.uniform1f(this.gradShadOP.uniform.u_angle,this.angle),t.uniform1f(this.gradShadOP.uniform.u_density,this.ratio),t.uniform1f(this.gradShadOP.uniform.u_max,this.max),t.uniform1f(this.gradShadOP.uniform.u_min,this.min),t.uniform1f(this.gradShadOP.uniform.u_size,this.size),t.uniform1f(this.gradShadOP.uniform.u_intensity,this.intensity),this.gradShadOP.attr.forEach((function(e){t.bindBuffer(e.bufferType,e.buffer),t.bufferData(e.bufferType,e.data,e.drawType),t.enableVertexAttribArray(e.attribute),t.vertexAttribPointer(e.attribute,e.size,e.valueType,!0,0,0)})),t.drawArrays(t.POINTS,0,(this.exData.posVec||[]).length/2)}function y(t){const{x:e=0,y:o=0,width:i=0,height:r=0}=this.imageConfig;t.useProgram(this.imageShaOP.program),t.uniform2fv(this.imageShaOP.uniform.u_resolution,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(this.imageShaOP.uniform.u_translate,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(this.imageShaOP.uniform.u_zoom,this.zoom?this.zoom:.01),t.uniform1f(this.imageShaOP.uniform.u_angle,this.angle),t.uniform1f(this.imageShaOP.uniform.u_density,this.ratio),this.imageShaOP.attr[0].data=new Float32Array([e,o,e+i,o,e,o+r,e,o+r,e+i,o,e+i,o+r]),this.imageShaOP.attr.forEach((function(e){t.bindBuffer(e.bufferType,e.buffer),t.bufferData(e.bufferType,e.data,e.drawType),t.enableVertexAttribArray(e.attribute),t.vertexAttribPointer(e.attribute,e.size,e.valueType,!0,0,0)})),t.uniform1i(this.imageShaOP.uniform.u_image,0),t.activeTexture(this.ctx.TEXTURE0),t.bindTexture(this.ctx.TEXTURE_2D,this.imageTexture),t.drawArrays(t.TRIANGLES,0,6)}function v(t){t.useProgram(this.colorShadOP.program),t.uniform4fv(this.colorShadOP.uniform.u_colorArr,this.gradient.value),t.uniform1f(this.colorShadOP.uniform.u_colorCount,this.gradient.length),t.uniform1fv(this.colorShadOP.uniform.u_offset,new Float32Array(this.gradient.offset)),t.uniform1f(this.colorShadOP.uniform.u_opacity,this.opacity),this.colorShadOP.attr.forEach((function(e){t.bindBuffer(e.bufferType,e.buffer),t.bufferData(e.bufferType,e.data,e.drawType),t.enableVertexAttribArray(e.attribute),t.vertexAttribPointer(e.attribute,e.size,e.valueType,!0,0,0)})),t.uniform1i(this.colorShadOP.uniform.u_framebuffer,0),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.fbTexObj),t.drawArrays(t.TRIANGLES,0,6)}function E(t){const e=this.zoom||.1,o=this.width/2,i=this.height/2,r=this.angle;let n=(t.x-o)/o*e,a=(t.y-i)/i*e;if(0!==r){const t=Math.cos(r),e=Math.sin(r),o=t*n-e*a;a=e*n+t*a,n=o}return n=n*o+o-this.translate[0],a=a*i+i-this.translate[1],{x:n,y:a}}return x.prototype.resize=function(){const t=this.dom.clientHeight,e=this.dom.clientWidth;this.layer.setAttribute("height",t*n),this.layer.setAttribute("width",e*n),this.layer.style.height=`${t}px`,this.layer.style.width=`${e}px`,this.width=e,this.height=t,this.ctx.viewport(0,0,this.width*n,this.height*n),this.render(this.exData)},x.prototype.clear=function(){this.ctx.clear(this.ctx.COLOR_BUFFER_BIT|this.ctx.DEPTH_BUFFER_BIT)},x.prototype.setMax=function(t){_=t,this.render(this.exData)},x.prototype.setMin=function(t){m=t,this.render(this.exData)},x.prototype.setTranslate=function(t){this.translate=2===t.length?t:[0,0],this.render(this.exData)},x.prototype.setZoom=function(t){this.zoom=void 0!==t?t:1,this.render(this.exData)},x.prototype.setRotationAngle=function(t){this.angle=void 0!==t?t:0,this.render(this.exData)},x.prototype.setSize=function(t){this.size=void 0!==t?t:20,this.render(this.exData)},x.prototype.setIntensity=function(t){this.intensity=void 0!==t?t:1,this.render(this.exData)},x.prototype.setOpacity=function(t){this.opacity=void 0!==t?t:1,this.render(this.exData)},x.prototype.setBackgroundImage=function(t){const e=this;t.url&&(d=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE),this.imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",this.imageConfig=null,u=t.width||this.width,f=t.height||this.height,u=u>d?d:u,f=f>d?d:f,function(t,e,o){const i=new Image;i.crossOrigin="anonymous",i.onload=e,i.onerror=o,i.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:f,width:u,image:this},e.render(e.exData||{})}),(function(t){console.error("Image Load Error",t)})))},x.prototype.addData=function(t,e){const o=this;for(let i=0;it[i].value&&(o.min=t[i].value),o.max 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 <= 10; ++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 "};export{t as default};
\ No newline at end of file
+function t(t,i={}){let n,a,s,u,f,h,c,l=[],m=[],_=0,d=null,g=0,T=0;function p(t){return null==t}function x(t){return"number"!=typeof t}function y(t){if(t.constructor!==Array)throw new Error("Invalid gradient: Wrong Gradient type, expected Array");if(t.length<2)throw new Error("Invalid gradient: 2 or more values expected");if(!function(t){for(let e=0;e1||t<0)throw this.intensity=t>1?1:0,new Error("Invalid Intensity value "+t);return this.intensity=t,this},A.prototype.setOpacity=function(t){if(p(t)||x(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},A.prototype.setBackgroundImage=function(t){const e=this;if(t.url)return d=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE),this.imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",c=null,u=t.width||this.width,f=t.height||this.height,u=u>d?d:u,f=f>d?d:f,function(t,e,r){const o=new Image;o.crossOrigin="anonymous",o.onload=e,o.onerror=r,o.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),c={x:t.x||0,y:t.y||0,height:f,width:u,image:this},e.render()}),(function(t){throw new Error("Image Load Error",t)})),this},A.prototype.clearData=function(){this.heatmapData=[],h={},this.render()},A.prototype.addData=function(t,e){const r=this;for(let o=0;ot[o].value&&(r.min=t[o].value),r.max 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"},o={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 "};export{t as default};
\ No newline at end of file
diff --git a/dist/visualHeatmap.js b/dist/visualHeatmap.js
index a34c404..903e9bd 100644
--- a/dist/visualHeatmap.js
+++ b/dist/visualHeatmap.js
@@ -1,5 +1,5 @@
/*!
- * Heatmap v1.0.5
+ * Heatmap v1.1.0
* (c) 2024 Narayana Swamy (narayanaswamy14@gmail.com)
* @license BSD-3-Clause
*/
@@ -16,29 +16,63 @@
let buffer2;
let rVec = [];
let pLen = 0;
- let dataMinValue = null;
- let dataMaxValue = null;
let maxTextureSize = null;
let imgWidth;
let imgHeight;
+ let hearmapExData;
+ let imageConfig;
+ let configMin = 0;
+ let configMax = 0;
+
+
+ function isNullUndefined (val) {
+ return val === null || val === undefined;
+ }
+
+ function isNotNumber (val) {
+ return typeof val !== 'number';
+ }
+
+ function isSortedAscending (arr) {
+ for (let i = 0; i < arr.length - 1; i++) {
+ if (arr[i + 1].offset - arr[i].offset < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
function gradientMapper (grad) {
- const arr = [];
+ if (grad.constructor !== Array) {
+ throw new Error('Invalid gradient: Wrong Gradient type, expected Array');
+ }
+
+ if (grad.length < 2) {
+ throw new Error('Invalid gradient: 2 or more values expected');
+ }
+
+ if (!isSortedAscending(grad)) {
+ throw new Error('Invalid gradient: Gradient is not sorted');
+ }
+
const gradLength = grad.length;
- const offSetsArray = [];
-
- grad.forEach(function (d) {
- arr.push(d.color[0] / 255);
- arr.push(d.color[1] / 255);
- arr.push(d.color[2] / 255);
- arr.push(d.color[3] === undefined ? 1.0 : d.color[3]);
- offSetsArray.push(d.offset);
+ const values = new Float32Array(gradLength * 4);
+ const offsets = new Array(gradLength);
+
+ grad.forEach(function (d, i) {
+ const baseIndex = i * 4;
+ values[baseIndex] = d.color[0] / 255;
+ values[baseIndex + 1] = d.color[1] / 255;
+ values[baseIndex + 2] = d.color[2] / 255;
+ values[baseIndex + 3] = d.color[3] !== undefined ? d.color[3] : 1.0;
+ offsets[i] = d.offset;
});
+
return {
- value: new Float32Array(arr),
+ value: values,
length: gradLength,
- offset: offSetsArray
+ offset: offsets
};
}
@@ -49,8 +83,8 @@
var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
if (!compiled) {
var lastError = ctx.getShaderInfoLog(shader);
- console.error("*** Error compiling shader '" + shader + "':" + lastError);
ctx.deleteShader(shader);
+ throw new Error("*** Error compiling shader '" + shader + "':" + lastError);
}
return shader;
}
@@ -68,9 +102,8 @@
var linked = ctx.getProgramParameter(program, ctx.LINK_STATUS);
if (!linked) {
var lastError = ctx.getProgramInfoLog(program);
- console.error('Error in program linking:' + lastError);
ctx.deleteProgram(program);
- return null;
+ throw new Error('Error in program linking:' + lastError);
} else {
return program;
}
@@ -199,68 +232,111 @@
}
function Chart (context, config) {
- let res;
- if (typeof context === 'string') {
- res = document.querySelector(context);
- } else if (context instanceof Element) {
- res = context;
- } else {
- throw new Error('Context must be either a string or an Element');
- }
- const height = res.clientHeight;
- const width = res.clientWidth;
- const layer = document.createElement('canvas');
- const ctx = layer.getContext('webgl2', {
- premultipliedAlpha: false,
- depth: false,
- antialias: true,
- alpha: true,
- preserveDrawingBuffer: false
- });
- ratio = getPixlRatio(ctx);
- ctx.clearColor(0, 0, 0, 0);
- ctx.enable(ctx.BLEND);
- ctx.blendEquation(ctx.FUNC_ADD);
- ctx.blendFunc(ctx.ONE, ctx.ONE_MINUS_SRC_ALPHA);
- ctx.depthMask(true);
- layer.setAttribute('height', height * ratio);
- layer.setAttribute('width', width * ratio);
- layer.style.height = `${height}px`;
- layer.style.width = `${width}px`;
- layer.style.position = 'absolute';
- res.appendChild(layer);
-
- this.gradient = gradientMapper(config.gradient);
- this.ctx = ctx;
- this.width = width;
- this.height = height;
- this.layer = layer;
- this.dom = res;
- this.gradShadOP = createGradiantShader(this.ctx);
- this.colorShadOP = createColorShader(this.ctx);
- this.imageShaOP = createImageShader(this.ctx);
- this.fbTexObj = ctx.createTexture();
- this.fbo = ctx.createFramebuffer();
-
- this.size = config.size ? config.size : 20.0;
- dataMaxValue = config.max ? config.max : null;
- dataMinValue = config.min ? config.min : null;
- this.intensity = config.intensity ? config.intensity : 1.0;
- this.translate = (config.translate && config.translate.length === 2) ? config.translate : [0, 0];
- this.zoom = (config.zoom ? config.zoom : 1.0);
- this.angle = (config.rotationAngle ? config.rotationAngle : 0.0);
- this.opacity = config.opacity ? config.opacity : 1.0;
- this.ratio = ratio;
-
- if (config.backgroundImage && config.backgroundImage.url) {
- this.setBackgroundImage(config.backgroundImage);
- }
+ try {
+ let res;
+ if (typeof context === 'string') {
+ res = document.querySelector(context);
+ } else if (context instanceof Element) {
+ res = context;
+ } else {
+ throw new Error('Context must be either a string or an Element');
+ }
+ const height = res.clientHeight;
+ const width = res.clientWidth;
+ const layer = document.createElement('canvas');
+ const ctx = layer.getContext('webgl2', {
+ premultipliedAlpha: false,
+ depth: false,
+ antialias: true,
+ alpha: true,
+ preserveDrawingBuffer: false
+ });
+ ratio = getPixlRatio(ctx);
+ ctx.clearColor(0, 0, 0, 0);
+ ctx.enable(ctx.BLEND);
+ ctx.blendEquation(ctx.FUNC_ADD);
+ ctx.blendFunc(ctx.ONE, ctx.ONE_MINUS_SRC_ALPHA);
+ ctx.depthMask(true);
+ layer.setAttribute('height', height * ratio);
+ layer.setAttribute('width', width * ratio);
+ layer.style.height = `${height}px`;
+ layer.style.width = `${width}px`;
+ layer.style.position = 'absolute';
+ res.appendChild(layer);
+
+ this.ctx = ctx;
+ this.width = width;
+ this.height = height;
+ this.layer = layer;
+ this.dom = res;
+ this.gradShadOP = createGradiantShader(this.ctx);
+ this.colorShadOP = createColorShader(this.ctx);
+ this.imageShaOP = createImageShader(this.ctx);
+ this.fbTexObj = ctx.createTexture();
+ this.fbo = ctx.createFramebuffer();
+
+ if (!isNullUndefined(config.size)) {
+ this.setSize(config.size);
+ } else {
+ this.size = 20.0;
+ }
- this.rawData = [];
+ if (!isNullUndefined(config.max)) {
+ this.setMax(config.max);
+ } else {
+ configMax = null;
+ }
+
+ if (!isNullUndefined(config.min)) {
+ this.setMin(config.min);
+ } else {
+ configMin = null;
+ }
- ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
+ if (!isNullUndefined(config.intensity)) {
+ this.setIntensity(config.intensity);
+ } else {
+ this.intensity = 1.0;
+ }
+
+ if (!isNullUndefined(config.translate)) {
+ this.setTranslate(config.translate);
+ } else {
+ this.translate = [0, 0];
+ }
+
+ if (!isNullUndefined(config.zoom)) {
+ this.setZoom(config.zoom);
+ } else {
+ this.zoom = 1.0;
+ }
- this.render(this.exData || {});
+ if (!isNullUndefined(config.angle)) {
+ this.setRotationAngle(config.angle);
+ } else {
+ this.angle = 0.0;
+ }
+
+ if (!isNullUndefined(config.opacity)) {
+ this.setOpacity(config.opacity);
+ } else {
+ this.opacity = 1.0;
+ }
+
+ this.gradient = gradientMapper(config.gradient);
+
+ this.ratio = ratio;
+
+ if (config.backgroundImage && config.backgroundImage.url) {
+ this.setBackgroundImage(config.backgroundImage);
+ }
+
+ this.heatmapData = [];
+
+ this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+ } catch (error) {
+ console.error(error);
+ }
}
Chart.prototype.resize = function () {
@@ -272,10 +348,9 @@
this.layer.style.width = `${width}px`;
this.width = width;
this.height = height;
- this.ctx.viewport(0, 0, this.width * ratio, this.height * ratio);
-
+ this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
/* Perform update */
- this.render(this.exData);
+ this.render(hearmapExData);
};
Chart.prototype.clear = function () {
@@ -283,43 +358,90 @@
};
Chart.prototype.setMax = function (max) {
- dataMaxValue = max;
- this.render(this.exData);
+ if (isNullUndefined(max) || isNotNumber(max)) {
+ throw new Error('Invalid max: Expected Number');
+ }
+
+ configMax = max;
+ return this;
};
Chart.prototype.setMin = function (min) {
- dataMinValue = min;
- this.render(this.exData);
+ if (isNullUndefined(min) || isNotNumber(min)) {
+ throw new Error('Invalid min: Expected Number');
+ }
+
+ configMin = min;
+ return this;
+ };
+
+ Chart.prototype.setGradient = function (gradient) {
+ this.gradient = gradientMapper(gradient);
+ return this;
};
Chart.prototype.setTranslate = function (translate) {
- this.translate = (translate.length === 2) ? translate : [0, 0];
- this.render(this.exData);
+ if (translate.constructor !== Array) {
+ throw new Error('Invalid Translate: Translate has to be of Array type');
+ }
+ if (translate.length !== 2) {
+ throw new Error('Translate has to be of length 2');
+ }
+ this.translate = translate;
+ return this;
};
Chart.prototype.setZoom = function (zoom) {
- this.zoom = zoom !== undefined ? zoom : 1.0;
- this.render(this.exData);
+ if (isNullUndefined(zoom) || isNotNumber(zoom)) {
+ throw new Error('Invalid zoom: Expected Number');
+ }
+
+ this.zoom = zoom;
+ return this;
};
Chart.prototype.setRotationAngle = function (angle) {
- this.angle = angle !== undefined ? angle : 0.0;
- this.render(this.exData);
+ if (isNullUndefined(angle) || isNotNumber(angle)) {
+ throw new Error('Invalid Angle: Expected Number');
+ }
+
+ this.angle = angle;
+ return this;
};
Chart.prototype.setSize = function (size) {
- this.size = size !== undefined ? size : 20.0;
- this.render(this.exData);
+ if (isNullUndefined(size) || isNotNumber(size)) {
+ throw new Error('Invalid Size: Expected Number');
+ }
+
+ this.size = size;
+ return this;
};
Chart.prototype.setIntensity = function (intensity) {
- this.intensity = intensity !== undefined ? intensity : 1.0;
- this.render(this.exData);
+ if (isNullUndefined(intensity) || isNotNumber(intensity)) {
+ this.intensity = 1.0; // applying default intensity
+ throw new Error('Invalid Intensity: Expected Number');
+ }
+
+ if (intensity > 1 || intensity < 0) {
+ this.intensity = intensity > 1 ? 1 : 0; // Setting bound value
+ throw new Error('Invalid Intensity value ' + intensity);
+ }
+ this.intensity = intensity;
+ return this;
};
Chart.prototype.setOpacity = function (opacity) {
- this.opacity = opacity !== undefined ? opacity : 1.0;
- this.render(this.exData);
+ if (isNullUndefined(opacity) || isNotNumber(opacity)) {
+ throw new Error('Invalid Opacity: Expected Number');
+ }
+
+ if (opacity > 1 || opacity < 0) {
+ throw new Error('Invalid Opacity value ' + opacity);
+ }
+ this.opacity = opacity;
+ return this;
};
Chart.prototype.setBackgroundImage = function (config) {
@@ -331,7 +453,7 @@
maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE);
this.imageTexture = this.ctx.createTexture();
this.type = 'TEXTURE_2D';
- this.imageConfig = null;
+ imageConfig = null;
imgWidth = config.width || this.width;
imgHeight = config.height || this.height;
@@ -359,7 +481,7 @@
this
);
- self.imageConfig = {
+ imageConfig = {
x: config.x || 0,
y: config.y || 0,
height: imgHeight,
@@ -367,10 +489,17 @@
image: this
};
- self.render(self.exData || {});
+ self.render();
}, function onErrorCallBack (error) {
- console.error('Image Load Error', error);
+ throw new Error('Image Load Error', error);
});
+ return this;
+ };
+
+ Chart.prototype.clearData = function () {
+ this.heatmapData = [];
+ hearmapExData = {};
+ this.render();
};
Chart.prototype.addData = function (data, transIntactFlag) {
@@ -379,15 +508,24 @@
if (transIntactFlag) {
transCoOr.call(self, data[i]);
}
- this.rawData.push(data[i]);
+ this.heatmapData.push(data[i]);
}
- this.renderData(this.rawData);
+ this.renderData(this.heatmapData);
+ return this;
};
Chart.prototype.renderData = function (data) {
- const exData = extractData(data);
- this.rawData = data;
- this.render(exData);
+ if (data.constructor !== Array) {
+ throw new Error('Expected Array type');
+ }
+ hearmapExData = extractData(data);
+ this.heatmapData = data;
+ this.render();
+ return this;
+ };
+
+ Chart.prototype.render = function () {
+ renderExec.call(this);
};
Chart.prototype.projection = function (data) {
@@ -424,9 +562,8 @@
return { x: posX, y: posY };
};
- Chart.prototype.render = function (exData) {
+ function renderExec () {
const ctx = this.ctx;
- this.exData = exData;
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT);
@@ -439,19 +576,20 @@
ctx.bindFramebuffer(ctx.FRAMEBUFFER, this.fbo);
ctx.framebufferTexture2D(ctx.FRAMEBUFFER, ctx.COLOR_ATTACHMENT0, ctx.TEXTURE_2D, this.fbTexObj, 0);
- renderHeatGrad.call(this, ctx, exData);
+ if (hearmapExData) {
+ renderHeatGrad.call(this, ctx, hearmapExData);
+ }
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
- if (this.imageConfig) {
- renderImage.call(this, ctx);
+ if (imageConfig) {
+ renderImage.call(this, ctx, imageConfig);
}
renderColorGradiant.call(this, ctx);
- };
-
+ }
function renderHeatGrad (ctx, exData) {
ctx.useProgram(this.gradShadOP.program);
- this.min = dataMinValue !== null ? dataMinValue : exData?.minMax?.min ?? 0;
- this.max = dataMaxValue !== null ? dataMaxValue : exData?.minMax?.max ?? 0;
+ this.min = configMin !== null ? configMin : exData?.minMax?.min ?? 0;
+ this.max = configMax !== null ? configMax : exData?.minMax?.max ?? 0;
this.gradShadOP.attr[0].data = exData.posVec || [];
this.gradShadOP.attr[1].data = exData.rVec || [];
@@ -472,11 +610,11 @@
ctx.vertexAttribPointer(d.attribute, d.size, d.valueType, true, 0, 0);
});
- ctx.drawArrays(ctx.POINTS, 0, (this.exData.posVec || []).length / 2);
+ ctx.drawArrays(ctx.POINTS, 0, (exData.posVec || []).length / 2);
}
- function renderImage (ctx) {
- const { x = 0, y = 0, width = 0, height = 0 } = this.imageConfig;
+ function renderImage (ctx, imageConfig) {
+ const { x = 0, y = 0, width = 0, height = 0 } = imageConfig;
ctx.useProgram(this.imageShaOP.program);
@@ -546,6 +684,9 @@
posX = (posX * halfWidth) + halfWidth - this.translate[0];
posY = (posY * halfHeight) + halfHeight - this.translate[1];
+ data.x = posX;
+ data.y = posY;
+
return { x: posX, y: posY };
}
@@ -575,57 +716,51 @@
var GradShaders = {
vertex: `#version 300 es
- in vec2 a_position;
- in float a_intensity;
- uniform float u_size;
- uniform vec2 u_resolution;
- uniform vec2 u_translate;
- uniform float u_zoom;
- uniform float u_angle;
- uniform float u_density;
- out float v_i;
-
- vec2 rotation(vec2 v, float a, float aspect) {
- float s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c);
- mat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);
- mat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);
- return scaleMatInv * m * scaleMat * v;
- }
+ in vec2 a_position;
+ in float a_intensity;
+ uniform float u_size;
+ uniform vec2 u_resolution;
+ uniform vec2 u_translate;
+ uniform float u_zoom;
+ uniform float u_angle;
+ uniform float u_density;
+ out float v_i;
+
+ vec2 rotation(vec2 v, float a, float aspect) {
+ float s = sin(a); float c = cos(a); mat2 rotationMat = mat2(c, -s, s, c);
+ mat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);
+ mat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);
+ return scaleMatInv * rotationMat * scaleMat * v;
+ }
- void main() {
- vec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);
- vec2 zeroToTwo = zeroToOne * 2.0 - 1.0;
- float zoomFactor = u_zoom;
- if (zoomFactor == 0.0) {
- zoomFactor = 0.1;
- }
- zeroToTwo = zeroToTwo / zoomFactor;
- if (u_angle != 0.0) {
- zeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);
- }
- gl_Position = vec4(zeroToTwo , 0, 1);
- gl_PointSize = u_size * u_density;
- v_i = a_intensity;
- }`,
+ void main() {
+ vec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);
+ vec2 zeroToTwo = zeroToOne * 2.0 - 1.0;
+ float zoomFactor = max(u_zoom, 0.1);
+ zeroToTwo = zeroToTwo / zoomFactor;
+ if (u_angle != 0.0) {
+ zeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);
+ }
+ gl_Position = vec4(zeroToTwo , 0, 1);
+ gl_PointSize = u_size * u_density;
+ v_i = a_intensity;
+ }`,
fragment: `#version 300 es
- precision mediump float;
- uniform float u_max;
- uniform float u_min;
- uniform float u_intensity;
- in float v_i;
- out vec4 fragColor;
- void main() {
- float r = 0.0;
- vec2 cxy = 2.0 * gl_PointCoord - 1.0;
- r = dot(cxy, cxy);
- float deno = u_max - u_min;
- if (deno <= 0.0) {
- deno = 1.0;
- }
- if(r <= 1.0) {
- fragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));
- }
- }`
+ precision mediump float;
+ uniform float u_max;
+ uniform float u_min;
+ uniform float u_intensity;
+ in float v_i;
+ out vec4 fragColor;
+ void main() {
+ float r = 0.0;
+ vec2 cxy = 2.0 * gl_PointCoord - 1.0;
+ r = dot(cxy, cxy);
+ float deno = max(u_max - u_min, 1.0);
+ if(r <= 1.0) {
+ fragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));
+ }
+ }`
};
var ColorShader = {
@@ -661,7 +796,7 @@
if (alpha <= u_offset[0]) {
color_ = u_colorArr[0];
} else {
- for (int i = 1; i <= 10; ++i) {
+ for (int i = 1; i <= 20; ++i) {
if (alpha <= u_offset[i]) {
color_ = mix( u_colorArr[i - 1], u_colorArr[i], remap( u_offset[i - 1], u_offset[i], alpha ) );
color_ = color_ * mix( u_colorArr[i - 1][3], u_colorArr[i][3], remap( u_offset[i - 1], u_offset[i], alpha ));
diff --git a/dist/visualHeatmap.min.js b/dist/visualHeatmap.min.js
index 1ab2b47..5e15a0d 100644
--- a/dist/visualHeatmap.min.js
+++ b/dist/visualHeatmap.min.js
@@ -1,6 +1,6 @@
/*!
- * Heatmap v1.0.5
+ * Heatmap v1.1.0
* (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";var t={vertex:"#version 300 es\n\tin vec2 a_position;\n\tin float a_intensity;\n\tuniform float u_size;\n\tuniform vec2 u_resolution;\n\tuniform vec2 u_translate; \n\tuniform float u_zoom; \n\tuniform float u_angle; \n\tuniform float u_density;\n\tout float v_i;\n\n\tvec2 rotation(vec2 v, float a, float aspect) {\n\t\tfloat s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c); \n\t\tmat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);\n\t\tmat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);\n\t\treturn scaleMatInv * m * scaleMat * v;\n\t}\n\n\tvoid main() {\n\t\tvec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);\n\t\tvec2 zeroToTwo = zeroToOne * 2.0 - 1.0;\n\t\tfloat zoomFactor = u_zoom;\n\t\tif (zoomFactor == 0.0) {\n\t\t\tzoomFactor = 0.1;\n\t\t}\n\t\tzeroToTwo = zeroToTwo / zoomFactor;\n\t\tif (u_angle != 0.0) {\n\t\t\tzeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);\n\t\t}\n\t\tgl_Position = vec4(zeroToTwo , 0, 1);\n\t\tgl_PointSize = u_size * u_density;\n\t\tv_i = a_intensity;\n\t}",fragment:"#version 300 es\n\tprecision mediump float;\n\tuniform float u_max;\n\tuniform float u_min;\n\tuniform float u_intensity;\n\tin float v_i;\n\tout vec4 fragColor;\n\tvoid main() {\n\t\tfloat r = 0.0; \n\t\tvec2 cxy = 2.0 * gl_PointCoord - 1.0;\n\t\tr = dot(cxy, cxy);\n\t\tfloat deno = u_max - u_min;\n\t\tif (deno <= 0.0) {\n\t\t\tdeno = 1.0;\n\t\t}\n\t\tif(r <= 1.0) {\n\t\t\tfragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));\n\t\t}\n\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 <= 10; ++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"},o={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 "};return function(i,r={}){let n,a,s,u,f,h=[],l=[],c=0,m=null,_=null,d=null;function g(t,e,o){var i=t.createShader(t[e]);if(t.shaderSource(i,o),t.compileShader(i),!t.getShaderParameter(i,t.COMPILE_STATUS)){var r=t.getShaderInfoLog(i);console.error("*** Error compiling shader '"+i+"':"+r),t.deleteShader(i)}return i}function T(t,e){var o=g(t,"VERTEX_SHADER",e.vertex),i=g(t,"FRAGMENT_SHADER",e.fragment),r=t.createProgram();if(t.attachShader(r,o),t.attachShader(r,i),t.linkProgram(r),t.getProgramParameter(r,t.LINK_STATUS))return r;var n=t.getProgramInfoLog(r);return console.error("Error in program linking:"+n),t.deleteProgram(r),null}function x(i,r){let a;if("string"==typeof i)a=document.querySelector(i);else{if(!(i instanceof Element))throw new Error("Context must be either a string or an Element");a=i}const s=a.clientHeight,u=a.clientWidth,f=document.createElement("canvas"),h=f.getContext("webgl2",{premultipliedAlpha:!1,depth:!1,antialias:!0,alpha:!0,preserveDrawingBuffer:!1});n=function(t){const e=window.devicePixelRatio||1,o=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return e/o}(h),h.clearColor(0,0,0,0),h.enable(h.BLEND),h.blendEquation(h.FUNC_ADD),h.blendFunc(h.ONE,h.ONE_MINUS_SRC_ALPHA),h.depthMask(!0),f.setAttribute("height",s*n),f.setAttribute("width",u*n),f.style.height=`${s}px`,f.style.width=`${u}px`,f.style.position="absolute",a.appendChild(f),this.gradient=function(t){const e=[],o=t.length,i=[];return t.forEach((function(t){e.push(t.color[0]/255),e.push(t.color[1]/255),e.push(t.color[2]/255),e.push(void 0===t.color[3]?1:t.color[3]),i.push(t.offset)})),{value:new Float32Array(e),length:o,offset:i}}(r.gradient),this.ctx=h,this.width=u,this.height=s,this.layer=f,this.dom=a,this.gradShadOP=function(e){var o=T(e,t);return{program:o,attr:[{bufferType:e.ARRAY_BUFFER,buffer:e.createBuffer(),drawType:e.STATIC_DRAW,valueType:e.FLOAT,size:2,attribute:e.getAttribLocation(o,"a_position"),data:new Float32Array([])},{bufferType:e.ARRAY_BUFFER,buffer:e.createBuffer(),drawType:e.STATIC_DRAW,valueType:e.FLOAT,size:1,attribute:e.getAttribLocation(o,"a_intensity"),data:new Float32Array([])}],uniform:{u_resolution:e.getUniformLocation(o,"u_resolution"),u_max:e.getUniformLocation(o,"u_max"),u_min:e.getUniformLocation(o,"u_min"),u_size:e.getUniformLocation(o,"u_size"),u_intensity:e.getUniformLocation(o,"u_intensity"),u_translate:e.getUniformLocation(o,"u_translate"),u_zoom:e.getUniformLocation(o,"u_zoom"),u_angle:e.getUniformLocation(o,"u_angle"),u_density:e.getUniformLocation(o,"u_density")}}}(this.ctx),this.colorShadOP=function(t){var o=T(t,e);return{program:o,attr:[{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(o,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_framebuffer:t.getUniformLocation(o,"u_framebuffer"),u_colorArr:t.getUniformLocation(o,"u_colorArr"),u_colorCount:t.getUniformLocation(o,"u_colorCount"),u_opacity:t.getUniformLocation(o,"u_opacity"),u_offset:t.getUniformLocation(o,"u_offset")}}}(this.ctx),this.imageShaOP=function(t){var e=T(t,o);return{program:e,attr:[{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(e,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(e,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_resolution:t.getUniformLocation(e,"u_resolution"),u_image:t.getUniformLocation(e,"u_image"),u_translate:t.getUniformLocation(e,"u_translate"),u_zoom:t.getUniformLocation(e,"u_zoom"),u_angle:t.getUniformLocation(e,"u_angle"),u_density:t.getUniformLocation(e,"u_density")}}}(this.ctx),this.fbTexObj=h.createTexture(),this.fbo=h.createFramebuffer(),this.size=r.size?r.size:20,_=r.max?r.max:null,m=r.min?r.min:null,this.intensity=r.intensity?r.intensity:1,this.translate=r.translate&&2===r.translate.length?r.translate:[0,0],this.zoom=r.zoom?r.zoom:1,this.angle=r.rotationAngle?r.rotationAngle:0,this.opacity=r.opacity?r.opacity:1,this.ratio=n,r.backgroundImage&&r.backgroundImage.url&&this.setBackgroundImage(r.backgroundImage),this.rawData=[],h.viewport(0,0,h.canvas.width,h.canvas.height),this.render(this.exData||{})}function p(t,e){t.useProgram(this.gradShadOP.program),this.min=null!==m?m:e?.minMax?.min??0,this.max=null!==_?_:e?.minMax?.max??0,this.gradShadOP.attr[0].data=e.posVec||[],this.gradShadOP.attr[1].data=e.rVec||[],t.uniform2fv(this.gradShadOP.uniform.u_resolution,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(this.gradShadOP.uniform.u_translate,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(this.gradShadOP.uniform.u_zoom,this.zoom?this.zoom:.01),t.uniform1f(this.gradShadOP.uniform.u_angle,this.angle),t.uniform1f(this.gradShadOP.uniform.u_density,this.ratio),t.uniform1f(this.gradShadOP.uniform.u_max,this.max),t.uniform1f(this.gradShadOP.uniform.u_min,this.min),t.uniform1f(this.gradShadOP.uniform.u_size,this.size),t.uniform1f(this.gradShadOP.uniform.u_intensity,this.intensity),this.gradShadOP.attr.forEach((function(e){t.bindBuffer(e.bufferType,e.buffer),t.bufferData(e.bufferType,e.data,e.drawType),t.enableVertexAttribArray(e.attribute),t.vertexAttribPointer(e.attribute,e.size,e.valueType,!0,0,0)})),t.drawArrays(t.POINTS,0,(this.exData.posVec||[]).length/2)}function y(t){const{x:e=0,y:o=0,width:i=0,height:r=0}=this.imageConfig;t.useProgram(this.imageShaOP.program),t.uniform2fv(this.imageShaOP.uniform.u_resolution,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(this.imageShaOP.uniform.u_translate,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(this.imageShaOP.uniform.u_zoom,this.zoom?this.zoom:.01),t.uniform1f(this.imageShaOP.uniform.u_angle,this.angle),t.uniform1f(this.imageShaOP.uniform.u_density,this.ratio),this.imageShaOP.attr[0].data=new Float32Array([e,o,e+i,o,e,o+r,e,o+r,e+i,o,e+i,o+r]),this.imageShaOP.attr.forEach((function(e){t.bindBuffer(e.bufferType,e.buffer),t.bufferData(e.bufferType,e.data,e.drawType),t.enableVertexAttribArray(e.attribute),t.vertexAttribPointer(e.attribute,e.size,e.valueType,!0,0,0)})),t.uniform1i(this.imageShaOP.uniform.u_image,0),t.activeTexture(this.ctx.TEXTURE0),t.bindTexture(this.ctx.TEXTURE_2D,this.imageTexture),t.drawArrays(t.TRIANGLES,0,6)}function v(t){t.useProgram(this.colorShadOP.program),t.uniform4fv(this.colorShadOP.uniform.u_colorArr,this.gradient.value),t.uniform1f(this.colorShadOP.uniform.u_colorCount,this.gradient.length),t.uniform1fv(this.colorShadOP.uniform.u_offset,new Float32Array(this.gradient.offset)),t.uniform1f(this.colorShadOP.uniform.u_opacity,this.opacity),this.colorShadOP.attr.forEach((function(e){t.bindBuffer(e.bufferType,e.buffer),t.bufferData(e.bufferType,e.data,e.drawType),t.enableVertexAttribArray(e.attribute),t.vertexAttribPointer(e.attribute,e.size,e.valueType,!0,0,0)})),t.uniform1i(this.colorShadOP.uniform.u_framebuffer,0),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.fbTexObj),t.drawArrays(t.TRIANGLES,0,6)}function E(t){const e=this.zoom||.1,o=this.width/2,i=this.height/2,r=this.angle;let n=(t.x-o)/o*e,a=(t.y-i)/i*e;if(0!==r){const t=Math.cos(r),e=Math.sin(r),o=t*n-e*a;a=e*n+t*a,n=o}return n=n*o+o-this.translate[0],a=a*i+i-this.translate[1],{x:n,y:a}}return x.prototype.resize=function(){const t=this.dom.clientHeight,e=this.dom.clientWidth;this.layer.setAttribute("height",t*n),this.layer.setAttribute("width",e*n),this.layer.style.height=`${t}px`,this.layer.style.width=`${e}px`,this.width=e,this.height=t,this.ctx.viewport(0,0,this.width*n,this.height*n),this.render(this.exData)},x.prototype.clear=function(){this.ctx.clear(this.ctx.COLOR_BUFFER_BIT|this.ctx.DEPTH_BUFFER_BIT)},x.prototype.setMax=function(t){_=t,this.render(this.exData)},x.prototype.setMin=function(t){m=t,this.render(this.exData)},x.prototype.setTranslate=function(t){this.translate=2===t.length?t:[0,0],this.render(this.exData)},x.prototype.setZoom=function(t){this.zoom=void 0!==t?t:1,this.render(this.exData)},x.prototype.setRotationAngle=function(t){this.angle=void 0!==t?t:0,this.render(this.exData)},x.prototype.setSize=function(t){this.size=void 0!==t?t:20,this.render(this.exData)},x.prototype.setIntensity=function(t){this.intensity=void 0!==t?t:1,this.render(this.exData)},x.prototype.setOpacity=function(t){this.opacity=void 0!==t?t:1,this.render(this.exData)},x.prototype.setBackgroundImage=function(t){const e=this;t.url&&(d=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE),this.imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",this.imageConfig=null,u=t.width||this.width,f=t.height||this.height,u=u>d?d:u,f=f>d?d:f,function(t,e,o){const i=new Image;i.crossOrigin="anonymous",i.onload=e,i.onerror=o,i.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:f,width:u,image:this},e.render(e.exData||{})}),(function(t){console.error("Image Load Error",t)})))},x.prototype.addData=function(t,e){const o=this;for(let i=0;it[i].value&&(o.min=t[i].value),o.max 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"},r={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 "};return function(o,i={}){let n,a,s,u,f,h,c,l=[],m=[],_=0,d=null,g=0,T=0;function p(t){return null==t}function x(t){return"number"!=typeof t}function y(t){if(t.constructor!==Array)throw new Error("Invalid gradient: Wrong Gradient type, expected Array");if(t.length<2)throw new Error("Invalid gradient: 2 or more values expected");if(!function(t){for(let e=0;e1||t<0)throw this.intensity=t>1?1:0,new Error("Invalid Intensity value "+t);return this.intensity=t,this},A.prototype.setOpacity=function(t){if(p(t)||x(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},A.prototype.setBackgroundImage=function(t){const e=this;if(t.url)return d=this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE),this.imageTexture=this.ctx.createTexture(),this.type="TEXTURE_2D",c=null,u=t.width||this.width,f=t.height||this.height,u=u>d?d:u,f=f>d?d:f,function(t,e,r){const o=new Image;o.crossOrigin="anonymous",o.onload=e,o.onerror=r,o.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),c={x:t.x||0,y:t.y||0,height:f,width:u,image:this},e.render()}),(function(t){throw new Error("Image Load Error",t)})),this},A.prototype.clearData=function(){this.heatmapData=[],h={},this.render()},A.prototype.addData=function(t,e){const r=this;for(let o=0;ot[o].value&&(r.min=t[o].value),r.max 1 || intensity < 0) {
+ this.intensity = intensity > 1 ? 1 : 0; // Setting bound value
+ throw new Error('Invalid Intensity value ' + intensity);
+ }
+ this.intensity = intensity;
+ return this;
};
Chart.prototype.setOpacity = function (opacity) {
- this.opacity = opacity !== undefined ? opacity : 1.0;
- this.render(this.exData);
+ if (isNullUndefined(opacity) || isNotNumber(opacity)) {
+ throw new Error('Invalid Opacity: Expected Number');
+ }
+
+ if (opacity > 1 || opacity < 0) {
+ throw new Error('Invalid Opacity value ' + opacity);
+ }
+ this.opacity = opacity;
+ return this;
};
Chart.prototype.setBackgroundImage = function (config) {
@@ -320,7 +442,7 @@ export default function Heatmap (context, config = {}) {
maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE);
this.imageTexture = this.ctx.createTexture();
this.type = 'TEXTURE_2D';
- this.imageConfig = null;
+ imageConfig = null;
imgWidth = config.width || this.width;
imgHeight = config.height || this.height;
@@ -348,7 +470,7 @@ export default function Heatmap (context, config = {}) {
this
);
- self.imageConfig = {
+ imageConfig = {
x: config.x || 0,
y: config.y || 0,
height: imgHeight,
@@ -356,10 +478,17 @@ export default function Heatmap (context, config = {}) {
image: this
};
- self.render(self.exData || {});
+ self.render();
}, function onErrorCallBack (error) {
- console.error('Image Load Error', error);
+ throw new Error('Image Load Error', error);
});
+ return this;
+ };
+
+ Chart.prototype.clearData = function () {
+ this.heatmapData = [];
+ hearmapExData = {};
+ this.render();
};
Chart.prototype.addData = function (data, transIntactFlag) {
@@ -368,15 +497,24 @@ export default function Heatmap (context, config = {}) {
if (transIntactFlag) {
transCoOr.call(self, data[i]);
}
- this.rawData.push(data[i]);
+ this.heatmapData.push(data[i]);
}
- this.renderData(this.rawData);
+ this.renderData(this.heatmapData);
+ return this;
};
Chart.prototype.renderData = function (data) {
- const exData = extractData(data);
- this.rawData = data;
- this.render(exData);
+ if (data.constructor !== Array) {
+ throw new Error('Expected Array type');
+ }
+ hearmapExData = extractData(data);
+ this.heatmapData = data;
+ this.render();
+ return this;
+ };
+
+ Chart.prototype.render = function () {
+ renderExec.call(this);
};
Chart.prototype.projection = function (data) {
@@ -413,9 +551,8 @@ export default function Heatmap (context, config = {}) {
return { x: posX, y: posY };
};
- Chart.prototype.render = function (exData) {
+ function renderExec () {
const ctx = this.ctx;
- this.exData = exData;
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT);
@@ -428,10 +565,12 @@ export default function Heatmap (context, config = {}) {
ctx.bindFramebuffer(ctx.FRAMEBUFFER, this.fbo);
ctx.framebufferTexture2D(ctx.FRAMEBUFFER, ctx.COLOR_ATTACHMENT0, ctx.TEXTURE_2D, this.fbTexObj, 0);
- renderHeatGrad.call(this, ctx, exData);
+ if (hearmapExData) {
+ renderHeatGrad.call(this, ctx, hearmapExData);
+ }
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
- if (this.imageConfig) {
- renderImage.call(this, ctx);
+ if (imageConfig) {
+ renderImage.call(this, ctx, imageConfig);
}
renderColorGradiant.call(this, ctx);
};
@@ -439,8 +578,8 @@ export default function Heatmap (context, config = {}) {
function renderHeatGrad (ctx, exData) {
ctx.useProgram(this.gradShadOP.program);
- this.min = dataMinValue !== null ? dataMinValue : exData?.minMax?.min ?? 0;
- this.max = dataMaxValue !== null ? dataMaxValue : exData?.minMax?.max ?? 0;
+ this.min = configMin !== null ? configMin : exData?.minMax?.min ?? 0;
+ this.max = configMax !== null ? configMax : exData?.minMax?.max ?? 0;
this.gradShadOP.attr[0].data = exData.posVec || [];
this.gradShadOP.attr[1].data = exData.rVec || [];
@@ -461,11 +600,11 @@ export default function Heatmap (context, config = {}) {
ctx.vertexAttribPointer(d.attribute, d.size, d.valueType, true, 0, 0);
});
- ctx.drawArrays(ctx.POINTS, 0, (this.exData.posVec || []).length / 2);
+ ctx.drawArrays(ctx.POINTS, 0, (exData.posVec || []).length / 2);
}
- function renderImage (ctx) {
- const { x = 0, y = 0, width = 0, height = 0 } = this.imageConfig;
+ function renderImage (ctx, imageConfig) {
+ const { x = 0, y = 0, width = 0, height = 0 } = imageConfig;
ctx.useProgram(this.imageShaOP.program);
@@ -535,6 +674,9 @@ export default function Heatmap (context, config = {}) {
posX = (posX * halfWidth) + halfWidth - this.translate[0];
posY = (posY * halfHeight) + halfHeight - this.translate[1];
+ data.x = posX;
+ data.y = posY;
+
return { x: posX, y: posY };
}
@@ -564,57 +706,51 @@ function getPixlRatio (ctx) {
var GradShaders = {
vertex: `#version 300 es
- in vec2 a_position;
- in float a_intensity;
- uniform float u_size;
- uniform vec2 u_resolution;
- uniform vec2 u_translate;
- uniform float u_zoom;
- uniform float u_angle;
- uniform float u_density;
- out float v_i;
-
- vec2 rotation(vec2 v, float a, float aspect) {
- float s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c);
- mat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);
- mat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);
- return scaleMatInv * m * scaleMat * v;
- }
+ in vec2 a_position;
+ in float a_intensity;
+ uniform float u_size;
+ uniform vec2 u_resolution;
+ uniform vec2 u_translate;
+ uniform float u_zoom;
+ uniform float u_angle;
+ uniform float u_density;
+ out float v_i;
+
+ vec2 rotation(vec2 v, float a, float aspect) {
+ float s = sin(a); float c = cos(a); mat2 rotationMat = mat2(c, -s, s, c);
+ mat2 scaleMat = mat2(aspect, 0.0, 0.0, 1.0);
+ mat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);
+ return scaleMatInv * rotationMat * scaleMat * v;
+ }
- void main() {
- vec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);
- vec2 zeroToTwo = zeroToOne * 2.0 - 1.0;
- float zoomFactor = u_zoom;
- if (zoomFactor == 0.0) {
- zoomFactor = 0.1;
- }
- zeroToTwo = zeroToTwo / zoomFactor;
- if (u_angle != 0.0) {
- zeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);
- }
- gl_Position = vec4(zeroToTwo , 0, 1);
- gl_PointSize = u_size * u_density;
- v_i = a_intensity;
- }`,
+ void main() {
+ vec2 zeroToOne = (a_position * u_density + u_translate * u_density) / (u_resolution);
+ vec2 zeroToTwo = zeroToOne * 2.0 - 1.0;
+ float zoomFactor = max(u_zoom, 0.1);
+ zeroToTwo = zeroToTwo / zoomFactor;
+ if (u_angle != 0.0) {
+ zeroToTwo = rotation(zeroToTwo, u_angle, u_resolution.x / u_resolution.y);
+ }
+ gl_Position = vec4(zeroToTwo , 0, 1);
+ gl_PointSize = u_size * u_density;
+ v_i = a_intensity;
+ }`,
fragment: `#version 300 es
- precision mediump float;
- uniform float u_max;
- uniform float u_min;
- uniform float u_intensity;
- in float v_i;
- out vec4 fragColor;
- void main() {
- float r = 0.0;
- vec2 cxy = 2.0 * gl_PointCoord - 1.0;
- r = dot(cxy, cxy);
- float deno = u_max - u_min;
- if (deno <= 0.0) {
- deno = 1.0;
- }
- if(r <= 1.0) {
- fragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));
- }
- }`
+ precision mediump float;
+ uniform float u_max;
+ uniform float u_min;
+ uniform float u_intensity;
+ in float v_i;
+ out vec4 fragColor;
+ void main() {
+ float r = 0.0;
+ vec2 cxy = 2.0 * gl_PointCoord - 1.0;
+ r = dot(cxy, cxy);
+ float deno = max(u_max - u_min, 1.0);
+ if(r <= 1.0) {
+ fragColor = vec4(0, 0, 0, ((v_i - u_min) / (deno)) * u_intensity * (1.0 - sqrt(r)));
+ }
+ }`
};
var ColorShader = {
@@ -650,7 +786,7 @@ var ColorShader = {
if (alpha <= u_offset[0]) {
color_ = u_colorArr[0];
} else {
- for (int i = 1; i <= 10; ++i) {
+ for (int i = 1; i <= 20; ++i) {
if (alpha <= u_offset[i]) {
color_ = mix( u_colorArr[i - 1], u_colorArr[i], remap( u_offset[i - 1], u_offset[i], alpha ) );
color_ = color_ * mix( u_colorArr[i - 1][3], u_colorArr[i][3], remap( u_offset[i - 1], u_offset[i], alpha ));