@grafit/era-dependencies 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -0
- package/vendor/fonts/FontAwesome.otf +0 -0
- package/vendor/fonts/fontawesome-webfont.eot +0 -0
- package/vendor/fonts/fontawesome-webfont.svg +685 -0
- package/vendor/fonts/fontawesome-webfont.ttf +0 -0
- package/vendor/fonts/fontawesome-webfont.woff +0 -0
- package/vendor/fonts/fontawesome-webfont.woff2 +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.eot +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.svg +288 -0
- package/vendor/fonts/glyphicons-halflings-regular.ttf +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.woff +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.woff2 +0 -0
- package/vendor/scripts/angular/angular-cookies.js +322 -0
- package/vendor/scripts/angular/angular-file-upload.js +2087 -0
- package/vendor/scripts/angular/angular-filter.js +2287 -0
- package/vendor/scripts/angular/angular-locale_ru-ru.js +143 -0
- package/vendor/scripts/angular/angular-route.js +1069 -0
- package/vendor/scripts/angular/angular-sanitize.js +738 -0
- package/vendor/scripts/angular/angular-ui-router-0.2.18.js +4539 -0
- package/vendor/scripts/angular/angular.js +31768 -0
- package/vendor/scripts/angular/datetimepicker.js +578 -0
- package/vendor/scripts/angular/datetimepicker.templates.js +30 -0
- package/vendor/scripts/angular/mask.min.js +7 -0
- package/vendor/scripts/angular/ng-table.js +1518 -0
- package/vendor/scripts/angular/select.js +2356 -0
- package/vendor/scripts/angular/ui-bootstrap-tpls-2.1.3.js +7536 -0
- package/vendor/scripts/angular/uploader.js +3 -0
- package/vendor/scripts/bootbox.js +985 -0
- package/vendor/scripts/bootstrap.js +2377 -0
- package/vendor/scripts/es6-shim.js +3837 -0
- package/vendor/scripts/highchart/highcharts-more.src.js +3165 -0
- package/vendor/scripts/highchart/highstock.src.js +32008 -0
- package/vendor/scripts/highchart/modules/boost.src.js +2721 -0
- package/vendor/scripts/highchart/modules/exporting.src.js +951 -0
- package/vendor/scripts/jquery/jquery.js +11008 -0
- package/vendor/scripts/jquery.datetimepicker.full.js +2911 -0
- package/vendor/scripts/keycloak.js +2382 -0
- package/vendor/scripts/lodash.js +16733 -0
- package/vendor/scripts/moment-with-locales.js +12251 -0
- package/vendor/scripts/moment.js +4234 -0
- package/vendor/scripts/old/datepicker-ru.js +38 -0
- package/vendor/scripts/old/jquery-ui-1.11.1.js +16375 -0
- package/vendor/scripts/old/jquery.form.js +1278 -0
- package/vendor/scripts/perfect-scrollbar.js +1549 -0
- package/vendor/scripts/pickmeup/pickmeup-locales.js +11 -0
- package/vendor/scripts/pickmeup/pickmeup.js +1383 -0
- package/vendor/scripts/quill.js +9676 -0
- package/vendor/scripts/socket.io.min.js +3 -0
- package/vendor/scripts/textAngular/angular-spectrum-colorpicker.min.js +2 -0
- package/vendor/scripts/textAngular/spectrum.min.js +1 -0
- package/vendor/scripts/textAngular/textAngular-dropdownToggle.js +38 -0
- package/vendor/scripts/textAngular/textAngular-rangy.min.js +478 -0
- package/vendor/scripts/textAngular/textAngular-sanitize.min.js +322 -0
- package/vendor/scripts/textAngular/textAngular.min.js +1481 -0
- package/vendor/scripts/textAngular/textAngularSetup.js +1013 -0
- package/vendor/styles/bootstrap-theme.css +587 -0
- package/vendor/styles/bootstrap-theme.css.map +1 -0
- package/vendor/styles/bootstrap-theme.min.css +6 -0
- package/vendor/styles/bootstrap-theme.min.css.map +1 -0
- package/vendor/styles/bootstrap.css +6757 -0
- package/vendor/styles/bootstrap.css.map +1 -0
- package/vendor/styles/bootstrap.min.css +6 -0
- package/vendor/styles/bootstrap.min.css.map +1 -0
- package/vendor/styles/datetimepicker.css +115 -0
- package/vendor/styles/font-awesome.css +2199 -0
- package/vendor/styles/jquery.datetimepicker.min.css +1 -0
- package/vendor/styles/ng-table.css +136 -0
- package/vendor/styles/normalize.css +424 -0
- package/vendor/styles/perfect-scrollbar.css +165 -0
- package/vendor/styles/pickmeup.css +137 -0
- package/vendor/styles/spectrum.min.css +1 -0
- package/vendor/styles/textAngular.css +193 -0
|
@@ -0,0 +1,2721 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Highcharts JS v5.0.12 (2017-05-24)
|
|
3
|
+
* Boost module
|
|
4
|
+
*
|
|
5
|
+
* (c) 2010-2017 Highsoft AS
|
|
6
|
+
* Author: Torstein Honsi
|
|
7
|
+
*
|
|
8
|
+
* License: www.highcharts.com/license
|
|
9
|
+
*/
|
|
10
|
+
'use strict';
|
|
11
|
+
(function(factory) {
|
|
12
|
+
if (typeof module === 'object' && module.exports) {
|
|
13
|
+
module.exports = factory;
|
|
14
|
+
} else {
|
|
15
|
+
factory(Highcharts);
|
|
16
|
+
}
|
|
17
|
+
}(function(Highcharts) {
|
|
18
|
+
(function(H) {
|
|
19
|
+
/**
|
|
20
|
+
* License: www.highcharts.com/license
|
|
21
|
+
* Author: Christer Vasseng, Torstein Honsi
|
|
22
|
+
*
|
|
23
|
+
* This is an experimental Highcharts module that draws long data series on a canvas
|
|
24
|
+
* in order to increase performance of the initial load time and tooltip responsiveness.
|
|
25
|
+
*
|
|
26
|
+
* Compatible with WebGL compatible browsers (not IE < 11).
|
|
27
|
+
*
|
|
28
|
+
* Development plan
|
|
29
|
+
* - Column range.
|
|
30
|
+
* - Check how it works with Highstock and data grouping. Currently it only works when navigator.adaptToUpdatedData
|
|
31
|
+
* is false. It is also recommended to set scrollbar.liveRedraw to false.
|
|
32
|
+
* - Check inverted charts.
|
|
33
|
+
* - Chart callback should be async after last series is drawn. (But not necessarily, we don't do
|
|
34
|
+
that with initial series animation).
|
|
35
|
+
*
|
|
36
|
+
* If this module is taken in as part of the core
|
|
37
|
+
* - All the loading logic should be merged with core. Update styles in the core.
|
|
38
|
+
* - Most of the method wraps should probably be added directly in parent methods.
|
|
39
|
+
*
|
|
40
|
+
* Notes for boost mode
|
|
41
|
+
* - Area lines are not drawn
|
|
42
|
+
* - Lines are not drawn on scatter charts
|
|
43
|
+
* - Zones and negativeColor don't work
|
|
44
|
+
* - Columns are always one pixel wide. Don't set the threshold too low.
|
|
45
|
+
* - Disable animations
|
|
46
|
+
* - Marker shapes are not supported: markers will always be circles
|
|
47
|
+
*
|
|
48
|
+
* Optimizing tips for users
|
|
49
|
+
* - Set extremes (min, max) explicitly on the axes in order for Highcharts to avoid computing extremes.
|
|
50
|
+
* - Set enableMouseTracking to false on the series to improve total rendering time.
|
|
51
|
+
* - The default threshold is set based on one series. If you have multiple, dense series, the combined
|
|
52
|
+
* number of points drawn gets higher, and you may want to set the threshold lower in order to
|
|
53
|
+
* use optimizations.
|
|
54
|
+
* - If drawing large scatter charts, it's beneficial to set the marker radius to a value
|
|
55
|
+
* less than 1. This is to add additional spacing to make the chart more readable.
|
|
56
|
+
* - If the value increments on both the X and Y axis aren't small, consider setting
|
|
57
|
+
* useGPUTranslations to true on the boost settings object. If you do this and
|
|
58
|
+
* the increments are small (e.g. datetime axis with small time increments)
|
|
59
|
+
* it may cause rendering issues due to floating point rounding errors,
|
|
60
|
+
* so your millage may vary.
|
|
61
|
+
*
|
|
62
|
+
* Settings
|
|
63
|
+
* There are two ways of setting the boost threshold:
|
|
64
|
+
* - Per. series: boost based on number of points in individual series
|
|
65
|
+
* - Per. chart: boost based on the number of series
|
|
66
|
+
*
|
|
67
|
+
* To set the series boost threshold, set seriesBoostThreshold on the chart object.
|
|
68
|
+
* To set the series-specific threshold, set boostThreshold on the series object.
|
|
69
|
+
*
|
|
70
|
+
* In addition, the following can be set in the boost object:
|
|
71
|
+
* {
|
|
72
|
+
* //Wether or not to use alpha blending
|
|
73
|
+
* useAlpha: boolean - default: true
|
|
74
|
+
* //Set to true to perform translations on the GPU.
|
|
75
|
+
* //Much faster, but may cause rendering issues
|
|
76
|
+
* //when using values far from 0 due to floating point
|
|
77
|
+
* //rounding issues
|
|
78
|
+
* useGPUTranslations: boolean - default: false
|
|
79
|
+
* //Use pre-allocated buffers, much faster,
|
|
80
|
+
* //but may cause rendering issues with some data sets
|
|
81
|
+
* usePreallocated: boolean - default: false
|
|
82
|
+
* //Output rendering time in console
|
|
83
|
+
* timeRendering: boolean - default: false
|
|
84
|
+
* //Output processing time in console
|
|
85
|
+
* timeSeriesProcessing: boolean - default: false
|
|
86
|
+
* //Output setup time in console
|
|
87
|
+
* timeSetup: boolean - default: false
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Set the series threshold for when the boost should kick in globally.
|
|
93
|
+
*
|
|
94
|
+
* Setting to e.g. 20 will cause the whole chart to enter boost mode
|
|
95
|
+
* if there are 20 or more series active. When the chart is in boost mode,
|
|
96
|
+
* every series in it will be rendered to a common canvas. This offers
|
|
97
|
+
* a significant speed improvment in charts with a very high
|
|
98
|
+
* amount of series.
|
|
99
|
+
*
|
|
100
|
+
* Note: only available when including the boost module.
|
|
101
|
+
*
|
|
102
|
+
* @default null
|
|
103
|
+
* @apioption boost.seriesThreshold
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Set the point threshold for when a series should enter boost mode.
|
|
108
|
+
*
|
|
109
|
+
* Setting it to e.g. 2000 will cause the series to enter boost mode
|
|
110
|
+
* when there are 2000 or more points in the series.
|
|
111
|
+
*
|
|
112
|
+
* Note: only available when including the boost module.
|
|
113
|
+
*
|
|
114
|
+
* @default 5000
|
|
115
|
+
* @apioption series.boostThreshold
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
/* global Float32Array */
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
var win = H.win,
|
|
122
|
+
doc = win.document,
|
|
123
|
+
noop = function() {},
|
|
124
|
+
Color = H.Color,
|
|
125
|
+
Series = H.Series,
|
|
126
|
+
seriesTypes = H.seriesTypes,
|
|
127
|
+
each = H.each,
|
|
128
|
+
extend = H.extend,
|
|
129
|
+
addEvent = H.addEvent,
|
|
130
|
+
fireEvent = H.fireEvent,
|
|
131
|
+
grep = H.grep,
|
|
132
|
+
isNumber = H.isNumber,
|
|
133
|
+
merge = H.merge,
|
|
134
|
+
pick = H.pick,
|
|
135
|
+
wrap = H.wrap,
|
|
136
|
+
plotOptions = H.getOptions().plotOptions,
|
|
137
|
+
CHUNK_SIZE = 50000,
|
|
138
|
+
index;
|
|
139
|
+
|
|
140
|
+
// Register color names since GL can't render those directly.
|
|
141
|
+
Color.prototype.names = {
|
|
142
|
+
aliceblue: '#f0f8ff',
|
|
143
|
+
antiquewhite: '#faebd7',
|
|
144
|
+
aqua: '#00ffff',
|
|
145
|
+
aquamarine: '#7fffd4',
|
|
146
|
+
azure: '#f0ffff',
|
|
147
|
+
beige: '#f5f5dc',
|
|
148
|
+
bisque: '#ffe4c4',
|
|
149
|
+
black: '#000000',
|
|
150
|
+
blanchedalmond: '#ffebcd',
|
|
151
|
+
blue: '#0000ff',
|
|
152
|
+
blueviolet: '#8a2be2',
|
|
153
|
+
brown: '#a52a2a',
|
|
154
|
+
burlywood: '#deb887',
|
|
155
|
+
cadetblue: '#5f9ea0',
|
|
156
|
+
chartreuse: '#7fff00',
|
|
157
|
+
chocolate: '#d2691e',
|
|
158
|
+
coral: '#ff7f50',
|
|
159
|
+
cornflowerblue: '#6495ed',
|
|
160
|
+
cornsilk: '#fff8dc',
|
|
161
|
+
crimson: '#dc143c',
|
|
162
|
+
cyan: '#00ffff',
|
|
163
|
+
darkblue: '#00008b',
|
|
164
|
+
darkcyan: '#008b8b',
|
|
165
|
+
darkgoldenrod: '#b8860b',
|
|
166
|
+
darkgray: '#a9a9a9',
|
|
167
|
+
darkgreen: '#006400',
|
|
168
|
+
darkkhaki: '#bdb76b',
|
|
169
|
+
darkmagenta: '#8b008b',
|
|
170
|
+
darkolivegreen: '#556b2f',
|
|
171
|
+
darkorange: '#ff8c00',
|
|
172
|
+
darkorchid: '#9932cc',
|
|
173
|
+
darkred: '#8b0000',
|
|
174
|
+
darksalmon: '#e9967a',
|
|
175
|
+
darkseagreen: '#8fbc8f',
|
|
176
|
+
darkslateblue: '#483d8b',
|
|
177
|
+
darkslategray: '#2f4f4f',
|
|
178
|
+
darkturquoise: '#00ced1',
|
|
179
|
+
darkviolet: '#9400d3',
|
|
180
|
+
deeppink: '#ff1493',
|
|
181
|
+
deepskyblue: '#00bfff',
|
|
182
|
+
dimgray: '#696969',
|
|
183
|
+
dodgerblue: '#1e90ff',
|
|
184
|
+
feldspar: '#d19275',
|
|
185
|
+
firebrick: '#b22222',
|
|
186
|
+
floralwhite: '#fffaf0',
|
|
187
|
+
forestgreen: '#228b22',
|
|
188
|
+
fuchsia: '#ff00ff',
|
|
189
|
+
gainsboro: '#dcdcdc',
|
|
190
|
+
ghostwhite: '#f8f8ff',
|
|
191
|
+
gold: '#ffd700',
|
|
192
|
+
goldenrod: '#daa520',
|
|
193
|
+
gray: '#808080',
|
|
194
|
+
green: '#008000',
|
|
195
|
+
greenyellow: '#adff2f',
|
|
196
|
+
honeydew: '#f0fff0',
|
|
197
|
+
hotpink: '#ff69b4',
|
|
198
|
+
indianred: '#cd5c5c',
|
|
199
|
+
indigo: '#4b0082',
|
|
200
|
+
ivory: '#fffff0',
|
|
201
|
+
khaki: '#f0e68c',
|
|
202
|
+
lavender: '#e6e6fa',
|
|
203
|
+
lavenderblush: '#fff0f5',
|
|
204
|
+
lawngreen: '#7cfc00',
|
|
205
|
+
lemonchiffon: '#fffacd',
|
|
206
|
+
lightblue: '#add8e6',
|
|
207
|
+
lightcoral: '#f08080',
|
|
208
|
+
lightcyan: '#e0ffff',
|
|
209
|
+
lightgoldenrodyellow: '#fafad2',
|
|
210
|
+
lightgrey: '#d3d3d3',
|
|
211
|
+
lightgreen: '#90ee90',
|
|
212
|
+
lightpink: '#ffb6c1',
|
|
213
|
+
lightsalmon: '#ffa07a',
|
|
214
|
+
lightseagreen: '#20b2aa',
|
|
215
|
+
lightskyblue: '#87cefa',
|
|
216
|
+
lightslateblue: '#8470ff',
|
|
217
|
+
lightslategray: '#778899',
|
|
218
|
+
lightsteelblue: '#b0c4de',
|
|
219
|
+
lightyellow: '#ffffe0',
|
|
220
|
+
lime: '#00ff00',
|
|
221
|
+
limegreen: '#32cd32',
|
|
222
|
+
linen: '#faf0e6',
|
|
223
|
+
magenta: '#ff00ff',
|
|
224
|
+
maroon: '#800000',
|
|
225
|
+
mediumaquamarine: '#66cdaa',
|
|
226
|
+
mediumblue: '#0000cd',
|
|
227
|
+
mediumorchid: '#ba55d3',
|
|
228
|
+
mediumpurple: '#9370d8',
|
|
229
|
+
mediumseagreen: '#3cb371',
|
|
230
|
+
mediumslateblue: '#7b68ee',
|
|
231
|
+
mediumspringgreen: '#00fa9a',
|
|
232
|
+
mediumturquoise: '#48d1cc',
|
|
233
|
+
mediumvioletred: '#c71585',
|
|
234
|
+
midnightblue: '#191970',
|
|
235
|
+
mintcream: '#f5fffa',
|
|
236
|
+
mistyrose: '#ffe4e1',
|
|
237
|
+
moccasin: '#ffe4b5',
|
|
238
|
+
navajowhite: '#ffdead',
|
|
239
|
+
navy: '#000080',
|
|
240
|
+
oldlace: '#fdf5e6',
|
|
241
|
+
olive: '#808000',
|
|
242
|
+
olivedrab: '#6b8e23',
|
|
243
|
+
orange: '#ffa500',
|
|
244
|
+
orangered: '#ff4500',
|
|
245
|
+
orchid: '#da70d6',
|
|
246
|
+
palegoldenrod: '#eee8aa',
|
|
247
|
+
palegreen: '#98fb98',
|
|
248
|
+
paleturquoise: '#afeeee',
|
|
249
|
+
palevioletred: '#d87093',
|
|
250
|
+
papayawhip: '#ffefd5',
|
|
251
|
+
peachpuff: '#ffdab9',
|
|
252
|
+
peru: '#cd853f',
|
|
253
|
+
pink: '#ffc0cb',
|
|
254
|
+
plum: '#dda0dd',
|
|
255
|
+
powderblue: '#b0e0e6',
|
|
256
|
+
purple: '#800080',
|
|
257
|
+
red: '#ff0000',
|
|
258
|
+
rosybrown: '#bc8f8f',
|
|
259
|
+
royalblue: '#4169e1',
|
|
260
|
+
saddlebrown: '#8b4513',
|
|
261
|
+
salmon: '#fa8072',
|
|
262
|
+
sandybrown: '#f4a460',
|
|
263
|
+
seagreen: '#2e8b57',
|
|
264
|
+
seashell: '#fff5ee',
|
|
265
|
+
sienna: '#a0522d',
|
|
266
|
+
silver: '#c0c0c0',
|
|
267
|
+
skyblue: '#87ceeb',
|
|
268
|
+
slateblue: '#6a5acd',
|
|
269
|
+
slategray: '#708090',
|
|
270
|
+
snow: '#fffafa',
|
|
271
|
+
springgreen: '#00ff7f',
|
|
272
|
+
steelblue: '#4682b4',
|
|
273
|
+
tan: '#d2b48c',
|
|
274
|
+
teal: '#008080',
|
|
275
|
+
thistle: '#d8bfd8',
|
|
276
|
+
tomato: '#ff6347',
|
|
277
|
+
turquoise: '#40e0d0',
|
|
278
|
+
violet: '#ee82ee',
|
|
279
|
+
violetred: '#d02090',
|
|
280
|
+
wheat: '#f5deb3',
|
|
281
|
+
white: '#ffffff',
|
|
282
|
+
whitesmoke: '#f5f5f5',
|
|
283
|
+
yellow: '#ffff00',
|
|
284
|
+
yellowgreen: '#9acd32'
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Tolerant max() funciton
|
|
289
|
+
* @return {number} max value
|
|
290
|
+
*/
|
|
291
|
+
function patientMax() {
|
|
292
|
+
var args = Array.prototype.slice.call(arguments),
|
|
293
|
+
r = -Number.MAX_VALUE;
|
|
294
|
+
|
|
295
|
+
each(args, function(t) {
|
|
296
|
+
if (typeof t !== 'undefined' && typeof t.length !== 'undefined') {
|
|
297
|
+
//r = r < t.length ? t.length : r;
|
|
298
|
+
if (t.length > 0) {
|
|
299
|
+
r = t.length;
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return r;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
* Returns true if we should force chart series boosting
|
|
310
|
+
*/
|
|
311
|
+
function shouldForceChartSeriesBoosting(chart) {
|
|
312
|
+
// If there are more than five series currently boosting,
|
|
313
|
+
// we should boost the whole chart to avoid running out of webgl contexts.
|
|
314
|
+
var sboostCount = 0,
|
|
315
|
+
series;
|
|
316
|
+
|
|
317
|
+
if (chart.series.length > 1) {
|
|
318
|
+
for (var i = 0; i < chart.series.length; i++) {
|
|
319
|
+
series = chart.series[i];
|
|
320
|
+
if (patientMax(
|
|
321
|
+
series.processedXData,
|
|
322
|
+
series.options.data,
|
|
323
|
+
series.points
|
|
324
|
+
) >= (series.options.boostThreshold || Number.MAX_VALUE)) {
|
|
325
|
+
sboostCount++;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return sboostCount > 5;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/*
|
|
334
|
+
* Returns true if the chart is in series boost mode
|
|
335
|
+
* @param chart {Highchart.Chart} - the chart to check
|
|
336
|
+
* @returns {Boolean} - true if the chart is in series boost mode
|
|
337
|
+
*/
|
|
338
|
+
function isChartSeriesBoosting(chart) {
|
|
339
|
+
return shouldForceChartSeriesBoosting(chart) || chart.series.length >= pick(
|
|
340
|
+
chart.options.boost && chart.options.boost.seriesThreshold,
|
|
341
|
+
50
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/*
|
|
346
|
+
* Returns true if the series is in boost mode
|
|
347
|
+
* @param series {Highchart.Series} - the series to check
|
|
348
|
+
* @returns {boolean} - true if the series is in boost mode
|
|
349
|
+
*/
|
|
350
|
+
function isSeriesBoosting(series) {
|
|
351
|
+
return isChartSeriesBoosting(series.chart) ||
|
|
352
|
+
patientMax(
|
|
353
|
+
series.processedXData,
|
|
354
|
+
series.options.data,
|
|
355
|
+
series.points
|
|
356
|
+
) >= (series.options.boostThreshold || Number.MAX_VALUE);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
360
|
+
// START OF WEBGL ABSTRACTIONS
|
|
361
|
+
|
|
362
|
+
/*
|
|
363
|
+
* A static shader mimicing axis translation functions found in parts/Axis
|
|
364
|
+
* @param gl {WebGLContext} - the context in which the shader is active
|
|
365
|
+
*/
|
|
366
|
+
function GLShader(gl) {
|
|
367
|
+
var vertShade = [
|
|
368
|
+
/* eslint-disable */
|
|
369
|
+
'#version 100',
|
|
370
|
+
'precision highp float;',
|
|
371
|
+
|
|
372
|
+
'attribute vec4 aVertexPosition;',
|
|
373
|
+
'attribute vec4 aColor;',
|
|
374
|
+
|
|
375
|
+
'varying highp vec2 position;',
|
|
376
|
+
'varying highp vec4 vColor;',
|
|
377
|
+
|
|
378
|
+
'uniform mat4 uPMatrix;',
|
|
379
|
+
'uniform float pSize;',
|
|
380
|
+
|
|
381
|
+
'uniform float translatedThreshold;',
|
|
382
|
+
'uniform bool hasThreshold;',
|
|
383
|
+
|
|
384
|
+
'uniform bool skipTranslation;',
|
|
385
|
+
|
|
386
|
+
'uniform float xAxisTrans;',
|
|
387
|
+
'uniform float xAxisMin;',
|
|
388
|
+
'uniform float xAxisMinPad;',
|
|
389
|
+
'uniform float xAxisPointRange;',
|
|
390
|
+
'uniform float xAxisLen;',
|
|
391
|
+
'uniform bool xAxisPostTranslate;',
|
|
392
|
+
'uniform float xAxisOrdinalSlope;',
|
|
393
|
+
'uniform float xAxisOrdinalOffset;',
|
|
394
|
+
'uniform float xAxisPos;',
|
|
395
|
+
'uniform bool xAxisCVSCoord;',
|
|
396
|
+
|
|
397
|
+
'uniform float yAxisTrans;',
|
|
398
|
+
'uniform float yAxisMin;',
|
|
399
|
+
'uniform float yAxisMinPad;',
|
|
400
|
+
'uniform float yAxisPointRange;',
|
|
401
|
+
'uniform float yAxisLen;',
|
|
402
|
+
'uniform bool yAxisPostTranslate;',
|
|
403
|
+
'uniform float yAxisOrdinalSlope;',
|
|
404
|
+
'uniform float yAxisOrdinalOffset;',
|
|
405
|
+
'uniform float yAxisPos;',
|
|
406
|
+
'uniform bool yAxisCVSCoord;',
|
|
407
|
+
|
|
408
|
+
'uniform bool isBubble;',
|
|
409
|
+
'uniform bool bubbleSizeByArea;',
|
|
410
|
+
'uniform float bubbleZMin;',
|
|
411
|
+
'uniform float bubbleZMax;',
|
|
412
|
+
'uniform float bubbleZThreshold;',
|
|
413
|
+
'uniform float bubbleMinSize;',
|
|
414
|
+
'uniform float bubbleMaxSize;',
|
|
415
|
+
'uniform bool bubbleSizeAbs;',
|
|
416
|
+
'uniform bool isInverted;',
|
|
417
|
+
|
|
418
|
+
'float bubbleRadius(){',
|
|
419
|
+
'float value = aVertexPosition.w;',
|
|
420
|
+
'float zMax = bubbleZMax;',
|
|
421
|
+
'float zMin = bubbleZMin;',
|
|
422
|
+
'float radius = 0.0;',
|
|
423
|
+
'float pos = 0.0;',
|
|
424
|
+
'float zRange = zMax - zMin;',
|
|
425
|
+
|
|
426
|
+
'if (bubbleSizeAbs){',
|
|
427
|
+
'value = value - bubbleZThreshold;',
|
|
428
|
+
'zMax = max(zMax - bubbleZThreshold, zMin - bubbleZThreshold);',
|
|
429
|
+
'zMin = 0.0;',
|
|
430
|
+
'}',
|
|
431
|
+
|
|
432
|
+
'if (value < zMin){',
|
|
433
|
+
'radius = bubbleZMin / 2.0 - 1.0;',
|
|
434
|
+
'} else {',
|
|
435
|
+
'pos = zRange > 0.0 ? (value - zMin) / zRange : 0.5;',
|
|
436
|
+
'if (bubbleSizeByArea && pos > 0.0){',
|
|
437
|
+
'pos = sqrt(pos);',
|
|
438
|
+
'}',
|
|
439
|
+
'radius = ceil(bubbleMinSize + pos * (bubbleMaxSize - bubbleMinSize)) / 2.0;',
|
|
440
|
+
'}',
|
|
441
|
+
|
|
442
|
+
'return radius * 2.0;',
|
|
443
|
+
'}',
|
|
444
|
+
|
|
445
|
+
'float translate(float val,',
|
|
446
|
+
'float pointPlacement,',
|
|
447
|
+
'float localA,',
|
|
448
|
+
'float localMin,',
|
|
449
|
+
'float minPixelPadding,',
|
|
450
|
+
'float pointRange,',
|
|
451
|
+
'float len,',
|
|
452
|
+
'bool cvsCoord',
|
|
453
|
+
'){',
|
|
454
|
+
|
|
455
|
+
'float sign = 1.0;',
|
|
456
|
+
'float cvsOffset = 0.0;',
|
|
457
|
+
|
|
458
|
+
'if (cvsCoord) {',
|
|
459
|
+
'sign *= -1.0;',
|
|
460
|
+
'cvsOffset = len;',
|
|
461
|
+
'}',
|
|
462
|
+
|
|
463
|
+
'return sign * (val - localMin) * localA + cvsOffset + ',
|
|
464
|
+
'(sign * minPixelPadding);', //' + localA * pointPlacement * pointRange;',
|
|
465
|
+
'}',
|
|
466
|
+
|
|
467
|
+
'float xToPixels(float value){',
|
|
468
|
+
'if (skipTranslation){',
|
|
469
|
+
'return value;// + xAxisPos;',
|
|
470
|
+
'}',
|
|
471
|
+
|
|
472
|
+
'return translate(value, 0.0, xAxisTrans, xAxisMin, xAxisMinPad, xAxisPointRange, xAxisLen, xAxisCVSCoord);// + xAxisPos;',
|
|
473
|
+
'}',
|
|
474
|
+
|
|
475
|
+
'float yToPixels(float value, float checkTreshold){',
|
|
476
|
+
'float v;',
|
|
477
|
+
'if (skipTranslation){',
|
|
478
|
+
'v = value;// + yAxisPos;',
|
|
479
|
+
'} else {',
|
|
480
|
+
'v = translate(value, 0.0, yAxisTrans, yAxisMin, yAxisMinPad, yAxisPointRange, yAxisLen, yAxisCVSCoord);// + yAxisPos;',
|
|
481
|
+
'}',
|
|
482
|
+
'if (checkTreshold > 0.0 && hasThreshold) {',
|
|
483
|
+
'v = min(v, translatedThreshold);',
|
|
484
|
+
'}',
|
|
485
|
+
'return v;',
|
|
486
|
+
'}',
|
|
487
|
+
|
|
488
|
+
'void main(void) {',
|
|
489
|
+
'if (isBubble){',
|
|
490
|
+
'gl_PointSize = bubbleRadius();',
|
|
491
|
+
'} else {',
|
|
492
|
+
'gl_PointSize = pSize;',
|
|
493
|
+
'}',
|
|
494
|
+
//'gl_PointSize = 10.0;',
|
|
495
|
+
'vColor = aColor;',
|
|
496
|
+
|
|
497
|
+
'if (isInverted) {',
|
|
498
|
+
'gl_Position = uPMatrix * vec4(xToPixels(aVertexPosition.y) + yAxisPos, yToPixels(aVertexPosition.x, aVertexPosition.z) + xAxisPos, 0.0, 1.0);',
|
|
499
|
+
'} else {',
|
|
500
|
+
'gl_Position = uPMatrix * vec4(xToPixels(aVertexPosition.x) + xAxisPos, yToPixels(aVertexPosition.y, aVertexPosition.z) + yAxisPos, 0.0, 1.0);',
|
|
501
|
+
'}',
|
|
502
|
+
//'gl_Position = uPMatrix * vec4(aVertexPosition.x, aVertexPosition.y, 0.0, 1.0);',
|
|
503
|
+
'}'
|
|
504
|
+
/* eslint-enable */
|
|
505
|
+
].join('\n'),
|
|
506
|
+
//Fragment shader source
|
|
507
|
+
fragShade = [
|
|
508
|
+
/* eslint-disable */
|
|
509
|
+
'precision highp float;',
|
|
510
|
+
'uniform vec4 fillColor;',
|
|
511
|
+
'varying highp vec2 position;',
|
|
512
|
+
'varying highp vec4 vColor;',
|
|
513
|
+
'uniform sampler2D uSampler;',
|
|
514
|
+
'uniform bool isCircle;',
|
|
515
|
+
'uniform bool hasColor;',
|
|
516
|
+
|
|
517
|
+
// 'vec4 toColor(float value, vec2 point) {',
|
|
518
|
+
// 'return vec4(0.0, 0.0, 0.0, 0.0);',
|
|
519
|
+
// '}',
|
|
520
|
+
|
|
521
|
+
'void main(void) {',
|
|
522
|
+
'vec4 col = fillColor;',
|
|
523
|
+
|
|
524
|
+
'if (hasColor) {',
|
|
525
|
+
'col = vColor;',
|
|
526
|
+
'}',
|
|
527
|
+
|
|
528
|
+
'if (isCircle) {',
|
|
529
|
+
'gl_FragColor = col * texture2D(uSampler, gl_PointCoord.st);',
|
|
530
|
+
'} else {',
|
|
531
|
+
'gl_FragColor = col;',
|
|
532
|
+
'}',
|
|
533
|
+
'}'
|
|
534
|
+
/* eslint-enable */
|
|
535
|
+
].join('\n'),
|
|
536
|
+
uLocations = {},
|
|
537
|
+
//The shader program
|
|
538
|
+
shaderProgram,
|
|
539
|
+
//Uniform handle to the perspective matrix
|
|
540
|
+
pUniform,
|
|
541
|
+
//Uniform for point size
|
|
542
|
+
psUniform,
|
|
543
|
+
//Uniform for fill color
|
|
544
|
+
fillColorUniform,
|
|
545
|
+
//Uniform for isBubble
|
|
546
|
+
isBubbleUniform,
|
|
547
|
+
//Uniform for bubble abs sizing
|
|
548
|
+
bubbleSizeAbsUniform,
|
|
549
|
+
bubbleSizeAreaUniform,
|
|
550
|
+
//Skip translation uniform
|
|
551
|
+
skipTranslationUniform,
|
|
552
|
+
//Set to 1 if circle
|
|
553
|
+
isCircleUniform,
|
|
554
|
+
//Uniform for invertion
|
|
555
|
+
isInverted,
|
|
556
|
+
//Texture uniform
|
|
557
|
+
uSamplerUniform;
|
|
558
|
+
|
|
559
|
+
/* String to shader program
|
|
560
|
+
* @param {string} str - the program source
|
|
561
|
+
* @param {string} type - the program type: either `vertex` or `fragment`
|
|
562
|
+
* @returns {bool|shader}
|
|
563
|
+
*/
|
|
564
|
+
function stringToProgram(str, type) {
|
|
565
|
+
var t = type === 'vertex' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER,
|
|
566
|
+
shader = gl.createShader(t);
|
|
567
|
+
|
|
568
|
+
gl.shaderSource(shader, str);
|
|
569
|
+
gl.compileShader(shader);
|
|
570
|
+
|
|
571
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
572
|
+
//console.error('shader error:', gl.getShaderInfoLog(shader));
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
return shader;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/*
|
|
579
|
+
* Create the shader.
|
|
580
|
+
* Loads the shader program statically defined above
|
|
581
|
+
*/
|
|
582
|
+
function createShader() {
|
|
583
|
+
var v = stringToProgram(vertShade, 'vertex'),
|
|
584
|
+
f = stringToProgram(fragShade, 'fragment');
|
|
585
|
+
|
|
586
|
+
if (!v || !f) {
|
|
587
|
+
shaderProgram = false;
|
|
588
|
+
//console.error('error creating shader program');
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function uloc(n) {
|
|
593
|
+
return gl.getUniformLocation(shaderProgram, n);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
shaderProgram = gl.createProgram();
|
|
597
|
+
|
|
598
|
+
gl.attachShader(shaderProgram, v);
|
|
599
|
+
gl.attachShader(shaderProgram, f);
|
|
600
|
+
gl.linkProgram(shaderProgram);
|
|
601
|
+
|
|
602
|
+
gl.useProgram(shaderProgram);
|
|
603
|
+
|
|
604
|
+
gl.bindAttribLocation(shaderProgram, 0, 'aVertexPosition');
|
|
605
|
+
|
|
606
|
+
pUniform = uloc('uPMatrix');
|
|
607
|
+
psUniform = uloc('pSize');
|
|
608
|
+
fillColorUniform = uloc('fillColor');
|
|
609
|
+
isBubbleUniform = uloc('isBubble');
|
|
610
|
+
bubbleSizeAbsUniform = uloc('bubbleSizeAbs');
|
|
611
|
+
bubbleSizeAreaUniform = uloc('bubbleSizeByArea');
|
|
612
|
+
uSamplerUniform = uloc('uSampler');
|
|
613
|
+
skipTranslationUniform = uloc('skipTranslation');
|
|
614
|
+
isCircleUniform = uloc('isCircle');
|
|
615
|
+
isInverted = uloc('isInverted');
|
|
616
|
+
|
|
617
|
+
return true;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/*
|
|
621
|
+
* Destroy the shader
|
|
622
|
+
*/
|
|
623
|
+
function destroy() {
|
|
624
|
+
if (gl && shaderProgram) {
|
|
625
|
+
gl.deleteProgram(shaderProgram);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/*
|
|
630
|
+
* Bind the shader.
|
|
631
|
+
* This makes the shader the active one until another one is bound,
|
|
632
|
+
* or until 0 is bound.
|
|
633
|
+
*/
|
|
634
|
+
function bind() {
|
|
635
|
+
gl.useProgram(shaderProgram);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/*
|
|
639
|
+
* Set a uniform value.
|
|
640
|
+
* This uses a hash map to cache uniform locations.
|
|
641
|
+
* @param name {string} - the name of the uniform to set
|
|
642
|
+
* @param val {float} - the value to set
|
|
643
|
+
*/
|
|
644
|
+
function setUniform(name, val) {
|
|
645
|
+
var u = uLocations[name] = uLocations[name] ||
|
|
646
|
+
gl.getUniformLocation(shaderProgram, name);
|
|
647
|
+
gl.uniform1f(u, val);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/*
|
|
651
|
+
* Set the active texture
|
|
652
|
+
* @param texture - the texture
|
|
653
|
+
*/
|
|
654
|
+
function setTexture() {
|
|
655
|
+
gl.uniform1i(uSamplerUniform, 0);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/*
|
|
659
|
+
* Set if inversion state
|
|
660
|
+
* @flag is the state
|
|
661
|
+
*/
|
|
662
|
+
function setInverted(flag) {
|
|
663
|
+
gl.uniform1i(isInverted, flag);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
////////////////////////////////////////////////////////////////////////////
|
|
667
|
+
|
|
668
|
+
/*
|
|
669
|
+
* Enable/disable circle drawing
|
|
670
|
+
*/
|
|
671
|
+
function setDrawAsCircle(flag) {
|
|
672
|
+
gl.uniform1i(isCircleUniform, flag ? 1 : 0);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/*
|
|
676
|
+
* Flush
|
|
677
|
+
*/
|
|
678
|
+
function reset() {
|
|
679
|
+
gl.uniform1i(isBubbleUniform, 0);
|
|
680
|
+
gl.uniform1i(isCircleUniform, 0);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/*
|
|
684
|
+
* Set bubble uniforms
|
|
685
|
+
* @param series {Highcharts.Series} - the series to use
|
|
686
|
+
*/
|
|
687
|
+
function setBubbleUniforms(series, zCalcMin, zCalcMax) {
|
|
688
|
+
var seriesOptions = series.options,
|
|
689
|
+
zMin = Number.MAX_VALUE,
|
|
690
|
+
zMax = -Number.MAX_VALUE;
|
|
691
|
+
|
|
692
|
+
if (series.type === 'bubble') {
|
|
693
|
+
zMin = pick(seriesOptions.zMin, Math.min(
|
|
694
|
+
zMin,
|
|
695
|
+
Math.max(
|
|
696
|
+
zCalcMin,
|
|
697
|
+
seriesOptions.displayNegative === false ?
|
|
698
|
+
seriesOptions.zThreshold : -Number.MAX_VALUE
|
|
699
|
+
)
|
|
700
|
+
));
|
|
701
|
+
|
|
702
|
+
zMax = pick(seriesOptions.zMax, Math.max(zMax, zCalcMax));
|
|
703
|
+
|
|
704
|
+
gl.uniform1i(isBubbleUniform, 1);
|
|
705
|
+
gl.uniform1i(isCircleUniform, 1);
|
|
706
|
+
gl.uniform1i(bubbleSizeAreaUniform, series.options.sizeBy !== 'width');
|
|
707
|
+
gl.uniform1i(bubbleSizeAbsUniform, series.options.sizeByAbsoluteValue);
|
|
708
|
+
|
|
709
|
+
setUniform('bubbleZMin', zMin);
|
|
710
|
+
setUniform('bubbleZMax', zMax);
|
|
711
|
+
setUniform('bubbleZThreshold', series.options.zThreshold);
|
|
712
|
+
setUniform('bubbleMinSize', series.minPxSize);
|
|
713
|
+
setUniform('bubbleMaxSize', series.maxPxSize);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/*
|
|
718
|
+
* Set the Color uniform.
|
|
719
|
+
* @param color {Array<float>} - an array with RGBA values
|
|
720
|
+
*/
|
|
721
|
+
function setColor(color) {
|
|
722
|
+
gl.uniform4f(
|
|
723
|
+
fillColorUniform,
|
|
724
|
+
color[0] / 255.0,
|
|
725
|
+
color[1] / 255.0,
|
|
726
|
+
color[2] / 255.0,
|
|
727
|
+
color[3]
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/*
|
|
732
|
+
* Set skip translation
|
|
733
|
+
*/
|
|
734
|
+
function setSkipTranslation(flag) {
|
|
735
|
+
gl.uniform1i(skipTranslationUniform, flag === true ? 1 : 0);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/*
|
|
739
|
+
* Set the perspective matrix
|
|
740
|
+
* @param m {Matrix4x4} - the matrix
|
|
741
|
+
*/
|
|
742
|
+
function setPMatrix(m) {
|
|
743
|
+
gl.uniformMatrix4fv(pUniform, false, m);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/*
|
|
747
|
+
* Set the point size.
|
|
748
|
+
* @param p {float} - point size
|
|
749
|
+
*/
|
|
750
|
+
function setPointSize(p) {
|
|
751
|
+
gl.uniform1f(psUniform, p);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/*
|
|
755
|
+
* Get the shader program handle
|
|
756
|
+
* @returns {GLInt} - the handle for the program
|
|
757
|
+
*/
|
|
758
|
+
function getProgram() {
|
|
759
|
+
return shaderProgram;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (gl) {
|
|
763
|
+
createShader();
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return {
|
|
767
|
+
psUniform: function() {
|
|
768
|
+
return psUniform;
|
|
769
|
+
},
|
|
770
|
+
pUniform: function() {
|
|
771
|
+
return pUniform;
|
|
772
|
+
},
|
|
773
|
+
fillColorUniform: function() {
|
|
774
|
+
return fillColorUniform;
|
|
775
|
+
},
|
|
776
|
+
setBubbleUniforms: setBubbleUniforms,
|
|
777
|
+
bind: bind,
|
|
778
|
+
program: getProgram,
|
|
779
|
+
create: createShader,
|
|
780
|
+
setUniform: setUniform,
|
|
781
|
+
setPMatrix: setPMatrix,
|
|
782
|
+
setColor: setColor,
|
|
783
|
+
setPointSize: setPointSize,
|
|
784
|
+
setSkipTranslation: setSkipTranslation,
|
|
785
|
+
setTexture: setTexture,
|
|
786
|
+
setDrawAsCircle: setDrawAsCircle,
|
|
787
|
+
reset: reset,
|
|
788
|
+
setInverted: setInverted,
|
|
789
|
+
destroy: destroy
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/*
|
|
794
|
+
* Vertex Buffer abstraction
|
|
795
|
+
* A vertex buffer is a set of vertices which are passed to the GPU
|
|
796
|
+
* in a single call.
|
|
797
|
+
* @param gl {WebGLContext} - the context in which to create the buffer
|
|
798
|
+
* @param shader {GLShader} - the shader to use
|
|
799
|
+
*/
|
|
800
|
+
function GLVertexBuffer(gl, shader, dataComponents /*, type */ ) {
|
|
801
|
+
var buffer = false,
|
|
802
|
+
vertAttribute = false,
|
|
803
|
+
components = dataComponents || 2,
|
|
804
|
+
preAllocated = false,
|
|
805
|
+
iterator = 0,
|
|
806
|
+
data;
|
|
807
|
+
|
|
808
|
+
// type = type || 'float';
|
|
809
|
+
|
|
810
|
+
function destroy() {
|
|
811
|
+
if (buffer) {
|
|
812
|
+
gl.deleteBuffer(buffer);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/*
|
|
817
|
+
* Build the buffer
|
|
818
|
+
* @param dataIn {Array<float>} - a 0 padded array of indices
|
|
819
|
+
* @param attrib {String} - the name of the Attribute to bind the buffer to
|
|
820
|
+
* @param dataComponents {Integer} - the number of components per. indice
|
|
821
|
+
*/
|
|
822
|
+
function build(dataIn, attrib, dataComponents) {
|
|
823
|
+
|
|
824
|
+
data = dataIn || [];
|
|
825
|
+
|
|
826
|
+
if ((!data || data.length === 0) && !preAllocated) {
|
|
827
|
+
//console.error('trying to render empty vbuffer');
|
|
828
|
+
buffer = false;
|
|
829
|
+
return false;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
components = dataComponents || components;
|
|
833
|
+
|
|
834
|
+
if (buffer) {
|
|
835
|
+
gl.deleteBuffer(buffer);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
buffer = gl.createBuffer();
|
|
839
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
840
|
+
gl.bufferData(
|
|
841
|
+
gl.ARRAY_BUFFER,
|
|
842
|
+
preAllocated || new Float32Array(data),
|
|
843
|
+
gl.STATIC_DRAW
|
|
844
|
+
);
|
|
845
|
+
|
|
846
|
+
// gl.bindAttribLocation(shader.program(), 0, 'aVertexPosition');
|
|
847
|
+
vertAttribute = gl.getAttribLocation(shader.program(), attrib);
|
|
848
|
+
gl.enableVertexAttribArray(vertAttribute);
|
|
849
|
+
|
|
850
|
+
return true;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/*
|
|
854
|
+
* Bind the buffer
|
|
855
|
+
*/
|
|
856
|
+
function bind() {
|
|
857
|
+
if (!buffer) {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// gl.bindAttribLocation(shader.program(), 0, 'aVertexPosition');
|
|
862
|
+
//gl.enableVertexAttribArray(vertAttribute);
|
|
863
|
+
//gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
864
|
+
gl.vertexAttribPointer(vertAttribute, components, gl.FLOAT, false, 0, 0);
|
|
865
|
+
//gl.enableVertexAttribArray(vertAttribute);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/*
|
|
869
|
+
* Render the buffer
|
|
870
|
+
* @param from {Integer} - the start indice
|
|
871
|
+
* @param to {Integer} - the end indice
|
|
872
|
+
* @param drawMode {String} - the draw mode
|
|
873
|
+
*/
|
|
874
|
+
function render(from, to, drawMode) {
|
|
875
|
+
var length = preAllocated ? preAllocated.length : data.length;
|
|
876
|
+
|
|
877
|
+
if (!buffer) {
|
|
878
|
+
return false;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (!length) {
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
if (!from || from > length || from < 0) {
|
|
886
|
+
from = 0;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (!to || to > length) {
|
|
890
|
+
to = length;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
drawMode = drawMode || 'points';
|
|
894
|
+
|
|
895
|
+
gl.drawArrays(
|
|
896
|
+
gl[drawMode.toUpperCase()],
|
|
897
|
+
from / components,
|
|
898
|
+
(to - from) / components
|
|
899
|
+
);
|
|
900
|
+
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
function push(x, y, a, b) {
|
|
905
|
+
if (preAllocated) { // && iterator <= preAllocated.length - 4) {
|
|
906
|
+
preAllocated[++iterator] = x;
|
|
907
|
+
preAllocated[++iterator] = y;
|
|
908
|
+
preAllocated[++iterator] = a;
|
|
909
|
+
preAllocated[++iterator] = b;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/*
|
|
914
|
+
* Note about pre-allocated buffers:
|
|
915
|
+
* - This is slower for charts with many series
|
|
916
|
+
*/
|
|
917
|
+
function allocate(size) {
|
|
918
|
+
size *= 4;
|
|
919
|
+
iterator = -1;
|
|
920
|
+
|
|
921
|
+
//if (!preAllocated || (preAllocated && preAllocated.length !== size)) {
|
|
922
|
+
preAllocated = new Float32Array(size);
|
|
923
|
+
//}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
////////////////////////////////////////////////////////////////////////////
|
|
927
|
+
return {
|
|
928
|
+
destroy: destroy,
|
|
929
|
+
bind: bind,
|
|
930
|
+
data: data,
|
|
931
|
+
build: build,
|
|
932
|
+
render: render,
|
|
933
|
+
allocate: allocate,
|
|
934
|
+
push: push
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/* Main renderer. Used to render series.
|
|
939
|
+
* Notes to self:
|
|
940
|
+
* - May be able to build a point map by rendering to a separate canvas
|
|
941
|
+
* and encoding values in the color data.
|
|
942
|
+
* - Need to figure out a way to transform the data quicker
|
|
943
|
+
*/
|
|
944
|
+
function GLRenderer(postRenderCallback) {
|
|
945
|
+
var // Shader
|
|
946
|
+
shader = false,
|
|
947
|
+
// Vertex buffers - keyed on shader attribute name
|
|
948
|
+
vbuffer = false,
|
|
949
|
+
// Opengl context
|
|
950
|
+
gl = false,
|
|
951
|
+
// Width of our viewport in pixels
|
|
952
|
+
width = 0,
|
|
953
|
+
// Height of our viewport in pixels
|
|
954
|
+
height = 0,
|
|
955
|
+
// The data to render - array of coordinates
|
|
956
|
+
data = false,
|
|
957
|
+
// The marker data
|
|
958
|
+
markerData = false,
|
|
959
|
+
// Is the texture ready?
|
|
960
|
+
textureIsReady = false,
|
|
961
|
+
// Exports
|
|
962
|
+
exports = {},
|
|
963
|
+
// Is it inited?
|
|
964
|
+
isInited = false,
|
|
965
|
+
// The series stack
|
|
966
|
+
series = [],
|
|
967
|
+
// Texture for circles
|
|
968
|
+
circleTexture = doc.createElement('canvas'),
|
|
969
|
+
// Context for circle texture
|
|
970
|
+
circleCtx = circleTexture.getContext('2d'),
|
|
971
|
+
// Handle for the circle texture
|
|
972
|
+
circleTextureHandle,
|
|
973
|
+
// Things to draw as "rectangles" (i.e lines)
|
|
974
|
+
asBar = {
|
|
975
|
+
'column': true,
|
|
976
|
+
'area': true
|
|
977
|
+
},
|
|
978
|
+
asCircle = {
|
|
979
|
+
'scatter': true,
|
|
980
|
+
'bubble': true
|
|
981
|
+
},
|
|
982
|
+
//Render settings
|
|
983
|
+
settings = {
|
|
984
|
+
pointSize: 1,
|
|
985
|
+
lineWidth: 3,
|
|
986
|
+
fillColor: '#AA00AA',
|
|
987
|
+
useAlpha: true,
|
|
988
|
+
usePreallocated: false,
|
|
989
|
+
useGPUTranslations: false,
|
|
990
|
+
timeRendering: false,
|
|
991
|
+
timeSeriesProcessing: false,
|
|
992
|
+
timeSetup: false
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
////////////////////////////////////////////////////////////////////////////
|
|
996
|
+
|
|
997
|
+
function setOptions(options) {
|
|
998
|
+
merge(true, settings, options);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
function seriesPointCount(series) {
|
|
1002
|
+
var isStacked,
|
|
1003
|
+
xData,
|
|
1004
|
+
s;
|
|
1005
|
+
|
|
1006
|
+
if (isSeriesBoosting(series)) {
|
|
1007
|
+
isStacked = !!series.options.stacking;
|
|
1008
|
+
xData = series.xData || series.options.xData || series.processedXData;
|
|
1009
|
+
s = (isStacked ? series.data : (xData || series.options.data)).length;
|
|
1010
|
+
|
|
1011
|
+
if (series.type === 'treemap') {
|
|
1012
|
+
s *= 12;
|
|
1013
|
+
} else if (series.type === 'heatmap') {
|
|
1014
|
+
s *= 6;
|
|
1015
|
+
} else if (asBar[series.type]) {
|
|
1016
|
+
s *= 2;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
return s;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
return 0;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/* Allocate a float buffer to fit all series */
|
|
1026
|
+
function allocateBuffer(chart) {
|
|
1027
|
+
var s = 0;
|
|
1028
|
+
|
|
1029
|
+
if (!settings.usePreallocated) {
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
each(chart.series, function(series) {
|
|
1034
|
+
if (isSeriesBoosting(series)) {
|
|
1035
|
+
s += seriesPointCount(series);
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
vbuffer.allocate(s);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function allocateBufferForSingleSeries(series) {
|
|
1043
|
+
var s = 0;
|
|
1044
|
+
|
|
1045
|
+
if (!settings.usePreallocated) {
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
if (isSeriesBoosting(series)) {
|
|
1050
|
+
s = seriesPointCount(series);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
vbuffer.allocate(s);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/*
|
|
1057
|
+
* Returns an orthographic perspective matrix
|
|
1058
|
+
* @param {number} width - the width of the viewport in pixels
|
|
1059
|
+
* @param {number} height - the height of the viewport in pixels
|
|
1060
|
+
*/
|
|
1061
|
+
function orthoMatrix(width, height) {
|
|
1062
|
+
var near = 0,
|
|
1063
|
+
far = 1;
|
|
1064
|
+
|
|
1065
|
+
return [
|
|
1066
|
+
2 / width, 0, 0, 0,
|
|
1067
|
+
0, -(2 / height), 0, 0,
|
|
1068
|
+
0, 0, -2 / (far - near), 0, -1, 1, -(far + near) / (far - near), 1
|
|
1069
|
+
];
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/*
|
|
1073
|
+
* Clear the depth and color buffer
|
|
1074
|
+
*/
|
|
1075
|
+
function clear() {
|
|
1076
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/*
|
|
1080
|
+
* Get the WebGL context
|
|
1081
|
+
* @returns {WebGLContext} - the context
|
|
1082
|
+
*/
|
|
1083
|
+
function getGL() {
|
|
1084
|
+
return gl;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/*
|
|
1088
|
+
* Push data for a single series
|
|
1089
|
+
* This calculates additional vertices and transforms the data to be
|
|
1090
|
+
* aligned correctly in memory
|
|
1091
|
+
*/
|
|
1092
|
+
function pushSeriesData(series, inst) {
|
|
1093
|
+
var isRange = series.pointArrayMap &&
|
|
1094
|
+
series.pointArrayMap.join(',') === 'low,high',
|
|
1095
|
+
chart = series.chart,
|
|
1096
|
+
options = series.options,
|
|
1097
|
+
isStacked = !!options.stacking,
|
|
1098
|
+
rawData = options.data,
|
|
1099
|
+
xExtremes = series.xAxis.getExtremes(),
|
|
1100
|
+
xMin = xExtremes.min,
|
|
1101
|
+
xMax = xExtremes.max,
|
|
1102
|
+
yExtremes = series.yAxis.getExtremes(),
|
|
1103
|
+
yMin = yExtremes.min,
|
|
1104
|
+
yMax = yExtremes.max,
|
|
1105
|
+
xData = series.xData || options.xData || series.processedXData,
|
|
1106
|
+
yData = series.yData || options.yData || series.processedYData,
|
|
1107
|
+
zData = series.zData || options.zData || series.processedZData,
|
|
1108
|
+
yAxis = series.yAxis,
|
|
1109
|
+
xAxis = series.xAxis,
|
|
1110
|
+
useRaw = !xData || xData.length === 0,
|
|
1111
|
+
// threshold = options.threshold,
|
|
1112
|
+
// yBottom = chart.yAxis[0].getThreshold(threshold),
|
|
1113
|
+
// hasThreshold = isNumber(threshold),
|
|
1114
|
+
// colorByPoint = series.options.colorByPoint,
|
|
1115
|
+
// This is required for color by point, so make sure this is
|
|
1116
|
+
// uncommented if enabling that
|
|
1117
|
+
// colorIndex = 0,
|
|
1118
|
+
// Required for color axis support
|
|
1119
|
+
// caxis,
|
|
1120
|
+
// connectNulls = options.connectNulls,
|
|
1121
|
+
// For some reason eslint doesn't pick up that this is actually used
|
|
1122
|
+
maxVal, //eslint-disable-line no-unused-vars
|
|
1123
|
+
points = series.points || false,
|
|
1124
|
+
lastX = false,
|
|
1125
|
+
minVal,
|
|
1126
|
+
color,
|
|
1127
|
+
scolor,
|
|
1128
|
+
sdata = isStacked ? series.data : (xData || rawData),
|
|
1129
|
+
closestLeft = {
|
|
1130
|
+
x: Number.MIN_VALUE,
|
|
1131
|
+
y: 0
|
|
1132
|
+
},
|
|
1133
|
+
closestRight = {
|
|
1134
|
+
x: Number.MIN_VALUE,
|
|
1135
|
+
y: 0
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
if (options.boostData && options.boostData.length > 0) {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
series.closestPointRangePx = Number.MAX_VALUE;
|
|
1143
|
+
|
|
1144
|
+
// Push color to color buffer - need to do this per. vertex
|
|
1145
|
+
function pushColor(color) {
|
|
1146
|
+
if (color) {
|
|
1147
|
+
inst.colorData.push(color[0]);
|
|
1148
|
+
inst.colorData.push(color[1]);
|
|
1149
|
+
inst.colorData.push(color[2]);
|
|
1150
|
+
inst.colorData.push(color[3]);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
//Push a vertice to the data buffer
|
|
1155
|
+
function vertice(x, y, checkTreshold, pointSize, color) {
|
|
1156
|
+
pushColor(color);
|
|
1157
|
+
if (settings.usePreallocated) {
|
|
1158
|
+
vbuffer.push(x, y, checkTreshold ? 1 : 0, pointSize || 1);
|
|
1159
|
+
} else {
|
|
1160
|
+
data.push(x);
|
|
1161
|
+
data.push(y);
|
|
1162
|
+
data.push(checkTreshold ? 1 : 0);
|
|
1163
|
+
data.push(pointSize || 1);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// Push a rectangle to the data buffer
|
|
1168
|
+
function pushRect(x, y, w, h, color) {
|
|
1169
|
+
pushColor(color);
|
|
1170
|
+
vertice(x + w, y);
|
|
1171
|
+
pushColor(color);
|
|
1172
|
+
vertice(x, y);
|
|
1173
|
+
pushColor(color);
|
|
1174
|
+
vertice(x, y + h);
|
|
1175
|
+
|
|
1176
|
+
pushColor(color);
|
|
1177
|
+
vertice(x, y + h);
|
|
1178
|
+
pushColor(color);
|
|
1179
|
+
vertice(x + w, y + h);
|
|
1180
|
+
pushColor(color);
|
|
1181
|
+
vertice(x + w, y);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// Special case for point shapes
|
|
1185
|
+
if (points && points.length > 0) {
|
|
1186
|
+
|
|
1187
|
+
// If we're doing points, we assume that the points are already
|
|
1188
|
+
// translated, so we skip the shader translation.
|
|
1189
|
+
inst.skipTranslation = true;
|
|
1190
|
+
// Force triangle draw mode
|
|
1191
|
+
inst.drawMode = 'triangles';
|
|
1192
|
+
|
|
1193
|
+
// We don't have a z component in the shader, so we need to sort.
|
|
1194
|
+
if (points[0].node && points[0].node.levelDynamic) {
|
|
1195
|
+
points.sort(function(a, b) {
|
|
1196
|
+
if (a.node) {
|
|
1197
|
+
if (a.node.levelDynamic > b.node.levelDynamic) {
|
|
1198
|
+
return 1;
|
|
1199
|
+
} else if (a.node.levelDynamic < b.node.levelDynamic) {
|
|
1200
|
+
return -1;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return 0;
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
each(points, function(point) {
|
|
1208
|
+
var plotY = point.plotY,
|
|
1209
|
+
shapeArgs,
|
|
1210
|
+
swidth,
|
|
1211
|
+
pointAttr;
|
|
1212
|
+
|
|
1213
|
+
if (plotY !== undefined && !isNaN(plotY) && point.y !== null) {
|
|
1214
|
+
shapeArgs = point.shapeArgs;
|
|
1215
|
+
|
|
1216
|
+
|
|
1217
|
+
pointAttr = point.series.pointAttribs(point);
|
|
1218
|
+
|
|
1219
|
+
swidth = pointAttr['stroke-width'] || 0;
|
|
1220
|
+
|
|
1221
|
+
// Handle point colors
|
|
1222
|
+
color = H.color(pointAttr.fill).rgba;
|
|
1223
|
+
color[0] /= 255.0;
|
|
1224
|
+
color[1] /= 255.0;
|
|
1225
|
+
color[2] /= 255.0;
|
|
1226
|
+
|
|
1227
|
+
// So there are two ways of doing this. Either we can
|
|
1228
|
+
// create a rectangle of two triangles, or we can do a
|
|
1229
|
+
// point and use point size. Latter is faster, but
|
|
1230
|
+
// only supports squares. So we're doing triangles.
|
|
1231
|
+
// We could also use one color per. vertice to get
|
|
1232
|
+
// better color interpolation.
|
|
1233
|
+
|
|
1234
|
+
// If there's stroking, we do an additional rect
|
|
1235
|
+
//if (pointAttr.stroke !== 'none' && swidth && swidth > 0) {
|
|
1236
|
+
if (series.type === 'treemap') {
|
|
1237
|
+
swidth = swidth || 1;
|
|
1238
|
+
scolor = H.color(pointAttr.stroke).rgba;
|
|
1239
|
+
|
|
1240
|
+
scolor[0] /= 255.0;
|
|
1241
|
+
scolor[1] /= 255.0;
|
|
1242
|
+
scolor[2] /= 255.0;
|
|
1243
|
+
|
|
1244
|
+
pushRect(
|
|
1245
|
+
shapeArgs.x,
|
|
1246
|
+
shapeArgs.y,
|
|
1247
|
+
shapeArgs.width,
|
|
1248
|
+
shapeArgs.height,
|
|
1249
|
+
scolor
|
|
1250
|
+
);
|
|
1251
|
+
|
|
1252
|
+
swidth /= 2;
|
|
1253
|
+
}
|
|
1254
|
+
// } else {
|
|
1255
|
+
// swidth = 0;
|
|
1256
|
+
// }
|
|
1257
|
+
|
|
1258
|
+
pushRect(
|
|
1259
|
+
shapeArgs.x + swidth,
|
|
1260
|
+
shapeArgs.y + swidth,
|
|
1261
|
+
shapeArgs.width - (swidth * 2),
|
|
1262
|
+
shapeArgs.height - (swidth * 2),
|
|
1263
|
+
color
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Extract color axis
|
|
1272
|
+
// each(chart.axes || [], function (a) {
|
|
1273
|
+
// if (H.ColorAxis && a instanceof H.ColorAxis) {
|
|
1274
|
+
// caxis = a;
|
|
1275
|
+
// }
|
|
1276
|
+
// });
|
|
1277
|
+
|
|
1278
|
+
each(sdata, function(d, i) {
|
|
1279
|
+
var x,
|
|
1280
|
+
y,
|
|
1281
|
+
z,
|
|
1282
|
+
px = false,
|
|
1283
|
+
nx = false,
|
|
1284
|
+
// This is in fact used.
|
|
1285
|
+
low, //eslint-disable-line no-unused-vars
|
|
1286
|
+
chartDestroyed = typeof chart.index === 'undefined',
|
|
1287
|
+
nextInside = false,
|
|
1288
|
+
prevInside = false,
|
|
1289
|
+
pcolor = false,
|
|
1290
|
+
drawAsBar = asBar[series.type],
|
|
1291
|
+
isXInside = false,
|
|
1292
|
+
isYInside = true;
|
|
1293
|
+
|
|
1294
|
+
if (chartDestroyed) {
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// Uncomment this to enable color by point.
|
|
1299
|
+
// This currently left disabled as the charts look really ugly
|
|
1300
|
+
// when enabled and there's a lot of points.
|
|
1301
|
+
// Leaving in for the future (tm).
|
|
1302
|
+
// if (colorByPoint) {
|
|
1303
|
+
// colorIndex = ++colorIndex % series.chart.options.colors.length;
|
|
1304
|
+
// pcolor = toRGBAFast(series.chart.options.colors[colorIndex]);
|
|
1305
|
+
// pcolor[0] /= 255.0;
|
|
1306
|
+
// pcolor[1] /= 255.0;
|
|
1307
|
+
// pcolor[2] /= 255.0;
|
|
1308
|
+
// }
|
|
1309
|
+
|
|
1310
|
+
if (useRaw) {
|
|
1311
|
+
x = d[0];
|
|
1312
|
+
y = d[1];
|
|
1313
|
+
|
|
1314
|
+
if (sdata[i + 1]) {
|
|
1315
|
+
nx = sdata[i + 1][0];
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
if (sdata[i - 1]) {
|
|
1319
|
+
px = sdata[i - 1][0];
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
if (d.length >= 3) {
|
|
1323
|
+
z = d[2];
|
|
1324
|
+
|
|
1325
|
+
if (d[2] > inst.zMax) {
|
|
1326
|
+
inst.zMax = d[2];
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
if (d[2] < inst.zMin) {
|
|
1330
|
+
inst.zMin = d[2];
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
} else {
|
|
1335
|
+
x = d;
|
|
1336
|
+
y = yData[i];
|
|
1337
|
+
|
|
1338
|
+
if (sdata[i + 1]) {
|
|
1339
|
+
nx = sdata[i + 1];
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (sdata[i - 1]) {
|
|
1343
|
+
px = sdata[i - 1];
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
if (zData && zData.length) {
|
|
1347
|
+
z = zData[i];
|
|
1348
|
+
|
|
1349
|
+
if (zData[i] > inst.zMax) {
|
|
1350
|
+
inst.zMax = zData[i];
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
if (zData[i] < inst.zMin) {
|
|
1354
|
+
inst.zMin = zData[i];
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
if (nx && nx >= xMin && nx <= xMax) {
|
|
1360
|
+
nextInside = true;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
if (px && px >= xMin && px <= xMax) {
|
|
1364
|
+
prevInside = true;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
if (isRange) {
|
|
1368
|
+
if (useRaw) {
|
|
1369
|
+
y = d.slice(1, 3);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
low = y[0];
|
|
1373
|
+
y = y[1];
|
|
1374
|
+
|
|
1375
|
+
} else if (isStacked) {
|
|
1376
|
+
x = d.x;
|
|
1377
|
+
y = d.stackY;
|
|
1378
|
+
low = y - d.y;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
if (!series.requireSorting) {
|
|
1382
|
+
isYInside = y >= yMin && y <= yMax;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
if (x > xMax && closestRight.x < xMax) {
|
|
1386
|
+
closestRight.x = x;
|
|
1387
|
+
closestRight.y = y;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
if (x < xMin && closestLeft.x < xMin) {
|
|
1391
|
+
closestLeft.x = x;
|
|
1392
|
+
closestLeft.y = y;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (y !== 0 && (!y || !isYInside)) {
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
if (x >= xMin && x <= xMax) {
|
|
1400
|
+
isXInside = true;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
if (!isXInside && !nextInside && !prevInside) {
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// Skip translations - temporary floating point fix
|
|
1408
|
+
if (!settings.useGPUTranslations) {
|
|
1409
|
+
inst.skipTranslation = true;
|
|
1410
|
+
x = xAxis.toPixels(x, true);
|
|
1411
|
+
y = yAxis.toPixels(y, true);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
if (drawAsBar) {
|
|
1415
|
+
|
|
1416
|
+
maxVal = y;
|
|
1417
|
+
minVal = 0;
|
|
1418
|
+
|
|
1419
|
+
if (y < 0) {
|
|
1420
|
+
minVal = y;
|
|
1421
|
+
y = 0;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
if (!settings.useGPUTranslations) {
|
|
1425
|
+
minVal = yAxis.toPixels(minVal, true);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
// Need to add an extra point here
|
|
1429
|
+
vertice(x, minVal, 0, 0, pcolor);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// No markers on out of bounds things.
|
|
1433
|
+
// Out of bound things are shown if and only if the next
|
|
1434
|
+
// or previous point is inside the rect.
|
|
1435
|
+
if (inst.hasMarkers) { // && isXInside) {
|
|
1436
|
+
// x = H.correctFloat(
|
|
1437
|
+
// Math.min(Math.max(-1e5, xAxis.translate(
|
|
1438
|
+
// x,
|
|
1439
|
+
// 0,
|
|
1440
|
+
// 0,
|
|
1441
|
+
// 0,
|
|
1442
|
+
// 1,
|
|
1443
|
+
// 0.5,
|
|
1444
|
+
// false
|
|
1445
|
+
// )), 1e5)
|
|
1446
|
+
// );
|
|
1447
|
+
|
|
1448
|
+
if (lastX !== false) {
|
|
1449
|
+
series.closestPointRangePx = Math.min(
|
|
1450
|
+
series.closestPointRangePx,
|
|
1451
|
+
Math.abs(x - lastX)
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
vertice(
|
|
1457
|
+
x,
|
|
1458
|
+
y,
|
|
1459
|
+
0,
|
|
1460
|
+
series.type === 'bubble' ? (z || 1) : 2,
|
|
1461
|
+
pcolor
|
|
1462
|
+
);
|
|
1463
|
+
|
|
1464
|
+
// Uncomment this to support color axis.
|
|
1465
|
+
// if (caxis) {
|
|
1466
|
+
// color = H.color(caxis.toColor(y)).rgba;
|
|
1467
|
+
|
|
1468
|
+
// inst.colorData.push(color[0] / 255.0);
|
|
1469
|
+
// inst.colorData.push(color[1] / 255.0);
|
|
1470
|
+
// inst.colorData.push(color[2] / 255.0);
|
|
1471
|
+
// inst.colorData.push(color[3]);
|
|
1472
|
+
// }
|
|
1473
|
+
|
|
1474
|
+
lastX = x;
|
|
1475
|
+
|
|
1476
|
+
//return true;
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1479
|
+
function pushSupplementPoint(point) {
|
|
1480
|
+
if (!settings.useGPUTranslations) {
|
|
1481
|
+
inst.skipTranslation = true;
|
|
1482
|
+
point.x = xAxis.toPixels(point.x, true);
|
|
1483
|
+
point.y = yAxis.toPixels(point.y, true);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// We should only do this for lines, and we should ignore markers
|
|
1487
|
+
// since there's no point here that would have a marker.
|
|
1488
|
+
|
|
1489
|
+
vertice(
|
|
1490
|
+
point.x,
|
|
1491
|
+
point.y,
|
|
1492
|
+
0,
|
|
1493
|
+
2
|
|
1494
|
+
);
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
if (!lastX) {
|
|
1498
|
+
// There are no points within the selected range
|
|
1499
|
+
pushSupplementPoint(closestLeft);
|
|
1500
|
+
pushSupplementPoint(closestRight);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/*
|
|
1505
|
+
* Push a series to the renderer
|
|
1506
|
+
* If we render the series immediatly, we don't have to loop later
|
|
1507
|
+
* @param s {Highchart.Series} - the series to push
|
|
1508
|
+
*/
|
|
1509
|
+
function pushSeries(s) {
|
|
1510
|
+
if (series.length > 0) {
|
|
1511
|
+
series[series.length - 1].to = data.length;
|
|
1512
|
+
if (series[series.length - 1].hasMarkers) {
|
|
1513
|
+
series[series.length - 1].markerTo = markerData.length;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
if (settings.timeSeriesProcessing) {
|
|
1518
|
+
console.time('building ' + s.type + ' series'); //eslint-disable-line no-console
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
series.push({
|
|
1522
|
+
from: data.length,
|
|
1523
|
+
markerFrom: markerData.length,
|
|
1524
|
+
// Push RGBA values to this array to use per. point coloring.
|
|
1525
|
+
// It should be 0-padded, so each component should be pushed in
|
|
1526
|
+
// succession.
|
|
1527
|
+
colorData: [],
|
|
1528
|
+
series: s,
|
|
1529
|
+
zMin: Number.MAX_VALUE,
|
|
1530
|
+
zMax: -Number.MAX_VALUE,
|
|
1531
|
+
hasMarkers: s.options.marker ? s.options.marker.enabled !== false : false,
|
|
1532
|
+
showMarksers: true,
|
|
1533
|
+
drawMode: ({
|
|
1534
|
+
'area': 'lines',
|
|
1535
|
+
'arearange': 'lines',
|
|
1536
|
+
'areaspline': 'line_strip',
|
|
1537
|
+
'column': 'lines',
|
|
1538
|
+
'line': 'line_strip',
|
|
1539
|
+
'scatter': 'points',
|
|
1540
|
+
'heatmap': 'triangles',
|
|
1541
|
+
'treemap': 'triangles',
|
|
1542
|
+
'bubble': 'points'
|
|
1543
|
+
})[s.type] || 'line_strip'
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
// Add the series data to our buffer(s)
|
|
1547
|
+
pushSeriesData(s, series[series.length - 1]);
|
|
1548
|
+
|
|
1549
|
+
if (settings.timeSeriesProcessing) {
|
|
1550
|
+
console.timeEnd('building ' + s.type + ' series'); //eslint-disable-line no-console
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
/*
|
|
1555
|
+
* Flush the renderer.
|
|
1556
|
+
* This removes pushed series and vertices.
|
|
1557
|
+
* Should be called after clearing and before rendering
|
|
1558
|
+
*/
|
|
1559
|
+
function flush() {
|
|
1560
|
+
series = [];
|
|
1561
|
+
exports.data = data = [];
|
|
1562
|
+
markerData = [];
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
/*
|
|
1566
|
+
* Pass x-axis to shader
|
|
1567
|
+
* @param axis {Highcharts.Axis} - the x-axis
|
|
1568
|
+
*/
|
|
1569
|
+
function setXAxis(axis) {
|
|
1570
|
+
if (!shader) {
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
shader.setUniform('xAxisTrans', axis.transA);
|
|
1575
|
+
shader.setUniform('xAxisMin', axis.min);
|
|
1576
|
+
shader.setUniform('xAxisMinPad', axis.minPixelPadding);
|
|
1577
|
+
shader.setUniform('xAxisPointRange', axis.pointRange);
|
|
1578
|
+
shader.setUniform('xAxisLen', axis.len);
|
|
1579
|
+
shader.setUniform('xAxisPos', axis.pos);
|
|
1580
|
+
shader.setUniform('xAxisCVSCoord', !axis.horiz);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
/*
|
|
1584
|
+
* Pass y-axis to shader
|
|
1585
|
+
* @param axis {Highcharts.Axis} - the y-axis
|
|
1586
|
+
*/
|
|
1587
|
+
function setYAxis(axis) {
|
|
1588
|
+
if (!shader) {
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
shader.setUniform('yAxisTrans', axis.transA);
|
|
1593
|
+
shader.setUniform('yAxisMin', axis.min);
|
|
1594
|
+
shader.setUniform('yAxisMinPad', axis.minPixelPadding);
|
|
1595
|
+
shader.setUniform('yAxisPointRange', axis.pointRange);
|
|
1596
|
+
shader.setUniform('yAxisLen', axis.len);
|
|
1597
|
+
shader.setUniform('yAxisPos', axis.pos);
|
|
1598
|
+
shader.setUniform('yAxisCVSCoord', !axis.horiz);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/*
|
|
1602
|
+
* Set the translation threshold
|
|
1603
|
+
* @param has {boolean} - has threshold flag
|
|
1604
|
+
* @param translation {Float} - the threshold
|
|
1605
|
+
*/
|
|
1606
|
+
function setThreshold(has, translation) {
|
|
1607
|
+
shader.setUniform('hasThreshold', has);
|
|
1608
|
+
shader.setUniform('translatedThreshold', translation);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/*
|
|
1612
|
+
* Render the data
|
|
1613
|
+
* This renders all pushed series.
|
|
1614
|
+
*/
|
|
1615
|
+
function render(chart) {
|
|
1616
|
+
|
|
1617
|
+
if (chart) {
|
|
1618
|
+
if (!chart.chartHeight || !chart.chartWidth) {
|
|
1619
|
+
//chart.setChartSize();
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
width = chart.chartWidth || 800;
|
|
1623
|
+
height = chart.chartHeight || 400;
|
|
1624
|
+
} else {
|
|
1625
|
+
return false;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
if (!gl || !width || !height) {
|
|
1629
|
+
return false;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
if (settings.timeRendering) {
|
|
1633
|
+
console.time('gl rendering'); //eslint-disable-line no-console
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
shader.bind();
|
|
1637
|
+
|
|
1638
|
+
gl.viewport(0, 0, width, height);
|
|
1639
|
+
shader.setPMatrix(orthoMatrix(width, height));
|
|
1640
|
+
|
|
1641
|
+
if (settings.lineWidth > 1 && !H.isMS) {
|
|
1642
|
+
gl.lineWidth(settings.lineWidth);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
vbuffer.build(exports.data, 'aVertexPosition', 4);
|
|
1646
|
+
vbuffer.bind();
|
|
1647
|
+
|
|
1648
|
+
if (textureIsReady) {
|
|
1649
|
+
gl.bindTexture(gl.TEXTURE_2D, circleTextureHandle);
|
|
1650
|
+
shader.setTexture(circleTextureHandle);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
shader.setInverted(chart.options.chart ? chart.options.chart.inverted : false);
|
|
1654
|
+
|
|
1655
|
+
// Render the series
|
|
1656
|
+
each(series, function(s, si) {
|
|
1657
|
+
var options = s.series.options,
|
|
1658
|
+
threshold = options.threshold,
|
|
1659
|
+
hasThreshold = isNumber(threshold),
|
|
1660
|
+
yBottom = s.series.yAxis.getThreshold(threshold),
|
|
1661
|
+
translatedThreshold = yBottom,
|
|
1662
|
+
cbuffer,
|
|
1663
|
+
showMarkers = pick(
|
|
1664
|
+
options.marker ? options.marker.enabled : null,
|
|
1665
|
+
s.series.xAxis.isRadial ? true : null,
|
|
1666
|
+
s.series.closestPointRangePx >
|
|
1667
|
+
2 * ((
|
|
1668
|
+
options.marker ?
|
|
1669
|
+
options.marker.radius :
|
|
1670
|
+
10
|
|
1671
|
+
) || 10)
|
|
1672
|
+
),
|
|
1673
|
+
fillColor = s.series.fillOpacity ?
|
|
1674
|
+
new Color(s.series.color).setOpacity(
|
|
1675
|
+
pick(options.fillOpacity, 0.85)
|
|
1676
|
+
).get() :
|
|
1677
|
+
s.series.color,
|
|
1678
|
+
color;
|
|
1679
|
+
|
|
1680
|
+
vbuffer.bind();
|
|
1681
|
+
|
|
1682
|
+
if (options.colorByPoint) {
|
|
1683
|
+
fillColor = s.series.chart.options.colors[si];
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
color = H.color(fillColor).rgba;
|
|
1687
|
+
|
|
1688
|
+
if (!settings.useAlpha) {
|
|
1689
|
+
color[3] = 1.0;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
//Blending
|
|
1693
|
+
if (options.boostBlending === 'add') {
|
|
1694
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
|
|
1695
|
+
gl.blendEquation(gl.FUNC_ADD);
|
|
1696
|
+
|
|
1697
|
+
} else if (options.boostBlending === 'mult') {
|
|
1698
|
+
gl.blendFunc(gl.DST_COLOR, gl.ZERO);
|
|
1699
|
+
|
|
1700
|
+
} else if (options.boostBlending === 'darken') {
|
|
1701
|
+
gl.blendFunc(gl.ONE, gl.ONE);
|
|
1702
|
+
gl.blendEquation(gl.FUNC_MIN);
|
|
1703
|
+
|
|
1704
|
+
} else {
|
|
1705
|
+
//gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);//, gl.ONE, gl.ZERO);
|
|
1706
|
+
//gl.blendEquation(gl.FUNC_ADD);
|
|
1707
|
+
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
shader.reset();
|
|
1711
|
+
|
|
1712
|
+
// If there are entries in the colorData buffer, build and bind it.
|
|
1713
|
+
if (s.colorData.length > 0) {
|
|
1714
|
+
shader.setUniform('hasColor', 1.0);
|
|
1715
|
+
cbuffer = GLVertexBuffer(gl, shader); //eslint-disable-line new-cap
|
|
1716
|
+
cbuffer.build(s.colorData, 'aColor', 4);
|
|
1717
|
+
cbuffer.bind();
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// Set series specific uniforms
|
|
1721
|
+
shader.setColor(color);
|
|
1722
|
+
setXAxis(s.series.xAxis);
|
|
1723
|
+
setYAxis(s.series.yAxis);
|
|
1724
|
+
setThreshold(hasThreshold, translatedThreshold);
|
|
1725
|
+
|
|
1726
|
+
if (s.drawMode === 'points') {
|
|
1727
|
+
if (options.marker && options.marker.radius) {
|
|
1728
|
+
shader.setPointSize(options.marker.radius * 2.0);
|
|
1729
|
+
} else {
|
|
1730
|
+
shader.setPointSize(1);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// If set to true, the toPixels translations in the shader
|
|
1735
|
+
// is skipped, i.e it's assumed that the value is a pixel coord.
|
|
1736
|
+
shader.setSkipTranslation(s.skipTranslation);
|
|
1737
|
+
|
|
1738
|
+
if (s.series.type === 'bubble') {
|
|
1739
|
+
shader.setBubbleUniforms(s.series, s.zMin, s.zMax);
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
shader.setDrawAsCircle((asCircle[s.series.type] && textureIsReady) || false);
|
|
1743
|
+
|
|
1744
|
+
// Do the actual rendering
|
|
1745
|
+
vbuffer.render(s.from, s.to, s.drawMode);
|
|
1746
|
+
|
|
1747
|
+
if (s.hasMarkers && showMarkers) {
|
|
1748
|
+
if (options.marker && options.marker.radius) {
|
|
1749
|
+
shader.setPointSize(options.marker.radius * 2.0);
|
|
1750
|
+
} else {
|
|
1751
|
+
shader.setPointSize(10);
|
|
1752
|
+
}
|
|
1753
|
+
shader.setDrawAsCircle(true);
|
|
1754
|
+
vbuffer.render(s.from, s.to, 'POINTS');
|
|
1755
|
+
}
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
vbuffer.destroy();
|
|
1759
|
+
|
|
1760
|
+
if (settings.timeRendering) {
|
|
1761
|
+
console.timeEnd('gl rendering'); //eslint-disable-line no-console
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
flush();
|
|
1765
|
+
|
|
1766
|
+
if (postRenderCallback) {
|
|
1767
|
+
postRenderCallback();
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
/*
|
|
1772
|
+
* Render the data when ready
|
|
1773
|
+
*/
|
|
1774
|
+
function renderWhenReady(chart) {
|
|
1775
|
+
clear();
|
|
1776
|
+
|
|
1777
|
+
if (chart.renderer.forExport) {
|
|
1778
|
+
return render(chart);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
if (isInited) {
|
|
1782
|
+
render(chart);
|
|
1783
|
+
} else {
|
|
1784
|
+
setTimeout(function() {
|
|
1785
|
+
renderWhenReady(chart);
|
|
1786
|
+
}, 1);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/*
|
|
1791
|
+
* Set the viewport size in pixels
|
|
1792
|
+
* Creates an orthographic perspective matrix and applies it.
|
|
1793
|
+
* @param w {Integer} - the width of the viewport
|
|
1794
|
+
* @param h {Integer} - the height of the viewport
|
|
1795
|
+
*/
|
|
1796
|
+
function setSize(w, h) {
|
|
1797
|
+
// Skip if there's no change
|
|
1798
|
+
if (width === w && h === h) {
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
width = w;
|
|
1803
|
+
height = h;
|
|
1804
|
+
|
|
1805
|
+
shader.bind();
|
|
1806
|
+
shader.setPMatrix(orthoMatrix(width, height));
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
/*
|
|
1810
|
+
* Init OpenGL
|
|
1811
|
+
* @param canvas {HTMLCanvas} - the canvas to render to
|
|
1812
|
+
*/
|
|
1813
|
+
function init(canvas, noFlush) {
|
|
1814
|
+
var i = 0,
|
|
1815
|
+
contexts = [
|
|
1816
|
+
'webgl',
|
|
1817
|
+
'experimental-webgl',
|
|
1818
|
+
'moz-webgl',
|
|
1819
|
+
'webkit-3d'
|
|
1820
|
+
];
|
|
1821
|
+
|
|
1822
|
+
isInited = false;
|
|
1823
|
+
|
|
1824
|
+
if (!canvas) {
|
|
1825
|
+
return false;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
if (settings.timeSetup) {
|
|
1829
|
+
console.time('gl setup'); //eslint-disable-line no-console
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
for (; i < contexts.length; i++) {
|
|
1833
|
+
gl = canvas.getContext(contexts[i]);
|
|
1834
|
+
if (gl) {
|
|
1835
|
+
break;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
if (gl) {
|
|
1840
|
+
if (!noFlush) {
|
|
1841
|
+
flush();
|
|
1842
|
+
}
|
|
1843
|
+
} else {
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
gl.enable(gl.BLEND);
|
|
1848
|
+
// gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
|
|
1849
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
1850
|
+
gl.disable(gl.DEPTH_TEST);
|
|
1851
|
+
gl.depthMask(gl.FALSE);
|
|
1852
|
+
|
|
1853
|
+
shader = GLShader(gl); //eslint-disable-line new-cap
|
|
1854
|
+
vbuffer = GLVertexBuffer(gl, shader); //eslint-disable-line new-cap
|
|
1855
|
+
|
|
1856
|
+
textureIsReady = false;
|
|
1857
|
+
|
|
1858
|
+
// Set up the circle texture used for bubbles
|
|
1859
|
+
circleTextureHandle = gl.createTexture();
|
|
1860
|
+
|
|
1861
|
+
// Draw the circle
|
|
1862
|
+
circleTexture.width = 512;
|
|
1863
|
+
circleTexture.height = 512;
|
|
1864
|
+
|
|
1865
|
+
circleCtx.fillStyle = '#FFF';
|
|
1866
|
+
circleCtx.beginPath();
|
|
1867
|
+
circleCtx.arc(256, 256, 256, 0, 2 * Math.PI);
|
|
1868
|
+
circleCtx.fill();
|
|
1869
|
+
|
|
1870
|
+
try {
|
|
1871
|
+
|
|
1872
|
+
gl.bindTexture(gl.TEXTURE_2D, circleTextureHandle);
|
|
1873
|
+
|
|
1874
|
+
gl.texImage2D(
|
|
1875
|
+
gl.TEXTURE_2D,
|
|
1876
|
+
0,
|
|
1877
|
+
gl.RGBA,
|
|
1878
|
+
gl.RGBA,
|
|
1879
|
+
gl.UNSIGNED_BYTE,
|
|
1880
|
+
circleTexture
|
|
1881
|
+
);
|
|
1882
|
+
|
|
1883
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
1884
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
1885
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
1886
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
1887
|
+
|
|
1888
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
1889
|
+
|
|
1890
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
1891
|
+
|
|
1892
|
+
textureIsReady = true;
|
|
1893
|
+
} catch (e) {}
|
|
1894
|
+
|
|
1895
|
+
isInited = true;
|
|
1896
|
+
|
|
1897
|
+
if (settings.timeSetup) {
|
|
1898
|
+
console.timeEnd('gl setup'); //eslint-disable-line no-console
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
/*
|
|
1905
|
+
* Check if we have a valid OGL context
|
|
1906
|
+
* @returns {Boolean} - true if the context is valid
|
|
1907
|
+
*/
|
|
1908
|
+
function valid() {
|
|
1909
|
+
return gl !== false;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
/*
|
|
1913
|
+
* Check if the renderer has been initialized
|
|
1914
|
+
* @returns {Boolean} - true if it has, false if not
|
|
1915
|
+
*/
|
|
1916
|
+
function inited() {
|
|
1917
|
+
return isInited;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
function destroy() {
|
|
1921
|
+
vbuffer.destroy();
|
|
1922
|
+
shader.destroy();
|
|
1923
|
+
if (gl) {
|
|
1924
|
+
//gl.deleteTexture(circleTextureHandle);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
////////////////////////////////////////////////////////////////////////////
|
|
1929
|
+
exports = {
|
|
1930
|
+
allocateBufferForSingleSeries: allocateBufferForSingleSeries,
|
|
1931
|
+
pushSeries: pushSeries,
|
|
1932
|
+
setSize: setSize,
|
|
1933
|
+
inited: inited,
|
|
1934
|
+
setThreshold: setThreshold,
|
|
1935
|
+
init: init,
|
|
1936
|
+
render: renderWhenReady,
|
|
1937
|
+
settings: settings,
|
|
1938
|
+
valid: valid,
|
|
1939
|
+
clear: clear,
|
|
1940
|
+
flush: flush,
|
|
1941
|
+
setXAxis: setXAxis,
|
|
1942
|
+
setYAxis: setYAxis,
|
|
1943
|
+
data: data,
|
|
1944
|
+
gl: getGL,
|
|
1945
|
+
allocateBuffer: allocateBuffer,
|
|
1946
|
+
destroy: destroy,
|
|
1947
|
+
setOptions: setOptions
|
|
1948
|
+
};
|
|
1949
|
+
|
|
1950
|
+
return exports;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
// END OF WEBGL ABSTRACTIONS
|
|
1954
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
1955
|
+
|
|
1956
|
+
/*
|
|
1957
|
+
* Create a canvas + context and attach it to the target
|
|
1958
|
+
* @param target {Highcharts.Chart|Highcharts.Series} - the canvas target
|
|
1959
|
+
* @param chart {Highcharts.Chart} - the chart
|
|
1960
|
+
*/
|
|
1961
|
+
function createAndAttachRenderer(chart, series) {
|
|
1962
|
+
var width = chart.chartWidth,
|
|
1963
|
+
height = chart.chartHeight,
|
|
1964
|
+
target = chart,
|
|
1965
|
+
targetGroup = chart.seriesGroup || series.group,
|
|
1966
|
+
swapXY = function(proceed, x, y, a, b, c, d) {
|
|
1967
|
+
proceed.call(series, y, x, a, b, c, d);
|
|
1968
|
+
};
|
|
1969
|
+
|
|
1970
|
+
if (isChartSeriesBoosting(chart)) {
|
|
1971
|
+
target = chart;
|
|
1972
|
+
} else {
|
|
1973
|
+
target = series;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
if (target.ogl) {
|
|
1977
|
+
//target.ogl.destroy();
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
if (!target.image) {
|
|
1981
|
+
target.canvas = doc.createElement('canvas');
|
|
1982
|
+
|
|
1983
|
+
target.image = chart.renderer.image(
|
|
1984
|
+
'',
|
|
1985
|
+
0,
|
|
1986
|
+
0,
|
|
1987
|
+
width,
|
|
1988
|
+
height
|
|
1989
|
+
).add(targetGroup);
|
|
1990
|
+
|
|
1991
|
+
target.boostClipRect = chart.renderer.clipRect(
|
|
1992
|
+
chart.plotLeft,
|
|
1993
|
+
chart.plotTop,
|
|
1994
|
+
chart.plotWidth,
|
|
1995
|
+
chart.chartHeight
|
|
1996
|
+
);
|
|
1997
|
+
|
|
1998
|
+
target.image.clip(target.boostClipRect);
|
|
1999
|
+
|
|
2000
|
+
if (target.inverted) {
|
|
2001
|
+
each(['moveTo', 'lineTo', 'rect', 'arc'], function(fn) {
|
|
2002
|
+
wrap(false, fn, swapXY);
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
if (target instanceof H.Chart) {
|
|
2007
|
+
target.markerGroup = target.renderer.g().add(targetGroup);
|
|
2008
|
+
|
|
2009
|
+
target.markerGroup.translate(series.xAxis.pos, series.yAxis.pos);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
target.canvas.width = width;
|
|
2014
|
+
target.canvas.height = height;
|
|
2015
|
+
|
|
2016
|
+
target.image.attr({
|
|
2017
|
+
x: 0,
|
|
2018
|
+
y: 0,
|
|
2019
|
+
width: width,
|
|
2020
|
+
height: height,
|
|
2021
|
+
style: 'pointer-events: none'
|
|
2022
|
+
});
|
|
2023
|
+
|
|
2024
|
+
target.boostClipRect.attr({
|
|
2025
|
+
x: chart.plotLeft,
|
|
2026
|
+
y: chart.plotTop,
|
|
2027
|
+
width: chart.plotWidth,
|
|
2028
|
+
height: chart.chartHeight
|
|
2029
|
+
});
|
|
2030
|
+
|
|
2031
|
+
if (!target.ogl) {
|
|
2032
|
+
|
|
2033
|
+
|
|
2034
|
+
target.ogl = GLRenderer(function() { // eslint-disable-line new-cap
|
|
2035
|
+
target.image.attr({
|
|
2036
|
+
href: target.canvas.toDataURL('image/png')
|
|
2037
|
+
});
|
|
2038
|
+
}); //eslint-disable-line new-cap
|
|
2039
|
+
|
|
2040
|
+
target.ogl.init(target.canvas);
|
|
2041
|
+
// target.ogl.clear();
|
|
2042
|
+
target.ogl.setOptions(chart.options.boost || {});
|
|
2043
|
+
|
|
2044
|
+
if (target instanceof H.Chart) {
|
|
2045
|
+
target.ogl.allocateBuffer(chart);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
target.ogl.setSize(width, height);
|
|
2050
|
+
|
|
2051
|
+
return target.ogl;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/*
|
|
2055
|
+
* Performs the actual render if the renderer is
|
|
2056
|
+
* attached to the series.
|
|
2057
|
+
* @param renderer {OGLRenderer} - the renderer
|
|
2058
|
+
* @param series {Highcharts.Series} - the series
|
|
2059
|
+
*/
|
|
2060
|
+
function renderIfNotSeriesBoosting(renderer, series, chart) {
|
|
2061
|
+
if (renderer &&
|
|
2062
|
+
series.image &&
|
|
2063
|
+
series.canvas &&
|
|
2064
|
+
!isChartSeriesBoosting(chart || series.chart)
|
|
2065
|
+
) {
|
|
2066
|
+
renderer.render(chart || series.chart);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
function allocateIfNotSeriesBoosting(renderer, series) {
|
|
2071
|
+
if (renderer &&
|
|
2072
|
+
series.image &&
|
|
2073
|
+
series.canvas &&
|
|
2074
|
+
!isChartSeriesBoosting(series.chart)
|
|
2075
|
+
) {
|
|
2076
|
+
renderer.allocateBufferForSingleSeries(series);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
/*
|
|
2081
|
+
* An "async" foreach loop.
|
|
2082
|
+
* Uses a setTimeout to keep the loop from blocking the UI thread
|
|
2083
|
+
* @param arr {Array} - the array to loop through
|
|
2084
|
+
* @param fn {Function} - the callback to call for each item
|
|
2085
|
+
* @param finalFunc {Function} - the callback to call when done
|
|
2086
|
+
* @param chunkSize {Number} - the number of iterations per. timeout
|
|
2087
|
+
* @param i {Number} - the current index
|
|
2088
|
+
* @param noTimeout {Boolean} - set to true to skip timeouts
|
|
2089
|
+
*/
|
|
2090
|
+
function eachAsync(arr, fn, finalFunc, chunkSize, i, noTimeout) {
|
|
2091
|
+
i = i || 0;
|
|
2092
|
+
chunkSize = chunkSize || CHUNK_SIZE;
|
|
2093
|
+
|
|
2094
|
+
var threshold = i + chunkSize,
|
|
2095
|
+
proceed = true;
|
|
2096
|
+
|
|
2097
|
+
while (proceed && i < threshold && i < arr.length) {
|
|
2098
|
+
proceed = fn(arr[i], i);
|
|
2099
|
+
++i;
|
|
2100
|
+
}
|
|
2101
|
+
if (proceed) {
|
|
2102
|
+
if (i < arr.length) {
|
|
2103
|
+
|
|
2104
|
+
if (noTimeout) {
|
|
2105
|
+
eachAsync(arr, fn, finalFunc, chunkSize, i, noTimeout);
|
|
2106
|
+
} else if (win.requestAnimationFrame) {
|
|
2107
|
+
//If available, do requestAnimationFrame - shaves off a few ms
|
|
2108
|
+
win.requestAnimationFrame(function() {
|
|
2109
|
+
eachAsync(arr, fn, finalFunc, chunkSize, i);
|
|
2110
|
+
});
|
|
2111
|
+
} else {
|
|
2112
|
+
setTimeout(function() {
|
|
2113
|
+
eachAsync(arr, fn, finalFunc, chunkSize, i);
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
} else if (finalFunc) {
|
|
2118
|
+
finalFunc();
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
2124
|
+
// Following is the parts of the boost that's common between OGL/Legacy
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* Return a full Point object based on the index.
|
|
2128
|
+
* The boost module uses stripped point objects for performance reasons.
|
|
2129
|
+
* @param {Number} boostPoint A stripped-down point object
|
|
2130
|
+
* @returns {Object} A Point object as per http://api.highcharts.com/highcharts#Point
|
|
2131
|
+
*/
|
|
2132
|
+
Series.prototype.getPoint = function(boostPoint) {
|
|
2133
|
+
var point = boostPoint,
|
|
2134
|
+
xData = this.xData || this.options.xData || this.processedXData || false;
|
|
2135
|
+
|
|
2136
|
+
if (boostPoint && !(boostPoint instanceof this.pointClass)) {
|
|
2137
|
+
point = (new this.pointClass()).init( // eslint-disable-line new-cap
|
|
2138
|
+
this,
|
|
2139
|
+
this.options.data[boostPoint.i],
|
|
2140
|
+
xData ? xData[boostPoint.i] : undefined
|
|
2141
|
+
);
|
|
2142
|
+
|
|
2143
|
+
point.category = point.x;
|
|
2144
|
+
|
|
2145
|
+
point.dist = boostPoint.dist;
|
|
2146
|
+
point.distX = boostPoint.distX;
|
|
2147
|
+
point.plotX = boostPoint.plotX;
|
|
2148
|
+
point.plotY = boostPoint.plotY;
|
|
2149
|
+
point.index = boostPoint.i;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
return point;
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
/**
|
|
2156
|
+
* Return a point instance from the k-d-tree
|
|
2157
|
+
*/
|
|
2158
|
+
wrap(Series.prototype, 'searchPoint', function(proceed) {
|
|
2159
|
+
return this.getPoint(
|
|
2160
|
+
proceed.apply(this, [].slice.call(arguments, 1))
|
|
2161
|
+
);
|
|
2162
|
+
});
|
|
2163
|
+
|
|
2164
|
+
/**
|
|
2165
|
+
* Extend series.destroy to also remove the fake k-d-tree points (#5137).
|
|
2166
|
+
* Normally this is handled by Series.destroy that calls Point.destroy,
|
|
2167
|
+
* but the fake search points are not registered like that.
|
|
2168
|
+
*/
|
|
2169
|
+
wrap(Series.prototype, 'destroy', function(proceed) {
|
|
2170
|
+
var series = this,
|
|
2171
|
+
chart = series.chart;
|
|
2172
|
+
|
|
2173
|
+
if (chart.markerGroup === series.markerGroup) {
|
|
2174
|
+
series.markerGroup = null;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
if (chart.hoverPoints) {
|
|
2178
|
+
chart.hoverPoints = grep(chart.hoverPoints, function(point) {
|
|
2179
|
+
return point.series === series;
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
if (chart.hoverPoint && chart.hoverPoint.series === series) {
|
|
2184
|
+
chart.hoverPoint = null;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
proceed.call(this);
|
|
2188
|
+
});
|
|
2189
|
+
|
|
2190
|
+
/**
|
|
2191
|
+
* Do not compute extremes when min and max are set.
|
|
2192
|
+
* If we use this in the core, we can add the hook
|
|
2193
|
+
* to hasExtremes to the methods directly.
|
|
2194
|
+
*/
|
|
2195
|
+
wrap(Series.prototype, 'getExtremes', function(proceed) {
|
|
2196
|
+
if (!isSeriesBoosting(this) || (!this.hasExtremes || !this.hasExtremes())) {
|
|
2197
|
+
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
|
|
2201
|
+
// Set default options
|
|
2202
|
+
each([
|
|
2203
|
+
'area',
|
|
2204
|
+
'arearange',
|
|
2205
|
+
'column',
|
|
2206
|
+
'line',
|
|
2207
|
+
'scatter',
|
|
2208
|
+
'heatmap',
|
|
2209
|
+
'bubble',
|
|
2210
|
+
'treemap',
|
|
2211
|
+
'heatmap'
|
|
2212
|
+
],
|
|
2213
|
+
function(type) {
|
|
2214
|
+
if (plotOptions[type]) {
|
|
2215
|
+
plotOptions[type].boostThreshold = 5000;
|
|
2216
|
+
plotOptions[type].boostData = [];
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
);
|
|
2220
|
+
|
|
2221
|
+
/**
|
|
2222
|
+
* Override a bunch of methods the same way. If the number of points is
|
|
2223
|
+
* below the threshold, run the original method. If not, check for a
|
|
2224
|
+
* canvas version or do nothing.
|
|
2225
|
+
*
|
|
2226
|
+
* Note that we're not overriding any of these for heatmaps.
|
|
2227
|
+
*/
|
|
2228
|
+
each([
|
|
2229
|
+
'translate',
|
|
2230
|
+
'generatePoints',
|
|
2231
|
+
'drawTracker',
|
|
2232
|
+
'drawPoints',
|
|
2233
|
+
'render'
|
|
2234
|
+
], function(method) {
|
|
2235
|
+
function branch(proceed) {
|
|
2236
|
+
var letItPass = this.options.stacking &&
|
|
2237
|
+
(method === 'translate' || method === 'generatePoints');
|
|
2238
|
+
|
|
2239
|
+
if (!isSeriesBoosting(this) ||
|
|
2240
|
+
letItPass ||
|
|
2241
|
+
this.type === 'heatmap' ||
|
|
2242
|
+
this.type === 'treemap'
|
|
2243
|
+
) {
|
|
2244
|
+
|
|
2245
|
+
// Clear image
|
|
2246
|
+
if (method === 'render' && this.image && !isChartSeriesBoosting(this.chart)) {
|
|
2247
|
+
this.image.attr({
|
|
2248
|
+
href: ''
|
|
2249
|
+
});
|
|
2250
|
+
this.animate = null; // We're zooming in, don't run animation
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
proceed.call(this);
|
|
2254
|
+
|
|
2255
|
+
// If a canvas version of the method exists, like renderCanvas(), run
|
|
2256
|
+
} else if (this[method + 'Canvas']) {
|
|
2257
|
+
this[method + 'Canvas']();
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
wrap(Series.prototype, method, branch);
|
|
2262
|
+
|
|
2263
|
+
// A special case for some types - their translate method is already wrapped
|
|
2264
|
+
if (method === 'translate') {
|
|
2265
|
+
if (seriesTypes.column) {
|
|
2266
|
+
wrap(seriesTypes.column.prototype, method, branch);
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
if (seriesTypes.arearange) {
|
|
2270
|
+
wrap(seriesTypes.arearange.prototype, method, branch);
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
if (seriesTypes.treemap) {
|
|
2274
|
+
wrap(seriesTypes.treemap.prototype, method, branch);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
});
|
|
2278
|
+
|
|
2279
|
+
/*
|
|
2280
|
+
* Returns true if the current browser supports webgl
|
|
2281
|
+
*/
|
|
2282
|
+
function hasWebGLSupport() {
|
|
2283
|
+
var i = 0,
|
|
2284
|
+
canvas,
|
|
2285
|
+
contexts = ['webgl', 'experimental-webgl', 'moz-webgl', 'webkit-3d'],
|
|
2286
|
+
context = false;
|
|
2287
|
+
|
|
2288
|
+
if (typeof win.WebGLRenderingContext !== 'undefined') {
|
|
2289
|
+
canvas = doc.createElement('canvas');
|
|
2290
|
+
|
|
2291
|
+
for (; i < contexts.length; i++) {
|
|
2292
|
+
try {
|
|
2293
|
+
context = canvas.getContext(contexts[i]);
|
|
2294
|
+
if (typeof context !== 'undefined' && context !== null) {
|
|
2295
|
+
return true;
|
|
2296
|
+
}
|
|
2297
|
+
} catch (e) {
|
|
2298
|
+
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
return false;
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
/* Used for treemap|heatmap.drawPoints */
|
|
2307
|
+
function pointDrawHandler(proceed) {
|
|
2308
|
+
if (!isSeriesBoosting(this)) {
|
|
2309
|
+
return proceed.call(this);
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
//Make sure we have a valid OGL context
|
|
2313
|
+
var renderer = createAndAttachRenderer(this.chart, this);
|
|
2314
|
+
|
|
2315
|
+
if (renderer) {
|
|
2316
|
+
allocateIfNotSeriesBoosting(renderer, this);
|
|
2317
|
+
renderer.pushSeries(this);
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
renderIfNotSeriesBoosting(renderer, this);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
|
|
2324
|
+
|
|
2325
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
2326
|
+
// We're wrapped in a closure, so just return if there's no webgl support
|
|
2327
|
+
|
|
2328
|
+
if (!hasWebGLSupport()) {
|
|
2329
|
+
if (typeof H.initCanvasBoost !== 'undefined') {
|
|
2330
|
+
// Fallback to canvas boost
|
|
2331
|
+
H.initCanvasBoost();
|
|
2332
|
+
} else {
|
|
2333
|
+
H.error(26);
|
|
2334
|
+
}
|
|
2335
|
+
} else {
|
|
2336
|
+
|
|
2337
|
+
////////////////////////////////////////////////////////////////////////////
|
|
2338
|
+
// GL-SPECIFIC WRAPPINGS FOLLOWS
|
|
2339
|
+
|
|
2340
|
+
/** If the series is a heatmap or treemap, or if the series is not boosting
|
|
2341
|
+
* do the default behaviour. Otherwise, process if the series has no
|
|
2342
|
+
* extremes.
|
|
2343
|
+
*/
|
|
2344
|
+
wrap(Series.prototype, 'processData', function(proceed) {
|
|
2345
|
+
// If this is a heatmap, do default behaviour
|
|
2346
|
+
if (!isSeriesBoosting(this) ||
|
|
2347
|
+
this.type === 'heatmap' ||
|
|
2348
|
+
this.type === 'treemap') {
|
|
2349
|
+
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
if (!this.hasExtremes || !this.hasExtremes(true)) {
|
|
2353
|
+
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
2356
|
+
|
|
2357
|
+
H.extend(Series.prototype, {
|
|
2358
|
+
pointRange: 0,
|
|
2359
|
+
directTouch: false,
|
|
2360
|
+
allowDG: false, // No data grouping, let boost handle large data
|
|
2361
|
+
hasExtremes: function(checkX) {
|
|
2362
|
+
var options = this.options,
|
|
2363
|
+
data = options.data,
|
|
2364
|
+
xAxis = this.xAxis && this.xAxis.options,
|
|
2365
|
+
yAxis = this.yAxis && this.yAxis.options;
|
|
2366
|
+
|
|
2367
|
+
return data.length > (options.boostThreshold || Number.MAX_VALUE) &&
|
|
2368
|
+
isNumber(yAxis.min) && isNumber(yAxis.max) &&
|
|
2369
|
+
(!checkX || (isNumber(xAxis.min) && isNumber(xAxis.max)));
|
|
2370
|
+
},
|
|
2371
|
+
|
|
2372
|
+
/**
|
|
2373
|
+
* If implemented in the core, parts of this can probably be
|
|
2374
|
+
* shared with other similar methods in Highcharts.
|
|
2375
|
+
*/
|
|
2376
|
+
destroyGraphics: function() {
|
|
2377
|
+
var series = this,
|
|
2378
|
+
points = this.points,
|
|
2379
|
+
point,
|
|
2380
|
+
i;
|
|
2381
|
+
|
|
2382
|
+
if (points) {
|
|
2383
|
+
for (i = 0; i < points.length; i = i + 1) {
|
|
2384
|
+
point = points[i];
|
|
2385
|
+
if (point && point.graphic) {
|
|
2386
|
+
point.graphic = point.graphic.destroy();
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
each(['graph', 'area', 'tracker'], function(prop) {
|
|
2392
|
+
if (series[prop]) {
|
|
2393
|
+
series[prop] = series[prop].destroy();
|
|
2394
|
+
}
|
|
2395
|
+
});
|
|
2396
|
+
},
|
|
2397
|
+
|
|
2398
|
+
renderCanvas: function() {
|
|
2399
|
+
var series = this,
|
|
2400
|
+
options = series.options || {},
|
|
2401
|
+
renderer = false,
|
|
2402
|
+
chart = series.chart,
|
|
2403
|
+
xAxis = this.xAxis,
|
|
2404
|
+
yAxis = this.yAxis,
|
|
2405
|
+
//ctx,
|
|
2406
|
+
//c = 0,
|
|
2407
|
+
xData = options.xData || series.processedXData,
|
|
2408
|
+
yData = options.yData || series.processedYData,
|
|
2409
|
+
|
|
2410
|
+
rawData = options.data,
|
|
2411
|
+
xExtremes = xAxis.getExtremes(),
|
|
2412
|
+
xMin = xExtremes.min,
|
|
2413
|
+
xMax = xExtremes.max,
|
|
2414
|
+
yExtremes = yAxis.getExtremes(),
|
|
2415
|
+
yMin = yExtremes.min,
|
|
2416
|
+
yMax = yExtremes.max,
|
|
2417
|
+
pointTaken = {},
|
|
2418
|
+
lastClientX,
|
|
2419
|
+
sampling = !!series.sampling,
|
|
2420
|
+
points,
|
|
2421
|
+
enableMouseTracking = options.enableMouseTracking !== false,
|
|
2422
|
+
threshold = options.threshold,
|
|
2423
|
+
yBottom = yAxis.getThreshold(threshold),
|
|
2424
|
+
isRange = series.pointArrayMap &&
|
|
2425
|
+
series.pointArrayMap.join(',') === 'low,high',
|
|
2426
|
+
isStacked = !!options.stacking,
|
|
2427
|
+
cropStart = series.cropStart || 0,
|
|
2428
|
+
requireSorting = series.requireSorting,
|
|
2429
|
+
useRaw = !xData,
|
|
2430
|
+
minVal,
|
|
2431
|
+
maxVal,
|
|
2432
|
+
minI,
|
|
2433
|
+
maxI,
|
|
2434
|
+
|
|
2435
|
+
addKDPoint = function(clientX, plotY, i) {
|
|
2436
|
+
//Shaves off about 60ms compared to repeated concatination
|
|
2437
|
+
index = clientX + ',' + plotY;
|
|
2438
|
+
|
|
2439
|
+
// The k-d tree requires series points.
|
|
2440
|
+
// Reduce the amount of points, since the time to build the
|
|
2441
|
+
// tree increases exponentially.
|
|
2442
|
+
if (enableMouseTracking && !pointTaken[index]) {
|
|
2443
|
+
pointTaken[index] = true;
|
|
2444
|
+
|
|
2445
|
+
if (chart.inverted) {
|
|
2446
|
+
clientX = xAxis.len - clientX;
|
|
2447
|
+
plotY = yAxis.len - plotY;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
points.push({
|
|
2451
|
+
clientX: clientX,
|
|
2452
|
+
plotX: clientX,
|
|
2453
|
+
plotY: plotY,
|
|
2454
|
+
i: cropStart + i
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
|
|
2459
|
+
// Get or create the renderer
|
|
2460
|
+
renderer = createAndAttachRenderer(chart, series);
|
|
2461
|
+
|
|
2462
|
+
if (!this.visible) {
|
|
2463
|
+
if (!isChartSeriesBoosting(chart) && renderer) {
|
|
2464
|
+
renderer.clear();
|
|
2465
|
+
this.image.attr({
|
|
2466
|
+
href: ''
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
// If we are zooming out from SVG mode, destroy the graphics
|
|
2473
|
+
if (this.points || this.graph) {
|
|
2474
|
+
this.destroyGraphics();
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
// If we're rendering per. series we should create the marker groups
|
|
2478
|
+
// as usual.
|
|
2479
|
+
if (!isChartSeriesBoosting(chart)) {
|
|
2480
|
+
this.markerGroup = series.plotGroup(
|
|
2481
|
+
'markerGroup',
|
|
2482
|
+
'markers',
|
|
2483
|
+
true,
|
|
2484
|
+
1,
|
|
2485
|
+
chart.seriesGroup
|
|
2486
|
+
);
|
|
2487
|
+
} else {
|
|
2488
|
+
//Use a single group for the markers
|
|
2489
|
+
this.markerGroup = chart.markerGroup;
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
points = this.points = [];
|
|
2493
|
+
|
|
2494
|
+
// Do not start building while drawing
|
|
2495
|
+
series.buildKDTree = noop;
|
|
2496
|
+
|
|
2497
|
+
if (renderer) {
|
|
2498
|
+
allocateIfNotSeriesBoosting(renderer, this);
|
|
2499
|
+
renderer.pushSeries(series);
|
|
2500
|
+
// Perform the actual renderer if we're on series level
|
|
2501
|
+
renderIfNotSeriesBoosting(renderer, this, chart);
|
|
2502
|
+
//console.log(series, chart);
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
/* This builds the KD-tree */
|
|
2506
|
+
function processPoint(d, i) {
|
|
2507
|
+
var x,
|
|
2508
|
+
y,
|
|
2509
|
+
clientX,
|
|
2510
|
+
plotY,
|
|
2511
|
+
isNull,
|
|
2512
|
+
low,
|
|
2513
|
+
chartDestroyed = typeof chart.index === 'undefined',
|
|
2514
|
+
isYInside = true;
|
|
2515
|
+
|
|
2516
|
+
if (!chartDestroyed) {
|
|
2517
|
+
if (useRaw) {
|
|
2518
|
+
x = d[0];
|
|
2519
|
+
y = d[1];
|
|
2520
|
+
} else {
|
|
2521
|
+
x = d;
|
|
2522
|
+
y = yData[i];
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
// Resolve low and high for range series
|
|
2526
|
+
if (isRange) {
|
|
2527
|
+
if (useRaw) {
|
|
2528
|
+
y = d.slice(1, 3);
|
|
2529
|
+
}
|
|
2530
|
+
low = y[0];
|
|
2531
|
+
y = y[1];
|
|
2532
|
+
} else if (isStacked) {
|
|
2533
|
+
x = d.x;
|
|
2534
|
+
y = d.stackY;
|
|
2535
|
+
low = y - d.y;
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
isNull = y === null;
|
|
2539
|
+
|
|
2540
|
+
// Optimize for scatter zooming
|
|
2541
|
+
if (!requireSorting) {
|
|
2542
|
+
isYInside = y >= yMin && y <= yMax;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
if (!isNull && x >= xMin && x <= xMax && isYInside) {
|
|
2546
|
+
|
|
2547
|
+
// We use ceil to allow the KD tree to work with sub
|
|
2548
|
+
// pixels, which can be used in boost to space pixels
|
|
2549
|
+
clientX = Math.ceil(xAxis.toPixels(x, true));
|
|
2550
|
+
|
|
2551
|
+
if (sampling) {
|
|
2552
|
+
if (minI === undefined || clientX === lastClientX) {
|
|
2553
|
+
if (!isRange) {
|
|
2554
|
+
low = y;
|
|
2555
|
+
}
|
|
2556
|
+
if (maxI === undefined || y > maxVal) {
|
|
2557
|
+
maxVal = y;
|
|
2558
|
+
maxI = i;
|
|
2559
|
+
}
|
|
2560
|
+
if (minI === undefined || low < minVal) {
|
|
2561
|
+
minVal = low;
|
|
2562
|
+
minI = i;
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
}
|
|
2566
|
+
if (clientX !== lastClientX) { // Add points and reset
|
|
2567
|
+
if (minI !== undefined) { // then maxI is also a number
|
|
2568
|
+
plotY = yAxis.toPixels(maxVal, true);
|
|
2569
|
+
yBottom = yAxis.toPixels(minVal, true);
|
|
2570
|
+
|
|
2571
|
+
addKDPoint(clientX, plotY, maxI);
|
|
2572
|
+
if (yBottom !== plotY) {
|
|
2573
|
+
addKDPoint(clientX, yBottom, minI);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
minI = maxI = undefined;
|
|
2578
|
+
lastClientX = clientX;
|
|
2579
|
+
}
|
|
2580
|
+
} else {
|
|
2581
|
+
plotY = Math.ceil(yAxis.toPixels(y, true));
|
|
2582
|
+
addKDPoint(clientX, plotY, i);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
return !chartDestroyed;
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
function doneProcessing() {
|
|
2591
|
+
fireEvent(series, 'renderedCanvas');
|
|
2592
|
+
// Pass tests in Pointer.
|
|
2593
|
+
// Replace this with a single property, and replace when zooming
|
|
2594
|
+
// in below boostThreshold.
|
|
2595
|
+
series.directTouch = false;
|
|
2596
|
+
series.options.stickyTracking = true;
|
|
2597
|
+
|
|
2598
|
+
// Go back to prototype, ready to build
|
|
2599
|
+
delete series.buildKDTree;
|
|
2600
|
+
series.buildKDTree();
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// Loop over the points to build the k-d tree
|
|
2604
|
+
eachAsync(
|
|
2605
|
+
isStacked ? series.data : (xData || rawData),
|
|
2606
|
+
processPoint,
|
|
2607
|
+
doneProcessing,
|
|
2608
|
+
chart.renderer.forExport ? Number.MAX_VALUE : undefined
|
|
2609
|
+
);
|
|
2610
|
+
}
|
|
2611
|
+
});
|
|
2612
|
+
|
|
2613
|
+
/*
|
|
2614
|
+
* We need to handle heatmaps separatly, since we can't perform the
|
|
2615
|
+
* size/color calculations in the shader easily.
|
|
2616
|
+
*
|
|
2617
|
+
* This likely needs future optimization.
|
|
2618
|
+
*
|
|
2619
|
+
*/
|
|
2620
|
+
each(['heatmap', 'treemap'],
|
|
2621
|
+
function(t) {
|
|
2622
|
+
if (seriesTypes[t]) {
|
|
2623
|
+
wrap(seriesTypes[t].prototype, 'drawPoints', pointDrawHandler);
|
|
2624
|
+
seriesTypes[t].prototype.directTouch = false; // Use k-d-tree
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
);
|
|
2628
|
+
|
|
2629
|
+
if (seriesTypes.bubble) {
|
|
2630
|
+
// By default, the bubble series does not use the KD-tree, so force it
|
|
2631
|
+
// to.
|
|
2632
|
+
delete seriesTypes.bubble.prototype.buildKDTree;
|
|
2633
|
+
seriesTypes.bubble.prototype.directTouch = false;
|
|
2634
|
+
|
|
2635
|
+
// Needed for markers to work correctly
|
|
2636
|
+
wrap(
|
|
2637
|
+
seriesTypes.bubble.prototype,
|
|
2638
|
+
'markerAttribs',
|
|
2639
|
+
function(proceed) {
|
|
2640
|
+
if (isSeriesBoosting(this)) {
|
|
2641
|
+
return false;
|
|
2642
|
+
}
|
|
2643
|
+
return proceed.apply(this, [].slice.call(arguments, 1));
|
|
2644
|
+
}
|
|
2645
|
+
);
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
seriesTypes.scatter.prototype.fill = true;
|
|
2649
|
+
|
|
2650
|
+
extend(seriesTypes.area.prototype, {
|
|
2651
|
+
fill: true,
|
|
2652
|
+
fillOpacity: true,
|
|
2653
|
+
sampling: true
|
|
2654
|
+
});
|
|
2655
|
+
|
|
2656
|
+
extend(seriesTypes.column.prototype, {
|
|
2657
|
+
fill: true,
|
|
2658
|
+
sampling: true
|
|
2659
|
+
});
|
|
2660
|
+
|
|
2661
|
+
wrap(Series.prototype, 'setVisible', function(proceed, vis) {
|
|
2662
|
+
proceed.call(this, vis, false);
|
|
2663
|
+
if (this.visible === false && this.ogl && this.canvas && this.image) {
|
|
2664
|
+
this.ogl.clear();
|
|
2665
|
+
this.image.attr({
|
|
2666
|
+
href: ''
|
|
2667
|
+
});
|
|
2668
|
+
} else {
|
|
2669
|
+
this.chart.redraw();
|
|
2670
|
+
}
|
|
2671
|
+
});
|
|
2672
|
+
|
|
2673
|
+
/**
|
|
2674
|
+
* Take care of the canvas blitting
|
|
2675
|
+
*/
|
|
2676
|
+
H.Chart.prototype.callbacks.push(function(chart) {
|
|
2677
|
+
|
|
2678
|
+
/* Convert chart-level canvas to image */
|
|
2679
|
+
function canvasToSVG() {
|
|
2680
|
+
if (chart.ogl && isChartSeriesBoosting(chart)) {
|
|
2681
|
+
chart.ogl.render(chart);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
/* Clear chart-level canvas */
|
|
2686
|
+
function preRender() {
|
|
2687
|
+
|
|
2688
|
+
if (!isChartSeriesBoosting(chart) && chart.didBoost) {
|
|
2689
|
+
chart.didBoost = false;
|
|
2690
|
+
// Clear the canvas
|
|
2691
|
+
if (chart.image) {
|
|
2692
|
+
chart.image.attr({
|
|
2693
|
+
href: ''
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
if (chart.canvas && chart.ogl && isChartSeriesBoosting(chart)) {
|
|
2699
|
+
chart.didBoost = true;
|
|
2700
|
+
|
|
2701
|
+
// Allocate
|
|
2702
|
+
chart.ogl.allocateBuffer(chart);
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
//see #6518 + #6739
|
|
2706
|
+
if (chart.markerGroup && chart.xAxis && chart.xAxis.length > 0 && chart.yAxis && chart.yAxis.length > 0) {
|
|
2707
|
+
chart.markerGroup.translate(
|
|
2708
|
+
chart.xAxis[0].pos,
|
|
2709
|
+
chart.yAxis[0].pos
|
|
2710
|
+
);
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
addEvent(chart, 'predraw', preRender);
|
|
2716
|
+
addEvent(chart, 'render', canvasToSVG);
|
|
2717
|
+
});
|
|
2718
|
+
} // if hasCanvasSupport
|
|
2719
|
+
|
|
2720
|
+
}(Highcharts));
|
|
2721
|
+
}));
|