visual-heatmap
Advanced tools
Comparing version 2.0.1 to 2.1.0
@@ -6,709 +6,4 @@ /*! | ||
*/ | ||
function Heatmap (context, config = {}) { | ||
let ratio; | ||
let buffer; | ||
let posVec = []; | ||
let buffer2; | ||
let rVec = []; | ||
let pLen = 0; | ||
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) { | ||
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 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: values, | ||
length: gradLength, | ||
offset: offsets | ||
}; | ||
} | ||
function createShader (ctx, type, src) { | ||
var shader = ctx.createShader(ctx[type]); | ||
ctx.shaderSource(shader, src); | ||
ctx.compileShader(shader); | ||
var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); | ||
if (!compiled) { | ||
var lastError = ctx.getShaderInfoLog(shader); | ||
ctx.deleteShader(shader); | ||
throw new Error("*** Error compiling shader '" + shader + "':" + lastError); | ||
} | ||
return shader; | ||
} | ||
function createProgram (ctx, shader) { | ||
var vshader = createShader(ctx, 'VERTEX_SHADER', shader.vertex); | ||
var fshader = createShader(ctx, 'FRAGMENT_SHADER', shader.fragment); | ||
var program = ctx.createProgram(); | ||
ctx.attachShader(program, vshader); | ||
ctx.attachShader(program, fshader); | ||
ctx.linkProgram(program); | ||
var linked = ctx.getProgramParameter(program, ctx.LINK_STATUS); | ||
if (!linked) { | ||
var lastError = ctx.getProgramInfoLog(program); | ||
ctx.deleteProgram(program); | ||
throw new Error('Error in program linking:' + lastError); | ||
} else { | ||
return program; | ||
} | ||
} | ||
function createImageShader (ctx) { | ||
var program = createProgram(ctx, imageShaders); | ||
return { | ||
program: program, | ||
attr: [{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_position'), | ||
data: new Float32Array([]) | ||
}, { | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_texCoord'), | ||
data: new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]) | ||
}], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, 'u_resolution'), | ||
u_image: ctx.getUniformLocation(program, 'u_image'), | ||
u_translate: ctx.getUniformLocation(program, 'u_translate'), | ||
u_zoom: ctx.getUniformLocation(program, 'u_zoom'), | ||
u_angle: ctx.getUniformLocation(program, 'u_angle'), | ||
u_density: ctx.getUniformLocation(program, 'u_density') | ||
} | ||
}; | ||
} | ||
function createGradiantShader (ctx) { | ||
var program = createProgram(ctx, GradShaders); | ||
return { | ||
program: program, | ||
attr: [{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_position'), | ||
data: new Float32Array([]) | ||
}, { | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 1, | ||
attribute: ctx.getAttribLocation(program, 'a_intensity'), | ||
data: new Float32Array([]) | ||
}], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, 'u_resolution'), | ||
u_max: ctx.getUniformLocation(program, 'u_max'), | ||
u_min: ctx.getUniformLocation(program, 'u_min'), | ||
u_size: ctx.getUniformLocation(program, 'u_size'), | ||
u_intensity: ctx.getUniformLocation(program, 'u_intensity'), | ||
u_translate: ctx.getUniformLocation(program, 'u_translate'), | ||
u_zoom: ctx.getUniformLocation(program, 'u_zoom'), | ||
u_angle: ctx.getUniformLocation(program, 'u_angle'), | ||
u_density: ctx.getUniformLocation(program, 'u_density') | ||
} | ||
}; | ||
} | ||
function createColorShader (ctx) { | ||
var program = createProgram(ctx, ColorShader); | ||
return { | ||
program: program, | ||
attr: [{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_texCoord'), | ||
data: new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]) | ||
}], | ||
uniform: { | ||
u_framebuffer: ctx.getUniformLocation(program, 'u_framebuffer'), | ||
u_colorArr: ctx.getUniformLocation(program, 'u_colorArr'), | ||
u_colorCount: ctx.getUniformLocation(program, 'u_colorCount'), | ||
u_opacity: ctx.getUniformLocation(program, 'u_opacity'), | ||
u_offset: ctx.getUniformLocation(program, 'u_offset') | ||
} | ||
}; | ||
} | ||
function extractData (data) { | ||
const len = data.length; | ||
if (pLen !== len) { | ||
buffer = new ArrayBuffer(len * 8); | ||
posVec = new Float32Array(buffer); | ||
buffer2 = new ArrayBuffer(len * 4); | ||
rVec = new Float32Array(buffer2); | ||
pLen = len; | ||
} | ||
const dataMinMaxValue = { | ||
min: Infinity, | ||
max: -Infinity | ||
}; | ||
for (let i = 0; i < len; i++) { | ||
posVec[i * 2] = data[i].x; | ||
posVec[(i * 2) + 1] = data[i].y; | ||
rVec[i] = data[i].value; | ||
if (dataMinMaxValue.min > data[i].value) { | ||
dataMinMaxValue.min = data[i].value; | ||
} | ||
if (dataMinMaxValue.max < data[i].value) { | ||
dataMinMaxValue.max = data[i].value; | ||
} | ||
} | ||
return { | ||
posVec: posVec, | ||
rVec: rVec, | ||
minMax: dataMinMaxValue | ||
}; | ||
} | ||
function Chart (context, config) { | ||
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; | ||
} | ||
if (!isNullUndefined(config.max)) { | ||
this.setMax(config.max); | ||
} else { | ||
configMax = null; | ||
} | ||
if (!isNullUndefined(config.min)) { | ||
this.setMin(config.min); | ||
} else { | ||
configMin = null; | ||
} | ||
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; | ||
} | ||
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 () { | ||
const height = this.dom.clientHeight; | ||
const width = this.dom.clientWidth; | ||
this.layer.setAttribute('height', height * ratio); | ||
this.layer.setAttribute('width', width * ratio); | ||
this.layer.style.height = `${height}px`; | ||
this.layer.style.width = `${width}px`; | ||
this.width = width; | ||
this.height = height; | ||
this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); | ||
/* Perform update */ | ||
this.render(hearmapExData); | ||
}; | ||
Chart.prototype.clear = function () { | ||
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT); | ||
}; | ||
Chart.prototype.setMax = function (max) { | ||
if (isNullUndefined(max) || isNotNumber(max)) { | ||
throw new Error('Invalid max: Expected Number'); | ||
} | ||
configMax = max; | ||
return this; | ||
}; | ||
Chart.prototype.setMin = function (min) { | ||
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) { | ||
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) { | ||
if (isNullUndefined(zoom) || isNotNumber(zoom)) { | ||
throw new Error('Invalid zoom: Expected Number'); | ||
} | ||
this.zoom = zoom; | ||
return this; | ||
}; | ||
Chart.prototype.setRotationAngle = function (angle) { | ||
if (isNullUndefined(angle) || isNotNumber(angle)) { | ||
throw new Error('Invalid Angle: Expected Number'); | ||
} | ||
this.angle = angle; | ||
return this; | ||
}; | ||
Chart.prototype.setSize = function (size) { | ||
if (isNullUndefined(size) || isNotNumber(size)) { | ||
throw new Error('Invalid Size: Expected Number'); | ||
} | ||
this.size = size; | ||
return this; | ||
}; | ||
Chart.prototype.setIntensity = function (intensity) { | ||
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) { | ||
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) { | ||
const self = this; | ||
if (!config.url) { | ||
return; | ||
} | ||
maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE); | ||
this.imageTexture = this.ctx.createTexture(); | ||
this.type = 'TEXTURE_2D'; | ||
imageConfig = null; | ||
imgWidth = config.width || this.width; | ||
imgHeight = config.height || this.height; | ||
imgWidth = imgWidth > maxTextureSize ? maxTextureSize : imgWidth; | ||
imgHeight = imgHeight > maxTextureSize ? maxTextureSize : imgHeight; | ||
imageInstance(config.url, function onUpdateCallBack () { | ||
self.ctx.activeTexture(self.ctx.TEXTURE0); | ||
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); | ||
self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_MAG_FILTER, self.ctx.LINEAR); | ||
self.ctx.texImage2D( | ||
self.ctx.TEXTURE_2D, | ||
0, | ||
self.ctx.RGBA, | ||
this.naturalWidth, | ||
this.naturalHeight, | ||
0, | ||
self.ctx.RGBA, | ||
self.ctx.UNSIGNED_BYTE, | ||
this | ||
); | ||
imageConfig = { | ||
x: config.x || 0, | ||
y: config.y || 0, | ||
height: imgHeight, | ||
width: imgWidth, | ||
image: this | ||
}; | ||
self.render(); | ||
}, function onErrorCallBack (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) { | ||
const self = this; | ||
for (let i = 0; i < data.length; i++) { | ||
if (transIntactFlag) { | ||
transCoOr.call(self, data[i]); | ||
} | ||
this.heatmapData.push(data[i]); | ||
} | ||
this.renderData(this.heatmapData); | ||
return this; | ||
}; | ||
Chart.prototype.renderData = function (data) { | ||
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) { | ||
// Pre-compute constants and repetitive calculations | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const translateX = this.translate[0]; | ||
const translateY = this.translate[1]; | ||
const angle = this.angle; | ||
const aspect = this.width / this.height; | ||
// Calculate the adjusted positions | ||
let posX = (data.x + translateX - halfWidth) / (halfWidth * zoomFactor); | ||
let posY = (data.y + translateY - halfHeight) / (halfHeight * zoomFactor); | ||
posX *= aspect; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(-angle); | ||
const sinAngle = Math.sin(-angle); | ||
const xNew = (cosAngle * posX) - (sinAngle * posY); | ||
posY = (sinAngle * posX) + (cosAngle * posY); | ||
posX = xNew; | ||
} | ||
posX *= 1.0 / aspect; | ||
// Scale back and adjust the position | ||
posX = (posX * halfWidth) + halfWidth; | ||
posY = (posY * halfHeight) + halfHeight; | ||
return { x: posX, y: posY }; | ||
}; | ||
function renderExec () { | ||
const ctx = this.ctx; | ||
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT); | ||
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); | ||
if (hearmapExData) { | ||
renderHeatGrad.call(this, ctx, hearmapExData); | ||
} | ||
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); | ||
if (imageConfig) { | ||
renderImage.call(this, ctx, imageConfig); | ||
} | ||
renderColorGradiant.call(this, ctx); | ||
} | ||
function renderHeatGrad (ctx, exData) { | ||
ctx.useProgram(this.gradShadOP.program); | ||
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 || []; | ||
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) { | ||
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.drawArrays(ctx.POINTS, 0, (exData.posVec || []).length / 2); | ||
} | ||
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([x, y, x + width, y, x, y + height, x, y + height, x + width, y, x + width, y + height]); | ||
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.activeTexture(this.ctx.TEXTURE0); | ||
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) { | ||
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.activeTexture(ctx.TEXTURE0); | ||
ctx.bindTexture(ctx.TEXTURE_2D, this.fbTexObj); | ||
ctx.drawArrays(ctx.TRIANGLES, 0, 6); | ||
} | ||
function transCoOr (data) { | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const angle = this.angle; | ||
// Combine operations to reduce the number of arithmetic steps | ||
let posX = (data.x - halfWidth) / halfWidth * zoomFactor; | ||
let posY = (data.y - halfHeight) / halfHeight * zoomFactor; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(angle); | ||
const sinAngle = Math.sin(angle); | ||
const xNew = (cosAngle * posX) - (sinAngle * posY); | ||
posY = (sinAngle * posX) + (cosAngle * posY); | ||
posX = xNew; | ||
} | ||
// Scale back and adjust the position | ||
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 }; | ||
} | ||
function imageInstance (url, onLoad, onError) { | ||
const imageIns = new Image(); | ||
imageIns.crossOrigin = 'anonymous'; | ||
imageIns.onload = onLoad; | ||
imageIns.onerror = onError; | ||
imageIns.src = url; | ||
return imageIns; | ||
} | ||
return new Chart(context, config); | ||
} | ||
function getPixlRatio (ctx) { | ||
const dpr = window.devicePixelRatio || 1; | ||
const bsr = ctx.webkitBackingStorePixelRatio || | ||
ctx.mozBackingStorePixelRatio || | ||
ctx.msBackingStorePixelRatio || | ||
ctx.oBackingStorePixelRatio || | ||
ctx.backingStorePixelRatio || 1; | ||
return dpr / bsr; | ||
} | ||
var GradShaders = { | ||
vertex: `#version 300 es | ||
const GradShader = { | ||
vertex: `#version 300 es | ||
in vec2 a_position; | ||
@@ -743,3 +38,3 @@ in float a_intensity; | ||
}`, | ||
fragment: `#version 300 es | ||
fragment: `#version 300 es | ||
precision mediump float; | ||
@@ -759,7 +54,6 @@ uniform float u_max; | ||
} | ||
}` | ||
}`, | ||
}; | ||
var ColorShader = { | ||
vertex: `#version 300 es | ||
const ColorShader = { | ||
vertex: `#version 300 es | ||
precision highp float; | ||
@@ -774,3 +68,3 @@ in vec2 a_texCoord; | ||
`, | ||
fragment: `#version 300 es | ||
fragment: `#version 300 es | ||
precision mediump float; | ||
@@ -816,7 +110,6 @@ in vec2 v_texCoord; | ||
} | ||
` | ||
`, | ||
}; | ||
var imageShaders = { | ||
vertex: `#version 300 es | ||
const ImageShader = { | ||
vertex: `#version 300 es | ||
precision highp float; | ||
@@ -856,3 +149,3 @@ in vec2 a_position; | ||
`, | ||
fragment: `#version 300 es | ||
fragment: `#version 300 es | ||
precision mediump float; | ||
@@ -865,5 +158,762 @@ uniform sampler2D u_image; | ||
} | ||
` | ||
`, | ||
}; | ||
export { Heatmap as default }; | ||
function createShader(ctx, type, src) { | ||
const shader = ctx.createShader(ctx[type]); | ||
if (!shader) { | ||
throw new Error("Failed to create shader."); | ||
} | ||
ctx.shaderSource(shader, src); | ||
ctx.compileShader(shader); | ||
const compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); | ||
if (!compiled) { | ||
const lastError = ctx.getShaderInfoLog(shader); | ||
ctx.deleteShader(shader); | ||
throw new Error("*** Error compiling shader '" + shader + "':" + lastError); | ||
} | ||
return shader; | ||
} | ||
function createProgram(ctx, shader) { | ||
const vshader = createShader(ctx, "VERTEX_SHADER", shader.vertex); | ||
const fshader = createShader(ctx, "FRAGMENT_SHADER", shader.fragment); | ||
const program = ctx.createProgram(); | ||
if (!program) { | ||
throw new Error("Failed to create program."); | ||
} | ||
ctx.attachShader(program, vshader); | ||
ctx.attachShader(program, fshader); | ||
ctx.linkProgram(program); | ||
const linked = ctx.getProgramParameter(program, ctx.LINK_STATUS); | ||
if (!linked) { | ||
const lastError = ctx.getProgramInfoLog(program); | ||
ctx.deleteProgram(program); | ||
throw new Error("Error in program linking:" + lastError); | ||
} | ||
else { | ||
return program; | ||
} | ||
} | ||
const createImageShader = function (ctx, shader) { | ||
const program = createProgram(ctx, shader); | ||
const positionBuffer = ctx.createBuffer(); | ||
if (!positionBuffer) { | ||
throw new Error("Failed to create position buffer."); | ||
} | ||
const texCoordBuffer = ctx.createBuffer(); | ||
if (!texCoordBuffer) { | ||
throw new Error("Failed to create texture coordinate buffer."); | ||
} | ||
return { | ||
program: program, | ||
attr: [ | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: positionBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_position"), | ||
data: new Float32Array([]), | ||
}, | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: texCoordBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_texCoord"), | ||
data: new Float32Array([ | ||
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, | ||
]), | ||
}, | ||
], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, "u_resolution"), | ||
u_image: ctx.getUniformLocation(program, "u_image"), | ||
u_translate: ctx.getUniformLocation(program, "u_translate"), | ||
u_zoom: ctx.getUniformLocation(program, "u_zoom"), | ||
u_angle: ctx.getUniformLocation(program, "u_angle"), | ||
u_density: ctx.getUniformLocation(program, "u_density"), | ||
}, | ||
}; | ||
}; | ||
const createGradiantShader = function (ctx, shader) { | ||
const program = createProgram(ctx, shader); | ||
const positionBuffer = ctx.createBuffer(); | ||
if (!positionBuffer) { | ||
throw new Error("Failed to create position buffer."); | ||
} | ||
const intensityBuffer = ctx.createBuffer(); | ||
if (!intensityBuffer) { | ||
throw new Error("Failed to create intensity buffer."); | ||
} | ||
return { | ||
program: program, | ||
attr: [ | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: positionBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_position"), | ||
data: new Float32Array([]), | ||
}, | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: intensityBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 1, | ||
attribute: ctx.getAttribLocation(program, "a_intensity"), | ||
data: new Float32Array([]), | ||
}, | ||
], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, "u_resolution"), | ||
u_max: ctx.getUniformLocation(program, "u_max"), | ||
u_min: ctx.getUniformLocation(program, "u_min"), | ||
u_size: ctx.getUniformLocation(program, "u_size"), | ||
u_intensity: ctx.getUniformLocation(program, "u_intensity"), | ||
u_translate: ctx.getUniformLocation(program, "u_translate"), | ||
u_zoom: ctx.getUniformLocation(program, "u_zoom"), | ||
u_angle: ctx.getUniformLocation(program, "u_angle"), | ||
u_density: ctx.getUniformLocation(program, "u_density"), | ||
}, | ||
}; | ||
}; | ||
const createColorShader = function (ctx, shader) { | ||
const program = createProgram(ctx, shader); | ||
const texCoordBuffer = ctx.createBuffer(); | ||
if (!texCoordBuffer) { | ||
throw new Error("Failed to create texture coordinate buffer."); | ||
} | ||
return { | ||
program: program, | ||
attr: [ | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: texCoordBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_texCoord"), | ||
data: new Float32Array([ | ||
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, | ||
]), | ||
}, | ||
], | ||
uniform: { | ||
u_framebuffer: ctx.getUniformLocation(program, "u_framebuffer"), | ||
u_colorArr: ctx.getUniformLocation(program, "u_colorArr"), | ||
u_colorCount: ctx.getUniformLocation(program, "u_colorCount"), | ||
u_opacity: ctx.getUniformLocation(program, "u_opacity"), | ||
u_offset: ctx.getUniformLocation(program, "u_offset"), | ||
}, | ||
}; | ||
}; | ||
function isNullUndefined(val) { | ||
return val === null || val === undefined; | ||
} | ||
function isNotNumber(val) { | ||
return typeof val !== "number"; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
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; | ||
} | ||
/** @see https://codereview.chromium.org/156833002/ */ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function getPixelRatio(ctx) { | ||
const dpr = window.devicePixelRatio || 1; | ||
const bsr = ctx.webkitBackingStorePixelRatio || | ||
ctx.mozBackingStorePixelRatio || | ||
ctx.msBackingStorePixelRatio || | ||
ctx.oBackingStorePixelRatio || | ||
ctx.backingStorePixelRatio || | ||
1; | ||
return dpr / bsr; | ||
} | ||
function gradientMapper(grad) { | ||
if (!Array.isArray(grad) || grad.length < 2) { | ||
throw new Error("Invalid gradient: Expected an array with at least 2 elements."); | ||
} | ||
if (!isSortedAscending(grad)) { | ||
throw new Error("Invalid gradient: Gradient is not sorted"); | ||
} | ||
const gradLength = grad.length; | ||
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: values, | ||
length: gradLength, | ||
offset: offsets, | ||
}; | ||
} | ||
function extractData(data) { | ||
const self = this; | ||
const len = data.length; | ||
let { posVec = new Float32Array(), rVec = new Float32Array() } = (self.hearmapExData || {}); | ||
if (self._pDataLength !== len) { | ||
posVec = new Float32Array(new ArrayBuffer(len * 8)); | ||
rVec = new Float32Array(new ArrayBuffer(len * 4)); | ||
self._pDataLength = len; | ||
} | ||
const dataMinMaxValue = { | ||
min: Infinity, | ||
max: -Infinity, | ||
}; | ||
for (let i = 0; i < len; i++) { | ||
posVec[i * 2] = data[i].x; | ||
posVec[i * 2 + 1] = data[i].y; | ||
rVec[i] = data[i].value; | ||
if (dataMinMaxValue.min > data[i].value) { | ||
dataMinMaxValue.min = data[i].value; | ||
} | ||
if (dataMinMaxValue.max < data[i].value) { | ||
dataMinMaxValue.max = data[i].value; | ||
} | ||
} | ||
return { | ||
posVec: posVec, | ||
rVec: rVec, | ||
minMax: dataMinMaxValue, | ||
}; | ||
} | ||
function transCoOr(data) { | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const { angle, translate } = this; | ||
// Combine operations to reduce the number of arithmetic steps | ||
let posX = ((data.x - halfWidth) / halfWidth) * zoomFactor; | ||
let posY = ((data.y - halfHeight) / halfHeight) * zoomFactor; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(angle); | ||
const sinAngle = Math.sin(angle); | ||
posY = sinAngle * posX + cosAngle * posY; | ||
posX = cosAngle * posX - sinAngle * posY; | ||
} | ||
// Scale back and adjust the position | ||
posX = posX * halfWidth + halfWidth - translate[0]; | ||
posY = posY * halfHeight + halfHeight - translate[1]; | ||
data.x = posX; | ||
data.y = posY; | ||
return { x: posX, y: posY }; | ||
} | ||
function renderExec() { | ||
const ctx = this.ctx; | ||
if (!ctx) { | ||
return; | ||
} | ||
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT); | ||
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); | ||
if (this.hearmapExData) { | ||
renderHeatGrad.call(this, ctx, this.hearmapExData); | ||
} | ||
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); | ||
if (this.imageConfig) { | ||
renderImage.call(this, ctx, this.imageConfig); | ||
} | ||
renderColorGradiant.call(this, ctx); | ||
} | ||
function renderHeatGrad(ctx, exData) { | ||
var _a, _b, _c, _d; | ||
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(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); | ||
ctx.vertexAttribPointer(d.attribute, d.size, d.valueType, true, 0, 0); | ||
}); | ||
ctx.drawArrays(ctx.POINTS, 0, (exData.posVec || []).length / 2); | ||
} | ||
function renderImage(ctx, imageConfig) { | ||
const { x = 0, y = 0, width = 0, height = 0 } = imageConfig; | ||
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, | ||
y, | ||
x, | ||
y + height, | ||
x, | ||
y + height, | ||
x + width, | ||
y, | ||
x + width, | ||
y + height, | ||
]); | ||
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(u_image, 0); | ||
ctx.activeTexture(this.ctx.TEXTURE0); | ||
ctx.bindTexture(this.ctx.TEXTURE_2D, this._imageTexture); | ||
ctx.drawArrays(ctx.TRIANGLES, 0, 6); | ||
} | ||
function renderColorGradiant(ctx) { | ||
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(u_framebuffer, 0); | ||
ctx.activeTexture(ctx.TEXTURE0); | ||
ctx.bindTexture(ctx.TEXTURE_2D, this._fbTexObj); | ||
ctx.drawArrays(ctx.TRIANGLES, 0, 6); | ||
} | ||
function imageInstance(url, onLoad, onError) { | ||
const imageIns = new Image(); | ||
imageIns.crossOrigin = "anonymous"; | ||
imageIns.onload = onLoad; | ||
imageIns.onerror = onError; | ||
imageIns.src = url; | ||
return imageIns; | ||
} | ||
class HeatmapRenderer { | ||
constructor(container, config) { | ||
this.ctx = null; | ||
this.ratio = 1; | ||
this.width = 0; | ||
this.height = 0; | ||
this.imageConfig = null; | ||
this.configMin = null; | ||
this.configMax = null; | ||
this.min = 0; | ||
this.max = 0; | ||
this.size = 0; | ||
this.zoom = 0; | ||
this.angle = 0; | ||
this.intensity = 0; | ||
this.translate = [0, 0]; | ||
this.opacity = 0; | ||
this.hearmapExData = {}; | ||
this.gradient = null; | ||
this._imageTexture = null; | ||
this._pDataLength = undefined; | ||
this.imgWidth = 0; | ||
this.imgHeight = 0; | ||
this.heatmapData = []; | ||
this.type = ""; | ||
try { | ||
const res = typeof container === "string" | ||
? document.querySelector(container) | ||
: container instanceof HTMLElement | ||
? container | ||
: null; | ||
if (!res) { | ||
throw new Error("Context must be either a string or an Element"); | ||
} | ||
const { clientHeight: height, clientWidth: width } = res; | ||
const layer = document.createElement("canvas"); | ||
const ctx = layer.getContext("webgl2", { | ||
premultipliedAlpha: false, | ||
depth: false, | ||
antialias: true, | ||
alpha: true, | ||
preserveDrawingBuffer: false, | ||
}); | ||
this.ratio = getPixelRatio(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 * this.ratio).toString()); | ||
layer.setAttribute("width", (width * this.ratio).toString()); | ||
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.imageConfig = null; | ||
this.configMin = null; | ||
this.configMax = null; | ||
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(); | ||
if (!isNullUndefined(config.size)) { | ||
this.setSize(config.size); | ||
} | ||
else { | ||
this.size = 20.0; | ||
} | ||
if (!isNullUndefined(config.max)) { | ||
this.setMax(config.max); | ||
} | ||
else { | ||
this.configMax = null; | ||
} | ||
if (!isNullUndefined(config.min)) { | ||
this.setMin(config.min); | ||
} | ||
else { | ||
this.configMin = null; | ||
} | ||
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; | ||
} | ||
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); | ||
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); | ||
} | ||
} | ||
/** | ||
* Invoke resize method on container resize. | ||
*/ | ||
resize() { | ||
const height = this.dom.clientHeight; | ||
const width = this.dom.clientWidth; | ||
this.layer.setAttribute("height", (height * this.ratio).toString()); | ||
this.layer.setAttribute("width", (width * this.ratio).toString()); | ||
this.layer.style.height = `${height}px`; | ||
this.layer.style.width = `${width}px`; | ||
this.width = width; | ||
this.height = height; | ||
this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); | ||
/* Perform update */ | ||
this.render(); | ||
} | ||
clear() { | ||
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT); | ||
} | ||
/** | ||
* Set the maximum data value for relative gradient calculations | ||
* @param max - number | ||
* @returns instance | ||
*/ | ||
setMax(max) { | ||
if (isNullUndefined(max) || isNotNumber(max)) { | ||
throw new Error("Invalid max: Expected Number"); | ||
} | ||
this.configMax = max; | ||
return this; | ||
} | ||
/** | ||
* Set the minimum data value for relative gradient calculations | ||
* @param min - number | ||
* @returns instance | ||
*/ | ||
setMin(min) { | ||
if (isNullUndefined(min) || isNotNumber(min)) { | ||
throw new Error("Invalid min: Expected Number"); | ||
} | ||
this.configMin = min; | ||
return this; | ||
} | ||
/** | ||
* Accepts array of objects with color value and offset | ||
* @param gradient - Color Gradient | ||
* @returns instance | ||
*/ | ||
setGradient(gradient) { | ||
this.gradient = gradientMapper(gradient); | ||
return this; | ||
} | ||
/** | ||
* Set the translate on the Heatmap | ||
* @param translate - Accepts array [x, y] | ||
* @returns instance | ||
*/ | ||
setTranslate(translate) { | ||
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; | ||
} | ||
/** | ||
* Set the zoom transformation on the Heatmap | ||
* @param zoom - Accepts float value | ||
* @returns instance | ||
*/ | ||
setZoom(zoom) { | ||
if (isNullUndefined(zoom) || isNotNumber(zoom)) { | ||
throw new Error("Invalid zoom: Expected Number"); | ||
} | ||
this.zoom = zoom; | ||
return this; | ||
} | ||
/** | ||
* Set the rotation transformation on the Heatmap | ||
* @param angle - Accepts angle in radians | ||
* @returns instance | ||
*/ | ||
setRotationAngle(angle) { | ||
if (isNullUndefined(angle) || isNotNumber(angle)) { | ||
throw new Error("Invalid Angle: Expected Number"); | ||
} | ||
this.angle = angle; | ||
return this; | ||
} | ||
/** | ||
* Set the point radius | ||
* @param size - Accepts float value | ||
* @returns instance | ||
*/ | ||
setSize(size) { | ||
if (isNullUndefined(size) || isNotNumber(size)) { | ||
throw new Error("Invalid Size: Expected Number"); | ||
} | ||
this.size = size; | ||
return this; | ||
} | ||
/** | ||
* Set the intensity factor | ||
* @param intensity - Accepts float value | ||
* @returns instance | ||
*/ | ||
setIntensity(intensity) { | ||
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; | ||
} | ||
/** | ||
* Set the opacity factor | ||
* @param opacity - The opacity factor. | ||
* @returns instance | ||
*/ | ||
setOpacity(opacity) { | ||
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; | ||
} | ||
/** | ||
* Set the background image | ||
* @param config - Accepts Object with { url, height, width, x, and y} properties | ||
* @returns instance | ||
*/ | ||
setBackgroundImage(config) { | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const self = this; | ||
if (!config.url) { | ||
return; | ||
} | ||
const maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE); | ||
this._imageTexture = this.ctx.createTexture(); | ||
this.type = "TEXTURE_2D"; | ||
this.imageConfig = null; | ||
this.imgWidth = config.width || this.width; | ||
this.imgHeight = config.height || this.height; | ||
this.imgWidth = | ||
this.imgWidth > maxTextureSize ? maxTextureSize : this.imgWidth; | ||
this.imgHeight = | ||
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.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); | ||
self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_MAG_FILTER, self.ctx.LINEAR); | ||
self.ctx.texImage2D(self.ctx.TEXTURE_2D, 0, self.ctx.RGBA, this.naturalWidth, this.naturalHeight, 0, self.ctx.RGBA, self.ctx.UNSIGNED_BYTE, this); | ||
self.imageConfig = { | ||
x: config.x || 0, | ||
y: config.y || 0, | ||
height: self.imgHeight, | ||
width: self.imgWidth, | ||
image: this, | ||
}; | ||
self.render(); | ||
}, function onErrorCallBack(error) { | ||
throw new Error(`Image Load Error, ${error}`); | ||
}); | ||
return this; | ||
} | ||
/** | ||
* Clear heatmap | ||
*/ | ||
clearData() { | ||
this.heatmapData = []; | ||
this.hearmapExData = {}; | ||
this.render(); | ||
} | ||
/** | ||
* 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, transIntactFlag) { | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const self = this; | ||
for (let i = 0; i < data.length; i++) { | ||
if (transIntactFlag) { | ||
transCoOr.call(self, data[i]); | ||
} | ||
this.heatmapData.push(data[i]); | ||
} | ||
this.renderData(this.heatmapData); | ||
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 | ||
*/ | ||
renderData(data) { | ||
if (data.constructor !== Array) { | ||
throw new Error("Expected Array type"); | ||
} | ||
this.hearmapExData = extractData.call(this, data); | ||
this.heatmapData = data; | ||
this.render(); | ||
return this; | ||
} | ||
/** | ||
* 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 | ||
* @param data - The data point to project. | ||
* @returns projected data point. | ||
*/ | ||
projection(data) { | ||
// Pre-compute constants and repetitive calculations | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const translateX = this.translate[0]; | ||
const translateY = this.translate[1]; | ||
const angle = this.angle; | ||
const aspect = this.width / this.height; | ||
// Calculate the adjusted positions | ||
let posX = (data.x + translateX - halfWidth) / (halfWidth * zoomFactor); | ||
let posY = (data.y + translateY - halfHeight) / (halfHeight * zoomFactor); | ||
posX *= aspect; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(-angle); | ||
const sinAngle = Math.sin(-angle); | ||
const xNew = cosAngle * posX - sinAngle * posY; | ||
posY = sinAngle * posX + cosAngle * posY; | ||
posX = xNew; | ||
} | ||
posX *= 1.0 / aspect; | ||
// Scale back and adjust the position | ||
posX = posX * halfWidth + halfWidth; | ||
posY = posY * halfHeight + halfHeight; | ||
return { x: posX, y: posY }; | ||
} | ||
} | ||
function main (context, config) { | ||
return new HeatmapRenderer(context, config); | ||
} | ||
export { main as default }; |
@@ -6,2 +6,2 @@ /*! | ||
*/ | ||
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;e<t.length-1;e++)if(t[e+1].offset-t[e].offset<0)return!1;return!0}(t))throw new Error("Invalid gradient: Gradient is not sorted");const e=t.length,r=new Float32Array(4*e),o=new Array(e);return t.forEach((function(t,e){const i=4*e;r[i]=t.color[0]/255,r[i+1]=t.color[1]/255,r[i+2]=t.color[2]/255,r[i+3]=void 0!==t.color[3]?t.color[3]:1,o[e]=t.offset})),{value:r,length:e,offset:o}}function E(t,e,r){var o=t.createShader(t[e]);if(t.shaderSource(o,r),t.compileShader(o),!t.getShaderParameter(o,t.COMPILE_STATUS)){var i=t.getShaderInfoLog(o);throw t.deleteShader(o),new Error("*** Error compiling shader '"+o+"':"+i)}return o}function v(t,e){var r=E(t,"VERTEX_SHADER",e.vertex),o=E(t,"FRAGMENT_SHADER",e.fragment),i=t.createProgram();if(t.attachShader(i,r),t.attachShader(i,o),t.linkProgram(i),t.getProgramParameter(i,t.LINK_STATUS))return i;var n=t.getProgramInfoLog(i);throw t.deleteProgram(i),new Error("Error in program linking:"+n)}function A(t,i){try{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,r=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return e/r}(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.ctx=h,this.width=u,this.height=s,this.layer=f,this.dom=a,this.gradShadOP=function(t){var r=v(t,e);return{program:r,attr:[{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(r,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:1,attribute:t.getAttribLocation(r,"a_intensity"),data:new Float32Array([])}],uniform:{u_resolution:t.getUniformLocation(r,"u_resolution"),u_max:t.getUniformLocation(r,"u_max"),u_min:t.getUniformLocation(r,"u_min"),u_size:t.getUniformLocation(r,"u_size"),u_intensity:t.getUniformLocation(r,"u_intensity"),u_translate:t.getUniformLocation(r,"u_translate"),u_zoom:t.getUniformLocation(r,"u_zoom"),u_angle:t.getUniformLocation(r,"u_angle"),u_density:t.getUniformLocation(r,"u_density")}}}(this.ctx),this.colorShadOP=function(t){var e=v(t,r);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=v(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(),p(i.size)?this.size=20:this.setSize(i.size),p(i.max)?T=null:this.setMax(i.max),p(i.min)?g=null:this.setMin(i.min),p(i.intensity)?this.intensity=1:this.setIntensity(i.intensity),p(i.translate)?this.translate=[0,0]:this.setTranslate(i.translate),p(i.zoom)?this.zoom=1:this.setZoom(i.zoom),p(i.angle)?this.angle=0:this.setRotationAngle(i.angle),p(i.opacity)?this.opacity=1:this.setOpacity(i.opacity),this.gradient=y(i.gradient),this.ratio=n,i.backgroundImage&&i.backgroundImage.url&&this.setBackgroundImage(i.backgroundImage),this.heatmapData=[],this.ctx.viewport(0,0,this.ctx.canvas.width,this.ctx.canvas.height)}catch(t){console.error(t)}}function w(){const t=this.ctx;t.clear(t.COLOR_BUFFER_BIT|t.DEPTH_BUFFER_BIT),t.bindTexture(t.TEXTURE_2D,this.fbTexObj),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.width*this.ratio,this.height*this.ratio,0,t.RGBA,t.UNSIGNED_BYTE,null),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.bindFramebuffer(t.FRAMEBUFFER,this.fbo),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,this.fbTexObj,0),h&&b.call(this,t,h),t.bindFramebuffer(t.FRAMEBUFFER,null),c&&R.call(this,t,c),P.call(this,t)}function b(t,e){t.useProgram(this.gradShadOP.program),this.min=null!==g?g:e?.minMax?.min??0,this.max=null!==T?T: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,(e.posVec||[]).length/2)}function R(t,e){const{x:r=0,y:o=0,width:i=0,height:n=0}=e;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([r,o,r+i,o,r,o+n,r,o+n,r+i,o,r+i,o+n]),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 P(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 S(t){const e=this.zoom||.1,r=this.width/2,o=this.height/2,i=this.angle;let n=(t.x-r)/r*e,a=(t.y-o)/o*e;if(0!==i){const t=Math.cos(i),e=Math.sin(i),r=t*n-e*a;a=e*n+t*a,n=r}return n=n*r+r-this.translate[0],a=a*o+o-this.translate[1],t.x=n,t.y=a,{x:n,y:a}}return A.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.ctx.canvas.width,this.ctx.canvas.height),this.render(h)},A.prototype.clear=function(){this.ctx.clear(this.ctx.COLOR_BUFFER_BIT|this.ctx.DEPTH_BUFFER_BIT)},A.prototype.setMax=function(t){if(p(t)||x(t))throw new Error("Invalid max: Expected Number");return T=t,this},A.prototype.setMin=function(t){if(p(t)||x(t))throw new Error("Invalid min: Expected Number");return g=t,this},A.prototype.setGradient=function(t){return this.gradient=y(t),this},A.prototype.setTranslate=function(t){if(t.constructor!==Array)throw new Error("Invalid Translate: Translate has to be of Array type");if(2!==t.length)throw new Error("Translate has to be of length 2");return this.translate=t,this},A.prototype.setZoom=function(t){if(p(t)||x(t))throw new Error("Invalid zoom: Expected Number");return this.zoom=t,this},A.prototype.setRotationAngle=function(t){if(p(t)||x(t))throw new Error("Invalid Angle: Expected Number");return this.angle=t,this},A.prototype.setSize=function(t){if(p(t)||x(t))throw new Error("Invalid Size: Expected Number");return this.size=t,this},A.prototype.setIntensity=function(t){if(p(t)||x(t))throw this.intensity=1,new Error("Invalid Intensity: Expected Number");if(t>1||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;o<t.length;o++)e&&S.call(r,t[o]),this.heatmapData.push(t[o]);return this.renderData(this.heatmapData),this},A.prototype.renderData=function(t){if(t.constructor!==Array)throw new Error("Expected Array type");return h=function(t){const e=t.length;_!==e&&(a=new ArrayBuffer(8*e),l=new Float32Array(a),s=new ArrayBuffer(4*e),m=new Float32Array(s),_=e);const r={min:1/0,max:-1/0};for(let o=0;o<e;o++)l[2*o]=t[o].x,l[2*o+1]=t[o].y,m[o]=t[o].value,r.min>t[o].value&&(r.min=t[o].value),r.max<t[o].value&&(r.max=t[o].value);return{posVec:l,rVec:m,minMax:r}}(t),this.heatmapData=t,this.render(),this},A.prototype.render=function(){w.call(this)},A.prototype.projection=function(t){const e=this.zoom||.1,r=this.width/2,o=this.height/2,i=this.translate[0],n=this.translate[1],a=this.angle,s=this.width/this.height;let u=(t.x+i-r)/(r*e),f=(t.y+n-o)/(o*e);if(u*=s,0!==a){const t=Math.cos(-a),e=Math.sin(-a),r=t*u-e*f;f=e*u+t*f,u=r}return u*=1/s,u=u*r+r,f=f*o+o,{x:u,y:f}},new A(t,i)}var e={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}"},r={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"},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}; | ||
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;e<t.length-1;e++)if(t[e+1].offset-t[e].offset<0)return!1;return!0}(t))throw new Error("Invalid gradient: Gradient is not sorted");const e=t.length,i=new Float32Array(4*e),r=new Array(e);return t.forEach((function(t,e){const o=4*e;i[o]=t.color[0]/255,i[o+1]=t.color[1]/255,i[o+2]=t.color[2]/255,i[o+3]=void 0!==t.color[3]?t.color[3]:1,r[e]=t.offset})),{value:i,length:e,offset:r}}function u(t){const e=this,i=t.length;let{posVec:r=new Float32Array,rVec:o=new Float32Array}=e.hearmapExData||{};e._pDataLength!==i&&(r=new Float32Array(new ArrayBuffer(8*i)),o=new Float32Array(new ArrayBuffer(4*i)),e._pDataLength=i);const n={min:1/0,max:-1/0};for(let e=0;e<i;e++)r[2*e]=t[e].x,r[2*e+1]=t[e].y,o[e]=t[e].value,n.min>t[e].value&&(n.min=t[e].value),n.max<t[e].value&&(n.max=t[e].value);return{posVec:r,rVec:o,minMax:n}}function h(t){const e=this.zoom||.1,i=this.width/2,r=this.height/2,{angle:o,translate:n}=this;let a=(t.x-i)/i*e,s=(t.y-r)/r*e;if(0!==o){const t=Math.cos(o),e=Math.sin(o);s=e*a+t*s,a=t*a-e*s}return a=a*i+i-n[0],s=s*r+r-n[1],t.x=a,t.y=s,{x:a,y:s}}function l(){const t=this.ctx;t&&(t.clear(t.COLOR_BUFFER_BIT|t.DEPTH_BUFFER_BIT),t.bindTexture(t.TEXTURE_2D,this._fbTexObj),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.width*this.ratio,this.height*this.ratio,0,t.RGBA,t.UNSIGNED_BYTE,null),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.bindFramebuffer(t.FRAMEBUFFER,this._fbo),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,this._fbTexObj,0),this.hearmapExData&&f.call(this,t,this.hearmapExData),t.bindFramebuffer(t.FRAMEBUFFER,null),this.imageConfig&&c.call(this,t,this.imageConfig),m.call(this,t))}function f(t,e){var i,r,o,n;t.useProgram(this._gradShadOP.program);const{u_resolution:a,u_translate:s,u_zoom:u,u_angle:h,u_density:l,u_max:f,u_min:c,u_size:m,u_intensity:_}=this._gradShadOP.uniform;this.min=null!==this.configMin?this.configMin:null!==(r=null===(i=null==e?void 0:e.minMax)||void 0===i?void 0:i.min)&&void 0!==r?r:0,this.max=null!==this.configMax?this.configMax:null!==(n=null===(o=null==e?void 0:e.minMax)||void 0===o?void 0:o.max)&&void 0!==n?n:0,this._gradShadOP.attr[0].data=e.posVec||[],this._gradShadOP.attr[1].data=e.rVec||[],t.uniform2fv(a,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(s,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(u,this.zoom?this.zoom:.01),t.uniform1f(h,this.angle),t.uniform1f(l,this.ratio),t.uniform1f(f,this.max),t.uniform1f(c,this.min),t.uniform1f(m,this.size),t.uniform1f(_,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,(e.posVec||[]).length/2)}function c(t,e){const{x:i=0,y:r=0,width:o=0,height:n=0}=e,{u_resolution:a,u_translate:s,u_zoom:u,u_angle:h,u_density:l,u_image:f}=this._imageShaOP.uniform;t.useProgram(this._imageShaOP.program),t.uniform2fv(a,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(s,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(u,this.zoom?this.zoom:.01),t.uniform1f(h,this.angle),t.uniform1f(l,this.ratio),this._imageShaOP.attr[0].data=new Float32Array([i,r,i+o,r,i,r+n,i,r+n,i+o,r,i+o,r+n]),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(f,0),t.activeTexture(this.ctx.TEXTURE0),t.bindTexture(this.ctx.TEXTURE_2D,this._imageTexture),t.drawArrays(t.TRIANGLES,0,6)}function m(t){const{u_colorArr:e,u_colorCount:i,u_offset:r,u_opacity:o,u_framebuffer:n}=this._colorShadOP.uniform;t.useProgram(this._colorShadOP.program),t.uniform4fv(e,this.gradient.value),t.uniform1f(i,this.gradient.length),t.uniform1fv(r,new Float32Array(this.gradient.offset)),t.uniform1f(o,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(n,0),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this._fbTexObj),t.drawArrays(t.TRIANGLES,0,6)}class _{constructor(r,a){this.ctx=null,this.ratio=1,this.width=0,this.height=0,this.imageConfig=null,this.configMin=null,this.configMax=null,this.min=0,this.max=0,this.size=0,this.zoom=0,this.angle=0,this.intensity=0,this.translate=[0,0],this.opacity=0,this.hearmapExData={},this.gradient=null,this._imageTexture=null,this._pDataLength=void 0,this.imgWidth=0,this.imgHeight=0,this.heatmapData=[],this.type="";try{const u="string"==typeof r?document.querySelector(r):r instanceof HTMLElement?r:null;if(!u)throw new Error("Context must be either a string or an Element");const{clientHeight:h,clientWidth:l}=u,f=document.createElement("canvas"),c=f.getContext("webgl2",{premultipliedAlpha:!1,depth:!1,antialias:!0,alpha:!0,preserveDrawingBuffer:!1});this.ratio=function(t){return(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1)}(c),c.clearColor(0,0,0,0),c.enable(c.BLEND),c.blendEquation(c.FUNC_ADD),c.blendFunc(c.ONE,c.ONE_MINUS_SRC_ALPHA),c.depthMask(!0),f.setAttribute("height",(h*this.ratio).toString()),f.setAttribute("width",(l*this.ratio).toString()),f.style.height=`${h}px`,f.style.width=`${l}px`,f.style.position="absolute",u.appendChild(f),this.ctx=c,this.width=l,this.height=h,this.imageConfig=null,this.configMin=null,this.configMax=null,this.hearmapExData={},this.layer=f,this.dom=u,this._gradShadOP=function(t,e){const i=o(t,e),r=t.createBuffer();if(!r)throw new Error("Failed to create position buffer.");const n=t.createBuffer();if(!n)throw new Error("Failed to create intensity buffer.");return{program:i,attr:[{bufferType:t.ARRAY_BUFFER,buffer:r,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:n,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:1,attribute:t.getAttribLocation(i,"a_intensity"),data:new Float32Array([])}],uniform:{u_resolution:t.getUniformLocation(i,"u_resolution"),u_max:t.getUniformLocation(i,"u_max"),u_min:t.getUniformLocation(i,"u_min"),u_size:t.getUniformLocation(i,"u_size"),u_intensity:t.getUniformLocation(i,"u_intensity"),u_translate:t.getUniformLocation(i,"u_translate"),u_zoom:t.getUniformLocation(i,"u_zoom"),u_angle:t.getUniformLocation(i,"u_angle"),u_density:t.getUniformLocation(i,"u_density")}}}(this.ctx,t),this._colorShadOP=function(t,e){const i=o(t,e),r=t.createBuffer();if(!r)throw new Error("Failed to create texture coordinate buffer.");return{program:i,attr:[{bufferType:t.ARRAY_BUFFER,buffer:r,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_framebuffer:t.getUniformLocation(i,"u_framebuffer"),u_colorArr:t.getUniformLocation(i,"u_colorArr"),u_colorCount:t.getUniformLocation(i,"u_colorCount"),u_opacity:t.getUniformLocation(i,"u_opacity"),u_offset:t.getUniformLocation(i,"u_offset")}}}(this.ctx,e),this._imageShaOP=function(t,e){const i=o(t,e),r=t.createBuffer();if(!r)throw new Error("Failed to create position buffer.");const n=t.createBuffer();if(!n)throw new Error("Failed to create texture coordinate buffer.");return{program:i,attr:[{bufferType:t.ARRAY_BUFFER,buffer:r,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:n,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_resolution:t.getUniformLocation(i,"u_resolution"),u_image:t.getUniformLocation(i,"u_image"),u_translate:t.getUniformLocation(i,"u_translate"),u_zoom:t.getUniformLocation(i,"u_zoom"),u_angle:t.getUniformLocation(i,"u_angle"),u_density:t.getUniformLocation(i,"u_density")}}}(this.ctx,i),this._fbTexObj=c.createTexture(),this._fbo=c.createFramebuffer(),n(a.size)?this.size=20:this.setSize(a.size),n(a.max)?this.configMax=null:this.setMax(a.max),n(a.min)?this.configMin=null:this.setMin(a.min),n(a.intensity)?this.intensity=1:this.setIntensity(a.intensity),n(a.translate)?this.translate=[0,0]:this.setTranslate(a.translate),n(a.zoom)?this.zoom=1:this.setZoom(a.zoom),n(a.angle)?this.angle=0:this.setRotationAngle(a.angle),n(a.opacity)?this.opacity=1:this.setOpacity(a.opacity),this.gradient=s(a.gradient),a.backgroundImage&&a.backgroundImage.url&&this.setBackgroundImage(a.backgroundImage),this.heatmapData=[],this.ctx.viewport(0,0,this.ctx.canvas.width,this.ctx.canvas.height)}catch(t){console.error(t)}}resize(){const t=this.dom.clientHeight,e=this.dom.clientWidth;this.layer.setAttribute("height",(t*this.ratio).toString()),this.layer.setAttribute("width",(e*this.ratio).toString()),this.layer.style.height=`${t}px`,this.layer.style.width=`${e}px`,this.width=e,this.height=t,this.ctx.viewport(0,0,this.ctx.canvas.width,this.ctx.canvas.height),this.render()}clear(){this.ctx.clear(this.ctx.COLOR_BUFFER_BIT|this.ctx.DEPTH_BUFFER_BIT)}setMax(t){if(n(t)||a(t))throw new Error("Invalid max: Expected Number");return this.configMax=t,this}setMin(t){if(n(t)||a(t))throw new Error("Invalid min: Expected Number");return this.configMin=t,this}setGradient(t){return this.gradient=s(t),this}setTranslate(t){if(t.constructor!==Array)throw new Error("Invalid Translate: Translate has to be of Array type");if(2!==t.length)throw new Error("Translate has to be of length 2");return this.translate=t,this}setZoom(t){if(n(t)||a(t))throw new Error("Invalid zoom: Expected Number");return this.zoom=t,this}setRotationAngle(t){if(n(t)||a(t))throw new Error("Invalid Angle: Expected Number");return this.angle=t,this}setSize(t){if(n(t)||a(t))throw new Error("Invalid Size: Expected Number");return this.size=t,this}setIntensity(t){if(n(t)||a(t))throw this.intensity=1,new Error("Invalid Intensity: Expected Number");if(t>1||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<t.length;r++)e&&h.call(i,t[r]),this.heatmapData.push(t[r]);return this.renderData(this.heatmapData),this}renderData(t){if(t.constructor!==Array)throw new Error("Expected Array type");return this.hearmapExData=u.call(this,t),this.heatmapData=t,this.render(),this}render(){l.call(this)}projection(t){const e=this.zoom||.1,i=this.width/2,r=this.height/2,o=this.translate[0],n=this.translate[1],a=this.angle,s=this.width/this.height;let u=(t.x+o-i)/(i*e),h=(t.y+n-r)/(r*e);if(u*=s,0!==a){const t=Math.cos(-a),e=Math.sin(-a),i=t*u-e*h;h=e*u+t*h,u=i}return u*=1/s,u=u*i+i,h=h*r+r,{x:u,y:h}}}function g(t,e){return new _(t,e)}export{g as default}; |
@@ -12,709 +12,4 @@ /*! | ||
function Heatmap (context, config = {}) { | ||
let ratio; | ||
let buffer; | ||
let posVec = []; | ||
let buffer2; | ||
let rVec = []; | ||
let pLen = 0; | ||
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) { | ||
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 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: values, | ||
length: gradLength, | ||
offset: offsets | ||
}; | ||
} | ||
function createShader (ctx, type, src) { | ||
var shader = ctx.createShader(ctx[type]); | ||
ctx.shaderSource(shader, src); | ||
ctx.compileShader(shader); | ||
var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); | ||
if (!compiled) { | ||
var lastError = ctx.getShaderInfoLog(shader); | ||
ctx.deleteShader(shader); | ||
throw new Error("*** Error compiling shader '" + shader + "':" + lastError); | ||
} | ||
return shader; | ||
} | ||
function createProgram (ctx, shader) { | ||
var vshader = createShader(ctx, 'VERTEX_SHADER', shader.vertex); | ||
var fshader = createShader(ctx, 'FRAGMENT_SHADER', shader.fragment); | ||
var program = ctx.createProgram(); | ||
ctx.attachShader(program, vshader); | ||
ctx.attachShader(program, fshader); | ||
ctx.linkProgram(program); | ||
var linked = ctx.getProgramParameter(program, ctx.LINK_STATUS); | ||
if (!linked) { | ||
var lastError = ctx.getProgramInfoLog(program); | ||
ctx.deleteProgram(program); | ||
throw new Error('Error in program linking:' + lastError); | ||
} else { | ||
return program; | ||
} | ||
} | ||
function createImageShader (ctx) { | ||
var program = createProgram(ctx, imageShaders); | ||
return { | ||
program: program, | ||
attr: [{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_position'), | ||
data: new Float32Array([]) | ||
}, { | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_texCoord'), | ||
data: new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]) | ||
}], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, 'u_resolution'), | ||
u_image: ctx.getUniformLocation(program, 'u_image'), | ||
u_translate: ctx.getUniformLocation(program, 'u_translate'), | ||
u_zoom: ctx.getUniformLocation(program, 'u_zoom'), | ||
u_angle: ctx.getUniformLocation(program, 'u_angle'), | ||
u_density: ctx.getUniformLocation(program, 'u_density') | ||
} | ||
}; | ||
} | ||
function createGradiantShader (ctx) { | ||
var program = createProgram(ctx, GradShaders); | ||
return { | ||
program: program, | ||
attr: [{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_position'), | ||
data: new Float32Array([]) | ||
}, { | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 1, | ||
attribute: ctx.getAttribLocation(program, 'a_intensity'), | ||
data: new Float32Array([]) | ||
}], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, 'u_resolution'), | ||
u_max: ctx.getUniformLocation(program, 'u_max'), | ||
u_min: ctx.getUniformLocation(program, 'u_min'), | ||
u_size: ctx.getUniformLocation(program, 'u_size'), | ||
u_intensity: ctx.getUniformLocation(program, 'u_intensity'), | ||
u_translate: ctx.getUniformLocation(program, 'u_translate'), | ||
u_zoom: ctx.getUniformLocation(program, 'u_zoom'), | ||
u_angle: ctx.getUniformLocation(program, 'u_angle'), | ||
u_density: ctx.getUniformLocation(program, 'u_density') | ||
} | ||
}; | ||
} | ||
function createColorShader (ctx) { | ||
var program = createProgram(ctx, ColorShader); | ||
return { | ||
program: program, | ||
attr: [{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: ctx.createBuffer(), | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, 'a_texCoord'), | ||
data: new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]) | ||
}], | ||
uniform: { | ||
u_framebuffer: ctx.getUniformLocation(program, 'u_framebuffer'), | ||
u_colorArr: ctx.getUniformLocation(program, 'u_colorArr'), | ||
u_colorCount: ctx.getUniformLocation(program, 'u_colorCount'), | ||
u_opacity: ctx.getUniformLocation(program, 'u_opacity'), | ||
u_offset: ctx.getUniformLocation(program, 'u_offset') | ||
} | ||
}; | ||
} | ||
function extractData (data) { | ||
const len = data.length; | ||
if (pLen !== len) { | ||
buffer = new ArrayBuffer(len * 8); | ||
posVec = new Float32Array(buffer); | ||
buffer2 = new ArrayBuffer(len * 4); | ||
rVec = new Float32Array(buffer2); | ||
pLen = len; | ||
} | ||
const dataMinMaxValue = { | ||
min: Infinity, | ||
max: -Infinity | ||
}; | ||
for (let i = 0; i < len; i++) { | ||
posVec[i * 2] = data[i].x; | ||
posVec[(i * 2) + 1] = data[i].y; | ||
rVec[i] = data[i].value; | ||
if (dataMinMaxValue.min > data[i].value) { | ||
dataMinMaxValue.min = data[i].value; | ||
} | ||
if (dataMinMaxValue.max < data[i].value) { | ||
dataMinMaxValue.max = data[i].value; | ||
} | ||
} | ||
return { | ||
posVec: posVec, | ||
rVec: rVec, | ||
minMax: dataMinMaxValue | ||
}; | ||
} | ||
function Chart (context, config) { | ||
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; | ||
} | ||
if (!isNullUndefined(config.max)) { | ||
this.setMax(config.max); | ||
} else { | ||
configMax = null; | ||
} | ||
if (!isNullUndefined(config.min)) { | ||
this.setMin(config.min); | ||
} else { | ||
configMin = null; | ||
} | ||
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; | ||
} | ||
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 () { | ||
const height = this.dom.clientHeight; | ||
const width = this.dom.clientWidth; | ||
this.layer.setAttribute('height', height * ratio); | ||
this.layer.setAttribute('width', width * ratio); | ||
this.layer.style.height = `${height}px`; | ||
this.layer.style.width = `${width}px`; | ||
this.width = width; | ||
this.height = height; | ||
this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); | ||
/* Perform update */ | ||
this.render(hearmapExData); | ||
}; | ||
Chart.prototype.clear = function () { | ||
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT); | ||
}; | ||
Chart.prototype.setMax = function (max) { | ||
if (isNullUndefined(max) || isNotNumber(max)) { | ||
throw new Error('Invalid max: Expected Number'); | ||
} | ||
configMax = max; | ||
return this; | ||
}; | ||
Chart.prototype.setMin = function (min) { | ||
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) { | ||
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) { | ||
if (isNullUndefined(zoom) || isNotNumber(zoom)) { | ||
throw new Error('Invalid zoom: Expected Number'); | ||
} | ||
this.zoom = zoom; | ||
return this; | ||
}; | ||
Chart.prototype.setRotationAngle = function (angle) { | ||
if (isNullUndefined(angle) || isNotNumber(angle)) { | ||
throw new Error('Invalid Angle: Expected Number'); | ||
} | ||
this.angle = angle; | ||
return this; | ||
}; | ||
Chart.prototype.setSize = function (size) { | ||
if (isNullUndefined(size) || isNotNumber(size)) { | ||
throw new Error('Invalid Size: Expected Number'); | ||
} | ||
this.size = size; | ||
return this; | ||
}; | ||
Chart.prototype.setIntensity = function (intensity) { | ||
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) { | ||
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) { | ||
const self = this; | ||
if (!config.url) { | ||
return; | ||
} | ||
maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE); | ||
this.imageTexture = this.ctx.createTexture(); | ||
this.type = 'TEXTURE_2D'; | ||
imageConfig = null; | ||
imgWidth = config.width || this.width; | ||
imgHeight = config.height || this.height; | ||
imgWidth = imgWidth > maxTextureSize ? maxTextureSize : imgWidth; | ||
imgHeight = imgHeight > maxTextureSize ? maxTextureSize : imgHeight; | ||
imageInstance(config.url, function onUpdateCallBack () { | ||
self.ctx.activeTexture(self.ctx.TEXTURE0); | ||
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); | ||
self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_MAG_FILTER, self.ctx.LINEAR); | ||
self.ctx.texImage2D( | ||
self.ctx.TEXTURE_2D, | ||
0, | ||
self.ctx.RGBA, | ||
this.naturalWidth, | ||
this.naturalHeight, | ||
0, | ||
self.ctx.RGBA, | ||
self.ctx.UNSIGNED_BYTE, | ||
this | ||
); | ||
imageConfig = { | ||
x: config.x || 0, | ||
y: config.y || 0, | ||
height: imgHeight, | ||
width: imgWidth, | ||
image: this | ||
}; | ||
self.render(); | ||
}, function onErrorCallBack (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) { | ||
const self = this; | ||
for (let i = 0; i < data.length; i++) { | ||
if (transIntactFlag) { | ||
transCoOr.call(self, data[i]); | ||
} | ||
this.heatmapData.push(data[i]); | ||
} | ||
this.renderData(this.heatmapData); | ||
return this; | ||
}; | ||
Chart.prototype.renderData = function (data) { | ||
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) { | ||
// Pre-compute constants and repetitive calculations | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const translateX = this.translate[0]; | ||
const translateY = this.translate[1]; | ||
const angle = this.angle; | ||
const aspect = this.width / this.height; | ||
// Calculate the adjusted positions | ||
let posX = (data.x + translateX - halfWidth) / (halfWidth * zoomFactor); | ||
let posY = (data.y + translateY - halfHeight) / (halfHeight * zoomFactor); | ||
posX *= aspect; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(-angle); | ||
const sinAngle = Math.sin(-angle); | ||
const xNew = (cosAngle * posX) - (sinAngle * posY); | ||
posY = (sinAngle * posX) + (cosAngle * posY); | ||
posX = xNew; | ||
} | ||
posX *= 1.0 / aspect; | ||
// Scale back and adjust the position | ||
posX = (posX * halfWidth) + halfWidth; | ||
posY = (posY * halfHeight) + halfHeight; | ||
return { x: posX, y: posY }; | ||
}; | ||
function renderExec () { | ||
const ctx = this.ctx; | ||
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT); | ||
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); | ||
if (hearmapExData) { | ||
renderHeatGrad.call(this, ctx, hearmapExData); | ||
} | ||
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); | ||
if (imageConfig) { | ||
renderImage.call(this, ctx, imageConfig); | ||
} | ||
renderColorGradiant.call(this, ctx); | ||
} | ||
function renderHeatGrad (ctx, exData) { | ||
ctx.useProgram(this.gradShadOP.program); | ||
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 || []; | ||
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) { | ||
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.drawArrays(ctx.POINTS, 0, (exData.posVec || []).length / 2); | ||
} | ||
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([x, y, x + width, y, x, y + height, x, y + height, x + width, y, x + width, y + height]); | ||
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.activeTexture(this.ctx.TEXTURE0); | ||
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) { | ||
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.activeTexture(ctx.TEXTURE0); | ||
ctx.bindTexture(ctx.TEXTURE_2D, this.fbTexObj); | ||
ctx.drawArrays(ctx.TRIANGLES, 0, 6); | ||
} | ||
function transCoOr (data) { | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const angle = this.angle; | ||
// Combine operations to reduce the number of arithmetic steps | ||
let posX = (data.x - halfWidth) / halfWidth * zoomFactor; | ||
let posY = (data.y - halfHeight) / halfHeight * zoomFactor; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(angle); | ||
const sinAngle = Math.sin(angle); | ||
const xNew = (cosAngle * posX) - (sinAngle * posY); | ||
posY = (sinAngle * posX) + (cosAngle * posY); | ||
posX = xNew; | ||
} | ||
// Scale back and adjust the position | ||
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 }; | ||
} | ||
function imageInstance (url, onLoad, onError) { | ||
const imageIns = new Image(); | ||
imageIns.crossOrigin = 'anonymous'; | ||
imageIns.onload = onLoad; | ||
imageIns.onerror = onError; | ||
imageIns.src = url; | ||
return imageIns; | ||
} | ||
return new Chart(context, config); | ||
} | ||
function getPixlRatio (ctx) { | ||
const dpr = window.devicePixelRatio || 1; | ||
const bsr = ctx.webkitBackingStorePixelRatio || | ||
ctx.mozBackingStorePixelRatio || | ||
ctx.msBackingStorePixelRatio || | ||
ctx.oBackingStorePixelRatio || | ||
ctx.backingStorePixelRatio || 1; | ||
return dpr / bsr; | ||
} | ||
var GradShaders = { | ||
vertex: `#version 300 es | ||
const GradShader = { | ||
vertex: `#version 300 es | ||
in vec2 a_position; | ||
@@ -749,3 +44,3 @@ in float a_intensity; | ||
}`, | ||
fragment: `#version 300 es | ||
fragment: `#version 300 es | ||
precision mediump float; | ||
@@ -765,7 +60,6 @@ uniform float u_max; | ||
} | ||
}` | ||
}`, | ||
}; | ||
var ColorShader = { | ||
vertex: `#version 300 es | ||
const ColorShader = { | ||
vertex: `#version 300 es | ||
precision highp float; | ||
@@ -780,3 +74,3 @@ in vec2 a_texCoord; | ||
`, | ||
fragment: `#version 300 es | ||
fragment: `#version 300 es | ||
precision mediump float; | ||
@@ -822,7 +116,6 @@ in vec2 v_texCoord; | ||
} | ||
` | ||
`, | ||
}; | ||
var imageShaders = { | ||
vertex: `#version 300 es | ||
const ImageShader = { | ||
vertex: `#version 300 es | ||
precision highp float; | ||
@@ -862,3 +155,3 @@ in vec2 a_position; | ||
`, | ||
fragment: `#version 300 es | ||
fragment: `#version 300 es | ||
precision mediump float; | ||
@@ -871,7 +164,764 @@ uniform sampler2D u_image; | ||
} | ||
` | ||
`, | ||
}; | ||
return Heatmap; | ||
function createShader(ctx, type, src) { | ||
const shader = ctx.createShader(ctx[type]); | ||
if (!shader) { | ||
throw new Error("Failed to create shader."); | ||
} | ||
ctx.shaderSource(shader, src); | ||
ctx.compileShader(shader); | ||
const compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); | ||
if (!compiled) { | ||
const lastError = ctx.getShaderInfoLog(shader); | ||
ctx.deleteShader(shader); | ||
throw new Error("*** Error compiling shader '" + shader + "':" + lastError); | ||
} | ||
return shader; | ||
} | ||
function createProgram(ctx, shader) { | ||
const vshader = createShader(ctx, "VERTEX_SHADER", shader.vertex); | ||
const fshader = createShader(ctx, "FRAGMENT_SHADER", shader.fragment); | ||
const program = ctx.createProgram(); | ||
if (!program) { | ||
throw new Error("Failed to create program."); | ||
} | ||
ctx.attachShader(program, vshader); | ||
ctx.attachShader(program, fshader); | ||
ctx.linkProgram(program); | ||
const linked = ctx.getProgramParameter(program, ctx.LINK_STATUS); | ||
if (!linked) { | ||
const lastError = ctx.getProgramInfoLog(program); | ||
ctx.deleteProgram(program); | ||
throw new Error("Error in program linking:" + lastError); | ||
} | ||
else { | ||
return program; | ||
} | ||
} | ||
const createImageShader = function (ctx, shader) { | ||
const program = createProgram(ctx, shader); | ||
const positionBuffer = ctx.createBuffer(); | ||
if (!positionBuffer) { | ||
throw new Error("Failed to create position buffer."); | ||
} | ||
const texCoordBuffer = ctx.createBuffer(); | ||
if (!texCoordBuffer) { | ||
throw new Error("Failed to create texture coordinate buffer."); | ||
} | ||
return { | ||
program: program, | ||
attr: [ | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: positionBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_position"), | ||
data: new Float32Array([]), | ||
}, | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: texCoordBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_texCoord"), | ||
data: new Float32Array([ | ||
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, | ||
]), | ||
}, | ||
], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, "u_resolution"), | ||
u_image: ctx.getUniformLocation(program, "u_image"), | ||
u_translate: ctx.getUniformLocation(program, "u_translate"), | ||
u_zoom: ctx.getUniformLocation(program, "u_zoom"), | ||
u_angle: ctx.getUniformLocation(program, "u_angle"), | ||
u_density: ctx.getUniformLocation(program, "u_density"), | ||
}, | ||
}; | ||
}; | ||
const createGradiantShader = function (ctx, shader) { | ||
const program = createProgram(ctx, shader); | ||
const positionBuffer = ctx.createBuffer(); | ||
if (!positionBuffer) { | ||
throw new Error("Failed to create position buffer."); | ||
} | ||
const intensityBuffer = ctx.createBuffer(); | ||
if (!intensityBuffer) { | ||
throw new Error("Failed to create intensity buffer."); | ||
} | ||
return { | ||
program: program, | ||
attr: [ | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: positionBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_position"), | ||
data: new Float32Array([]), | ||
}, | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: intensityBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 1, | ||
attribute: ctx.getAttribLocation(program, "a_intensity"), | ||
data: new Float32Array([]), | ||
}, | ||
], | ||
uniform: { | ||
u_resolution: ctx.getUniformLocation(program, "u_resolution"), | ||
u_max: ctx.getUniformLocation(program, "u_max"), | ||
u_min: ctx.getUniformLocation(program, "u_min"), | ||
u_size: ctx.getUniformLocation(program, "u_size"), | ||
u_intensity: ctx.getUniformLocation(program, "u_intensity"), | ||
u_translate: ctx.getUniformLocation(program, "u_translate"), | ||
u_zoom: ctx.getUniformLocation(program, "u_zoom"), | ||
u_angle: ctx.getUniformLocation(program, "u_angle"), | ||
u_density: ctx.getUniformLocation(program, "u_density"), | ||
}, | ||
}; | ||
}; | ||
const createColorShader = function (ctx, shader) { | ||
const program = createProgram(ctx, shader); | ||
const texCoordBuffer = ctx.createBuffer(); | ||
if (!texCoordBuffer) { | ||
throw new Error("Failed to create texture coordinate buffer."); | ||
} | ||
return { | ||
program: program, | ||
attr: [ | ||
{ | ||
bufferType: ctx.ARRAY_BUFFER, | ||
buffer: texCoordBuffer, | ||
drawType: ctx.STATIC_DRAW, | ||
valueType: ctx.FLOAT, | ||
size: 2, | ||
attribute: ctx.getAttribLocation(program, "a_texCoord"), | ||
data: new Float32Array([ | ||
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, | ||
]), | ||
}, | ||
], | ||
uniform: { | ||
u_framebuffer: ctx.getUniformLocation(program, "u_framebuffer"), | ||
u_colorArr: ctx.getUniformLocation(program, "u_colorArr"), | ||
u_colorCount: ctx.getUniformLocation(program, "u_colorCount"), | ||
u_opacity: ctx.getUniformLocation(program, "u_opacity"), | ||
u_offset: ctx.getUniformLocation(program, "u_offset"), | ||
}, | ||
}; | ||
}; | ||
function isNullUndefined(val) { | ||
return val === null || val === undefined; | ||
} | ||
function isNotNumber(val) { | ||
return typeof val !== "number"; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
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; | ||
} | ||
/** @see https://codereview.chromium.org/156833002/ */ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function getPixelRatio(ctx) { | ||
const dpr = window.devicePixelRatio || 1; | ||
const bsr = ctx.webkitBackingStorePixelRatio || | ||
ctx.mozBackingStorePixelRatio || | ||
ctx.msBackingStorePixelRatio || | ||
ctx.oBackingStorePixelRatio || | ||
ctx.backingStorePixelRatio || | ||
1; | ||
return dpr / bsr; | ||
} | ||
function gradientMapper(grad) { | ||
if (!Array.isArray(grad) || grad.length < 2) { | ||
throw new Error("Invalid gradient: Expected an array with at least 2 elements."); | ||
} | ||
if (!isSortedAscending(grad)) { | ||
throw new Error("Invalid gradient: Gradient is not sorted"); | ||
} | ||
const gradLength = grad.length; | ||
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: values, | ||
length: gradLength, | ||
offset: offsets, | ||
}; | ||
} | ||
function extractData(data) { | ||
const self = this; | ||
const len = data.length; | ||
let { posVec = new Float32Array(), rVec = new Float32Array() } = (self.hearmapExData || {}); | ||
if (self._pDataLength !== len) { | ||
posVec = new Float32Array(new ArrayBuffer(len * 8)); | ||
rVec = new Float32Array(new ArrayBuffer(len * 4)); | ||
self._pDataLength = len; | ||
} | ||
const dataMinMaxValue = { | ||
min: Infinity, | ||
max: -Infinity, | ||
}; | ||
for (let i = 0; i < len; i++) { | ||
posVec[i * 2] = data[i].x; | ||
posVec[i * 2 + 1] = data[i].y; | ||
rVec[i] = data[i].value; | ||
if (dataMinMaxValue.min > data[i].value) { | ||
dataMinMaxValue.min = data[i].value; | ||
} | ||
if (dataMinMaxValue.max < data[i].value) { | ||
dataMinMaxValue.max = data[i].value; | ||
} | ||
} | ||
return { | ||
posVec: posVec, | ||
rVec: rVec, | ||
minMax: dataMinMaxValue, | ||
}; | ||
} | ||
function transCoOr(data) { | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const { angle, translate } = this; | ||
// Combine operations to reduce the number of arithmetic steps | ||
let posX = ((data.x - halfWidth) / halfWidth) * zoomFactor; | ||
let posY = ((data.y - halfHeight) / halfHeight) * zoomFactor; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(angle); | ||
const sinAngle = Math.sin(angle); | ||
posY = sinAngle * posX + cosAngle * posY; | ||
posX = cosAngle * posX - sinAngle * posY; | ||
} | ||
// Scale back and adjust the position | ||
posX = posX * halfWidth + halfWidth - translate[0]; | ||
posY = posY * halfHeight + halfHeight - translate[1]; | ||
data.x = posX; | ||
data.y = posY; | ||
return { x: posX, y: posY }; | ||
} | ||
function renderExec() { | ||
const ctx = this.ctx; | ||
if (!ctx) { | ||
return; | ||
} | ||
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT); | ||
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); | ||
if (this.hearmapExData) { | ||
renderHeatGrad.call(this, ctx, this.hearmapExData); | ||
} | ||
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); | ||
if (this.imageConfig) { | ||
renderImage.call(this, ctx, this.imageConfig); | ||
} | ||
renderColorGradiant.call(this, ctx); | ||
} | ||
function renderHeatGrad(ctx, exData) { | ||
var _a, _b, _c, _d; | ||
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(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); | ||
ctx.vertexAttribPointer(d.attribute, d.size, d.valueType, true, 0, 0); | ||
}); | ||
ctx.drawArrays(ctx.POINTS, 0, (exData.posVec || []).length / 2); | ||
} | ||
function renderImage(ctx, imageConfig) { | ||
const { x = 0, y = 0, width = 0, height = 0 } = imageConfig; | ||
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, | ||
y, | ||
x, | ||
y + height, | ||
x, | ||
y + height, | ||
x + width, | ||
y, | ||
x + width, | ||
y + height, | ||
]); | ||
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(u_image, 0); | ||
ctx.activeTexture(this.ctx.TEXTURE0); | ||
ctx.bindTexture(this.ctx.TEXTURE_2D, this._imageTexture); | ||
ctx.drawArrays(ctx.TRIANGLES, 0, 6); | ||
} | ||
function renderColorGradiant(ctx) { | ||
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(u_framebuffer, 0); | ||
ctx.activeTexture(ctx.TEXTURE0); | ||
ctx.bindTexture(ctx.TEXTURE_2D, this._fbTexObj); | ||
ctx.drawArrays(ctx.TRIANGLES, 0, 6); | ||
} | ||
function imageInstance(url, onLoad, onError) { | ||
const imageIns = new Image(); | ||
imageIns.crossOrigin = "anonymous"; | ||
imageIns.onload = onLoad; | ||
imageIns.onerror = onError; | ||
imageIns.src = url; | ||
return imageIns; | ||
} | ||
class HeatmapRenderer { | ||
constructor(container, config) { | ||
this.ctx = null; | ||
this.ratio = 1; | ||
this.width = 0; | ||
this.height = 0; | ||
this.imageConfig = null; | ||
this.configMin = null; | ||
this.configMax = null; | ||
this.min = 0; | ||
this.max = 0; | ||
this.size = 0; | ||
this.zoom = 0; | ||
this.angle = 0; | ||
this.intensity = 0; | ||
this.translate = [0, 0]; | ||
this.opacity = 0; | ||
this.hearmapExData = {}; | ||
this.gradient = null; | ||
this._imageTexture = null; | ||
this._pDataLength = undefined; | ||
this.imgWidth = 0; | ||
this.imgHeight = 0; | ||
this.heatmapData = []; | ||
this.type = ""; | ||
try { | ||
const res = typeof container === "string" | ||
? document.querySelector(container) | ||
: container instanceof HTMLElement | ||
? container | ||
: null; | ||
if (!res) { | ||
throw new Error("Context must be either a string or an Element"); | ||
} | ||
const { clientHeight: height, clientWidth: width } = res; | ||
const layer = document.createElement("canvas"); | ||
const ctx = layer.getContext("webgl2", { | ||
premultipliedAlpha: false, | ||
depth: false, | ||
antialias: true, | ||
alpha: true, | ||
preserveDrawingBuffer: false, | ||
}); | ||
this.ratio = getPixelRatio(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 * this.ratio).toString()); | ||
layer.setAttribute("width", (width * this.ratio).toString()); | ||
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.imageConfig = null; | ||
this.configMin = null; | ||
this.configMax = null; | ||
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(); | ||
if (!isNullUndefined(config.size)) { | ||
this.setSize(config.size); | ||
} | ||
else { | ||
this.size = 20.0; | ||
} | ||
if (!isNullUndefined(config.max)) { | ||
this.setMax(config.max); | ||
} | ||
else { | ||
this.configMax = null; | ||
} | ||
if (!isNullUndefined(config.min)) { | ||
this.setMin(config.min); | ||
} | ||
else { | ||
this.configMin = null; | ||
} | ||
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; | ||
} | ||
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); | ||
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); | ||
} | ||
} | ||
/** | ||
* Invoke resize method on container resize. | ||
*/ | ||
resize() { | ||
const height = this.dom.clientHeight; | ||
const width = this.dom.clientWidth; | ||
this.layer.setAttribute("height", (height * this.ratio).toString()); | ||
this.layer.setAttribute("width", (width * this.ratio).toString()); | ||
this.layer.style.height = `${height}px`; | ||
this.layer.style.width = `${width}px`; | ||
this.width = width; | ||
this.height = height; | ||
this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); | ||
/* Perform update */ | ||
this.render(); | ||
} | ||
clear() { | ||
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT); | ||
} | ||
/** | ||
* Set the maximum data value for relative gradient calculations | ||
* @param max - number | ||
* @returns instance | ||
*/ | ||
setMax(max) { | ||
if (isNullUndefined(max) || isNotNumber(max)) { | ||
throw new Error("Invalid max: Expected Number"); | ||
} | ||
this.configMax = max; | ||
return this; | ||
} | ||
/** | ||
* Set the minimum data value for relative gradient calculations | ||
* @param min - number | ||
* @returns instance | ||
*/ | ||
setMin(min) { | ||
if (isNullUndefined(min) || isNotNumber(min)) { | ||
throw new Error("Invalid min: Expected Number"); | ||
} | ||
this.configMin = min; | ||
return this; | ||
} | ||
/** | ||
* Accepts array of objects with color value and offset | ||
* @param gradient - Color Gradient | ||
* @returns instance | ||
*/ | ||
setGradient(gradient) { | ||
this.gradient = gradientMapper(gradient); | ||
return this; | ||
} | ||
/** | ||
* Set the translate on the Heatmap | ||
* @param translate - Accepts array [x, y] | ||
* @returns instance | ||
*/ | ||
setTranslate(translate) { | ||
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; | ||
} | ||
/** | ||
* Set the zoom transformation on the Heatmap | ||
* @param zoom - Accepts float value | ||
* @returns instance | ||
*/ | ||
setZoom(zoom) { | ||
if (isNullUndefined(zoom) || isNotNumber(zoom)) { | ||
throw new Error("Invalid zoom: Expected Number"); | ||
} | ||
this.zoom = zoom; | ||
return this; | ||
} | ||
/** | ||
* Set the rotation transformation on the Heatmap | ||
* @param angle - Accepts angle in radians | ||
* @returns instance | ||
*/ | ||
setRotationAngle(angle) { | ||
if (isNullUndefined(angle) || isNotNumber(angle)) { | ||
throw new Error("Invalid Angle: Expected Number"); | ||
} | ||
this.angle = angle; | ||
return this; | ||
} | ||
/** | ||
* Set the point radius | ||
* @param size - Accepts float value | ||
* @returns instance | ||
*/ | ||
setSize(size) { | ||
if (isNullUndefined(size) || isNotNumber(size)) { | ||
throw new Error("Invalid Size: Expected Number"); | ||
} | ||
this.size = size; | ||
return this; | ||
} | ||
/** | ||
* Set the intensity factor | ||
* @param intensity - Accepts float value | ||
* @returns instance | ||
*/ | ||
setIntensity(intensity) { | ||
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; | ||
} | ||
/** | ||
* Set the opacity factor | ||
* @param opacity - The opacity factor. | ||
* @returns instance | ||
*/ | ||
setOpacity(opacity) { | ||
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; | ||
} | ||
/** | ||
* Set the background image | ||
* @param config - Accepts Object with { url, height, width, x, and y} properties | ||
* @returns instance | ||
*/ | ||
setBackgroundImage(config) { | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const self = this; | ||
if (!config.url) { | ||
return; | ||
} | ||
const maxTextureSize = this.ctx.getParameter(this.ctx.MAX_TEXTURE_SIZE); | ||
this._imageTexture = this.ctx.createTexture(); | ||
this.type = "TEXTURE_2D"; | ||
this.imageConfig = null; | ||
this.imgWidth = config.width || this.width; | ||
this.imgHeight = config.height || this.height; | ||
this.imgWidth = | ||
this.imgWidth > maxTextureSize ? maxTextureSize : this.imgWidth; | ||
this.imgHeight = | ||
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.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); | ||
self.ctx.texParameteri(self.ctx.TEXTURE_2D, self.ctx.TEXTURE_MAG_FILTER, self.ctx.LINEAR); | ||
self.ctx.texImage2D(self.ctx.TEXTURE_2D, 0, self.ctx.RGBA, this.naturalWidth, this.naturalHeight, 0, self.ctx.RGBA, self.ctx.UNSIGNED_BYTE, this); | ||
self.imageConfig = { | ||
x: config.x || 0, | ||
y: config.y || 0, | ||
height: self.imgHeight, | ||
width: self.imgWidth, | ||
image: this, | ||
}; | ||
self.render(); | ||
}, function onErrorCallBack(error) { | ||
throw new Error(`Image Load Error, ${error}`); | ||
}); | ||
return this; | ||
} | ||
/** | ||
* Clear heatmap | ||
*/ | ||
clearData() { | ||
this.heatmapData = []; | ||
this.hearmapExData = {}; | ||
this.render(); | ||
} | ||
/** | ||
* 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, transIntactFlag) { | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const self = this; | ||
for (let i = 0; i < data.length; i++) { | ||
if (transIntactFlag) { | ||
transCoOr.call(self, data[i]); | ||
} | ||
this.heatmapData.push(data[i]); | ||
} | ||
this.renderData(this.heatmapData); | ||
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 | ||
*/ | ||
renderData(data) { | ||
if (data.constructor !== Array) { | ||
throw new Error("Expected Array type"); | ||
} | ||
this.hearmapExData = extractData.call(this, data); | ||
this.heatmapData = data; | ||
this.render(); | ||
return this; | ||
} | ||
/** | ||
* 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 | ||
* @param data - The data point to project. | ||
* @returns projected data point. | ||
*/ | ||
projection(data) { | ||
// Pre-compute constants and repetitive calculations | ||
const zoomFactor = this.zoom || 0.1; | ||
const halfWidth = this.width / 2; | ||
const halfHeight = this.height / 2; | ||
const translateX = this.translate[0]; | ||
const translateY = this.translate[1]; | ||
const angle = this.angle; | ||
const aspect = this.width / this.height; | ||
// Calculate the adjusted positions | ||
let posX = (data.x + translateX - halfWidth) / (halfWidth * zoomFactor); | ||
let posY = (data.y + translateY - halfHeight) / (halfHeight * zoomFactor); | ||
posX *= aspect; | ||
// Rotate the point if there's an angle | ||
if (angle !== 0.0) { | ||
const cosAngle = Math.cos(-angle); | ||
const sinAngle = Math.sin(-angle); | ||
const xNew = cosAngle * posX - sinAngle * posY; | ||
posY = sinAngle * posX + cosAngle * posY; | ||
posX = xNew; | ||
} | ||
posX *= 1.0 / aspect; | ||
// Scale back and adjust the position | ||
posX = posX * halfWidth + halfWidth; | ||
posY = posY * halfHeight + halfHeight; | ||
return { x: posX, y: posY }; | ||
} | ||
} | ||
function main (context, config) { | ||
return new HeatmapRenderer(context, config); | ||
} | ||
return main; | ||
})); |
@@ -6,2 +6,2 @@ /*! | ||
*/ | ||
!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\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"},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;e<t.length-1;e++)if(t[e+1].offset-t[e].offset<0)return!1;return!0}(t))throw new Error("Invalid gradient: Gradient is not sorted");const e=t.length,r=new Float32Array(4*e),o=new Array(e);return t.forEach((function(t,e){const i=4*e;r[i]=t.color[0]/255,r[i+1]=t.color[1]/255,r[i+2]=t.color[2]/255,r[i+3]=void 0!==t.color[3]?t.color[3]:1,o[e]=t.offset})),{value:r,length:e,offset:o}}function v(t,e,r){var o=t.createShader(t[e]);if(t.shaderSource(o,r),t.compileShader(o),!t.getShaderParameter(o,t.COMPILE_STATUS)){var i=t.getShaderInfoLog(o);throw t.deleteShader(o),new Error("*** Error compiling shader '"+o+"':"+i)}return o}function E(t,e){var r=v(t,"VERTEX_SHADER",e.vertex),o=v(t,"FRAGMENT_SHADER",e.fragment),i=t.createProgram();if(t.attachShader(i,r),t.attachShader(i,o),t.linkProgram(i),t.getProgramParameter(i,t.LINK_STATUS))return i;var n=t.getProgramInfoLog(i);throw t.deleteProgram(i),new Error("Error in program linking:"+n)}function A(o,i){try{let a;if("string"==typeof o)a=document.querySelector(o);else{if(!(o instanceof Element))throw new Error("Context must be either a string or an Element");a=o}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,r=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return e/r}(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.ctx=h,this.width=u,this.height=s,this.layer=f,this.dom=a,this.gradShadOP=function(e){var r=E(e,t);return{program:r,attr:[{bufferType:e.ARRAY_BUFFER,buffer:e.createBuffer(),drawType:e.STATIC_DRAW,valueType:e.FLOAT,size:2,attribute:e.getAttribLocation(r,"a_position"),data:new Float32Array([])},{bufferType:e.ARRAY_BUFFER,buffer:e.createBuffer(),drawType:e.STATIC_DRAW,valueType:e.FLOAT,size:1,attribute:e.getAttribLocation(r,"a_intensity"),data:new Float32Array([])}],uniform:{u_resolution:e.getUniformLocation(r,"u_resolution"),u_max:e.getUniformLocation(r,"u_max"),u_min:e.getUniformLocation(r,"u_min"),u_size:e.getUniformLocation(r,"u_size"),u_intensity:e.getUniformLocation(r,"u_intensity"),u_translate:e.getUniformLocation(r,"u_translate"),u_zoom:e.getUniformLocation(r,"u_zoom"),u_angle:e.getUniformLocation(r,"u_angle"),u_density:e.getUniformLocation(r,"u_density")}}}(this.ctx),this.colorShadOP=function(t){var r=E(t,e);return{program:r,attr:[{bufferType:t.ARRAY_BUFFER,buffer:t.createBuffer(),drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(r,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_framebuffer:t.getUniformLocation(r,"u_framebuffer"),u_colorArr:t.getUniformLocation(r,"u_colorArr"),u_colorCount:t.getUniformLocation(r,"u_colorCount"),u_opacity:t.getUniformLocation(r,"u_opacity"),u_offset:t.getUniformLocation(r,"u_offset")}}}(this.ctx),this.imageShaOP=function(t){var e=E(t,r);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(),p(i.size)?this.size=20:this.setSize(i.size),p(i.max)?T=null:this.setMax(i.max),p(i.min)?g=null:this.setMin(i.min),p(i.intensity)?this.intensity=1:this.setIntensity(i.intensity),p(i.translate)?this.translate=[0,0]:this.setTranslate(i.translate),p(i.zoom)?this.zoom=1:this.setZoom(i.zoom),p(i.angle)?this.angle=0:this.setRotationAngle(i.angle),p(i.opacity)?this.opacity=1:this.setOpacity(i.opacity),this.gradient=y(i.gradient),this.ratio=n,i.backgroundImage&&i.backgroundImage.url&&this.setBackgroundImage(i.backgroundImage),this.heatmapData=[],this.ctx.viewport(0,0,this.ctx.canvas.width,this.ctx.canvas.height)}catch(t){console.error(t)}}function w(){const t=this.ctx;t.clear(t.COLOR_BUFFER_BIT|t.DEPTH_BUFFER_BIT),t.bindTexture(t.TEXTURE_2D,this.fbTexObj),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.width*this.ratio,this.height*this.ratio,0,t.RGBA,t.UNSIGNED_BYTE,null),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.bindFramebuffer(t.FRAMEBUFFER,this.fbo),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,this.fbTexObj,0),h&&b.call(this,t,h),t.bindFramebuffer(t.FRAMEBUFFER,null),c&&R.call(this,t,c),P.call(this,t)}function b(t,e){t.useProgram(this.gradShadOP.program),this.min=null!==g?g:e?.minMax?.min??0,this.max=null!==T?T: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,(e.posVec||[]).length/2)}function R(t,e){const{x:r=0,y:o=0,width:i=0,height:n=0}=e;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([r,o,r+i,o,r,o+n,r,o+n,r+i,o,r+i,o+n]),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 P(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 S(t){const e=this.zoom||.1,r=this.width/2,o=this.height/2,i=this.angle;let n=(t.x-r)/r*e,a=(t.y-o)/o*e;if(0!==i){const t=Math.cos(i),e=Math.sin(i),r=t*n-e*a;a=e*n+t*a,n=r}return n=n*r+r-this.translate[0],a=a*o+o-this.translate[1],t.x=n,t.y=a,{x:n,y:a}}return A.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.ctx.canvas.width,this.ctx.canvas.height),this.render(h)},A.prototype.clear=function(){this.ctx.clear(this.ctx.COLOR_BUFFER_BIT|this.ctx.DEPTH_BUFFER_BIT)},A.prototype.setMax=function(t){if(p(t)||x(t))throw new Error("Invalid max: Expected Number");return T=t,this},A.prototype.setMin=function(t){if(p(t)||x(t))throw new Error("Invalid min: Expected Number");return g=t,this},A.prototype.setGradient=function(t){return this.gradient=y(t),this},A.prototype.setTranslate=function(t){if(t.constructor!==Array)throw new Error("Invalid Translate: Translate has to be of Array type");if(2!==t.length)throw new Error("Translate has to be of length 2");return this.translate=t,this},A.prototype.setZoom=function(t){if(p(t)||x(t))throw new Error("Invalid zoom: Expected Number");return this.zoom=t,this},A.prototype.setRotationAngle=function(t){if(p(t)||x(t))throw new Error("Invalid Angle: Expected Number");return this.angle=t,this},A.prototype.setSize=function(t){if(p(t)||x(t))throw new Error("Invalid Size: Expected Number");return this.size=t,this},A.prototype.setIntensity=function(t){if(p(t)||x(t))throw this.intensity=1,new Error("Invalid Intensity: Expected Number");if(t>1||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;o<t.length;o++)e&&S.call(r,t[o]),this.heatmapData.push(t[o]);return this.renderData(this.heatmapData),this},A.prototype.renderData=function(t){if(t.constructor!==Array)throw new Error("Expected Array type");return h=function(t){const e=t.length;_!==e&&(a=new ArrayBuffer(8*e),l=new Float32Array(a),s=new ArrayBuffer(4*e),m=new Float32Array(s),_=e);const r={min:1/0,max:-1/0};for(let o=0;o<e;o++)l[2*o]=t[o].x,l[2*o+1]=t[o].y,m[o]=t[o].value,r.min>t[o].value&&(r.min=t[o].value),r.max<t[o].value&&(r.max=t[o].value);return{posVec:l,rVec:m,minMax:r}}(t),this.heatmapData=t,this.render(),this},A.prototype.render=function(){w.call(this)},A.prototype.projection=function(t){const e=this.zoom||.1,r=this.width/2,o=this.height/2,i=this.translate[0],n=this.translate[1],a=this.angle,s=this.width/this.height;let u=(t.x+i-r)/(r*e),f=(t.y+n-o)/(o*e);if(u*=s,0!==a){const t=Math.cos(-a),e=Math.sin(-a),r=t*u-e*f;f=e*u+t*f,u=r}return u*=1/s,u=u*r+r,f=f*o+o,{x:u,y:f}},new A(o,i)}})); | ||
!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;e<t.length-1;e++)if(t[e+1].offset-t[e].offset<0)return!1;return!0}(t))throw new Error("Invalid gradient: Gradient is not sorted");const e=t.length,i=new Float32Array(4*e),r=new Array(e);return t.forEach((function(t,e){const o=4*e;i[o]=t.color[0]/255,i[o+1]=t.color[1]/255,i[o+2]=t.color[2]/255,i[o+3]=void 0!==t.color[3]?t.color[3]:1,r[e]=t.offset})),{value:i,length:e,offset:r}}function u(t){const e=this,i=t.length;let{posVec:r=new Float32Array,rVec:o=new Float32Array}=e.hearmapExData||{};e._pDataLength!==i&&(r=new Float32Array(new ArrayBuffer(8*i)),o=new Float32Array(new ArrayBuffer(4*i)),e._pDataLength=i);const n={min:1/0,max:-1/0};for(let e=0;e<i;e++)r[2*e]=t[e].x,r[2*e+1]=t[e].y,o[e]=t[e].value,n.min>t[e].value&&(n.min=t[e].value),n.max<t[e].value&&(n.max=t[e].value);return{posVec:r,rVec:o,minMax:n}}function h(t){const e=this.zoom||.1,i=this.width/2,r=this.height/2,{angle:o,translate:n}=this;let a=(t.x-i)/i*e,s=(t.y-r)/r*e;if(0!==o){const t=Math.cos(o),e=Math.sin(o);s=e*a+t*s,a=t*a-e*s}return a=a*i+i-n[0],s=s*r+r-n[1],t.x=a,t.y=s,{x:a,y:s}}function f(){const t=this.ctx;t&&(t.clear(t.COLOR_BUFFER_BIT|t.DEPTH_BUFFER_BIT),t.bindTexture(t.TEXTURE_2D,this._fbTexObj),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.width*this.ratio,this.height*this.ratio,0,t.RGBA,t.UNSIGNED_BYTE,null),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.bindFramebuffer(t.FRAMEBUFFER,this._fbo),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,this._fbTexObj,0),this.hearmapExData&&l.call(this,t,this.hearmapExData),t.bindFramebuffer(t.FRAMEBUFFER,null),this.imageConfig&&c.call(this,t,this.imageConfig),m.call(this,t))}function l(t,e){var i,r,o,n;t.useProgram(this._gradShadOP.program);const{u_resolution:a,u_translate:s,u_zoom:u,u_angle:h,u_density:f,u_max:l,u_min:c,u_size:m,u_intensity:_}=this._gradShadOP.uniform;this.min=null!==this.configMin?this.configMin:null!==(r=null===(i=null==e?void 0:e.minMax)||void 0===i?void 0:i.min)&&void 0!==r?r:0,this.max=null!==this.configMax?this.configMax:null!==(n=null===(o=null==e?void 0:e.minMax)||void 0===o?void 0:o.max)&&void 0!==n?n:0,this._gradShadOP.attr[0].data=e.posVec||[],this._gradShadOP.attr[1].data=e.rVec||[],t.uniform2fv(a,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(s,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(u,this.zoom?this.zoom:.01),t.uniform1f(h,this.angle),t.uniform1f(f,this.ratio),t.uniform1f(l,this.max),t.uniform1f(c,this.min),t.uniform1f(m,this.size),t.uniform1f(_,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,(e.posVec||[]).length/2)}function c(t,e){const{x:i=0,y:r=0,width:o=0,height:n=0}=e,{u_resolution:a,u_translate:s,u_zoom:u,u_angle:h,u_density:f,u_image:l}=this._imageShaOP.uniform;t.useProgram(this._imageShaOP.program),t.uniform2fv(a,new Float32Array([this.width*this.ratio,this.height*this.ratio])),t.uniform2fv(s,new Float32Array([this.translate[0],this.translate[1]])),t.uniform1f(u,this.zoom?this.zoom:.01),t.uniform1f(h,this.angle),t.uniform1f(f,this.ratio),this._imageShaOP.attr[0].data=new Float32Array([i,r,i+o,r,i,r+n,i,r+n,i+o,r,i+o,r+n]),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(l,0),t.activeTexture(this.ctx.TEXTURE0),t.bindTexture(this.ctx.TEXTURE_2D,this._imageTexture),t.drawArrays(t.TRIANGLES,0,6)}function m(t){const{u_colorArr:e,u_colorCount:i,u_offset:r,u_opacity:o,u_framebuffer:n}=this._colorShadOP.uniform;t.useProgram(this._colorShadOP.program),t.uniform4fv(e,this.gradient.value),t.uniform1f(i,this.gradient.length),t.uniform1fv(r,new Float32Array(this.gradient.offset)),t.uniform1f(o,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(n,0),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this._fbTexObj),t.drawArrays(t.TRIANGLES,0,6)}class _{constructor(r,a){this.ctx=null,this.ratio=1,this.width=0,this.height=0,this.imageConfig=null,this.configMin=null,this.configMax=null,this.min=0,this.max=0,this.size=0,this.zoom=0,this.angle=0,this.intensity=0,this.translate=[0,0],this.opacity=0,this.hearmapExData={},this.gradient=null,this._imageTexture=null,this._pDataLength=void 0,this.imgWidth=0,this.imgHeight=0,this.heatmapData=[],this.type="";try{const u="string"==typeof r?document.querySelector(r):r instanceof HTMLElement?r:null;if(!u)throw new Error("Context must be either a string or an Element");const{clientHeight:h,clientWidth:f}=u,l=document.createElement("canvas"),c=l.getContext("webgl2",{premultipliedAlpha:!1,depth:!1,antialias:!0,alpha:!0,preserveDrawingBuffer:!1});this.ratio=function(t){return(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1)}(c),c.clearColor(0,0,0,0),c.enable(c.BLEND),c.blendEquation(c.FUNC_ADD),c.blendFunc(c.ONE,c.ONE_MINUS_SRC_ALPHA),c.depthMask(!0),l.setAttribute("height",(h*this.ratio).toString()),l.setAttribute("width",(f*this.ratio).toString()),l.style.height=`${h}px`,l.style.width=`${f}px`,l.style.position="absolute",u.appendChild(l),this.ctx=c,this.width=f,this.height=h,this.imageConfig=null,this.configMin=null,this.configMax=null,this.hearmapExData={},this.layer=l,this.dom=u,this._gradShadOP=function(t,e){const i=o(t,e),r=t.createBuffer();if(!r)throw new Error("Failed to create position buffer.");const n=t.createBuffer();if(!n)throw new Error("Failed to create intensity buffer.");return{program:i,attr:[{bufferType:t.ARRAY_BUFFER,buffer:r,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:n,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:1,attribute:t.getAttribLocation(i,"a_intensity"),data:new Float32Array([])}],uniform:{u_resolution:t.getUniformLocation(i,"u_resolution"),u_max:t.getUniformLocation(i,"u_max"),u_min:t.getUniformLocation(i,"u_min"),u_size:t.getUniformLocation(i,"u_size"),u_intensity:t.getUniformLocation(i,"u_intensity"),u_translate:t.getUniformLocation(i,"u_translate"),u_zoom:t.getUniformLocation(i,"u_zoom"),u_angle:t.getUniformLocation(i,"u_angle"),u_density:t.getUniformLocation(i,"u_density")}}}(this.ctx,t),this._colorShadOP=function(t,e){const i=o(t,e),r=t.createBuffer();if(!r)throw new Error("Failed to create texture coordinate buffer.");return{program:i,attr:[{bufferType:t.ARRAY_BUFFER,buffer:r,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_framebuffer:t.getUniformLocation(i,"u_framebuffer"),u_colorArr:t.getUniformLocation(i,"u_colorArr"),u_colorCount:t.getUniformLocation(i,"u_colorCount"),u_opacity:t.getUniformLocation(i,"u_opacity"),u_offset:t.getUniformLocation(i,"u_offset")}}}(this.ctx,e),this._imageShaOP=function(t,e){const i=o(t,e),r=t.createBuffer();if(!r)throw new Error("Failed to create position buffer.");const n=t.createBuffer();if(!n)throw new Error("Failed to create texture coordinate buffer.");return{program:i,attr:[{bufferType:t.ARRAY_BUFFER,buffer:r,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_position"),data:new Float32Array([])},{bufferType:t.ARRAY_BUFFER,buffer:n,drawType:t.STATIC_DRAW,valueType:t.FLOAT,size:2,attribute:t.getAttribLocation(i,"a_texCoord"),data:new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1])}],uniform:{u_resolution:t.getUniformLocation(i,"u_resolution"),u_image:t.getUniformLocation(i,"u_image"),u_translate:t.getUniformLocation(i,"u_translate"),u_zoom:t.getUniformLocation(i,"u_zoom"),u_angle:t.getUniformLocation(i,"u_angle"),u_density:t.getUniformLocation(i,"u_density")}}}(this.ctx,i),this._fbTexObj=c.createTexture(),this._fbo=c.createFramebuffer(),n(a.size)?this.size=20:this.setSize(a.size),n(a.max)?this.configMax=null:this.setMax(a.max),n(a.min)?this.configMin=null:this.setMin(a.min),n(a.intensity)?this.intensity=1:this.setIntensity(a.intensity),n(a.translate)?this.translate=[0,0]:this.setTranslate(a.translate),n(a.zoom)?this.zoom=1:this.setZoom(a.zoom),n(a.angle)?this.angle=0:this.setRotationAngle(a.angle),n(a.opacity)?this.opacity=1:this.setOpacity(a.opacity),this.gradient=s(a.gradient),a.backgroundImage&&a.backgroundImage.url&&this.setBackgroundImage(a.backgroundImage),this.heatmapData=[],this.ctx.viewport(0,0,this.ctx.canvas.width,this.ctx.canvas.height)}catch(t){console.error(t)}}resize(){const t=this.dom.clientHeight,e=this.dom.clientWidth;this.layer.setAttribute("height",(t*this.ratio).toString()),this.layer.setAttribute("width",(e*this.ratio).toString()),this.layer.style.height=`${t}px`,this.layer.style.width=`${e}px`,this.width=e,this.height=t,this.ctx.viewport(0,0,this.ctx.canvas.width,this.ctx.canvas.height),this.render()}clear(){this.ctx.clear(this.ctx.COLOR_BUFFER_BIT|this.ctx.DEPTH_BUFFER_BIT)}setMax(t){if(n(t)||a(t))throw new Error("Invalid max: Expected Number");return this.configMax=t,this}setMin(t){if(n(t)||a(t))throw new Error("Invalid min: Expected Number");return this.configMin=t,this}setGradient(t){return this.gradient=s(t),this}setTranslate(t){if(t.constructor!==Array)throw new Error("Invalid Translate: Translate has to be of Array type");if(2!==t.length)throw new Error("Translate has to be of length 2");return this.translate=t,this}setZoom(t){if(n(t)||a(t))throw new Error("Invalid zoom: Expected Number");return this.zoom=t,this}setRotationAngle(t){if(n(t)||a(t))throw new Error("Invalid Angle: Expected Number");return this.angle=t,this}setSize(t){if(n(t)||a(t))throw new Error("Invalid Size: Expected Number");return this.size=t,this}setIntensity(t){if(n(t)||a(t))throw this.intensity=1,new Error("Invalid Intensity: Expected Number");if(t>1||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<t.length;r++)e&&h.call(i,t[r]),this.heatmapData.push(t[r]);return this.renderData(this.heatmapData),this}renderData(t){if(t.constructor!==Array)throw new Error("Expected Array type");return this.hearmapExData=u.call(this,t),this.heatmapData=t,this.render(),this}render(){f.call(this)}projection(t){const e=this.zoom||.1,i=this.width/2,r=this.height/2,o=this.translate[0],n=this.translate[1],a=this.angle,s=this.width/this.height;let u=(t.x+o-i)/(i*e),h=(t.y+n-r)/(r*e);if(u*=s,0!==a){const t=Math.cos(-a),e=Math.sin(-a),i=t*u-e*h;h=e*u+t*h,u=i}return u*=1/s,u=u*i+i,h=h*r+r,{x:u,y:h}}}return function(t,e){return new _(t,e)}})); |
{ | ||
"name": "visual-heatmap", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"description": "\"Advanced Visual Heatmap - High Scale webGL based rendering.\"", | ||
"module": "./dist/visualHeatmap.esm.js", | ||
"main": "./dist/visualHeatmap.js", | ||
"types": "./dist/types/main.d.ts", | ||
"scripts": { | ||
@@ -29,2 +30,8 @@ "dev": "rollup -wm -c rollup.config.js", | ||
"license": "BSD-3-Clause", | ||
"contributors": [ | ||
{ | ||
"name": "Mohit Kumar Toshniwal", | ||
"email": "mohitkrtoshniwal@gmail.com" | ||
} | ||
], | ||
"bugs": { | ||
@@ -35,11 +42,18 @@ "url": "https://github.com/nswamy14/visual-heatmap/issues" | ||
"devDependencies": { | ||
"@babel/core": "^7.24.4", | ||
"@babel/preset-env": "^7.24.4", | ||
"@rollup/plugin-babel": "^6.0.4", | ||
"@rollup/plugin-eslint": "^9.0.5", | ||
"@rollup/plugin-commonjs": "^22.0.2", | ||
"@rollup/plugin-node-resolve": "^13.3.0", | ||
"eslint": "^7.32.0", | ||
"@rollup/plugin-typescript": "^11.1.6", | ||
"@typescript-eslint/eslint-plugin": "^7.7.0", | ||
"@typescript-eslint/parser": "^7.7.0", | ||
"eslint": "^8.56.0", | ||
"eslint-config-prettier": "^6.15.0", | ||
"eslint-config-standard": "^14.1.1", | ||
"rollup": "^2.79.1", | ||
"rollup-plugin-terser": "^7.0.0" | ||
"rollup-plugin-terser": "^7.0.0", | ||
"tslib": "^2.6.2", | ||
"typescript": "^5.4.4" | ||
} | ||
} |
@@ -131,2 +131,5 @@ # Visual-Heatmap Js [![npm](https://img.shields.io/npm/v/visual-heatmap.svg)](https://www.npmjs.com/package/visual-heatmap) [![Downloads](https://img.shields.io/npm/dm/visual-heatmap.svg)](https://www.npmjs.com/package/visual-heatmap) | ||
### instance.setGradient(gradient) | ||
Api to set color gradient. Accepts array of objects with color value and offset. | ||
### instance.setIntensity(number) | ||
@@ -148,4 +151,7 @@ Api to set Intensity factor. Accepts float value as an input. | ||
### instance.resize() | ||
Api to rerender heatmep on parent container resizes. | ||
### instance.clear() | ||
Api to clear canvas. | ||
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
121313
13
2029
156
15
1