Skip to content

Fix fill tozero with undefined value and overlaping of fill area #2979

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/traces/scattergl/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,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
61 changes: 59 additions & 2 deletions src/traces/scattergl/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,74 @@ function convertLinePositions(gd, trace, positions) {
if(trace.line.shape === 'hv') {
linePositions = [];
for(i = 0; i < count - 1; i++) {
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
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]);
linePositions.push(positions[i * 2 + 1]);
} else {
linePositions.push(NaN);
linePositions.push(NaN);
}
linePositions.push(NaN);
linePositions.push(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]);
}
}
linePositions.push(positions[positions.length - 2]);
linePositions.push(positions[positions.length - 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]);
linePositions.push(positions[i * 2 + 1]);
} else {
linePositions.push(NaN);
linePositions.push(NaN);
}
linePositions.push(NaN);
linePositions.push(NaN);
}
else {
var midPtX = (positions[i * 2] + positions[i * 2 + 2]) / 2;
linePositions.push(positions[i * 2]);
linePositions.push(positions[i * 2 + 1]);
linePositions.push(positions[i * 2 + 2]);
linePositions.push(midPtX);
linePositions.push(positions[i * 2 + 1]);
linePositions.push(midPtX);
linePositions.push(positions[i * 2 + 3]);
}
}
linePositions.push(positions[positions.length - 2]);
linePositions.push(positions[positions.length - 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]);
linePositions.push(positions[i * 2 + 1]);
} else {
linePositions.push(NaN);
linePositions.push(NaN);
}
linePositions.push(NaN);
linePositions.push(NaN);
}
else {
var midPtY = (positions[i * 2 + 1] + positions[i * 2 + 3]) / 2;
linePositions.push(positions[i * 2]);
linePositions.push(positions[i * 2 + 1]);
linePositions.push(positions[i * 2]);
linePositions.push(midPtY);
linePositions.push(positions[i * 2 + 2]);
linePositions.push(midPtY);
}
}
linePositions.push(positions[positions.length - 2]);
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
122 changes: 100 additions & 22 deletions src/traces/scattergl/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,25 +259,69 @@ function sceneUpdate(gd, subplot) {
// draw traces in proper order
scene.draw = function draw() {
var i;
var we_must_draw_previous_line_marker = 0;
for(i = 0; i < scene.count; i++) {
var we_defer_somes_draw = 0;
if(scene.fill2d && scene.fillOptions[i] && scene.fillOptions[i].fillmode && scene.fillOptions[i].fillmode === 'tonext') {
Copy link
Contributor

@etpinard etpinard Oct 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you got the fill part right 🎉

The logic is a little hard to follow; it would be nice to to ♻️ the logic from the SVG version:

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');
}

by using _nexttrace and _prevtrace which are defined during linkTraces called in scattergl here.

Moreover, looks like the error bars are drawn above the lines, SVG scatter does it the other way around.


Oh well, I'll try out a few things and hopefully that one or two commits will be enough to bring this to the finish line.

Thanks again.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ErwanMAS I made two commits on branch etienne-scattergl-ordering which appear to make things work while reusing the SVG scatter logic, See:

Do they look ok to you? If so, I'll release them along with your work in next week's v1.42.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we_defer_somes_draw = 1;
} else {
if(we_must_draw_previous_line_marker && scene.line2d && scene.lineOptions[i - 1]) {
scene.line2d.draw(i - 1);
}
if(we_must_draw_previous_line_marker && scene.scatter2d && scene.markerOptions[i - 1]) {
scene.scatter2d.draw(i - 1);
}
if(we_must_draw_previous_line_marker && scene.error2d && scene.errorXOptions[i - 1]) {
scene.error2d.draw(i - 1);
}
if(we_must_draw_previous_line_marker && scene.error2d && scene.errorYOptions[i - 1]) {
scene.error2d.draw(i - 1 + scene.count);
}
we_must_draw_previous_line_marker = 0;
}
if(scene.fill2d && scene.fillOptions[i]) {
// must do all fills first
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Looks like this broke the gl2d_scatter_fill_self_next mock:

peek 2018-10-04 17-07

The first two traces have fill: 'tonext' meaning their fills should be behind both the data[0] and data[1] markers. See corresponding SVG structure:

image

available here -> https://rreusser.github.io/plotly-mock-viewer/#scatter_fill_self_next

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i pushed a new version .

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

Expand All @@ -287,12 +331,6 @@ function sceneUpdate(gd, subplot) {
scene.scatter2d.draw(scene.unselectBatch);
}

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

scene.dirty = false;
};

Expand Down Expand Up @@ -419,6 +457,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 @@ -441,16 +497,38 @@ function plot(gd, subplot, cdata) {
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 @@ -508,7 +586,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