Skip to content

Commit

Permalink
Merge pull request #3087 from plotly/etienne-scattergl-ordering
Browse files Browse the repository at this point in the history
Scattergl improvements for: fill tozero with bad values fix, fills layering fix, add some line.shape values
  • Loading branch information
etpinard authored Oct 11, 2018
2 parents 1539fea + d708132 commit 5a24245
Show file tree
Hide file tree
Showing 15 changed files with 701 additions and 71 deletions.
9 changes: 8 additions & 1 deletion src/traces/scatter/link_traces.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,16 @@ module.exports = function linkTraces(gd, plotinfo, cdscatter) {
}
}

if(trace.fill && (
trace.fill.substr(0, 6) === 'tozero' || trace.fill === 'toself' ||
(trace.fill.substr(0, 2) === 'to' && !trace._prevtrace))
) {
trace._ownfill = true;
}

prevtraces[group] = trace;
} else {
trace._prevtrace = trace._nexttrace = null;
trace._prevtrace = trace._nexttrace = trace._ownfill = null;
}
}

Expand Down
15 changes: 3 additions & 12 deletions src/traces/scatter/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,10 @@ function createFills(gd, traceJoin, plotinfo) {
var trace = d[0].trace;

var fillData = [];
if(trace.fill && (trace.fill.substr(0, 6) === 'tozero' || trace.fill === 'toself' ||
(trace.fill.substr(0, 2) === 'to' && !trace._prevtrace))
) {
fillData = ['_ownFill'];
}
if(trace._nexttrace) {
// make the fill-to-next path now for the NEXT trace, so it shows
// behind both lines.
fillData.push('_nextFill');
}
if(trace._ownfill) fillData.push('_ownFill');
if(trace._nexttrace) fillData.push('_nextFill');

var fillJoin = fills.selectAll('g')
.data(fillData, identity);
var fillJoin = fills.selectAll('g').data(fillData, identity);

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

Expand Down
11 changes: 11 additions & 0 deletions src/traces/scattergl/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ var attrs = module.exports = overrideAll({
line: {
color: scatterLineAttrs.color,
width: scatterLineAttrs.width,
shape: {
valType: 'enumerated',
values: ['linear', 'hv', 'vh', 'hvh', 'vhv'],
dflt: 'linear',
role: 'style',
editType: 'plot',
description: [
'Determines the line shape.',
'The values correspond to step-wise line shapes.'
].join(' ')
},
dash: {
valType: 'enumerated',
values: Object.keys(DASHES),
Expand Down
86 changes: 65 additions & 21 deletions src/traces/scattergl/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,8 @@ function getSymbolSdf(symbol) {
}

function convertLinePositions(gd, trace, positions) {
var count = positions.length / 2;
var len = positions.length;
var count = len / 2;
var linePositions;
var i;

Expand All @@ -394,38 +395,81 @@ function convertLinePositions(gd, trace, positions) {
linePositions = [];
for(i = 0; i < count - 1; i++) {
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
linePositions.push(NaN);
linePositions.push(NaN);
linePositions.push(NaN);
linePositions.push(NaN);
linePositions.push(NaN, NaN, NaN, NaN);
} else {
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
if(!isNaN(positions[i * 2 + 2]) && !isNaN(positions[i * 2 + 3])) {
linePositions.push(positions[i * 2 + 2], positions[i * 2 + 1]);
} else {
linePositions.push(NaN, NaN);
}
}
}
linePositions.push(positions[len - 2], positions[len - 1]);
} else if(trace.line.shape === 'hvh') {
linePositions = [];
for(i = 0; i < count - 1; i++) {
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1]) || isNaN(positions[i * 2 + 2]) || isNaN(positions[i * 2 + 3])) {
if(!isNaN(positions[i * 2]) && !isNaN(positions[i * 2 + 1])) {
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
} else {
linePositions.push(NaN, NaN);
}
linePositions.push(NaN, NaN);
}
else {
linePositions.push(positions[i * 2]);
linePositions.push(positions[i * 2 + 1]);
linePositions.push(positions[i * 2 + 2]);
linePositions.push(positions[i * 2 + 1]);
var midPtX = (positions[i * 2] + positions[i * 2 + 2]) / 2;
linePositions.push(
positions[i * 2],
positions[i * 2 + 1],
midPtX,
positions[i * 2 + 1],
midPtX,
positions[i * 2 + 3]
);
}
}
linePositions.push(positions[positions.length - 2]);
linePositions.push(positions[positions.length - 1]);
linePositions.push(positions[len - 2], positions[len - 1]);
} else if(trace.line.shape === 'vhv') {
linePositions = [];
for(i = 0; i < count - 1; i++) {
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1]) || isNaN(positions[i * 2 + 2]) || isNaN(positions[i * 2 + 3])) {
if(!isNaN(positions[i * 2]) && !isNaN(positions[i * 2 + 1])) {
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
} else {
linePositions.push(NaN, NaN);
}
linePositions.push(NaN, NaN);
}
else {
var midPtY = (positions[i * 2 + 1] + positions[i * 2 + 3]) / 2;
linePositions.push(
positions[i * 2],
positions[i * 2 + 1],
positions[i * 2],
midPtY,
positions[i * 2 + 2],
midPtY
);
}
}
linePositions.push(positions[len - 2], positions[len - 1]);
} else if(trace.line.shape === 'vh') {
linePositions = [];
for(i = 0; i < count - 1; i++) {
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
linePositions.push(NaN);
linePositions.push(NaN);
linePositions.push(NaN);
linePositions.push(NaN);
linePositions.push(NaN, NaN, NaN, NaN);
}
else {
linePositions.push(positions[i * 2]);
linePositions.push(positions[i * 2 + 1]);
linePositions.push(positions[i * 2]);
linePositions.push(positions[i * 2 + 3]);
linePositions.push(positions[i * 2], positions[i * 2 + 1]);
if(!isNaN(positions[i * 2 + 2]) && !isNaN(positions[i * 2 + 3])) {
linePositions.push(positions[i * 2], positions[i * 2 + 3]);
} else {
linePositions.push(NaN, NaN);
}
}
}
linePositions.push(positions[positions.length - 2]);
linePositions.push(positions[positions.length - 1]);
linePositions.push(positions[len - 2], positions[len - 1]);
} else {
linePositions = positions;
}
Expand Down
1 change: 1 addition & 0 deletions src/traces/scattergl/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
if(subTypes.hasLines(traceOut)) {
coerce('connectgaps');
handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
coerce('line.shape');
}

if(subTypes.hasMarkers(traceOut)) {
Expand Down
119 changes: 82 additions & 37 deletions src/traces/scattergl/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,39 +256,38 @@ function sceneUpdate(gd, subplot) {

// draw traces in proper order
scene.draw = function draw() {
var i;
for(i = 0; i < scene.count; i++) {
if(scene.fill2d && scene.fillOptions[i]) {
// must do all fills first
scene.fill2d.draw(i);
var count = scene.count;
var fill2d = scene.fill2d;
var error2d = scene.error2d;
var line2d = scene.line2d;
var scatter2d = scene.scatter2d;
var glText = scene.glText;
var select2d = scene.select2d;
var selectBatch = scene.selectBatch;
var unselectBatch = scene.unselectBatch;

for(var i = 0; i < count; i++) {
if(fill2d && scene.fillOrder[i]) {
fill2d.draw(scene.fillOrder[i]);
}
}
for(i = 0; i < scene.count; i++) {
if(scene.line2d && scene.lineOptions[i]) {
scene.line2d.draw(i);
if(line2d && scene.lineOptions[i]) {
line2d.draw(i);
}
if(scene.error2d && scene.errorXOptions[i]) {
scene.error2d.draw(i);
if(error2d) {
if(scene.errorXOptions[i]) error2d.draw(i);
if(scene.errorYOptions[i]) error2d.draw(i + count);
}
if(scene.error2d && scene.errorYOptions[i]) {
scene.error2d.draw(i + scene.count);
if(scatter2d && scene.markerOptions[i] && (!selectBatch || !selectBatch[i])) {
scatter2d.draw(i);
}
if(scene.scatter2d && scene.markerOptions[i] && (!scene.selectBatch || !scene.selectBatch[i])) {
// traces in no-selection mode
scene.scatter2d.draw(i);
if(glText[i] && scene.textOptions[i]) {
glText[i].render();
}
}

// draw traces in selection mode
if(scene.scatter2d && scene.select2d && scene.selectBatch) {
scene.select2d.draw(scene.selectBatch);
scene.scatter2d.draw(scene.unselectBatch);
}

for(i = 0; i < scene.count; i++) {
if(scene.glText[i] && scene.textOptions[i]) {
scene.glText[i].render();
}
if(scatter2d && select2d && selectBatch) {
select2d.draw(selectBatch);
scatter2d.draw(unselectBatch);
}

scene.dirty = false;
Expand Down Expand Up @@ -397,6 +396,24 @@ function plot(gd, subplot, cdata) {
}
if(scene.line2d) {
scene.line2d.update(scene.lineOptions);
scene.lineOptions = scene.lineOptions.map(function(lineOptions) {
if(lineOptions && lineOptions.positions) {
var pos = [], srcPos = lineOptions.positions;

var firstptdef = 0;
while(isNaN(srcPos[firstptdef]) || isNaN(srcPos[firstptdef + 1])) {
firstptdef += 2;
}
var lastptdef = srcPos.length - 2;
while(isNaN(srcPos[lastptdef]) || isNaN(srcPos[lastptdef + 1])) {
lastptdef += -2;
}
pos = pos.concat(srcPos.slice(firstptdef, lastptdef + 2));
lineOptions.positions = pos;
}
return lineOptions;
});
scene.line2d.update(scene.lineOptions);
}
if(scene.error2d) {
var errorBatch = (scene.errorXOptions || []).concat(scene.errorYOptions || []);
Expand All @@ -405,7 +422,9 @@ function plot(gd, subplot, cdata) {
if(scene.scatter2d) {
scene.scatter2d.update(scene.markerOptions);
}

// fill requires linked traces, so we generate it's positions here
scene.fillOrder = Lib.repeat(null, scene.count);
if(scene.fill2d) {
scene.fillOptions = scene.fillOptions.map(function(fillOptions, i) {
var cdscatter = cdata[i];
Expand All @@ -416,19 +435,46 @@ function plot(gd, subplot, cdata) {
var lineOptions = scene.lineOptions[i];
var last, j;

var fillData = [];
if(trace._ownfill) fillData.push(i);
if(trace._nexttrace) fillData.push(i + 1);
if(fillData.length) scene.fillOrder[i] = fillData;

var pos = [], srcPos = (lineOptions && lineOptions.positions) || stash.positions;

if(trace.fill === 'tozeroy') {
pos = [srcPos[0], 0];
pos = pos.concat(srcPos);
pos.push(srcPos[srcPos.length - 2]);
pos.push(0);
var firstpdef = 0;
while(isNaN(srcPos[firstpdef + 1])) {
firstpdef += 2;
}
var lastpdef = srcPos.length - 2;
while(isNaN(srcPos[lastpdef + 1])) {
lastpdef += -2;
}
if(srcPos[firstpdef + 1] !== 0) {
pos = [ srcPos[firstpdef], 0 ];
}
pos = pos.concat(srcPos.slice(firstpdef, lastpdef + 2));
if(srcPos[lastpdef + 1] !== 0) {
pos = pos.concat([ srcPos[lastpdef], 0 ]);
}
}
else if(trace.fill === 'tozerox') {
pos = [0, srcPos[1]];
pos = pos.concat(srcPos);
pos.push(0);
pos.push(srcPos[srcPos.length - 1]);
var firstptdef = 0;
while(isNaN(srcPos[firstptdef])) {
firstptdef += 2;
}
var lastptdef = srcPos.length - 2;
while(isNaN(srcPos[lastptdef])) {
lastptdef += -2;
}
if(srcPos[firstptdef] !== 0) {
pos = [ 0, srcPos[firstptdef + 1] ];
}
pos = pos.concat(srcPos.slice(firstptdef, lastptdef + 2));
if(srcPos[lastptdef] !== 0) {
pos = pos.concat([ 0, srcPos[lastptdef + 1]]);
}
}
else if(trace.fill === 'toself' || trace.fill === 'tonext') {
pos = [];
Expand Down Expand Up @@ -459,8 +505,7 @@ function plot(gd, subplot, cdata) {
for(i = Math.floor(nextPos.length / 2); i--;) {
var xx = nextPos[i * 2], yy = nextPos[i * 2 + 1];
if(isNaN(xx) || isNaN(yy)) continue;
pos.push(xx);
pos.push(yy);
pos.push(xx, yy);
}
fillOptions.fill = nextTrace.fillcolor;
}
Expand All @@ -486,7 +531,7 @@ function plot(gd, subplot, cdata) {
pos = pos.concat(prevLinePos);
fillOptions.hole = hole;
}

fillOptions.fillmode = trace.fill;
fillOptions.opacity = trace.opacity;
fillOptions.positions = pos;

Expand Down
Binary file modified test/image/baselines/gl2d_fill-ordering.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/gl2d_order_error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/gl2d_shape_line.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 5a24245

Please sign in to comment.