| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- /* *
- *
- * Copyright (c) 2019-2020 Highsoft AS
- *
- * Boost module: stripped-down renderer for higher performance
- *
- * License: highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- 'use strict';
- import U from '../../Core/Utilities.js';
- var clamp = U.clamp, error = U.error, pick = U.pick;
- /* eslint-disable valid-jsdoc */
- /**
- * A static shader mimicing axis translation functions found in Core/Axis
- *
- * @private
- * @function GLShader
- *
- * @param {WebGLContext} gl
- * the context in which the shader is active
- *
- * @return {*}
- */
- function GLShader(gl) {
- var vertShade = [
- /* eslint-disable max-len, @typescript-eslint/indent */
- '#version 100',
- '#define LN10 2.302585092994046',
- 'precision highp float;',
- 'attribute vec4 aVertexPosition;',
- 'attribute vec4 aColor;',
- 'varying highp vec2 position;',
- 'varying highp vec4 vColor;',
- 'uniform mat4 uPMatrix;',
- 'uniform float pSize;',
- 'uniform float translatedThreshold;',
- 'uniform bool hasThreshold;',
- 'uniform bool skipTranslation;',
- 'uniform float xAxisTrans;',
- 'uniform float xAxisMin;',
- 'uniform float xAxisMinPad;',
- 'uniform float xAxisPointRange;',
- 'uniform float xAxisLen;',
- 'uniform bool xAxisPostTranslate;',
- 'uniform float xAxisOrdinalSlope;',
- 'uniform float xAxisOrdinalOffset;',
- 'uniform float xAxisPos;',
- 'uniform bool xAxisCVSCoord;',
- 'uniform bool xAxisIsLog;',
- 'uniform bool xAxisReversed;',
- 'uniform float yAxisTrans;',
- 'uniform float yAxisMin;',
- 'uniform float yAxisMinPad;',
- 'uniform float yAxisPointRange;',
- 'uniform float yAxisLen;',
- 'uniform bool yAxisPostTranslate;',
- 'uniform float yAxisOrdinalSlope;',
- 'uniform float yAxisOrdinalOffset;',
- 'uniform float yAxisPos;',
- 'uniform bool yAxisCVSCoord;',
- 'uniform bool yAxisIsLog;',
- 'uniform bool yAxisReversed;',
- 'uniform bool isBubble;',
- 'uniform bool bubbleSizeByArea;',
- 'uniform float bubbleZMin;',
- 'uniform float bubbleZMax;',
- 'uniform float bubbleZThreshold;',
- 'uniform float bubbleMinSize;',
- 'uniform float bubbleMaxSize;',
- 'uniform bool bubbleSizeAbs;',
- 'uniform bool isInverted;',
- 'float bubbleRadius(){',
- 'float value = aVertexPosition.w;',
- 'float zMax = bubbleZMax;',
- 'float zMin = bubbleZMin;',
- 'float radius = 0.0;',
- 'float pos = 0.0;',
- 'float zRange = zMax - zMin;',
- 'if (bubbleSizeAbs){',
- 'value = value - bubbleZThreshold;',
- 'zMax = max(zMax - bubbleZThreshold, zMin - bubbleZThreshold);',
- 'zMin = 0.0;',
- '}',
- 'if (value < zMin){',
- 'radius = bubbleZMin / 2.0 - 1.0;',
- '} else {',
- 'pos = zRange > 0.0 ? (value - zMin) / zRange : 0.5;',
- 'if (bubbleSizeByArea && pos > 0.0){',
- 'pos = sqrt(pos);',
- '}',
- 'radius = ceil(bubbleMinSize + pos * (bubbleMaxSize - bubbleMinSize)) / 2.0;',
- '}',
- 'return radius * 2.0;',
- '}',
- 'float translate(float val,',
- 'float pointPlacement,',
- 'float localA,',
- 'float localMin,',
- 'float minPixelPadding,',
- 'float pointRange,',
- 'float len,',
- 'bool cvsCoord,',
- 'bool isLog,',
- 'bool reversed',
- '){',
- 'float sign = 1.0;',
- 'float cvsOffset = 0.0;',
- 'if (cvsCoord) {',
- 'sign *= -1.0;',
- 'cvsOffset = len;',
- '}',
- 'if (isLog) {',
- 'val = log(val) / LN10;',
- '}',
- 'if (reversed) {',
- 'sign *= -1.0;',
- 'cvsOffset -= sign * len;',
- '}',
- 'return sign * (val - localMin) * localA + cvsOffset + ',
- '(sign * minPixelPadding);',
- '}',
- 'float xToPixels(float value) {',
- 'if (skipTranslation){',
- 'return value;// + xAxisPos;',
- '}',
- 'return translate(value, 0.0, xAxisTrans, xAxisMin, xAxisMinPad, xAxisPointRange, xAxisLen, xAxisCVSCoord, xAxisIsLog, xAxisReversed);// + xAxisPos;',
- '}',
- 'float yToPixels(float value, float checkTreshold) {',
- 'float v;',
- 'if (skipTranslation){',
- 'v = value;// + yAxisPos;',
- '} else {',
- 'v = translate(value, 0.0, yAxisTrans, yAxisMin, yAxisMinPad, yAxisPointRange, yAxisLen, yAxisCVSCoord, yAxisIsLog, yAxisReversed);// + yAxisPos;',
- 'if (v > yAxisLen) {',
- 'v = yAxisLen;',
- '}',
- '}',
- 'if (checkTreshold > 0.0 && hasThreshold) {',
- 'v = min(v, translatedThreshold);',
- '}',
- 'return v;',
- '}',
- 'void main(void) {',
- 'if (isBubble){',
- 'gl_PointSize = bubbleRadius();',
- '} else {',
- 'gl_PointSize = pSize;',
- '}',
- // 'gl_PointSize = 10.0;',
- 'vColor = aColor;',
- 'if (skipTranslation && isInverted) {',
- // If we get translated values from JS, just swap them (x, y)
- 'gl_Position = uPMatrix * vec4(aVertexPosition.y + yAxisPos, aVertexPosition.x + xAxisPos, 0.0, 1.0);',
- '} else if (isInverted) {',
- // But when calculating pixel positions directly,
- // swap axes and values (x, y)
- 'gl_Position = uPMatrix * vec4(yToPixels(aVertexPosition.y, aVertexPosition.z) + yAxisPos, xToPixels(aVertexPosition.x) + xAxisPos, 0.0, 1.0);',
- '} else {',
- 'gl_Position = uPMatrix * vec4(xToPixels(aVertexPosition.x) + xAxisPos, yToPixels(aVertexPosition.y, aVertexPosition.z) + yAxisPos, 0.0, 1.0);',
- '}',
- // 'gl_Position = uPMatrix * vec4(aVertexPosition.x, aVertexPosition.y, 0.0, 1.0);',
- '}'
- /* eslint-enable max-len, @typescript-eslint/indent */
- ].join('\n'),
- // Fragment shader source
- fragShade = [
- /* eslint-disable max-len, @typescript-eslint/indent */
- 'precision highp float;',
- 'uniform vec4 fillColor;',
- 'varying highp vec2 position;',
- 'varying highp vec4 vColor;',
- 'uniform sampler2D uSampler;',
- 'uniform bool isCircle;',
- 'uniform bool hasColor;',
- // 'vec4 toColor(float value, vec2 point) {',
- // 'return vec4(0.0, 0.0, 0.0, 0.0);',
- // '}',
- 'void main(void) {',
- 'vec4 col = fillColor;',
- 'vec4 tcol;',
- 'if (hasColor) {',
- 'col = vColor;',
- '}',
- 'if (isCircle) {',
- 'tcol = texture2D(uSampler, gl_PointCoord.st);',
- 'col *= tcol;',
- 'if (tcol.r < 0.0) {',
- 'discard;',
- '} else {',
- 'gl_FragColor = col;',
- '}',
- '} else {',
- 'gl_FragColor = col;',
- '}',
- '}'
- /* eslint-enable max-len, @typescript-eslint/indent */
- ].join('\n'), uLocations = {},
- // The shader program
- shaderProgram,
- // Uniform handle to the perspective matrix
- pUniform,
- // Uniform for point size
- psUniform,
- // Uniform for fill color
- fillColorUniform,
- // Uniform for isBubble
- isBubbleUniform,
- // Uniform for bubble abs sizing
- bubbleSizeAbsUniform, bubbleSizeAreaUniform,
- // Skip translation uniform
- skipTranslationUniform,
- // Set to 1 if circle
- isCircleUniform,
- // Uniform for invertion
- isInverted,
- // Error stack
- errors = [],
- // Texture uniform
- uSamplerUniform;
- /**
- * Handle errors accumulated in errors stack
- * @private
- */
- function handleErrors() {
- if (errors.length) {
- error('[highcharts boost] shader error - ' + errors.join('\n'));
- }
- }
- /**
- * String to shader program
- * @private
- * @param {string} str - the program source
- * @param {string} type - the program type: either `vertex` or `fragment`
- * @returns {bool|shader}
- */
- function stringToProgram(str, type) {
- var t = type === 'vertex' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER, shader = gl.createShader(t);
- gl.shaderSource(shader, str);
- gl.compileShader(shader);
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
- errors.push('when compiling ' +
- type +
- ' shader:\n' +
- gl.getShaderInfoLog(shader));
- return false;
- }
- return shader;
- }
- /**
- * Create the shader.
- * Loads the shader program statically defined above
- * @private
- */
- function createShader() {
- var v = stringToProgram(vertShade, 'vertex'), f = stringToProgram(fragShade, 'fragment');
- if (!v || !f) {
- shaderProgram = false;
- handleErrors();
- return false;
- }
- /**
- * @private
- */
- function uloc(n) {
- return gl.getUniformLocation(shaderProgram, n);
- }
- shaderProgram = gl.createProgram();
- gl.attachShader(shaderProgram, v);
- gl.attachShader(shaderProgram, f);
- gl.linkProgram(shaderProgram);
- if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
- errors.push(gl.getProgramInfoLog(shaderProgram));
- handleErrors();
- shaderProgram = false;
- return false;
- }
- gl.useProgram(shaderProgram);
- gl.bindAttribLocation(shaderProgram, 0, 'aVertexPosition');
- pUniform = uloc('uPMatrix');
- psUniform = uloc('pSize');
- fillColorUniform = uloc('fillColor');
- isBubbleUniform = uloc('isBubble');
- bubbleSizeAbsUniform = uloc('bubbleSizeAbs');
- bubbleSizeAreaUniform = uloc('bubbleSizeByArea');
- uSamplerUniform = uloc('uSampler');
- skipTranslationUniform = uloc('skipTranslation');
- isCircleUniform = uloc('isCircle');
- isInverted = uloc('isInverted');
- return true;
- }
- /**
- * Destroy the shader
- * @private
- */
- function destroy() {
- if (gl && shaderProgram) {
- gl.deleteProgram(shaderProgram);
- shaderProgram = false;
- }
- }
- /**
- * Bind the shader.
- * This makes the shader the active one until another one is bound,
- * or until 0 is bound.
- * @private
- */
- function bind() {
- if (gl && shaderProgram) {
- gl.useProgram(shaderProgram);
- }
- }
- /**
- * Set a uniform value.
- * This uses a hash map to cache uniform locations.
- * @private
- * @param name {string} - the name of the uniform to set
- * @param val {float} - the value to set
- */
- function setUniform(name, val) {
- if (gl && shaderProgram) {
- var u = uLocations[name] = (uLocations[name] ||
- gl.getUniformLocation(shaderProgram, name));
- gl.uniform1f(u, val);
- }
- }
- /**
- * Set the active texture
- * @private
- * @param texture - the texture
- */
- function setTexture(texture) {
- if (gl && shaderProgram) {
- gl.uniform1i(uSamplerUniform, texture);
- }
- }
- /**
- * Set if inversion state
- * @private
- * @flag is the state
- */
- function setInverted(flag) {
- if (gl && shaderProgram) {
- gl.uniform1i(isInverted, flag);
- }
- }
- /**
- * Enable/disable circle drawing
- * @private
- */
- function setDrawAsCircle(flag) {
- if (gl && shaderProgram) {
- gl.uniform1i(isCircleUniform, flag ? 1 : 0);
- }
- }
- /**
- * Flush
- * @private
- */
- function reset() {
- if (gl && shaderProgram) {
- gl.uniform1i(isBubbleUniform, 0);
- gl.uniform1i(isCircleUniform, 0);
- }
- }
- /**
- * Set bubble uniforms
- * @private
- * @param series {Highcharts.Series} - the series to use
- */
- function setBubbleUniforms(series, zCalcMin, zCalcMax) {
- var seriesOptions = series.options, zMin = Number.MAX_VALUE, zMax = -Number.MAX_VALUE;
- if (gl && shaderProgram && series.type === 'bubble') {
- zMin = pick(seriesOptions.zMin, clamp(zCalcMin, seriesOptions.displayNegative === false ?
- seriesOptions.zThreshold : -Number.MAX_VALUE, zMin));
- zMax = pick(seriesOptions.zMax, Math.max(zMax, zCalcMax));
- gl.uniform1i(isBubbleUniform, 1);
- gl.uniform1i(isCircleUniform, 1);
- gl.uniform1i(bubbleSizeAreaUniform, (series.options.sizeBy !== 'width'));
- gl.uniform1i(bubbleSizeAbsUniform, series.options
- .sizeByAbsoluteValue);
- setUniform('bubbleZMin', zMin);
- setUniform('bubbleZMax', zMax);
- setUniform('bubbleZThreshold', series.options.zThreshold);
- setUniform('bubbleMinSize', series.minPxSize);
- setUniform('bubbleMaxSize', series.maxPxSize);
- }
- }
- /**
- * Set the Color uniform.
- * @private
- * @param color {Array<float>} - an array with RGBA values
- */
- function setColor(color) {
- if (gl && shaderProgram) {
- gl.uniform4f(fillColorUniform, color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, color[3]);
- }
- }
- /**
- * Set skip translation
- * @private
- */
- function setSkipTranslation(flag) {
- if (gl && shaderProgram) {
- gl.uniform1i(skipTranslationUniform, flag === true ? 1 : 0);
- }
- }
- /**
- * Set the perspective matrix
- * @private
- * @param m {Matrix4x4} - the matrix
- */
- function setPMatrix(m) {
- if (gl && shaderProgram) {
- gl.uniformMatrix4fv(pUniform, false, m);
- }
- }
- /**
- * Set the point size.
- * @private
- * @param p {float} - point size
- */
- function setPointSize(p) {
- if (gl && shaderProgram) {
- gl.uniform1f(psUniform, p);
- }
- }
- /**
- * Get the shader program handle
- * @private
- * @return {GLInt} - the handle for the program
- */
- function getProgram() {
- return shaderProgram;
- }
- if (gl) {
- if (!createShader()) {
- return false;
- }
- }
- return {
- psUniform: function () {
- return psUniform;
- },
- pUniform: function () {
- return pUniform;
- },
- fillColorUniform: function () {
- return fillColorUniform;
- },
- setBubbleUniforms: setBubbleUniforms,
- bind: bind,
- program: getProgram,
- create: createShader,
- setUniform: setUniform,
- setPMatrix: setPMatrix,
- setColor: setColor,
- setPointSize: setPointSize,
- setSkipTranslation: setSkipTranslation,
- setTexture: setTexture,
- setDrawAsCircle: setDrawAsCircle,
- reset: reset,
- setInverted: setInverted,
- destroy: destroy
- };
- }
- export default GLShader;
|