Skip to content

Commit ed38e70

Browse files
committed
don't create scattergl trace objects on initialization
- instead, create them on update when needed - this should speed up scattergl plotting especially when multiple traces are present - TODO: we'll need to reorder the trace object when adding a a 'mode' or an error bar to make sure e.g. lines are always drawn below scatter pts.
1 parent 0cf2ee1 commit ed38e70

File tree

1 file changed

+98
-90
lines changed

1 file changed

+98
-90
lines changed

src/traces/scattergl/convert.js

Lines changed: 98 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,13 @@ function LineWithMarkers(scene, uid) {
4949
this.idToIndex = [];
5050
this.bounds = [0, 0, 0, 0];
5151

52+
this.isVisible = false;
5253
this.hasLines = false;
53-
this.lineOptions = {
54+
this.hasErrorX = false;
55+
this.hasErrorY = false;
56+
this.hasMarkers = false;
57+
58+
this.line = this.initObject(createLine, {
5459
positions: new Float64Array(0),
5560
color: [0, 0, 0, 1],
5661
width: 1,
@@ -61,34 +66,25 @@ function LineWithMarkers(scene, uid) {
6166
[0, 0, 0, 1],
6267
[0, 0, 0, 1]],
6368
dashes: [1]
64-
};
65-
this.line = createLine(scene.glplot, this.lineOptions);
66-
this.line._trace = this;
69+
});
6770

68-
this.hasErrorX = false;
69-
this.errorXOptions = {
71+
this.errorX = this.initObject(createError, {
7072
positions: new Float64Array(0),
7173
errors: new Float64Array(0),
7274
lineWidth: 1,
7375
capSize: 0,
7476
color: [0, 0, 0, 1]
75-
};
76-
this.errorX = createError(scene.glplot, this.errorXOptions);
77-
this.errorX._trace = this;
77+
});
7878

79-
this.hasErrorY = false;
80-
this.errorYOptions = {
79+
this.errorY = this.initObject(createError, {
8180
positions: new Float64Array(0),
8281
errors: new Float64Array(0),
8382
lineWidth: 1,
8483
capSize: 0,
8584
color: [0, 0, 0, 1]
86-
};
87-
this.errorY = createError(scene.glplot, this.errorYOptions);
88-
this.errorY._trace = this;
85+
});
8986

90-
this.hasMarkers = false;
91-
this.scatterOptions = {
87+
var scatterOptions0 = {
9288
positions: new Float64Array(0),
9389
sizes: [],
9490
colors: [],
@@ -100,16 +96,48 @@ function LineWithMarkers(scene, uid) {
10096
borderSize: 1,
10197
borderColor: [0, 0, 0, 1]
10298
};
103-
this.scatter = createScatter(scene.glplot, this.scatterOptions);
104-
this.scatter._trace = this;
105-
this.fancyScatter = createFancyScatter(scene.glplot, this.scatterOptions);
106-
this.fancyScatter._trace = this;
10799

108-
this.isVisible = false;
100+
this.scatter = this.initObject(createScatter, scatterOptions0);
101+
this.fancyScatter = this.initObject(createFancyScatter, scatterOptions0);
109102
}
110103

111104
var proto = LineWithMarkers.prototype;
112105

106+
proto.initObject = function(createFn, options) {
107+
var _this = this;
108+
var glplot = _this.scene.glplot;
109+
var options0 = Lib.extendFlat({}, options);
110+
var obj = null;
111+
112+
// TODO how to handle ordering ???
113+
114+
function update() {
115+
if(!obj) {
116+
obj = createFn(glplot, options);
117+
obj._trace = _this;
118+
}
119+
obj.update(options);
120+
return obj;
121+
}
122+
123+
function clear() {
124+
if(obj) obj.update(options0);
125+
return obj;
126+
}
127+
128+
function dispose() {
129+
if(obj) obj.dispose();
130+
return obj;
131+
}
132+
133+
return {
134+
options: options,
135+
update: update,
136+
clear: clear,
137+
dispose: dispose
138+
};
139+
};
140+
113141
proto.handlePick = function(pickResult) {
114142
var index = pickResult.pointId;
115143

@@ -233,6 +261,7 @@ function _convertColor(colors, opacities, count) {
233261
* - markers
234262
*/
235263
proto.update = function(options) {
264+
236265
if(options.visible !== true) {
237266
this.isVisible = false;
238267
this.hasLines = false;
@@ -255,7 +284,11 @@ proto.update = function(options) {
255284
this.connectgaps = !!options.connectgaps;
256285

257286
if(!this.isVisible) {
258-
this.clear();
287+
this.line.clear();
288+
this.errorX.clear();
289+
this.errorY.clear();
290+
this.scatter.clear();
291+
this.fancyScatter.clear();
259292
}
260293
else if(this.isFancy(options)) {
261294
this.updateFancy(options);
@@ -292,22 +325,6 @@ function allFastTypesLikely(a) {
292325
return true;
293326
}
294327

295-
proto.clear = function() {
296-
this.lineOptions.positions = new Float64Array(0);
297-
this.line.update(this.lineOptions);
298-
299-
this.errorXOptions.positions = new Float64Array(0);
300-
this.errorX.update(this.errorXOptions);
301-
302-
this.errorYOptions.positions = new Float64Array(0);
303-
this.errorY.update(this.errorYOptions);
304-
305-
this.scatterOptions.positions = new Float64Array(0);
306-
this.scatterOptions.glyphs = [];
307-
this.scatter.update(this.scatterOptions);
308-
this.fancyScatter.update(this.scatterOptions);
309-
};
310-
311328
proto.updateFast = function(options) {
312329
var x = this.xData = this.pickXData = options.x;
313330
var y = this.yData = this.pickYData = options.y;
@@ -363,34 +380,30 @@ proto.updateFast = function(options) {
363380
var markerSize;
364381

365382
if(this.hasMarkers) {
366-
this.scatterOptions.positions = positions;
383+
this.scatter.options.positions = positions;
367384

368385
var markerColor = str2RGBArray(options.marker.color),
369386
borderColor = str2RGBArray(options.marker.line.color),
370387
opacity = (options.opacity) * (options.marker.opacity);
371388

372389
markerColor[3] *= opacity;
373-
this.scatterOptions.color = markerColor;
390+
this.scatter.options.color = markerColor;
374391

375392
borderColor[3] *= opacity;
376-
this.scatterOptions.borderColor = borderColor;
393+
this.scatter.options.borderColor = borderColor;
377394

378395
markerSize = options.marker.size;
379-
this.scatterOptions.size = markerSize;
380-
this.scatterOptions.borderSize = options.marker.line.width;
396+
this.scatter.options.size = markerSize;
397+
this.scatter.options.borderSize = options.marker.line.width;
381398

382-
this.scatter.update(this.scatterOptions);
399+
this.scatter.update();
383400
}
384401
else {
385-
this.scatterOptions.positions = new Float64Array(0);
386-
this.scatterOptions.glyphs = [];
387-
this.scatter.update(this.scatterOptions);
402+
this.scatter.clear();
388403
}
389404

390405
// turn off fancy scatter plot
391-
this.scatterOptions.positions = new Float64Array(0);
392-
this.scatterOptions.glyphs = [];
393-
this.fancyScatter.update(this.scatterOptions);
406+
this.fancyScatter.clear();
394407

395408
// add item for autorange routine
396409
this.expandAxesFast(bounds, markerSize);
@@ -464,16 +477,16 @@ proto.updateFancy = function(options) {
464477
var sizes;
465478

466479
if(this.hasMarkers) {
467-
this.scatterOptions.positions = positions;
480+
this.scatter.options.positions = positions;
468481

469482
// TODO rewrite convert function so that
470483
// we don't have to loop through the data another time
471484

472-
this.scatterOptions.sizes = new Array(pId);
473-
this.scatterOptions.glyphs = new Array(pId);
474-
this.scatterOptions.borderWidths = new Array(pId);
475-
this.scatterOptions.colors = new Array(pId * 4);
476-
this.scatterOptions.borderColors = new Array(pId * 4);
485+
this.scatter.options.sizes = new Array(pId);
486+
this.scatter.options.glyphs = new Array(pId);
487+
this.scatter.options.borderWidths = new Array(pId);
488+
this.scatter.options.colors = new Array(pId * 4);
489+
this.scatter.options.borderColors = new Array(pId * 4);
477490

478491
var markerSizeFunc = makeBubbleSizeFn(options),
479492
markerOpts = options.marker,
@@ -490,28 +503,24 @@ proto.updateFancy = function(options) {
490503
for(i = 0; i < pId; ++i) {
491504
index = idToIndex[i];
492505

493-
this.scatterOptions.sizes[i] = 4.0 * sizes[index];
494-
this.scatterOptions.glyphs[i] = glyphs[index];
495-
this.scatterOptions.borderWidths[i] = 0.5 * borderWidths[index];
506+
this.scatter.options.sizes[i] = 4.0 * sizes[index];
507+
this.scatter.options.glyphs[i] = glyphs[index];
508+
this.scatter.options.borderWidths[i] = 0.5 * borderWidths[index];
496509

497510
for(j = 0; j < 4; ++j) {
498-
this.scatterOptions.colors[4 * i + j] = colors[4 * index + j];
499-
this.scatterOptions.borderColors[4 * i + j] = borderColors[4 * index + j];
511+
this.scatter.options.colors[4 * i + j] = colors[4 * index + j];
512+
this.scatter.options.borderColors[4 * i + j] = borderColors[4 * index + j];
500513
}
501514
}
502515

503-
this.fancyScatter.update(this.scatterOptions);
516+
this.fancyScatter.update();
504517
}
505518
else {
506-
this.scatterOptions.positions = new Float64Array(0);
507-
this.scatterOptions.glyphs = [];
508-
this.fancyScatter.update(this.scatterOptions);
519+
this.fancyScatter.clear();
509520
}
510521

511522
// turn off fast scatter plot
512-
this.scatterOptions.positions = new Float64Array(0);
513-
this.scatterOptions.glyphs = [];
514-
this.scatter.update(this.scatterOptions);
523+
this.scatter.clear();
515524

516525
// add item for autorange routine
517526
this.expandAxesFancy(x, y, sizes);
@@ -535,61 +544,60 @@ proto.updateLines = function(options, positions) {
535544
}
536545
}
537546

538-
this.lineOptions.positions = linePositions;
547+
this.line.options.positions = linePositions;
539548

540549
var lineColor = convertColor(options.line.color, options.opacity, 1),
541-
lineWidth = Math.round(0.5 * this.lineOptions.width),
550+
lineWidth = Math.round(0.5 * this.line.options.width),
542551
dashes = (DASHES[options.line.dash] || [1]).slice();
543552

544553
for(i = 0; i < dashes.length; ++i) dashes[i] *= lineWidth;
545554

546555
switch(options.fill) {
547556
case 'tozeroy':
548-
this.lineOptions.fill = [false, true, false, false];
557+
this.line.options.fill = [false, true, false, false];
549558
break;
550559
case 'tozerox':
551-
this.lineOptions.fill = [true, false, false, false];
560+
this.line.options.fill = [true, false, false, false];
552561
break;
553562
default:
554-
this.lineOptions.fill = [false, false, false, false];
563+
this.line.options.fill = [false, false, false, false];
555564
break;
556565
}
557566

558567
var fillColor = str2RGBArray(options.fillcolor);
559568

560-
this.lineOptions.color = lineColor;
561-
this.lineOptions.width = 2.0 * options.line.width;
562-
this.lineOptions.dashes = dashes;
563-
this.lineOptions.fillColor = [fillColor, fillColor, fillColor, fillColor];
569+
this.line.options.color = lineColor;
570+
this.line.options.width = 2.0 * options.line.width;
571+
this.line.options.dashes = dashes;
572+
this.line.options.fillColor = [fillColor, fillColor, fillColor, fillColor];
573+
574+
this.line.update();
564575
}
565576
else {
566-
this.lineOptions.positions = new Float64Array(0);
577+
this.line.clear();
567578
}
568-
569-
this.line.update(this.lineOptions);
570579
};
571580

572581
proto.updateError = function(axLetter, options, positions, errors) {
573582
var errorObj = this['error' + axLetter],
574-
errorOptions = options['error_' + axLetter.toLowerCase()],
575-
errorObjOptions = this['error' + axLetter + 'Options'];
583+
errorOptions = options['error_' + axLetter.toLowerCase()];
576584

577585
if(axLetter.toLowerCase() === 'x' && errorOptions.copy_ystyle) {
578586
errorOptions = options.error_y;
579587
}
580588

581589
if(this['hasError' + axLetter]) {
582-
errorObjOptions.positions = positions;
583-
errorObjOptions.errors = errors;
584-
errorObjOptions.capSize = errorOptions.width;
585-
errorObjOptions.lineWidth = errorOptions.thickness / 2; // ballpark rescaling
586-
errorObjOptions.color = convertColor(errorOptions.color, 1, 1);
590+
errorObj.options.positions = positions;
591+
errorObj.options.errors = errors;
592+
errorObj.options.capSize = errorOptions.width;
593+
errorObj.options.lineWidth = errorOptions.thickness / 2; // ballpark rescaling
594+
errorObj.options.color = convertColor(errorOptions.color, 1, 1);
595+
596+
errorObj.update();
587597
}
588598
else {
589-
errorObjOptions.positions = new Float64Array(0);
599+
errorObj.clear();
590600
}
591-
592-
errorObj.update(errorObjOptions);
593601
};
594602

595603
proto.expandAxesFast = function(bounds, markerSize) {

0 commit comments

Comments
 (0)