diff --git a/src/traces/contour/colorbar.js b/src/traces/contour/colorbar.js index 2c15610dbb5..68be46e0234 100644 --- a/src/traces/contour/colorbar.js +++ b/src/traces/contour/colorbar.js @@ -13,6 +13,7 @@ var Plots = require('../../plots/plots'); var drawColorbar = require('../../components/colorbar/draw'); var makeColorMap = require('./make_color_map'); +var endPlus = require('./end_plus'); module.exports = function colorbar(gd, cd) { @@ -52,7 +53,7 @@ module.exports = function colorbar(gd, cd) { }) .levels({ start: contours.start, - end: contours.end, + end: endPlus(contours), size: cs }) .options(trace.colorbar)(); diff --git a/src/traces/contour/end_plus.js b/src/traces/contour/end_plus.js new file mode 100644 index 00000000000..8b8e9dc3f65 --- /dev/null +++ b/src/traces/contour/end_plus.js @@ -0,0 +1,18 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +/* + * tiny helper to move the end of the contours a little to prevent + * losing the last contour to rounding errors + */ +module.exports = function endPlus(contours) { + return contours.end + contours.size / 1e6; +}; diff --git a/src/traces/contour/find_all_paths.js b/src/traces/contour/find_all_paths.js index 5826932509e..273368094e2 100644 --- a/src/traces/contour/find_all_paths.js +++ b/src/traces/contour/find_all_paths.js @@ -84,13 +84,13 @@ function makePath(pi, loc, edgeflag) { if(equalPts(pts[pts.length - 1], pts[pts.length - 2])) pts.pop(); locStr = loc.join(','); + var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) || + (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2)), + closedLoop = (locStr === startLocStr) && (marchStep.join(',') === startStepStr); + // have we completed a loop, or reached an edge? - if((locStr === startLocStr && marchStep.join(',') === startStepStr) || - (edgeflag && ( - (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) || - (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2))))) { - break; - } + if((closedLoop) || (edgeflag && atEdge)) break; + mi = pi.crossings[locStr]; } diff --git a/src/traces/contour/make_color_map.js b/src/traces/contour/make_color_map.js index 9db97262636..fc54a155705 100644 --- a/src/traces/contour/make_color_map.js +++ b/src/traces/contour/make_color_map.js @@ -11,13 +11,14 @@ var d3 = require('d3'); var Colorscale = require('../../components/colorscale'); +var endPlus = require('./end_plus'); module.exports = function makeColorMap(trace) { var contours = trace.contours, start = contours.start, - end = contours.end, + end = endPlus(contours), cs = contours.size || 1, - nc = Math.floor((end + cs / 10 - start) / cs) + 1, + nc = Math.floor((end - start) / cs) + 1, extra = contours.coloring === 'lines' ? 0 : 1; var scl = trace.colorscale, diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index 2c0830cefd1..e33beda01f1 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -17,6 +17,7 @@ var Drawing = require('../../components/drawing'); var heatmapPlot = require('../heatmap/plot'); var makeCrossings = require('./make_crossings'); var findAllPaths = require('./find_all_paths'); +var endPlus = require('./end_plus'); module.exports = function plot(gd, plotinfo, cdcontours) { @@ -81,9 +82,10 @@ function plotOne(gd, plotinfo, cd) { function emptyPathinfo(contours, plotinfo, cd0) { var cs = contours.size, - pathinfo = []; + pathinfo = [], + end = endPlus(contours); - for(var ci = contours.start; ci < contours.end + cs / 10; ci += cs) { + for(var ci = contours.start; ci < end; ci += cs) { pathinfo.push({ level: ci, // all the cells with nontrivial marching index @@ -163,7 +165,8 @@ function makeFills(plotgroup, pathinfo, perimeter, contours) { } function joinAllPaths(pi, perimeter) { - var fullpath = (pi.edgepaths.length || pi.z[0][0] < pi.level) ? + var edgeVal2 = Math.min(pi.z[0][0], pi.z[0][1]), + fullpath = (pi.edgepaths.length || edgeVal2 <= pi.level) ? '' : ('M' + perimeter.join('L') + 'Z'), i = 0, startsleft = pi.edgepaths.map(function(v, i) { return i; }), diff --git a/src/traces/contour/style.js b/src/traces/contour/style.js index db4042f1961..3ce4e56c64c 100644 --- a/src/traces/contour/style.js +++ b/src/traces/contour/style.js @@ -34,21 +34,26 @@ module.exports = function style(gd) { var colorMap = makeColorMap(trace); - c.selectAll('g.contourlevel').each(function(d, i) { + c.selectAll('g.contourlevel').each(function(d) { d3.select(this).selectAll('path') .call(Drawing.lineGroupStyle, line.width, - contours.coloring === 'lines' ? colorMap(start + i * cs) : line.color, + contours.coloring === 'lines' ? colorMap(d.level) : line.color, line.dash); }); - c.selectAll('g.contourbg path') - .style('fill', colorMap(start - cs / 2)); + var firstFill; c.selectAll('g.contourfill path') - .style('fill', function(d, i) { - return colorMap(start + (i + 0.5) * cs); + .style('fill', function(d) { + if(firstFill === undefined) firstFill = d.level; + return colorMap(d.level + 0.5 * cs); }); + + if(firstFill === undefined) firstFill = start; + + c.selectAll('g.contourbg path') + .style('fill', colorMap(firstFill - 0.5 * cs)); }); heatmapStyle(gd); diff --git a/test/image/baselines/contour_edge_cases.png b/test/image/baselines/contour_edge_cases.png new file mode 100644 index 00000000000..eb13470cd1c Binary files /dev/null and b/test/image/baselines/contour_edge_cases.png differ diff --git a/test/image/mocks/contour_edge_cases.json b/test/image/mocks/contour_edge_cases.json new file mode 100644 index 00000000000..9b56c3ba3f8 --- /dev/null +++ b/test/image/mocks/contour_edge_cases.json @@ -0,0 +1,132 @@ +{ + "data": [ + { + "x": [0, 5], + "y": [0, 4], + "z": [[4, 3], [2, 1]], + "type": "contour", + "contours": { + "start": 1.1, + "end": 4.09, + "size": 1 + }, + "colorbar": {"x": 0.4, "y": 0.9, "len": 0.2} + }, + { + "x": [0, 5], + "y": [0, 4], + "z": [[4, 3], [2, 1]], + "type": "contour", + "contours": { + "start": 1, + "end": 4, + "size": 0.9999999 + }, + "colorbar": {"x": 1, "y": 0.9, "len": 0.2}, + "xaxis": "x2" + }, + { + "z": [[0, 0, 0, 0, 0, 0], + [0, 0, 9, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, -9, 0, 0], + [0, 0, 0, 0, 0, 0]], + "type": "contour", + "contours": { + "start": -0.000001, + "end": 0.000001, + "size": 0.000001 + }, + "colorbar": {"x": 0.4, "y": 0.65, "len": 0.2}, + "yaxis": "y2" + }, + { + "z": [[0, 0, 0, 0, 0, 0], + [0, 0, 9, -9, 0, 0], + [0, 0, 9, -9, 0, 0], + [0, 0, 9, -9, 0, 0], + [0, 0, 0, 0, 0, 0]], + "type": "contour", + "contours": { + "start": -0.000001, + "end": 0.000001, + "size": 0.000001 + }, + "colorbar": {"x": 1, "y": 0.65, "len": 0.2}, + "xaxis": "x2", + "yaxis": "y2" + }, + { + "z": [[0, 0, 0, 0, 0, 0], + [0, 9, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, -9, 0], + [0, 0, 0, 0, 0, 0]], + "type": "contour", + "contours": { + "start": -0.000001, + "end": 0.000001, + "size": 0.000001 + }, + "colorbar": {"x": 0.4, "y": 0.4, "len": 0.2}, + "yaxis": "y3" + }, + { + "z": [[0, 0, 0, 0, 0, 0], + [0, 0, -9, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 9, 0, 0], + [0, 0, 0, 0, 0, 0]], + "type": "contour", + "contours": { + "start": -0.0000005, + "end": 0.000001, + "size": 0.000001 + }, + "colorbar": {"x": 1, "y": 0.4, "len": 0.2}, + "xaxis": "x2", + "yaxis": "y3" + }, + { + "z": [[0, 0, 0, 0, 0, 0], + [0, 9, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, -9, 0], + [0, 0, 0, 0, 0, 0]], + "type": "contour", + "contours": { + "start": -0.0000005, + "end": 0.000001, + "size": 0.000001 + }, + "colorbar": {"x": 0.4, "y": 0.15, "len": 0.2}, + "yaxis": "y4" + }, + { + "z": [[0, 0, 0, 0, 0, 0], + [0, 0, 9, 0, 0, 0], + [0, -9, -9, -9, -9, 0], + [0, 0, 0, 9, 0, 0], + [0, 0, 0, 0, 0, 0]], + "type": "contour", + "contours": { + "start": -0.0000005, + "end": 0.000001, + "size": 0.000001 + }, + "colorbar": {"x": 1, "y": 0.15, "len": 0.2}, + "xaxis": "x2", + "yaxis": "y4" + } + ], + "layout": { + "xaxis": {"domain": [0, 0.4]}, + "xaxis2": {"domain": [0.6, 1]}, + "yaxis": {"domain": [0.8, 1]}, + "yaxis2": {"domain": [0.55, 0.75]}, + "yaxis3": {"domain": [0.3, 0.5]}, + "yaxis4": {"domain": [0.05, 0.25]}, + "width": 600, + "height": 800 + } +}