Skip to content

Commit 5a24245

Browse files
authored
Merge pull request #3087 from plotly/etienne-scattergl-ordering
Scattergl improvements for: fill tozero with bad values fix, fills layering fix, add some line.shape values
2 parents 1539fea + d708132 commit 5a24245

15 files changed

+701
-71
lines changed

src/traces/scatter/link_traces.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,16 @@ module.exports = function linkTraces(gd, plotinfo, cdscatter) {
7575
}
7676
}
7777

78+
if(trace.fill && (
79+
trace.fill.substr(0, 6) === 'tozero' || trace.fill === 'toself' ||
80+
(trace.fill.substr(0, 2) === 'to' && !trace._prevtrace))
81+
) {
82+
trace._ownfill = true;
83+
}
84+
7885
prevtraces[group] = trace;
7986
} else {
80-
trace._prevtrace = trace._nexttrace = null;
87+
trace._prevtrace = trace._nexttrace = trace._ownfill = null;
8188
}
8289
}
8390

src/traces/scatter/plot.js

+3-12
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,10 @@ function createFills(gd, traceJoin, plotinfo) {
9393
var trace = d[0].trace;
9494

9595
var fillData = [];
96-
if(trace.fill && (trace.fill.substr(0, 6) === 'tozero' || trace.fill === 'toself' ||
97-
(trace.fill.substr(0, 2) === 'to' && !trace._prevtrace))
98-
) {
99-
fillData = ['_ownFill'];
100-
}
101-
if(trace._nexttrace) {
102-
// make the fill-to-next path now for the NEXT trace, so it shows
103-
// behind both lines.
104-
fillData.push('_nextFill');
105-
}
96+
if(trace._ownfill) fillData.push('_ownFill');
97+
if(trace._nexttrace) fillData.push('_nextFill');
10698

107-
var fillJoin = fills.selectAll('g')
108-
.data(fillData, identity);
99+
var fillJoin = fills.selectAll('g').data(fillData, identity);
109100

110101
fillJoin.enter().append('g');
111102

src/traces/scattergl/attributes.js

+11
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ var attrs = module.exports = overrideAll({
4646
line: {
4747
color: scatterLineAttrs.color,
4848
width: scatterLineAttrs.width,
49+
shape: {
50+
valType: 'enumerated',
51+
values: ['linear', 'hv', 'vh', 'hvh', 'vhv'],
52+
dflt: 'linear',
53+
role: 'style',
54+
editType: 'plot',
55+
description: [
56+
'Determines the line shape.',
57+
'The values correspond to step-wise line shapes.'
58+
].join(' ')
59+
},
4960
dash: {
5061
valType: 'enumerated',
5162
values: Object.keys(DASHES),

src/traces/scattergl/convert.js

+65-21
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ function getSymbolSdf(symbol) {
385385
}
386386

387387
function convertLinePositions(gd, trace, positions) {
388-
var count = positions.length / 2;
388+
var len = positions.length;
389+
var count = len / 2;
389390
var linePositions;
390391
var i;
391392

@@ -394,38 +395,81 @@ function convertLinePositions(gd, trace, positions) {
394395
linePositions = [];
395396
for(i = 0; i < count - 1; i++) {
396397
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
397-
linePositions.push(NaN);
398-
linePositions.push(NaN);
399-
linePositions.push(NaN);
400-
linePositions.push(NaN);
398+
linePositions.push(NaN, NaN, NaN, NaN);
399+
} else {
400+
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
401+
if(!isNaN(positions[i * 2 + 2]) && !isNaN(positions[i * 2 + 3])) {
402+
linePositions.push(positions[i * 2 + 2], positions[i * 2 + 1]);
403+
} else {
404+
linePositions.push(NaN, NaN);
405+
}
406+
}
407+
}
408+
linePositions.push(positions[len - 2], positions[len - 1]);
409+
} else if(trace.line.shape === 'hvh') {
410+
linePositions = [];
411+
for(i = 0; i < count - 1; i++) {
412+
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1]) || isNaN(positions[i * 2 + 2]) || isNaN(positions[i * 2 + 3])) {
413+
if(!isNaN(positions[i * 2]) && !isNaN(positions[i * 2 + 1])) {
414+
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
415+
} else {
416+
linePositions.push(NaN, NaN);
417+
}
418+
linePositions.push(NaN, NaN);
401419
}
402420
else {
403-
linePositions.push(positions[i * 2]);
404-
linePositions.push(positions[i * 2 + 1]);
405-
linePositions.push(positions[i * 2 + 2]);
406-
linePositions.push(positions[i * 2 + 1]);
421+
var midPtX = (positions[i * 2] + positions[i * 2 + 2]) / 2;
422+
linePositions.push(
423+
positions[i * 2],
424+
positions[i * 2 + 1],
425+
midPtX,
426+
positions[i * 2 + 1],
427+
midPtX,
428+
positions[i * 2 + 3]
429+
);
407430
}
408431
}
409-
linePositions.push(positions[positions.length - 2]);
410-
linePositions.push(positions[positions.length - 1]);
432+
linePositions.push(positions[len - 2], positions[len - 1]);
433+
} else if(trace.line.shape === 'vhv') {
434+
linePositions = [];
435+
for(i = 0; i < count - 1; i++) {
436+
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1]) || isNaN(positions[i * 2 + 2]) || isNaN(positions[i * 2 + 3])) {
437+
if(!isNaN(positions[i * 2]) && !isNaN(positions[i * 2 + 1])) {
438+
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
439+
} else {
440+
linePositions.push(NaN, NaN);
441+
}
442+
linePositions.push(NaN, NaN);
443+
}
444+
else {
445+
var midPtY = (positions[i * 2 + 1] + positions[i * 2 + 3]) / 2;
446+
linePositions.push(
447+
positions[i * 2],
448+
positions[i * 2 + 1],
449+
positions[i * 2],
450+
midPtY,
451+
positions[i * 2 + 2],
452+
midPtY
453+
);
454+
}
455+
}
456+
linePositions.push(positions[len - 2], positions[len - 1]);
411457
} else if(trace.line.shape === 'vh') {
412458
linePositions = [];
413459
for(i = 0; i < count - 1; i++) {
414460
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
415-
linePositions.push(NaN);
416-
linePositions.push(NaN);
417-
linePositions.push(NaN);
418-
linePositions.push(NaN);
461+
linePositions.push(NaN, NaN, NaN, NaN);
419462
}
420463
else {
421-
linePositions.push(positions[i * 2]);
422-
linePositions.push(positions[i * 2 + 1]);
423-
linePositions.push(positions[i * 2]);
424-
linePositions.push(positions[i * 2 + 3]);
464+
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
465+
if(!isNaN(positions[i * 2 + 2]) && !isNaN(positions[i * 2 + 3])) {
466+
linePositions.push(positions[i * 2], positions[i * 2 + 3]);
467+
} else {
468+
linePositions.push(NaN, NaN);
469+
}
425470
}
426471
}
427-
linePositions.push(positions[positions.length - 2]);
428-
linePositions.push(positions[positions.length - 1]);
472+
linePositions.push(positions[len - 2], positions[len - 1]);
429473
} else {
430474
linePositions = positions;
431475
}

src/traces/scattergl/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
4242
if(subTypes.hasLines(traceOut)) {
4343
coerce('connectgaps');
4444
handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
45+
coerce('line.shape');
4546
}
4647

4748
if(subTypes.hasMarkers(traceOut)) {

src/traces/scattergl/index.js

+82-37
Original file line numberDiff line numberDiff line change
@@ -256,39 +256,38 @@ function sceneUpdate(gd, subplot) {
256256

257257
// draw traces in proper order
258258
scene.draw = function draw() {
259-
var i;
260-
for(i = 0; i < scene.count; i++) {
261-
if(scene.fill2d && scene.fillOptions[i]) {
262-
// must do all fills first
263-
scene.fill2d.draw(i);
259+
var count = scene.count;
260+
var fill2d = scene.fill2d;
261+
var error2d = scene.error2d;
262+
var line2d = scene.line2d;
263+
var scatter2d = scene.scatter2d;
264+
var glText = scene.glText;
265+
var select2d = scene.select2d;
266+
var selectBatch = scene.selectBatch;
267+
var unselectBatch = scene.unselectBatch;
268+
269+
for(var i = 0; i < count; i++) {
270+
if(fill2d && scene.fillOrder[i]) {
271+
fill2d.draw(scene.fillOrder[i]);
264272
}
265-
}
266-
for(i = 0; i < scene.count; i++) {
267-
if(scene.line2d && scene.lineOptions[i]) {
268-
scene.line2d.draw(i);
273+
if(line2d && scene.lineOptions[i]) {
274+
line2d.draw(i);
269275
}
270-
if(scene.error2d && scene.errorXOptions[i]) {
271-
scene.error2d.draw(i);
276+
if(error2d) {
277+
if(scene.errorXOptions[i]) error2d.draw(i);
278+
if(scene.errorYOptions[i]) error2d.draw(i + count);
272279
}
273-
if(scene.error2d && scene.errorYOptions[i]) {
274-
scene.error2d.draw(i + scene.count);
280+
if(scatter2d && scene.markerOptions[i] && (!selectBatch || !selectBatch[i])) {
281+
scatter2d.draw(i);
275282
}
276-
if(scene.scatter2d && scene.markerOptions[i] && (!scene.selectBatch || !scene.selectBatch[i])) {
277-
// traces in no-selection mode
278-
scene.scatter2d.draw(i);
283+
if(glText[i] && scene.textOptions[i]) {
284+
glText[i].render();
279285
}
280286
}
281287

282-
// draw traces in selection mode
283-
if(scene.scatter2d && scene.select2d && scene.selectBatch) {
284-
scene.select2d.draw(scene.selectBatch);
285-
scene.scatter2d.draw(scene.unselectBatch);
286-
}
287-
288-
for(i = 0; i < scene.count; i++) {
289-
if(scene.glText[i] && scene.textOptions[i]) {
290-
scene.glText[i].render();
291-
}
288+
if(scatter2d && select2d && selectBatch) {
289+
select2d.draw(selectBatch);
290+
scatter2d.draw(unselectBatch);
292291
}
293292

294293
scene.dirty = false;
@@ -397,6 +396,24 @@ function plot(gd, subplot, cdata) {
397396
}
398397
if(scene.line2d) {
399398
scene.line2d.update(scene.lineOptions);
399+
scene.lineOptions = scene.lineOptions.map(function(lineOptions) {
400+
if(lineOptions && lineOptions.positions) {
401+
var pos = [], srcPos = lineOptions.positions;
402+
403+
var firstptdef = 0;
404+
while(isNaN(srcPos[firstptdef]) || isNaN(srcPos[firstptdef + 1])) {
405+
firstptdef += 2;
406+
}
407+
var lastptdef = srcPos.length - 2;
408+
while(isNaN(srcPos[lastptdef]) || isNaN(srcPos[lastptdef + 1])) {
409+
lastptdef += -2;
410+
}
411+
pos = pos.concat(srcPos.slice(firstptdef, lastptdef + 2));
412+
lineOptions.positions = pos;
413+
}
414+
return lineOptions;
415+
});
416+
scene.line2d.update(scene.lineOptions);
400417
}
401418
if(scene.error2d) {
402419
var errorBatch = (scene.errorXOptions || []).concat(scene.errorYOptions || []);
@@ -405,7 +422,9 @@ function plot(gd, subplot, cdata) {
405422
if(scene.scatter2d) {
406423
scene.scatter2d.update(scene.markerOptions);
407424
}
425+
408426
// fill requires linked traces, so we generate it's positions here
427+
scene.fillOrder = Lib.repeat(null, scene.count);
409428
if(scene.fill2d) {
410429
scene.fillOptions = scene.fillOptions.map(function(fillOptions, i) {
411430
var cdscatter = cdata[i];
@@ -416,19 +435,46 @@ function plot(gd, subplot, cdata) {
416435
var lineOptions = scene.lineOptions[i];
417436
var last, j;
418437

438+
var fillData = [];
439+
if(trace._ownfill) fillData.push(i);
440+
if(trace._nexttrace) fillData.push(i + 1);
441+
if(fillData.length) scene.fillOrder[i] = fillData;
442+
419443
var pos = [], srcPos = (lineOptions && lineOptions.positions) || stash.positions;
420444

421445
if(trace.fill === 'tozeroy') {
422-
pos = [srcPos[0], 0];
423-
pos = pos.concat(srcPos);
424-
pos.push(srcPos[srcPos.length - 2]);
425-
pos.push(0);
446+
var firstpdef = 0;
447+
while(isNaN(srcPos[firstpdef + 1])) {
448+
firstpdef += 2;
449+
}
450+
var lastpdef = srcPos.length - 2;
451+
while(isNaN(srcPos[lastpdef + 1])) {
452+
lastpdef += -2;
453+
}
454+
if(srcPos[firstpdef + 1] !== 0) {
455+
pos = [ srcPos[firstpdef], 0 ];
456+
}
457+
pos = pos.concat(srcPos.slice(firstpdef, lastpdef + 2));
458+
if(srcPos[lastpdef + 1] !== 0) {
459+
pos = pos.concat([ srcPos[lastpdef], 0 ]);
460+
}
426461
}
427462
else if(trace.fill === 'tozerox') {
428-
pos = [0, srcPos[1]];
429-
pos = pos.concat(srcPos);
430-
pos.push(0);
431-
pos.push(srcPos[srcPos.length - 1]);
463+
var firstptdef = 0;
464+
while(isNaN(srcPos[firstptdef])) {
465+
firstptdef += 2;
466+
}
467+
var lastptdef = srcPos.length - 2;
468+
while(isNaN(srcPos[lastptdef])) {
469+
lastptdef += -2;
470+
}
471+
if(srcPos[firstptdef] !== 0) {
472+
pos = [ 0, srcPos[firstptdef + 1] ];
473+
}
474+
pos = pos.concat(srcPos.slice(firstptdef, lastptdef + 2));
475+
if(srcPos[lastptdef] !== 0) {
476+
pos = pos.concat([ 0, srcPos[lastptdef + 1]]);
477+
}
432478
}
433479
else if(trace.fill === 'toself' || trace.fill === 'tonext') {
434480
pos = [];
@@ -459,8 +505,7 @@ function plot(gd, subplot, cdata) {
459505
for(i = Math.floor(nextPos.length / 2); i--;) {
460506
var xx = nextPos[i * 2], yy = nextPos[i * 2 + 1];
461507
if(isNaN(xx) || isNaN(yy)) continue;
462-
pos.push(xx);
463-
pos.push(yy);
508+
pos.push(xx, yy);
464509
}
465510
fillOptions.fill = nextTrace.fillcolor;
466511
}
@@ -486,7 +531,7 @@ function plot(gd, subplot, cdata) {
486531
pos = pos.concat(prevLinePos);
487532
fillOptions.hole = hole;
488533
}
489-
534+
fillOptions.fillmode = trace.fill;
490535
fillOptions.opacity = trace.opacity;
491536
fillOptions.positions = pos;
492537

10 Bytes
Loading
Loading
57.9 KB
Loading
Loading
77.2 KB
Loading

0 commit comments

Comments
 (0)