diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index 48bc073f14d..beb37e90637 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -226,63 +226,17 @@ function plotOne(gd, plotinfo, cd) { // build the pixel map brick-by-brick // cruise through z-matrix row-by-row // build a brick at each z-matrix value - var yi = ypx(0), - yb = [yi, yi], - xbi = xrev ? 0 : 1, - ybi = yrev ? 0 : 1, - // for collecting an average luminosity of the heatmap - pixcount = 0, - rcount = 0, - gcount = 0, - bcount = 0, - brickWithPadding, - xb, - j, - xi, - v, - row, - c; - - function applyBrickPadding(trace, x0, x1, y0, y1, xIndex, xLength, yIndex, yLength) { - var padding = { - x0: x0, - x1: x1, - y0: y0, - y1: y1 - }, - xEdgeGap = trace.xgap * 2 / 3, - yEdgeGap = trace.ygap * 2 / 3, - xCenterGap = trace.xgap / 3, - yCenterGap = trace.ygap / 3; - - if(yIndex === yLength - 1) { // top edge brick - padding.y1 = y1 - yEdgeGap; - } - - if(xIndex === xLength - 1) { // right edge brick - padding.x0 = x0 + xEdgeGap; - } - - if(yIndex === 0) { // bottom edge brick - padding.y0 = y0 + yEdgeGap; - } - - if(xIndex === 0) { // left edge brick - padding.x1 = x1 - xEdgeGap; - } - - if(xIndex > 0 && xIndex < xLength - 1) { // brick in the center along x - padding.x0 = x0 + xCenterGap; - padding.x1 = x1 - xCenterGap; - } - - if(yIndex > 0 && yIndex < yLength - 1) { // brick in the center along y - padding.y0 = y0 + yCenterGap; - padding.y1 = y1 - yCenterGap; - } - - return padding; - } + var yi = ypx(0); + var yb = [yi, yi]; + var xbi = xrev ? 0 : 1; + var ybi = yrev ? 0 : 1; + // for collecting an average luminosity of the heatmap + var pixcount = 0; + var rcount = 0; + var gcount = 0; + var bcount = 0; + + var xb, j, xi, v, row, c; function setColor(v, pixsize) { if(v !== undefined) { @@ -401,6 +355,14 @@ function plotOne(gd, plotinfo, cd) { context.putImageData(imageData, 0, 0); } else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect + + // gaps do not need to be exact integers, but if they *are* we will get + // cleaner edges by rounding at least one edge + var xGap = trace.xgap; + var yGap = trace.ygap; + var xGapLeft = Math.floor(xGap / 2); + var yGapTop = Math.floor(yGap / 2); + for(j = 0; j < m; j++) { row = z[j]; yb.reverse(); @@ -421,20 +383,8 @@ function plotOne(gd, plotinfo, cd) { c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0])); context.fillStyle = 'rgba(' + c.join(',') + ')'; - brickWithPadding = applyBrickPadding(trace, - xb[0], - xb[1], - yb[0], - yb[1], - i, - n, - j, - m); - - context.fillRect(brickWithPadding.x0, - brickWithPadding.y0, - (brickWithPadding.x1 - brickWithPadding.x0), - (brickWithPadding.y1 - brickWithPadding.y0)); + context.fillRect(xb[0] + xGapLeft, yb[0] + yGapTop, + xb[1] - xb[0] - xGap, yb[1] - yb[0] - yGap); } } } diff --git a/test/image/baselines/heatmap_brick_padding.png b/test/image/baselines/heatmap_brick_padding.png index 150525879a0..d7e9f6e1549 100644 Binary files a/test/image/baselines/heatmap_brick_padding.png and b/test/image/baselines/heatmap_brick_padding.png differ diff --git a/test/image/mocks/heatmap_brick_padding.json b/test/image/mocks/heatmap_brick_padding.json index 10cd480c5d6..bfad761d6f1 100644 --- a/test/image/mocks/heatmap_brick_padding.json +++ b/test/image/mocks/heatmap_brick_padding.json @@ -2,25 +2,23 @@ "data": [ { "z": [ - [ - 1, - 20, - 30 - ], - [ - 20, - 1, - 60 - ], - [ - 30, - 60, - 1 - ] + [1, 2, 3, 4, 5], + [3, 4, 5, 6, 7], + [5, 6, 7, 8, 9], + [7, 8, 9, 8, 7], + [9, 8, 7, 6, 5] ], - "xgap": 9, - "ygap": 6, + "xgap": 20, + "ygap": 12, + "x0": 0.5, + "y0": 0.5, "type": "heatmap" } - ] + ], + "layout": { + "width": 400, + "height": 400, + "xaxis": {"zeroline": false, "showline": true, "mirror": true, "dtick": 1}, + "yaxis": {"zeroline": false, "showline": true, "mirror": true, "dtick": 1} + } } diff --git a/test/jasmine/bundle_tests/choropleth_test.js b/test/jasmine/bundle_tests/choropleth_test.js index f79094d6607..90e5f8880c2 100644 --- a/test/jasmine/bundle_tests/choropleth_test.js +++ b/test/jasmine/bundle_tests/choropleth_test.js @@ -5,6 +5,9 @@ var PlotlyChoropleth = require('@lib/choropleth'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var failTest = require('../assets/fail_test'); + +var LONG_TIMEOUT_INTERVAL = 5 * jasmine.DEFAULT_TIMEOUT_INTERVAL; describe('Bundle with choropleth', function() { @@ -12,17 +15,24 @@ describe('Bundle with choropleth', function() { Plotly.register(PlotlyChoropleth); + var gd; + var mock = require('@mocks/geo_multiple-usa-choropleths.json'); - beforeEach(function(done) { - Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done); + beforeEach(function() { + gd = createGraphDiv(); }); afterEach(destroyGraphDiv); - it('should graph choropleth traces', function() { - var nodes = d3.selectAll('g.trace.choropleth'); + it('should graph choropleth traces', function(done) { + Plotly.plot(gd, mock.data, mock.layout) + .then(function() { + var nodes = d3.selectAll('g.trace.choropleth'); - expect(nodes.size()).toEqual(4); - }); + expect(nodes.size()).toEqual(4); + }) + .catch(failTest) + .then(done); + }, LONG_TIMEOUT_INTERVAL); }); diff --git a/test/jasmine/tests/heatmap_test.js b/test/jasmine/tests/heatmap_test.js index 70c86e4e816..a102fd9b857 100644 --- a/test/jasmine/tests/heatmap_test.js +++ b/test/jasmine/tests/heatmap_test.js @@ -10,6 +10,7 @@ var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var supplyAllDefaults = require('../assets/supply_defaults'); +var failTest = require('../assets/fail_test'); describe('heatmap supplyDefaults', function() { @@ -542,52 +543,25 @@ describe('heatmap plot', function() { argumentsWithoutPadding = getContextStub.fillRect.calls.allArgs().slice(0); return Plotly.plot(gd, mockWithPadding.data, mockWithPadding.layout); }).then(function() { - var centerXGap = mockWithPadding.data[0].xgap / 3; - var centerYGap = mockWithPadding.data[0].ygap / 3; - var edgeXGap = mockWithPadding.data[0].xgap * 2 / 3; - var edgeYGap = mockWithPadding.data[0].ygap * 2 / 3; - - argumentsWithPadding = getContextStub.fillRect.calls.allArgs().slice(getContextStub.fillRect.calls.allArgs().length - 9); - expect(argumentsWithPadding).toEqual([ - [argumentsWithoutPadding[0][0], - argumentsWithoutPadding[0][1] + edgeYGap, - argumentsWithoutPadding[0][2] - edgeXGap, - argumentsWithoutPadding[0][3] - edgeYGap], - [argumentsWithoutPadding[1][0] + centerXGap, - argumentsWithoutPadding[1][1] + edgeYGap, - argumentsWithoutPadding[1][2] - edgeXGap, - argumentsWithoutPadding[1][3] - edgeYGap], - [argumentsWithoutPadding[2][0] + edgeXGap, - argumentsWithoutPadding[2][1] + edgeYGap, - argumentsWithoutPadding[2][2] - edgeXGap, - argumentsWithoutPadding[2][3] - edgeYGap], - [argumentsWithoutPadding[3][0], - argumentsWithoutPadding[3][1] + centerYGap, - argumentsWithoutPadding[3][2] - edgeXGap, - argumentsWithoutPadding[3][3] - edgeYGap], - [argumentsWithoutPadding[4][0] + centerXGap, - argumentsWithoutPadding[4][1] + centerYGap, - argumentsWithoutPadding[4][2] - edgeXGap, - argumentsWithoutPadding[4][3] - edgeYGap], - [argumentsWithoutPadding[5][0] + edgeXGap, - argumentsWithoutPadding[5][1] + centerYGap, - argumentsWithoutPadding[5][2] - edgeXGap, - argumentsWithoutPadding[5][3] - edgeYGap], - [argumentsWithoutPadding[6][0], - argumentsWithoutPadding[6][1], - argumentsWithoutPadding[6][2] - edgeXGap, - argumentsWithoutPadding[6][3] - edgeYGap], - [argumentsWithoutPadding[7][0] + centerXGap, - argumentsWithoutPadding[7][1], - argumentsWithoutPadding[7][2] - edgeXGap, - argumentsWithoutPadding[7][3] - edgeYGap], - [argumentsWithoutPadding[8][0] + edgeXGap, - argumentsWithoutPadding[8][1], - argumentsWithoutPadding[8][2] - edgeXGap, - argumentsWithoutPadding[8][3] - edgeYGap - ]]); - done(); - }); + var xGap = mockWithPadding.data[0].xgap; + var yGap = mockWithPadding.data[0].ygap; + var xGapLeft = xGap / 2; + var yGapTop = yGap / 2; + + argumentsWithPadding = getContextStub.fillRect.calls.allArgs() + .slice(getContextStub.fillRect.calls.allArgs().length - 25); + + expect(argumentsWithPadding.length).toBe(25); + + argumentsWithPadding.forEach(function(args, i) { + expect(args[0]).toBe(argumentsWithoutPadding[i][0] + xGapLeft, i); + expect(args[1]).toBe(argumentsWithoutPadding[i][1] + yGapTop, i); + expect(args[2]).toBe(argumentsWithoutPadding[i][2] - xGap, i); + expect(args[3]).toBe(argumentsWithoutPadding[i][3] - yGap, i); + }); + }) + .catch(failTest) + .then(done); }); });