/** * @Author: Caven * @Date: 2021-05-01 17:38:47 */ const __indexOf = [].indexOf || function(item) { for (let i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i } return -1 } const vertexShaderBlit = 'attribute vec4 position;\nvarying vec2 texcoord;\nvoid main(){\n texcoord = position.xy*0.5+0.5;\n gl_Position = position;\n}' const fragmentShaderBlit = '#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp int;\n precision highp float;\n#else\n precision mediump int;\n precision mediump float;\n#endif\nuniform sampler2D source;\nvarying vec2 texcoord;' const nukeVendorPrefix = function() { let getExtension, getSupportedExtensions, vendorRe, vendors if (window.WebGLRenderingContext != null) { vendors = ['WEBKIT', 'MOZ', 'MS', 'O'] vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/ getExtension = WebGLRenderingContext.prototype.getExtension WebGLRenderingContext.prototype.getExtension = function(name) { let extobj, match, vendor, _i, _len match = name.match(vendorRe) if (match !== null) { name = match[1] } extobj = getExtension.call(this, name) if (extobj === null) { for (_i = 0, _len = vendors.length; _i < _len; _i++) { vendor = vendors[_i] extobj = getExtension.call(this, vendor + '_' + name) if (extobj !== null) { return extobj } } return null } else { return extobj } } getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions return (WebGLRenderingContext.prototype.getSupportedExtensions = function() { let extension, match, result, supported, _i, _len supported = getSupportedExtensions.call(this) result = [] for (_i = 0, _len = supported.length; _i < _len; _i++) { extension = supported[_i] match = extension.match(vendorRe) if (match !== null) { extension = match[1] } if (__indexOf.call(result, extension) < 0) { result.push(extension) } } return result }) } } const textureFloatShims = function() { let checkColorBuffer, checkFloatLinear, checkSupport, checkTexture, createSourceCanvas, getExtension, getSupportedExtensions, name, shimExtensions, shimLookup, unshimExtensions, unshimLookup, _i, _len createSourceCanvas = function() { let canvas, ctx, imageData canvas = document.createElement('canvas') canvas.width = 2 canvas.height = 2 ctx = canvas.getContext('2d') imageData = ctx.getImageData(0, 0, 2, 2) imageData.data.set( new Uint8ClampedArray([ 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255 ]) ) ctx.putImageData(imageData, 0, 0) return canvas } createSourceCanvas() checkFloatLinear = function(gl, sourceType) { let buffer, cleanup, fragmentShader, framebuffer, positionLoc, program, readBuffer, result, source, sourceCanvas, sourceLoc, target, vertexShader, vertices program = gl.createProgram() vertexShader = gl.createShader(gl.VERTEX_SHADER) gl.attachShader(program, vertexShader) gl.shaderSource( vertexShader, 'attribute vec2 position;\nvoid main(){\n gl_Position = vec4(position, 0.0, 1.0);\n}' ) gl.compileShader(vertexShader) if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { throw gl.getShaderInfoLog(vertexShader) } fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) gl.attachShader(program, fragmentShader) gl.shaderSource( fragmentShader, 'uniform sampler2D source;\nvoid main(){\n gl_FragColor = texture2D(source, vec2(1.0, 1.0));\n}' ) gl.compileShader(fragmentShader) if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { throw gl.getShaderInfoLog(fragmentShader) } gl.linkProgram(program) if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { throw gl.getProgramInfoLog(program) } gl.useProgram(program) cleanup = function() { gl.deleteShader(fragmentShader) gl.deleteShader(vertexShader) gl.deleteProgram(program) gl.deleteBuffer(buffer) gl.deleteTexture(source) gl.deleteTexture(target) gl.deleteFramebuffer(framebuffer) gl.bindBuffer(gl.ARRAY_BUFFER, null) gl.useProgram(null) gl.bindTexture(gl.TEXTURE_2D, null) return gl.bindFramebuffer(gl.FRAMEBUFFER, null) } target = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, target) gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) framebuffer = gl.createFramebuffer() gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer) gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0 ) sourceCanvas = createSourceCanvas() source = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, source) gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, sourceType, sourceCanvas) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) vertices = new Float32Array([1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1]) buffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, buffer) gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW) positionLoc = gl.getAttribLocation(program, 'position') sourceLoc = gl.getUniformLocation(program, 'source') gl.enableVertexAttribArray(positionLoc) gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0) gl.uniform1i(sourceLoc, 0) gl.drawArrays(gl.TRIANGLES, 0, 6) readBuffer = new Uint8Array(4 * 4) gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer) result = Math.abs(readBuffer[0] - 127) < 10 cleanup() return result } checkTexture = function(gl, targetType) { let target target = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, target) gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null) if (gl.getError() === 0) { gl.deleteTexture(target) return true } else { gl.deleteTexture(target) return false } } checkColorBuffer = function(gl, targetType) { let check, framebuffer, target target = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, target) gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null) framebuffer = gl.createFramebuffer() gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer) gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0 ) check = gl.checkFramebufferStatus(gl.FRAMEBUFFER) gl.deleteTexture(target) gl.deleteFramebuffer(framebuffer) gl.bindTexture(gl.TEXTURE_2D, null) gl.bindFramebuffer(gl.FRAMEBUFFER, null) if (check === gl.FRAMEBUFFER_COMPLETE) { return true } else { return false } } shimExtensions = [] shimLookup = {} unshimExtensions = [] checkSupport = function() { let canvas, extobj, gl, halfFloatExt, halfFloatTexturing, singleFloatExt, singleFloatTexturing canvas = document.createElement('canvas') gl = null try { gl = canvas.getContext('experimental-webgl') if (gl === null) { gl = canvas.getContext('webgl') } // eslint-disable-next-line no-empty } catch (_error) {} if (gl != null) { singleFloatExt = gl.getExtension('OES_texture_float') if (singleFloatExt === null) { if (checkTexture(gl, gl.FLOAT)) { singleFloatTexturing = true shimExtensions.push('OES_texture_float') shimLookup.OES_texture_float = { shim: true } } else { singleFloatTexturing = false unshimExtensions.push('OES_texture_float') } } else { if (checkTexture(gl, gl.FLOAT)) { singleFloatTexturing = true shimExtensions.push('OES_texture_float') } else { singleFloatTexturing = false unshimExtensions.push('OES_texture_float') } } if (singleFloatTexturing) { extobj = gl.getExtension('WEBGL_color_buffer_float') if (extobj === null) { if (checkColorBuffer(gl, gl.FLOAT)) { shimExtensions.push('WEBGL_color_buffer_float') shimLookup.WEBGL_color_buffer_float = { shim: true, RGBA32F_EXT: 0x8814, RGB32F_EXT: 0x8815, FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211, UNSIGNED_NORMALIZED_EXT: 0x8c17 } } else { unshimExtensions.push('WEBGL_color_buffer_float') } } else { if (checkColorBuffer(gl, gl.FLOAT)) { shimExtensions.push('WEBGL_color_buffer_float') } else { unshimExtensions.push('WEBGL_color_buffer_float') } } extobj = gl.getExtension('OES_texture_float_linear') if (extobj === null) { if (checkFloatLinear(gl, gl.FLOAT)) { shimExtensions.push('OES_texture_float_linear') shimLookup.OES_texture_float_linear = { shim: true } } else { unshimExtensions.push('OES_texture_float_linear') } } else { if (checkFloatLinear(gl, gl.FLOAT)) { shimExtensions.push('OES_texture_float_linear') } else { unshimExtensions.push('OES_texture_float_linear') } } } halfFloatExt = gl.getExtension('OES_texture_half_float') if (halfFloatExt === null) { if (checkTexture(gl, 0x8d61)) { halfFloatTexturing = true shimExtensions.push('OES_texture_half_float') halfFloatExt = shimLookup.OES_texture_half_float = { HALF_FLOAT_OES: 0x8d61, shim: true } } else { halfFloatTexturing = false unshimExtensions.push('OES_texture_half_float') } } else { if (checkTexture(gl, halfFloatExt.HALF_FLOAT_OES)) { halfFloatTexturing = true shimExtensions.push('OES_texture_half_float') } else { halfFloatTexturing = false unshimExtensions.push('OES_texture_half_float') } } if (halfFloatTexturing) { extobj = gl.getExtension('EXT_color_buffer_half_float') if (extobj === null) { if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('EXT_color_buffer_half_float') shimLookup.EXT_color_buffer_half_float = { shim: true, RGBA16F_EXT: 0x881a, RGB16F_EXT: 0x881b, FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211, UNSIGNED_NORMALIZED_EXT: 0x8c17 } } else { unshimExtensions.push('EXT_color_buffer_half_float') } } else { if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('EXT_color_buffer_half_float') } else { unshimExtensions.push('EXT_color_buffer_half_float') } } extobj = gl.getExtension('OES_texture_half_float_linear') if (extobj === null) { if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('OES_texture_half_float_linear') return (shimLookup.OES_texture_half_float_linear = { shim: true }) } else { return unshimExtensions.push('OES_texture_half_float_linear') } } else { if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) { return shimExtensions.push('OES_texture_half_float_linear') } else { return unshimExtensions.push('OES_texture_half_float_linear') } } } } } if (window.WebGLRenderingContext != null) { checkSupport() unshimLookup = {} for (_i = 0, _len = unshimExtensions.length; _i < _len; _i++) { name = unshimExtensions[_i] unshimLookup[name] = true } getExtension = WebGLRenderingContext.prototype.getExtension WebGLRenderingContext.prototype.getExtension = function(name) { let extobj extobj = shimLookup[name] if (extobj === void 0) { if (unshimLookup[name]) { return null } else { return getExtension.call(this, name) } } else { return extobj } } getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions WebGLRenderingContext.prototype.getSupportedExtensions = function() { let extension, result, supported, _j, _k, _len1, _len2 supported = getSupportedExtensions.call(this) result = [] for (_j = 0, _len1 = supported.length; _j < _len1; _j++) { extension = supported[_j] if (unshimLookup[extension] === void 0) { result.push(extension) } } for (_k = 0, _len2 = shimExtensions.length; _k < _len2; _k++) { extension = shimExtensions[_k] if (__indexOf.call(result, extension) < 0) { result.push(extension) } } return result } return (WebGLRenderingContext.prototype.getFloatExtension = function(spec) { let candidate, candidates, half, halfFramebuffer, halfLinear, halfTexture, i, importance, preference, result, single, singleFramebuffer, singleLinear, singleTexture, use, _j, _k, _l, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2 if (spec.prefer == null) { spec.prefer = ['half'] } if (spec.require == null) { spec.require = [] } if (spec.throws == null) { spec.throws = true } singleTexture = this.getExtension('OES_texture_float') halfTexture = this.getExtension('OES_texture_half_float') singleFramebuffer = this.getExtension('WEBGL_color_buffer_float') halfFramebuffer = this.getExtension('EXT_color_buffer_half_float') singleLinear = this.getExtension('OES_texture_float_linear') halfLinear = this.getExtension('OES_texture_half_float_linear') single = { texture: singleTexture !== null, filterable: singleLinear !== null, renderable: singleFramebuffer !== null, score: 0, precision: 'single', half: false, single: true, type: this.FLOAT } half = { texture: halfTexture !== null, filterable: halfLinear !== null, renderable: halfFramebuffer !== null, score: 0, precision: 'half', half: true, single: false, type: (_ref = halfTexture != null ? halfTexture.HALF_FLOAT_OES : void 0) != null ? _ref : null } candidates = [] if (single.texture) { candidates.push(single) } if (half.texture) { candidates.push(half) } result = [] for (_j = 0, _len1 = candidates.length; _j < _len1; _j++) { candidate = candidates[_j] use = true _ref1 = spec.require for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { name = _ref1[_k] if (candidate[name] === false) { use = false } } if (use) { result.push(candidate) } } for (_l = 0, _len3 = result.length; _l < _len3; _l++) { candidate = result[_l] _ref2 = spec.prefer for (i = _m = 0, _len4 = _ref2.length; _m < _len4; i = ++_m) { preference = _ref2[i] importance = Math.pow(2, spec.prefer.length - i - 1) if (candidate[preference]) { candidate.score += importance } } } result.sort(function(a, b) { if (a.score === b.score) { return 0 } else if (a.score < b.score) { return 1 } else if (a.score > b.score) { return -1 } }) if (result.length === 0) { if (spec.throws) { throw 'No floating point texture support that is ' + spec.require.join(', ') } else { return null } } else { result = result[0] return { filterable: result.filterable, renderable: result.renderable, type: result.type, precision: result.precision } } }) } } nukeVendorPrefix() textureFloatShims() const Shader = (function() { function Shader(gl, _arg) { let fragment, vertex this.gl = gl vertex = _arg.vertex fragment = _arg.fragment this.program = this.gl.createProgram() this.vs = this.gl.createShader(this.gl.VERTEX_SHADER) this.fs = this.gl.createShader(this.gl.FRAGMENT_SHADER) this.gl.attachShader(this.program, this.vs) this.gl.attachShader(this.program, this.fs) this.compileShader(this.vs, vertex) this.compileShader(this.fs, fragment) this.link() this.value_cache = {} this.uniform_cache = {} this.attribCache = {} } Shader.prototype.attribLocation = function(name) { let location = this.attribCache[name] if (location === void 0) { location = this.attribCache[name] = this.gl.getAttribLocation( this.program, name ) } return location } Shader.prototype.compileShader = function(shader, source) { this.gl.shaderSource(shader, source) this.gl.compileShader(shader) if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { throw 'Shader Compile Error: ' + this.gl.getShaderInfoLog(shader) } } Shader.prototype.link = function() { this.gl.linkProgram(this.program) if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) { throw 'Shader Link Error: ' + this.gl.getProgramInfoLog(this.program) } } Shader.prototype.use = function() { this.gl.useProgram(this.program) return this } Shader.prototype.uniformLoc = function(name) { let location = this.uniform_cache[name] if (location === void 0) { location = this.uniform_cache[name] = this.gl.getUniformLocation( this.program, name ) } return location } Shader.prototype.int = function(name, value) { let cached, loc cached = this.value_cache[name] if (cached !== value) { this.value_cache[name] = value loc = this.uniformLoc(name) if (loc) { this.gl.uniform1i(loc, value) } } return this } Shader.prototype.vec2 = function(name, a, b) { let loc loc = this.uniformLoc(name) if (loc) { this.gl.uniform2f(loc, a, b) } return this } Shader.prototype.float = function(name, value) { let cached, loc cached = this.value_cache[name] if (cached !== value) { this.value_cache[name] = value loc = this.uniformLoc(name) if (loc) { this.gl.uniform1f(loc, value) } } return this } return Shader })() const Framebuffer = (function() { function Framebuffer(gl) { this.gl = gl this.buffer = this.gl.createFramebuffer() } Framebuffer.prototype.destroy = function() { return this.gl.deleteFRamebuffer(this.buffer) } Framebuffer.prototype.bind = function() { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffer) return this } Framebuffer.prototype.unbind = function() { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null) return this } Framebuffer.prototype.check = function() { let result result = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER) switch (result) { case this.gl.FRAMEBUFFER_UNSUPPORTED: throw 'Framebuffer is unsupported' // eslint-disable-next-line no-unreachable break case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: throw 'Framebuffer incomplete attachment' // eslint-disable-next-line no-unreachable break case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: throw 'Framebuffer incomplete dimensions' // eslint-disable-next-line no-unreachable break case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: throw 'Framebuffer incomplete missing attachment' } return this } Framebuffer.prototype.color = function(texture) { this.gl.framebufferTexture2D( this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, texture.target, texture.handle, 0 ) this.check() return this } Framebuffer.prototype.depth = function(buffer) { this.gl.framebufferRenderbuffer( this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, buffer.id ) this.check() return this } Framebuffer.prototype.destroy = function() { return this.gl.deleteFramebuffer(this.buffer) } return Framebuffer })() const Texture = (function() { function Texture(gl, params) { let _ref, _ref1 this.gl = gl if (params == null) { params = {} } this.channels = this.gl[ ((_ref = params.channels) != null ? _ref : 'rgba').toUpperCase() ] if (typeof params.type === 'number') { this.type = params.type } else { this.type = this.gl[ ((_ref1 = params.type) != null ? _ref1 : 'unsigned_byte').toUpperCase() ] } switch (this.channels) { case this.gl.RGBA: this.chancount = 4 break case this.gl.RGB: this.chancount = 3 break case this.gl.LUMINANCE_ALPHA: this.chancount = 2 break default: this.chancount = 1 } this.target = this.gl.TEXTURE_2D this.handle = this.gl.createTexture() } Texture.prototype.destroy = function() { return this.gl.deleteTexture(this.handle) } Texture.prototype.bind = function(unit) { if (unit == null) { unit = 0 } if (unit > 15) { throw 'Texture unit too large: ' + unit } this.gl.activeTexture(this.gl.TEXTURE0 + unit) this.gl.bindTexture(this.target, this.handle) return this } Texture.prototype.setSize = function(width, height) { this.width = width this.height = height this.gl.texImage2D( this.target, 0, this.channels, this.width, this.height, 0, this.channels, this.type, null ) return this } Texture.prototype.upload = function(data) { this.width = data.width this.height = data.height this.gl.texImage2D( this.target, 0, this.channels, this.channels, this.type, data ) return this } Texture.prototype.linear = function() { this.gl.texParameteri( this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR ) this.gl.texParameteri( this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR ) return this } Texture.prototype.nearest = function() { this.gl.texParameteri( this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST ) this.gl.texParameteri( this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST ) return this } Texture.prototype.clampToEdge = function() { this.gl.texParameteri( this.target, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE ) this.gl.texParameteri( this.target, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE ) return this } Texture.prototype.repeat = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT) this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT) return this } return Texture })() const Node = (function() { function Node(gl, width, height) { let floatExt this.gl = gl this.width = width this.height = height floatExt = this.gl.getFloatExtension({ require: ['renderable'] }) this.texture = new Texture(this.gl, { type: floatExt.type }) .bind(0) .setSize(this.width, this.height) .nearest() .clampToEdge() this.fbo = new Framebuffer(this.gl) .bind() .color(this.texture) .unbind() } Node.prototype.use = function() { return this.fbo.bind() } Node.prototype.bind = function(unit) { return this.texture.bind(unit) } Node.prototype.end = function() { return this.fbo.unbind() } Node.prototype.resize = function(width, height) { this.width = width this.height = height return this.texture.bind(0).setSize(this.width, this.height) } return Node })() const Heights = (function() { function Heights(heatmap, gl, width, height) { let i, _i, _ref this.heatmap = heatmap this.gl = gl this.width = width this.height = height this.shader = new Shader(this.gl, { vertex: 'attribute vec4 position, intensity;\nvarying vec2 off, dim;\nvarying float vIntensity;\nuniform vec2 viewport;\n\nvoid main(){\n dim = abs(position.zw);\n off = position.zw;\n vec2 pos = position.xy + position.zw;\n vIntensity = intensity.x;\n gl_Position = vec4((pos/viewport)*2.0-1.0, 0.0, 1.0);\n}', fragment: '#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp int;\n precision highp float;\n#else\n precision mediump int;\n precision mediump float;\n#endif\nvarying vec2 off, dim;\nvarying float vIntensity;\nvoid main(){\n float falloff = (1.0 - smoothstep(0.0, 1.0, length(off/dim)));\n float intensity = falloff*vIntensity;\n gl_FragColor = vec4(intensity);\n}' }) this.clampShader = new Shader(this.gl, { vertex: vertexShaderBlit, fragment: fragmentShaderBlit + 'uniform float low, high;\nvoid main(){\n gl_FragColor = vec4(clamp(texture2D(source, texcoord).rgb, low, high), 1.0);\n}' }) this.multiplyShader = new Shader(this.gl, { vertex: vertexShaderBlit, fragment: fragmentShaderBlit + 'uniform float value;\nvoid main(){\n gl_FragColor = vec4(texture2D(source, texcoord).rgb*value, 1.0);\n}' }) this.blurShader = new Shader(this.gl, { vertex: vertexShaderBlit, fragment: fragmentShaderBlit + 'uniform vec2 viewport;\nvoid main(){\n vec4 result = vec4(0.0);\n for(int x=-1; x<=1; x++){\n for(int y=-1; y<=1; y++){\n vec2 off = vec2(x,y)/viewport;\n //float factor = 1.0 - smoothstep(0.0, 1.5, length(off));\n float factor = 1.0;\n result += vec4(texture2D(source, texcoord+off).rgb*factor, factor);\n }\n }\n gl_FragColor = vec4(result.rgb/result.w, 1.0);\n}' }) this.nodeBack = new Node(this.gl, this.width, this.height) this.nodeFront = new Node(this.gl, this.width, this.height) this.vertexBuffer = this.gl.createBuffer() this.vertexSize = 8 this.maxPointCount = 1024 * 10 this.vertexBufferData = new Float32Array( this.maxPointCount * this.vertexSize * 6 ) this.vertexBufferViews = [] for ( i = _i = 0, _ref = this.maxPointCount; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i ) { this.vertexBufferViews.push( new Float32Array( this.vertexBufferData.buffer, 0, i * this.vertexSize * 6 ) ) } this.bufferIndex = 0 this.pointCount = 0 } Heights.prototype.resize = function(width, height) { this.width = width this.height = height this.nodeBack.resize(this.width, this.height) return this.nodeFront.resize(this.width, this.height) } Heights.prototype.update = function() { var intensityLoc, positionLoc if (this.pointCount > 0) { this.gl.enable(this.gl.BLEND) this.nodeFront.use() this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer) this.gl.bufferData( this.gl.ARRAY_BUFFER, this.vertexBufferViews[this.pointCount], this.gl.STREAM_DRAW ) positionLoc = this.shader.attribLocation('position') intensityLoc = this.shader.attribLocation('intensity') this.gl.enableVertexAttribArray(1) this.gl.vertexAttribPointer( positionLoc, 4, this.gl.FLOAT, false, 8 * 4, 0 ) this.gl.vertexAttribPointer( intensityLoc, 4, this.gl.FLOAT, false, 8 * 4, 4 * 4 ) this.shader.use().vec2('viewport', this.width, this.height) this.gl.drawArrays(this.gl.TRIANGLES, 0, this.pointCount * 6) this.gl.disableVertexAttribArray(1) this.pointCount = 0 this.bufferIndex = 0 this.nodeFront.end() return this.gl.disable(this.gl.BLEND) } } Heights.prototype.clear = function() { this.nodeFront.use() this.gl.clearColor(0, 0, 0, 1) this.gl.clear(this.gl.COLOR_BUFFER_BIT) return this.nodeFront.end() } Heights.prototype.clamp = function(min, max) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.heatmap.quad) this.gl.vertexAttribPointer(0, 4, this.gl.FLOAT, false, 0, 0) this.nodeFront.bind(0) this.nodeBack.use() this.clampShader .use() .int('source', 0) .float('low', min) .float('high', max) this.gl.drawArrays(this.gl.TRIANGLES, 0, 6) this.nodeBack.end() return this.swap() } Heights.prototype.multiply = function(value) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.heatmap.quad) this.gl.vertexAttribPointer(0, 4, this.gl.FLOAT, false, 0, 0) this.nodeFront.bind(0) this.nodeBack.use() this.multiplyShader .use() .int('source', 0) .float('value', value) this.gl.drawArrays(this.gl.TRIANGLES, 0, 6) this.nodeBack.end() return this.swap() } Heights.prototype.blur = function() { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.heatmap.quad) this.gl.vertexAttribPointer(0, 4, this.gl.FLOAT, false, 0, 0) this.nodeFront.bind(0) this.nodeBack.use() this.blurShader .use() .int('source', 0) .vec2('viewport', this.width, this.height) this.gl.drawArrays(this.gl.TRIANGLES, 0, 6) this.nodeBack.end() return this.swap() } Heights.prototype.swap = function() { let tmp = this.nodeFront this.nodeFront = this.nodeBack return (this.nodeBack = tmp) } Heights.prototype.addVertex = function(x, y, xs, ys, intensity) { this.vertexBufferData[this.bufferIndex++] = x this.vertexBufferData[this.bufferIndex++] = y this.vertexBufferData[this.bufferIndex++] = xs this.vertexBufferData[this.bufferIndex++] = ys this.vertexBufferData[this.bufferIndex++] = intensity this.vertexBufferData[this.bufferIndex++] = intensity this.vertexBufferData[this.bufferIndex++] = intensity return (this.vertexBufferData[this.bufferIndex++] = intensity) } Heights.prototype.addPoint = function(x, y, size, intensity) { let s if (size == null) { size = 50 } if (intensity == null) { intensity = 0.2 } if (this.pointCount >= this.maxPointCount - 1) { this.update() } y = this.height - y s = size / 2 this.addVertex(x, y, -s, -s, intensity) this.addVertex(x, y, +s, -s, intensity) this.addVertex(x, y, -s, +s, intensity) this.addVertex(x, y, -s, +s, intensity) this.addVertex(x, y, +s, -s, intensity) this.addVertex(x, y, +s, +s, intensity) return (this.pointCount += 1) } return Heights })() const WebGLHeatmap = (function() { function WebGLHeatmap(_arg) { let alphaEnd, alphaRange, alphaStart, error, getColorFun, gradientTexture, image, intensityToAlpha, output, quad, textureGradient, _ref, _ref1 ;(_ref = _arg != null ? _arg : {}), (this.canvas = _ref.canvas), (this.width = _ref.width), (this.height = _ref.height), (intensityToAlpha = _ref.intensityToAlpha), (gradientTexture = _ref.gradientTexture), (alphaRange = _ref.alphaRange) if (!this.canvas) { this.canvas = document.createElement('canvas') } try { this.gl = this.canvas.getContext('experimental-webgl', { depth: false, antialias: false }) if (this.gl === null) { this.gl = this.canvas.getContext('webgl', { depth: false, antialias: false }) if (this.gl === null) { throw 'WebGL not supported' } } } catch (_error) { error = _error throw 'WebGL not supported' } if (window.WebGLDebugUtils != null) { // eslint-disable-next-line no-undef this.gl = WebGLDebugUtils.makeDebugContext(this.gl, function( err, funcName, args ) { // eslint-disable-next-line no-undef throw WebGLDebugUtils.glEnumToString(err) + ' was caused by call to: ' + funcName }) } this.gl.enableVertexAttribArray(0) this.gl.blendFunc(this.gl.ONE, this.gl.ONE) if (gradientTexture) { textureGradient = this.gradientTexture = new Texture(this.gl, { channels: 'rgba' }) .bind(0) .setSize(2, 2) .nearest() .clampToEdge() if (typeof gradientTexture === 'string') { image = new Image() image.onload = function() { return textureGradient.bind().upload(image) } image.src = gradientTexture } else { if (gradientTexture.width > 0 && gradientTexture.height > 0) { textureGradient.upload(gradientTexture) } else { gradientTexture.onload = function() { return textureGradient.upload(gradientTexture) } } } getColorFun = 'uniform sampler2D gradientTexture;\nvec3 getColor(float intensity){\n return texture2D(gradientTexture, vec2(intensity, 0.0)).rgb;\n}' } else { textureGradient = null getColorFun = 'vec3 getColor(float intensity){\n vec3 blue = vec3(0.0, 0.0, 1.0);\n vec3 cyan = vec3(0.0, 1.0, 1.0);\n vec3 green = vec3(0.0, 1.0, 0.0);\n vec3 yellow = vec3(1.0, 1.0, 0.0);\n vec3 red = vec3(1.0, 0.0, 0.0);\n\n vec3 color = (\n fade(-0.25, 0.25, intensity)*blue +\n fade(0.0, 0.5, intensity)*cyan +\n fade(0.25, 0.75, intensity)*green +\n fade(0.5, 1.0, intensity)*yellow +\n smoothstep(0.75, 1.0, intensity)*red\n );\n return color;\n}' } if (intensityToAlpha == null) { intensityToAlpha = true } if (intensityToAlpha) { ;(_ref1 = alphaRange != null ? alphaRange : [0, 1]), (alphaStart = _ref1[0]), (alphaEnd = _ref1[1]) output = 'vec4 alphaFun(vec3 color, float intensity){\n float alpha = smoothstep(' + alphaStart.toFixed(8) + ', ' + alphaEnd.toFixed(8) + ', intensity);\n return vec4(color*alpha, alpha);\n}' } else { output = 'vec4 alphaFun(vec3 color, float intensity){\n return vec4(color, 1.0);\n}' } this.shader = new Shader(this.gl, { vertex: vertexShaderBlit, fragment: fragmentShaderBlit + ('float linstep(float low, float high, float value){\n return clamp((value-low)/(high-low), 0.0, 1.0);\n}\n\nfloat fade(float low, float high, float value){\n float mid = (low+high)*0.5;\n float range = (high-low)*0.5;\n float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\n\n' + getColorFun + '\n' + output + '\n\nvoid main(){\n float intensity = smoothstep(0.0, 1.0, texture2D(source, texcoord).r);\n vec3 color = getColor(intensity);\n gl_FragColor = alphaFun(color, intensity);\n}') }) if (this.width == null) { this.width = this.canvas.offsetWidth || 2 } if (this.height == null) { this.height = this.canvas.offsetHeight || 2 } this.canvas.width = this.width this.canvas.height = this.height this.gl.viewport(0, 0, this.width, this.height) this.quad = this.gl.createBuffer() this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quad) quad = new Float32Array([ -1, -1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, 1, 0, 1, 1, -1, 0, 1, 1, 1, 0, 1 ]) this.gl.bufferData(this.gl.ARRAY_BUFFER, quad, this.gl.STATIC_DRAW) this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null) this.heights = new Heights(this, this.gl, this.width, this.height) } WebGLHeatmap.prototype.adjustSize = function() { let canvasHeight, canvasWidth canvasWidth = this.canvas.offsetWidth || 2 canvasHeight = this.canvas.offsetHeight || 2 if (this.width !== canvasWidth || this.height !== canvasHeight) { this.gl.viewport(0, 0, canvasWidth, canvasHeight) this.canvas.width = canvasWidth this.canvas.height = canvasHeight this.width = canvasWidth this.height = canvasHeight return this.heights.resize(this.width, this.height) } } WebGLHeatmap.prototype.display = function() { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.quad) this.gl.vertexAttribPointer(0, 4, this.gl.FLOAT, false, 0, 0) this.heights.nodeFront.bind(0) if (this.gradientTexture) { this.gradientTexture.bind(1) } this.shader .use() .int('source', 0) .int('gradientTexture', 1) return this.gl.drawArrays(this.gl.TRIANGLES, 0, 6) } WebGLHeatmap.prototype.update = function() { return this.heights.update() } WebGLHeatmap.prototype.clear = function() { return this.heights.clear() } WebGLHeatmap.prototype.clamp = function(min, max) { if (min == null) { min = 0 } if (max == null) { max = 1 } return this.heights.clamp(min, max) } WebGLHeatmap.prototype.multiply = function(value) { if (value == null) { value = 0.95 } return this.heights.multiply(value) } WebGLHeatmap.prototype.blur = function() { return this.heights.blur() } WebGLHeatmap.prototype.addPoint = function(x, y, size, intensity) { return this.heights.addPoint(x, y, size, intensity) } WebGLHeatmap.prototype.addPoints = function(items) { let item, _i, _len, _results _results = [] for (_i = 0, _len = items.length; _i < _len; _i++) { item = items[_i] _results.push(this.addPoint(item.x, item.y, item.size, item.intensity)) } return _results } return WebGLHeatmap })() export function createWebGLHeatmap(params) { return new WebGLHeatmap(params) }