highcharts
Version:
JavaScript charting framework
1,196 lines (1,191 loc) • 170 kB
JavaScript
/**
* @license Highcharts JS v10.0.0 (2022-03-07)
*
* Boost module
*
* (c) 2010-2021 Highsoft AS
* Author: Torstein Honsi
*
* License: www.highcharts.com/license
*
* */
(function (factory) {
if (typeof module === 'object' && module.exports) {
factory['default'] = factory;
module.exports = factory;
} else if (typeof define === 'function' && define.amd) {
define('highcharts/modules/boost', ['highcharts'], function (Highcharts) {
factory(Highcharts);
factory.Highcharts = Highcharts;
return factory;
});
} else {
factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
}
}(function (Highcharts) {
'use strict';
var _modules = Highcharts ? Highcharts._modules : {};
function _registerModule(obj, path, args, fn) {
if (!obj.hasOwnProperty(path)) {
obj[path] = fn.apply(null, args);
if (typeof CustomEvent === 'function') {
window.dispatchEvent(
new CustomEvent(
'HighchartsModuleLoaded',
{ detail: { path: path, module: obj[path] }
})
);
}
}
}
_registerModule(_modules, 'Extensions/Boost/Boostables.js', [], function () {
/* *
*
* Copyright (c) 2019-2021 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
// These are the series we allow boosting for.
var boostables = [
'area',
'areaspline',
'arearange',
'column',
'columnrange',
'bar',
'line',
'scatter',
'heatmap',
'bubble',
'treemap'
];
return boostables;
});
_registerModule(_modules, 'Extensions/Boost/BoostableMap.js', [_modules['Extensions/Boost/Boostables.js']], function (boostables) {
/* *
*
* Copyright (c) 2019-2021 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
// These are the series we allow boosting for.
var boostableMap = {};
boostables.forEach(function (item) {
boostableMap[item] = 1;
});
return boostableMap;
});
_registerModule(_modules, 'Extensions/Boost/WGLShader.js', [_modules['Core/Utilities.js']], function (U) {
/* *
*
* Copyright (c) 2019-2021 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
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
*/
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 = texture2D(uSampler, gl_PointCoord.st);',
'if (hasColor) {',
'col = vColor;',
'}',
'if (isCircle) {',
'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`
*/
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
* @param {number} 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, pixelRatio) {
if (pixelRatio === void 0) { pixelRatio = 1; }
var seriesOptions = series.options,
zMin = Number.MAX_VALUE,
zMax = -Number.MAX_VALUE;
if (gl && shaderProgram && series.is('bubble')) {
var pxSizes = series.getPxExtremes();
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', pxSizes.minPxSize * pixelRatio);
setUniform('bubbleMaxSize', pxSizes.maxPxSize * pixelRatio);
}
}
/**
* 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
};
}
return GLShader;
});
_registerModule(_modules, 'Extensions/Boost/WGLVBuffer.js', [], function () {
/* *
*
* Copyright (c) 2019-2021 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* eslint-disable valid-jsdoc */
/**
* Vertex Buffer abstraction.
* A vertex buffer is a set of vertices which are passed to the GPU
* in a single call.
*
* @private
* @function GLVertexBuffer
* @param {WebGLContext} gl
* the context in which to create the buffer
* @param {GLShader} shader
* the shader to use
*/
function GLVertexBuffer(gl, shader, dataComponents
/* , type */
) {
var buffer = false,
vertAttribute = false,
components = dataComponents || 2,
preAllocated = false,
iterator = 0,
// farray = false,
data;
// type = type || 'float';
/**
* @private
*/
function destroy() {
if (buffer) {
gl.deleteBuffer(buffer);
buffer = false;
vertAttribute = false;
}
iterator = 0;
components = dataComponents || 2;
data = [];
}
/**
* Build the buffer
* @private
* @param dataIn {Array<float>} - a 0 padded array of indices
* @param attrib {String} - the name of the Attribute to bind the buffer to
* @param dataComponents {Integer} - the number of components per. indice
*/
function build(dataIn, attrib, dataComponents) {
var farray;
data = dataIn || [];
if ((!data || data.length === 0) && !preAllocated) {
// console.error('trying to render empty vbuffer');
destroy();
return false;
}
components = dataComponents || components;
if (buffer) {
gl.deleteBuffer(buffer);
}
if (!preAllocated) {
farray = new Float32Array(data);
}
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, preAllocated || farray, gl.STATIC_DRAW);
// gl.bindAttribLocation(shader.program(), 0, 'aVertexPosition');
vertAttribute = gl.getAttribLocation(shader.program(), attrib);
gl.enableVertexAttribArray(vertAttribute);
// Trigger cleanup
farray = false;
return true;
}
/**
* Bind the buffer
* @private
*/
function bind() {
if (!buffer) {
return false;
}
// gl.bindAttribLocation(shader.program(), 0, 'aVertexPosition');
// gl.enableVertexAttribArray(vertAttribute);
// gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(vertAttribute, components, gl.FLOAT, false, 0, 0);
// gl.enableVertexAttribArray(vertAttribute);
}
/**
* Render the buffer
* @private
* @param from {Integer} - the start indice
* @param to {Integer} - the end indice
* @param drawMode {String} - the draw mode
*/
function render(from, to, drawMode) {
var length = preAllocated ?
preAllocated.length : data.length;
if (!buffer) {
return false;
}
if (!length) {
return false;
}
if (!from || from > length || from < 0) {
from = 0;
}
if (!to || to > length) {
to = length;
}
if (from >= to) {
return false;
}
drawMode = drawMode || 'points';
gl.drawArrays(gl[drawMode.toUpperCase()], from / components, (to - from) / components);
return true;
}
/**
* @private
*/
function push(x, y, a, b) {
if (preAllocated) { // && iterator <= preAllocated.length - 4) {
preAllocated[++iterator] = x;
preAllocated[++iterator] = y;
preAllocated[++iterator] = a;
preAllocated[++iterator] = b;
}
}
/**
* Note about pre-allocated buffers:
* - This is slower for charts with many series
* @private
*/
function allocate(size) {
size *= 4;
iterator = -1;
preAllocated = new Float32Array(size);
}
// /////////////////////////////////////////////////////////////////////////
return {
destroy: destroy,
bind: bind,
data: data,
build: build,
render: render,
allocate: allocate,
push: push
};
}
return GLVertexBuffer;
});
_registerModule(_modules, 'Extensions/Boost/WGLRenderer.js', [_modules['Core/Color/Color.js'], _modules['Extensions/Boost/WGLShader.js'], _modules['Extensions/Boost/WGLVBuffer.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Color, GLShader, GLVertexBuffer, H, U) {
/* *
*
* Copyright (c) 2019-2021 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var color = Color.parse;
var doc = H.doc,
win = H.win;
var isNumber = U.isNumber,
isObject = U.isObject,
merge = U.merge,
objectEach = U.objectEach,
pick = U.pick;
/* eslint-disable valid-jsdoc */
/**
* Main renderer. Used to render series.
*
* Notes to self:
* - May be able to build a point map by rendering to a separate canvas and
* encoding values in the color data.
* - Need to figure out a way to transform the data quicker
*
* @private
* @function GLRenderer
*/
function GLRenderer(postRenderCallback) {
// // Shader
var shader = false,
// Vertex buffers - keyed on shader attribute name
vbuffer = false,
vlen = 0,
// Opengl context
gl = false,
// Width of our viewport in pixels
width = 0,
// Height of our viewport in pixels
height = 0,
// The data to render - array of coordinates
data = false,
// The marker data
markerData = false,
// Exports
exports = {},
// Is it inited?
isInited = false,
// The series stack
series = [],
// Texture handles
textureHandles = {},
// Things to draw as "rectangles" (i.e lines)
asBar = {
'column': true,
'columnrange': true,
'bar': true,
'area': true,
'areaspline': true,
'arearange': true
},
asCircle = {
'scatter': true,
'bubble': true
},
// Render settings
settings = {
pointSize: 1,
lineWidth: 1,
fillColor: '#AA00AA',
useAlpha: true,
usePreallocated: false,
useGPUTranslations: false,
debug: {
timeRendering: false,
timeSeriesProcessing: false,
timeSetup: false,
timeBufferCopy: false,
timeKDTree: false,
showSkipSummary: false
}
};
// /////////////////////////////////////////////////////////////////////////
/**
* @private
*/
function getPixelRatio() {
return settings.pixelRatio || win.devicePixelRatio || 1;
}
/**
* @private
*/
function setOptions(options) {
// The pixelRatio defaults to 1. This is an antipattern, we should
// refactor the Boost options to include an object of default options as
// base for the merge, like other components.
if (!('pixelRatio' in options)) {
options.pixelRatio = 1;
}
merge(true, settings, options);
}
/**
* @private
*/
function seriesPointCount(series) {
var isStacked,
xData,
s;
if (series.isSeriesBoosting) {
isStacked = !!series.options.stacking;
xData = (series.xData ||
series.options.xData ||
series.processedXData);
s = (isStacked ? series.data : (xData || series.options.data))
.length;
if (series.type === 'treemap') {
s *= 12;
}
else if (series.type === 'heatmap') {
s *= 6;
}
else if (asBar[series.type]) {
s *= 2;
}
return s;
}
return 0;
}
/**
* Allocate a float buffer to fit all series
* @private
*/
function allocateBuffer(chart) {
var s = 0;
if (!settings.usePreallocated) {
return;
}
chart.series.forEach(function (series) {
if (series.isSeriesBoosting) {
s += seriesPointCount(series);
}
});
vbuffer.allocate(s);
}
/**
* @private
*/
function allocateBufferForSingleSeries(series) {
var s = 0;
if (!settings.usePreallocated) {
return;
}
if (series.isSeriesBoosting) {
s = seriesPointCount(series);
}
vbuffer.allocate(s);
}
/**
* Returns an orthographic perspective matrix
* @private
* @param {number} width
* the width of the viewport in pixels
* @param {number} height
* the height of the viewport in pixels
*/
function orthoMatrix(width, height) {
var near = 0,
far = 1;
return [
2 / width, 0, 0, 0,
0, -(2 / height), 0, 0,
0, 0, -2 / (far - near), 0,
-1, 1, -(far + near) / (far - near), 1
];
}
/**
* Clear the depth and color buffer
* @private
*/
function clear() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
/**
* Get the WebGL context
* @private
* @return {WebGLContext}
* the context
*/
function getGL() {
return gl;
}
/**
* Push data for a single series
* This calculates additional vertices and transforms the data to be
* aligned correctly in memory
* @private
*/
function pushSeriesData(series, inst) {
var isRange = (series.pointArrayMap &&
series.pointArrayMap.join(',') === 'low,high'), chart = series.chart, options = series.options, isStacked = !!options.stacking, rawData = options.data, xExtremes = series.xAxis.getExtremes(), xMin = xExtremes.min, xMax = xExtremes.max, yExtremes = series.yAxis.getExtremes(), yMin = yExtremes.min, yMax = yExtremes.max, xData = series.xData || options.xData || series.processedXData, yData = series.yData || options.yData || series.processedYData, zData = (series.zData || options.zData ||
series.processedZData), yAxis = series.yAxis, xAxis = series.xAxis,
// plotHeight = series.chart.plotHeight,
plotWidth = series.chart.plotWidth, useRaw = !xData || xData.length === 0,
// threshold = options.threshold,
// yBottom = chart.yAxis[0].getThreshold(threshold),
// hasThreshold = isNumber(threshold),
// colorByPoint = series.options.colorByPoint,
// This is required for color by point, so make sure this is
// uncommented if enabling that
// colorIndex = 0,
// Required for color axis support
// caxis,
connectNulls = options.connectNulls,
// For some reason eslint/TypeScript don't pick up that this is
// actually used: --- bre1470: it is never read, just set
// maxVal: (number|undefined), // eslint-disable-line no-unused-vars
points = series.points || false, lastX = false, lastY = false, minVal, scolor, sdata = isStacked ? series.data : (xData || rawData), closestLeft = { x: Number.MAX_VALUE, y: 0 }, closestRight = { x: -Number.MAX_VALUE, y: 0 },
//
skipped = 0, hadPoints = false,
//
cullXThreshold = 1, cullYThreshold = 1,
// The following are used in the builder while loop
x, y, d, z, i = -1, px = false, nx = false, low, chartDestroyed = typeof chart.index === 'undefined', nextInside = false, prevInside = false, pcolor = false, drawAsBar = asBar[series.type], isXInside = false, isYInside = true, firstPoint = true, zoneAxis = options.zoneAxis || 'y', zones = options.zones || false, zoneColors, zoneDefColor = false, threshold = options.threshold, gapSize = false, pixelRatio = getPixelRatio();
if (options.boostData && options.boostData.length > 0) {
return;
}
if (options.gapSize) {
gapSize = options.gapUnit !== 'value' ?
options.gapSize * series.closestPointRange :
options.gapSize;
}
if (zones) {
zoneColors = [];
zones.forEach(function (zone, i) {
if (zone.color) {
var zoneColor = color(zone.color).rgba;
zoneColor[0] /= 255.0;
zoneColor[1] /= 255.0;
zoneColor[2] /= 255.0;
zoneColors[i] = zoneColor;
if (!zoneDefColor && typeof zone.value === 'undefined') {
zoneDefColor = zoneColor;
}
}
});
if (!zoneDefColor) {
var seriesColor = ((series.pointAttribs && series.pointAttribs().fill) ||
series.color);
zoneDefColor = color(seriesColor).rgba;
zoneDefColor[0] /= 255.0;
zoneDefColor[1] /= 255.0;
zoneDefColor[2] /= 255.0;
}
}
if (chart.inverted) {
// plotHeight = series.chart.plotWidth;
plotWidth = series.chart.plotHeight;
}
series.closestPointRangePx = Number.MAX_VALUE;
/**
* Push color to color buffer - need to do this per vertex.
* @private
*/
function pushColor(color) {
if (color) {
inst.colorData.push(color[0]);
inst.colorData.push(color[1]);
inst.colorData.push(color[2]);
inst.colorData.push(color[3]);
}
}
/**
* Push a vertice to the data buffer.
* @private
*/
function vertice(x, y, checkTreshold, pointSize, color) {
if (pointSize === void 0) { pointSize = 1; }
pushColor(color);
// Correct for pixel ratio
if (pixelRatio !== 1 && (!settings.useGPUTranslations ||
inst.skipTranslation)) {
x *= pixelRatio;
y *= pixelRatio;
pointSize *= pixelRatio;
}
if (settings.usePreallocated) {
vbuffer.push(x, y, checkTreshold ? 1 : 0, pointSize);
vlen += 4;
}
else {
data.push(x);
data.push(y);
data.push(checkTreshold ? pixelRatio : 0);
data.push(pointSize);
}
}
/**
* @private
*/
function closeSegment() {
if (inst.segments.length) {
inst.segments[inst.segments.length - 1].to = data.length || vlen;
}
}
/**
* Create a new segment for the current set.
* @private
*/
function beginSegment() {
// Insert a segment on the series.
// A segment is just a start indice.
// When adding a segment, if one exists from before, it should
// set the previous segment's end
if (inst.segments.length &&
inst.segments[inst.segments.length - 1].from === (data.length || vlen)) {
return;
}
closeSegment();
inst.segments.push({
from: data.length || vlen
});
}
/**
* Push a rectangle to the data buffer.
* @private
*/
function pushRect(x, y, w, h, color) {
pushColor(color);
vertice(x + w, y);
pushColor(color);
vertice(x, y);
pushColor(color);
vertice(x, y + h);
pushColor(color);
vertice(x, y + h);
pushColor(color);
vertice(x + w, y + h);
pushColor(color);
vertice(x + w, y);
}
// Create the first segment
beginSegment();
// Special case for point shapes
if (points && points.length > 0) {
// If we're doing points, we assume that the points are already
// translated, so we skip the shader translation.
inst.skipTranslation = true;
// Force triangle draw mode
inst.drawMode = 'triangles';
// We don't have a z component in the shader, so we need to sort.
if (points[0].node && points[0].node.levelDynamic) {
points.sort(function (a, b) {
if (a.node) {
if (a.node.levelDynamic >
b.node.levelDynamic) {
return 1;
}
if (a.node.levelDynamic <
b.node.levelDynamic) {
return -1;
}
}
return 0;
});
}
points.forEach(function (point) {
var plotY = point.plotY,
swidth,
pointAttr;
if (typeof plotY !== 'undefined' &&
!isNaN(plotY) &&
point.y !== null &&
point.shapeArgs) {
var _a = point.shapeArgs,
_b = _a.x,
x_1 = _b === void 0 ? 0 : _b,
_c = _a.y,
y_1 = _c === void 0 ? 0 : _c,
_d = _a.width,
width_1 = _d === void 0 ? 0 : _d,
_e = _a.height,
height_1 = _e === void 0 ? 0 : _e;
pointAttr = chart.styledMode ?
point.series
.colorAttribs(point) :
pointAttr = point.series.pointAttribs(point);
swidth = pointAttr['stroke-width'] || 0;
// Handle point colors
pcolor = color(pointAttr.fill).rgba;
pcolor[0] /= 255.0;
pcolor[1] /= 255.0;
pcolor[2] /= 255.0;
// So there are two ways of doing this. Either we can
// create a rectangle of two triangles, or we can do a
// point and use point size. Latter is faster, but
// only supports squares. So we're doing triangles.
// We could also use one color per. vertice to get
// better color interpolation.
// If there's stroking, we do an additional rect
if (series.is('treemap')) {
swidth = swidth || 1;
scolor = color(pointAttr.stroke).rgba;
scolor[0] /= 255.0;
scolor[1] /= 255.0;
scolor[2] /= 255.0;
pushRect(x_1, y_1, width_1, height_1, scolor);
swidth /= 2;
}
// } else {
// swidth = 0;
// }
// Fixes issues with inverted heatmaps (see #6981). The root
// cause is that the coordinate system is flipped. In other
// words, instead of [0,0] being top-left, it's
// bottom-right. This causes a vertical and horizontal flip
// in the resulting image, making it rotated 180 degrees.
if (series.is('heatmap') && chart.inverted) {
x_1 = xAxis.len - x_1;
y_1 = yAxis.len - y_1;
width_1 = -width_1;
height_1 = -height_1;
}
pushRect(x_1 + swidth, y_1 + swidth, width_1 - (swidth * 2), height_1 - (swidth * 2), pcolor);
}
});
closeSegment();
return;
}
var _loop_1 = function () {
d = sdata[++i];
if (typeof d === 'undefined') {
return "continue";
}
// px = x = y = z = nx = low = false;
// chartDestroyed = typeof chart.index === 'undefined';
// nextInside = prevInside = pcolor = isXInside = isYInside = false;
// drawAsBar = asBar[series.type];
if (chartDestroyed) {
return "break";
}
// Uncomment this to enable color by point.
// This currently left disabled as the charts look really ugly
// when enabled and there's a lot of points.
// Leaving in for the future (tm).
// if (colorByPoint) {
// colorIndex = ++colorIndex %
// series.chart.options.colors.length;
// pcolor = toRGBAFast(series.chart.options.colors[colorIndex]);
// pcolor[0] /= 255.0;
// pcolor[1] /= 255.0;
// pcolor[2] /= 255.0;
// }
// Handle the point.color option (#5999)
var pointOptions = rawData && rawData[i];
if (!useRaw && isObject(pointOptions, true)) {
if (pointOptions.color) {
pcolor = color(pointOptions.color).rgba;
pcolor[0] /= 255.0;
pcolor[1] /= 255.0;
pcolor[2] /= 255.0;
}
}
if (useRaw) {
x = d[0];
y = d[