highcharts
Version:
JavaScript charting framework
1,198 lines (1,193 loc) • 140 kB
JavaScript
/**
* @license Highcharts JS v8.0.0 (2019-12-10)
*
* Boost module
*
* (c) 2010-2019 Highsoft AS
* Author: Torstein Honsi
*
* License: www.highcharts.com/license
*
* This is a Highcharts module that draws long data series on a canvas in order
* to increase performance of the initial load time and tooltip responsiveness.
*
* Compatible with WebGL compatible browsers (not IE < 11).
*
* If this module is taken in as part of the core
* - All the loading logic should be merged with core. Update styles in the
* core.
* - Most of the method wraps should probably be added directly in parent
* methods.
*
* Notes for boost mode
* - Area lines are not drawn
* - Lines are not drawn on scatter charts
* - Zones and negativeColor don't work
* - Dash styles are not rendered on lines.
* - Columns are always one pixel wide. Don't set the threshold too low.
* - Disable animations
* - Marker shapes are not supported: markers will always be circles
*
* Optimizing tips for users
* - Set extremes (min, max) explicitly on the axes in order for Highcharts to
* avoid computing extremes.
* - Set enableMouseTracking to false on the series to improve total rendering
* time.
* - The default threshold is set based on one series. If you have multiple,
* dense series, the combined number of points drawn gets higher, and you may
* want to set the threshold lower in order to use optimizations.
* - If drawing large scatter charts, it's beneficial to set the marker radius
* to a value less than 1. This is to add additional spacing to make the chart
* more readable.
* - If the value increments on both the X and Y axis aren't small, consider
* setting useGPUTranslations to true on the boost settings object. If you do
* this and the increments are small (e.g. datetime axis with small time
* increments) it may cause rendering issues due to floating point rounding
* errors, so your millage may vary.
*
* Settings
* There are two ways of setting the boost threshold:
* - Per series: boost based on number of points in individual series
* - Per chart: boost based on the number of series
*
* To set the series boost threshold, set seriesBoostThreshold on the chart
* object.
* To set the series-specific threshold, set boostThreshold on the series
* object.
*
* In addition, the following can be set in the boost object:
* {
* //Wether or not to use alpha blending
* useAlpha: boolean - default: true
* //Set to true to perform translations on the GPU.
* //Much faster, but may cause rendering issues
* //when using values far from 0 due to floating point
* //rounding issues
* useGPUTranslations: boolean - default: false
* //Use pre-allocated buffers, much faster,
* //but may cause rendering issues with some data sets
* usePreallocated: boolean - default: false
* }
*/
'use strict';
(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) {
var _modules = Highcharts ? Highcharts._modules : {};
function _registerModule(obj, path, args, fn) {
if (!obj.hasOwnProperty(path)) {
obj[path] = fn.apply(null, args);
}
}
_registerModule(_modules, 'modules/boost/boostables.js', [], function () {
/* *
*
* Copyright (c) 2019-2019 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',
'arearange',
'column',
'columnrange',
'bar',
'line',
'scatter',
'heatmap',
'bubble',
'treemap'
];
return boostables;
});
_registerModule(_modules, 'modules/boost/boostable-map.js', [_modules['modules/boost/boostables.js']], function (boostables) {
/* *
*
* Copyright (c) 2019-2019 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, 'modules/boost/wgl-shader.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
/* *
*
* Copyright (c) 2019-2019 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;
var pick = H.pick;
/* eslint-disable valid-jsdoc */
/**
* A static shader mimicing axis translation functions found in parts/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) {
H.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
};
}
return GLShader;
});
_registerModule(_modules, 'modules/boost/wgl-vbuffer.js', [], function () {
/* *
*
* Copyright (c) 2019-2019 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
*
* @return {*}
*/
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;
}
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, 'modules/boost/wgl-renderer.js', [_modules['parts/Globals.js'], _modules['modules/boost/wgl-shader.js'], _modules['modules/boost/wgl-vbuffer.js'], _modules['parts/Utilities.js']], function (H, GLShader, GLVertexBuffer, U) {
/* *
*
* Copyright (c) 2019-2019 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
var isNumber = U.isNumber,
objEach = U.objectEach;
var win = H.win,
doc = win.document,
merge = H.merge,
some = H.some,
Color = H.Color,
pick = H.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
*
* @param {Function} postRenderCallback
*
* @return {*}
*/
function GLRenderer(postRenderCallback) {
// // Shader
var shader = false,
// Vertex buffers - keyed on shader attribute name
vbuffer = false,
// 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,
'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 setOptions(options) {
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
* @returns {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, color, 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, zones = options.zones || false, zoneDefColor = false, threshold = options.threshold, gapSize = false;
if (options.boostData && options.boostData.length > 0) {
return;
}
if (options.gapSize) {
gapSize = options.gapUnit !== 'value' ?
options.gapSize * series.closestPointRange :
options.gapSize;
}
if (zones) {
some(zones, function (zone) {
if (typeof zone.value === 'undefined') {
zoneDefColor = new H.Color(zone.color);
return true;
}
});
if (!zoneDefColor) {
zoneDefColor = ((series.pointAttribs && series.pointAttribs().fill) ||
series.color);
zoneDefColor = new H.Color(zoneDefColor);
}
}
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) {
pushColor(color);
if (settings.usePreallocated) {
vbuffer.push(x, y, checkTreshold ? 1 : 0, pointSize || 1);
}
else {
data.push(x);
data.push(y);
data.push(checkTreshold ? 1 : 0);
data.push(pointSize || 1);
}
}
/**
* @private
*/
function closeSegment() {
if (inst.segments.length) {
inst.segments[inst.segments.length - 1].to = data.length;
}
}
/**
* 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) {
return;
}
closeSegment();
inst.segments.push({
from: data.length
});
}
/**
* 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,
shapeArgs,
swidth,
pointAttr;
if (typeof plotY !== 'undefined' &&
!isNaN(plotY) &&
point.y !== null) {
shapeArgs = point.shapeArgs;
pointAttr = chart.styledMode ?
point.series
.colorAttribs(point) :
pointAttr = point.series.pointAttribs(point);
swidth = pointAttr['stroke-width'] || 0;
// Handle point colors
color = H.color(pointAttr.fill).rgba;
color[0] /= 255.0;
color[1] /= 255.0;
color[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.type === 'treemap') {
swidth = swidth || 1;
scolor = H.color(pointAttr.stroke).rgba;
scolor[0] /= 255.0;
scolor[1] /= 255.0;
scolor[2] /= 255.0;
pushRect(shapeArgs.x, shapeArgs.y, shapeArgs.width, shapeArgs.height, 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.type === 'heatmap' && chart.inverted) {
shapeArgs.x = xAxis.len - shapeArgs.x;
shapeArgs.y = yAxis.len - shapeArgs.y;
shapeArgs.width = -shapeArgs.width;
shapeArgs.height = -shapeArgs.height;
}
pushRect(shapeArgs.x + swidth, shapeArgs.y + swidth, shapeArgs.width - (swidth * 2), shapeArgs.height - (swidth * 2), color);
}
});
closeSegment();
return;
}
// Extract color axis
// (chart.axes || []).forEach(function (a) {
// if (H.ColorAxis && a instanceof H.ColorAxis) {
// caxis = a;
// }
// });
while (i < sdata.length - 1) {
d = sdata[++i];
// px = x = y = z = nx = low = false;
// chartDestroyed = typeof chart.index === 'undefined';
// nextInside = prevInside = pcolor = isXInside = isYInside = false;
// drawAsBar = asBar[series.type];
if (chartDestroyed) {
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;
// }
if (useRaw) {
x = d[0];
y = d[1];
if (sdata[i + 1]) {