NGToolsCSharp/NGTools/Scripts/Highcharts-7.1.1/code/es-modules/modules/boost/boost-init.js

387 lines
10 KiB
JavaScript
Raw Normal View History

2024-09-13 08:44:13 +00:00
/* *
*
* Copyright (c) 2019-2019 Highsoft AS
*
* Boost module: stripped-down renderer for higher performance
*
* License: highcharts.com/license
*
* */
'use strict';
import H from '../../parts/Globals.js';
import '../../parts/Series.js';
import butils from './boost-utils.js';
import createAndAttachRenderer from './boost-attach.js';
var addEvent = H.addEvent,
fireEvent = H.fireEvent,
extend = H.extend,
Series = H.Series,
seriesTypes = H.seriesTypes,
wrap = H.wrap,
noop = function () {},
eachAsync = butils.eachAsync,
pointDrawHandler = butils.pointDrawHandler,
allocateIfNotSeriesBoosting = butils.allocateIfNotSeriesBoosting,
renderIfNotSeriesBoosting = butils.renderIfNotSeriesBoosting,
shouldForceChartSeriesBoosting = butils.shouldForceChartSeriesBoosting,
index;
/**
* Initialize the boot module.
*
* @private
*/
function init() {
H.extend(Series.prototype, {
/**
* @private
* @function Highcharts.Series#renderCanvas
*/
renderCanvas: function () {
var series = this,
options = series.options || {},
renderer = false,
chart = series.chart,
xAxis = this.xAxis,
yAxis = this.yAxis,
xData = options.xData || series.processedXData,
yData = options.yData || series.processedYData,
rawData = options.data,
xExtremes = xAxis.getExtremes(),
xMin = xExtremes.min,
xMax = xExtremes.max,
yExtremes = yAxis.getExtremes(),
yMin = yExtremes.min,
yMax = yExtremes.max,
pointTaken = {},
lastClientX,
sampling = !!series.sampling,
points,
enableMouseTracking = options.enableMouseTracking !== false,
threshold = options.threshold,
yBottom = yAxis.getThreshold(threshold),
isRange = series.pointArrayMap &&
series.pointArrayMap.join(',') === 'low,high',
isStacked = !!options.stacking,
cropStart = series.cropStart || 0,
requireSorting = series.requireSorting,
useRaw = !xData,
minVal,
maxVal,
minI,
maxI,
boostOptions,
compareX = options.findNearestPointBy === 'x',
xDataFull = (
this.xData ||
this.options.xData ||
this.processedXData ||
false
),
addKDPoint = function (clientX, plotY, i) {
// We need to do ceil on the clientX to make things
// snap to pixel values. The renderer will frequently
// draw stuff on "sub-pixels".
clientX = Math.ceil(clientX);
// Shaves off about 60ms compared to repeated concatenation
index = compareX ? clientX : clientX + ',' + plotY;
// The k-d tree requires series points.
// Reduce the amount of points, since the time to build the
// tree increases exponentially.
if (enableMouseTracking && !pointTaken[index]) {
pointTaken[index] = true;
if (chart.inverted) {
clientX = xAxis.len - clientX;
plotY = yAxis.len - plotY;
}
points.push({
x: xDataFull ? xDataFull[cropStart + i] : false,
clientX: clientX,
plotX: clientX,
plotY: plotY,
i: cropStart + i
});
}
};
// Get or create the renderer
renderer = createAndAttachRenderer(chart, series);
chart.isBoosting = true;
boostOptions = renderer.settings;
if (!this.visible) {
return;
}
// If we are zooming out from SVG mode, destroy the graphics
if (this.points || this.graph) {
this.animate = null;
this.destroyGraphics();
}
// If we're rendering per. series we should create the marker groups
// as usual.
if (!chart.isChartSeriesBoosting()) {
this.markerGroup = series.plotGroup(
'markerGroup',
'markers',
true,
1,
chart.seriesGroup
);
} else {
// Use a single group for the markers
this.markerGroup = chart.markerGroup;
// When switching from chart boosting mode, destroy redundant
// series boosting targets
if (this.renderTarget) {
this.renderTarget = this.renderTarget.destroy();
}
}
points = this.points = [];
// Do not start building while drawing
series.buildKDTree = noop;
if (renderer) {
allocateIfNotSeriesBoosting(renderer, this);
renderer.pushSeries(series);
// Perform the actual renderer if we're on series level
renderIfNotSeriesBoosting(renderer, this, chart);
}
/* This builds the KD-tree */
function processPoint(d, i) {
var x,
y,
clientX,
plotY,
isNull,
low = false,
chartDestroyed = typeof chart.index === 'undefined',
isYInside = true;
if (!chartDestroyed) {
if (useRaw) {
x = d[0];
y = d[1];
} else {
x = d;
y = yData[i];
}
// Resolve low and high for range series
if (isRange) {
if (useRaw) {
y = d.slice(1, 3);
}
low = y[0];
y = y[1];
} else if (isStacked) {
x = d.x;
y = d.stackY;
low = y - d.y;
}
isNull = y === null;
// Optimize for scatter zooming
if (!requireSorting) {
isYInside = y >= yMin && y <= yMax;
}
if (!isNull && x >= xMin && x <= xMax && isYInside) {
clientX = xAxis.toPixels(x, true);
if (sampling) {
if (minI === undefined || clientX === lastClientX) {
if (!isRange) {
low = y;
}
if (maxI === undefined || y > maxVal) {
maxVal = y;
maxI = i;
}
if (minI === undefined || low < minVal) {
minVal = low;
minI = i;
}
}
// Add points and reset
if (clientX !== lastClientX) {
if (minI !== undefined) { // maxI is number too
plotY = yAxis.toPixels(maxVal, true);
yBottom = yAxis.toPixels(minVal, true);
addKDPoint(clientX, plotY, maxI);
if (yBottom !== plotY) {
addKDPoint(clientX, yBottom, minI);
}
}
minI = maxI = undefined;
lastClientX = clientX;
}
} else {
plotY = Math.ceil(yAxis.toPixels(y, true));
addKDPoint(clientX, plotY, i);
}
}
}
return !chartDestroyed;
}
function doneProcessing() {
fireEvent(series, 'renderedCanvas');
// Go back to prototype, ready to build
delete series.buildKDTree;
series.buildKDTree();
if (boostOptions.debug.timeKDTree) {
console.timeEnd('kd tree building'); // eslint-disable-line no-console
}
}
// Loop over the points to build the k-d tree - skip this if
// exporting
if (!chart.renderer.forExport) {
if (boostOptions.debug.timeKDTree) {
console.time('kd tree building'); // eslint-disable-line no-console
}
eachAsync(
isStacked ? series.data : (xData || rawData),
processPoint,
doneProcessing
);
}
}
});
/*
* We need to handle heatmaps separatly, since we can't perform the
* size/color calculations in the shader easily.
*
* This likely needs future optimization.
*/
['heatmap', 'treemap'].forEach(
function (t) {
if (seriesTypes[t]) {
wrap(seriesTypes[t].prototype, 'drawPoints', pointDrawHandler);
}
}
);
if (seriesTypes.bubble) {
// By default, the bubble series does not use the KD-tree, so force it
// to.
delete seriesTypes.bubble.prototype.buildKDTree;
// seriesTypes.bubble.prototype.directTouch = false;
// Needed for markers to work correctly
wrap(
seriesTypes.bubble.prototype,
'markerAttribs',
function (proceed) {
if (this.isSeriesBoosting) {
return false;
}
return proceed.apply(this, [].slice.call(arguments, 1));
}
);
}
seriesTypes.scatter.prototype.fill = true;
extend(seriesTypes.area.prototype, {
fill: true,
fillOpacity: true,
sampling: true
});
extend(seriesTypes.column.prototype, {
fill: true,
sampling: true
});
// Take care of the canvas blitting
H.Chart.prototype.callbacks.push(function (chart) {
/* Convert chart-level canvas to image */
function canvasToSVG() {
if (chart.ogl && chart.isChartSeriesBoosting()) {
chart.ogl.render(chart);
}
}
/* Clear chart-level canvas */
function preRender() {
// Reset force state
chart.boostForceChartBoost = undefined;
chart.boostForceChartBoost = shouldForceChartSeriesBoosting(chart);
chart.isBoosting = false;
if (!chart.isChartSeriesBoosting() && chart.didBoost) {
chart.didBoost = false;
}
// Clear the canvas
if (chart.boostClear) {
chart.boostClear();
}
if (chart.canvas && chart.ogl && chart.isChartSeriesBoosting()) {
chart.didBoost = true;
// Allocate
chart.ogl.allocateBuffer(chart);
}
// see #6518 + #6739
if (
chart.markerGroup &&
chart.xAxis &&
chart.xAxis.length > 0 &&
chart.yAxis &&
chart.yAxis.length > 0
) {
chart.markerGroup.translate(
chart.xAxis[0].pos,
chart.yAxis[0].pos
);
}
}
addEvent(chart, 'predraw', preRender);
addEvent(chart, 'render', canvasToSVG);
// addEvent(chart, 'zoom', function () {
// chart.boostForceChartBoost =
// shouldForceChartSeriesBoosting(chart);
// });
});
}
export default init;