Skip to content

Commit 521bd55

Browse files
authored
Merge pull request #2213 from plotly/heatmap-equal-gaps
Heatmap equal gaps
2 parents 49307f8 + 6f90335 commit 521bd55

File tree

5 files changed

+73
-141
lines changed

5 files changed

+73
-141
lines changed

src/traces/heatmap/plot.js

+21-71
Original file line numberDiff line numberDiff line change
@@ -226,63 +226,17 @@ function plotOne(gd, plotinfo, cd) {
226226
// build the pixel map brick-by-brick
227227
// cruise through z-matrix row-by-row
228228
// build a brick at each z-matrix value
229-
var yi = ypx(0),
230-
yb = [yi, yi],
231-
xbi = xrev ? 0 : 1,
232-
ybi = yrev ? 0 : 1,
233-
// for collecting an average luminosity of the heatmap
234-
pixcount = 0,
235-
rcount = 0,
236-
gcount = 0,
237-
bcount = 0,
238-
brickWithPadding,
239-
xb,
240-
j,
241-
xi,
242-
v,
243-
row,
244-
c;
245-
246-
function applyBrickPadding(trace, x0, x1, y0, y1, xIndex, xLength, yIndex, yLength) {
247-
var padding = {
248-
x0: x0,
249-
x1: x1,
250-
y0: y0,
251-
y1: y1
252-
},
253-
xEdgeGap = trace.xgap * 2 / 3,
254-
yEdgeGap = trace.ygap * 2 / 3,
255-
xCenterGap = trace.xgap / 3,
256-
yCenterGap = trace.ygap / 3;
257-
258-
if(yIndex === yLength - 1) { // top edge brick
259-
padding.y1 = y1 - yEdgeGap;
260-
}
261-
262-
if(xIndex === xLength - 1) { // right edge brick
263-
padding.x0 = x0 + xEdgeGap;
264-
}
265-
266-
if(yIndex === 0) { // bottom edge brick
267-
padding.y0 = y0 + yEdgeGap;
268-
}
269-
270-
if(xIndex === 0) { // left edge brick
271-
padding.x1 = x1 - xEdgeGap;
272-
}
273-
274-
if(xIndex > 0 && xIndex < xLength - 1) { // brick in the center along x
275-
padding.x0 = x0 + xCenterGap;
276-
padding.x1 = x1 - xCenterGap;
277-
}
278-
279-
if(yIndex > 0 && yIndex < yLength - 1) { // brick in the center along y
280-
padding.y0 = y0 + yCenterGap;
281-
padding.y1 = y1 - yCenterGap;
282-
}
283-
284-
return padding;
285-
}
229+
var yi = ypx(0);
230+
var yb = [yi, yi];
231+
var xbi = xrev ? 0 : 1;
232+
var ybi = yrev ? 0 : 1;
233+
// for collecting an average luminosity of the heatmap
234+
var pixcount = 0;
235+
var rcount = 0;
236+
var gcount = 0;
237+
var bcount = 0;
238+
239+
var xb, j, xi, v, row, c;
286240

287241
function setColor(v, pixsize) {
288242
if(v !== undefined) {
@@ -401,6 +355,14 @@ function plotOne(gd, plotinfo, cd) {
401355

402356
context.putImageData(imageData, 0, 0);
403357
} else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect
358+
359+
// gaps do not need to be exact integers, but if they *are* we will get
360+
// cleaner edges by rounding at least one edge
361+
var xGap = trace.xgap;
362+
var yGap = trace.ygap;
363+
var xGapLeft = Math.floor(xGap / 2);
364+
var yGapTop = Math.floor(yGap / 2);
365+
404366
for(j = 0; j < m; j++) {
405367
row = z[j];
406368
yb.reverse();
@@ -421,20 +383,8 @@ function plotOne(gd, plotinfo, cd) {
421383
c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0]));
422384
context.fillStyle = 'rgba(' + c.join(',') + ')';
423385

424-
brickWithPadding = applyBrickPadding(trace,
425-
xb[0],
426-
xb[1],
427-
yb[0],
428-
yb[1],
429-
i,
430-
n,
431-
j,
432-
m);
433-
434-
context.fillRect(brickWithPadding.x0,
435-
brickWithPadding.y0,
436-
(brickWithPadding.x1 - brickWithPadding.x0),
437-
(brickWithPadding.y1 - brickWithPadding.y0));
386+
context.fillRect(xb[0] + xGapLeft, yb[0] + yGapTop,
387+
xb[1] - xb[0] - xGap, yb[1] - yb[0] - yGap);
438388
}
439389
}
440390
}
-10.7 KB
Loading

test/image/mocks/heatmap_brick_padding.json

+16-18
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,23 @@
22
"data": [
33
{
44
"z": [
5-
[
6-
1,
7-
20,
8-
30
9-
],
10-
[
11-
20,
12-
1,
13-
60
14-
],
15-
[
16-
30,
17-
60,
18-
1
19-
]
5+
[1, 2, 3, 4, 5],
6+
[3, 4, 5, 6, 7],
7+
[5, 6, 7, 8, 9],
8+
[7, 8, 9, 8, 7],
9+
[9, 8, 7, 6, 5]
2010
],
21-
"xgap": 9,
22-
"ygap": 6,
11+
"xgap": 20,
12+
"ygap": 12,
13+
"x0": 0.5,
14+
"y0": 0.5,
2315
"type": "heatmap"
2416
}
25-
]
17+
],
18+
"layout": {
19+
"width": 400,
20+
"height": 400,
21+
"xaxis": {"zeroline": false, "showline": true, "mirror": true, "dtick": 1},
22+
"yaxis": {"zeroline": false, "showline": true, "mirror": true, "dtick": 1}
23+
}
2624
}

test/jasmine/bundle_tests/choropleth_test.js

+16-6
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,34 @@ var PlotlyChoropleth = require('@lib/choropleth');
55

66
var createGraphDiv = require('../assets/create_graph_div');
77
var destroyGraphDiv = require('../assets/destroy_graph_div');
8+
var failTest = require('../assets/fail_test');
9+
10+
var LONG_TIMEOUT_INTERVAL = 5 * jasmine.DEFAULT_TIMEOUT_INTERVAL;
811

912

1013
describe('Bundle with choropleth', function() {
1114
'use strict';
1215

1316
Plotly.register(PlotlyChoropleth);
1417

18+
var gd;
19+
1520
var mock = require('@mocks/geo_multiple-usa-choropleths.json');
1621

17-
beforeEach(function(done) {
18-
Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(done);
22+
beforeEach(function() {
23+
gd = createGraphDiv();
1924
});
2025

2126
afterEach(destroyGraphDiv);
2227

23-
it('should graph choropleth traces', function() {
24-
var nodes = d3.selectAll('g.trace.choropleth');
28+
it('should graph choropleth traces', function(done) {
29+
Plotly.plot(gd, mock.data, mock.layout)
30+
.then(function() {
31+
var nodes = d3.selectAll('g.trace.choropleth');
2532

26-
expect(nodes.size()).toEqual(4);
27-
});
33+
expect(nodes.size()).toEqual(4);
34+
})
35+
.catch(failTest)
36+
.then(done);
37+
}, LONG_TIMEOUT_INTERVAL);
2838
});

test/jasmine/tests/heatmap_test.js

+20-46
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var d3 = require('d3');
1010
var createGraphDiv = require('../assets/create_graph_div');
1111
var destroyGraphDiv = require('../assets/destroy_graph_div');
1212
var supplyAllDefaults = require('../assets/supply_defaults');
13+
var failTest = require('../assets/fail_test');
1314

1415

1516
describe('heatmap supplyDefaults', function() {
@@ -542,52 +543,25 @@ describe('heatmap plot', function() {
542543
argumentsWithoutPadding = getContextStub.fillRect.calls.allArgs().slice(0);
543544
return Plotly.plot(gd, mockWithPadding.data, mockWithPadding.layout);
544545
}).then(function() {
545-
var centerXGap = mockWithPadding.data[0].xgap / 3;
546-
var centerYGap = mockWithPadding.data[0].ygap / 3;
547-
var edgeXGap = mockWithPadding.data[0].xgap * 2 / 3;
548-
var edgeYGap = mockWithPadding.data[0].ygap * 2 / 3;
549-
550-
argumentsWithPadding = getContextStub.fillRect.calls.allArgs().slice(getContextStub.fillRect.calls.allArgs().length - 9);
551-
expect(argumentsWithPadding).toEqual([
552-
[argumentsWithoutPadding[0][0],
553-
argumentsWithoutPadding[0][1] + edgeYGap,
554-
argumentsWithoutPadding[0][2] - edgeXGap,
555-
argumentsWithoutPadding[0][3] - edgeYGap],
556-
[argumentsWithoutPadding[1][0] + centerXGap,
557-
argumentsWithoutPadding[1][1] + edgeYGap,
558-
argumentsWithoutPadding[1][2] - edgeXGap,
559-
argumentsWithoutPadding[1][3] - edgeYGap],
560-
[argumentsWithoutPadding[2][0] + edgeXGap,
561-
argumentsWithoutPadding[2][1] + edgeYGap,
562-
argumentsWithoutPadding[2][2] - edgeXGap,
563-
argumentsWithoutPadding[2][3] - edgeYGap],
564-
[argumentsWithoutPadding[3][0],
565-
argumentsWithoutPadding[3][1] + centerYGap,
566-
argumentsWithoutPadding[3][2] - edgeXGap,
567-
argumentsWithoutPadding[3][3] - edgeYGap],
568-
[argumentsWithoutPadding[4][0] + centerXGap,
569-
argumentsWithoutPadding[4][1] + centerYGap,
570-
argumentsWithoutPadding[4][2] - edgeXGap,
571-
argumentsWithoutPadding[4][3] - edgeYGap],
572-
[argumentsWithoutPadding[5][0] + edgeXGap,
573-
argumentsWithoutPadding[5][1] + centerYGap,
574-
argumentsWithoutPadding[5][2] - edgeXGap,
575-
argumentsWithoutPadding[5][3] - edgeYGap],
576-
[argumentsWithoutPadding[6][0],
577-
argumentsWithoutPadding[6][1],
578-
argumentsWithoutPadding[6][2] - edgeXGap,
579-
argumentsWithoutPadding[6][3] - edgeYGap],
580-
[argumentsWithoutPadding[7][0] + centerXGap,
581-
argumentsWithoutPadding[7][1],
582-
argumentsWithoutPadding[7][2] - edgeXGap,
583-
argumentsWithoutPadding[7][3] - edgeYGap],
584-
[argumentsWithoutPadding[8][0] + edgeXGap,
585-
argumentsWithoutPadding[8][1],
586-
argumentsWithoutPadding[8][2] - edgeXGap,
587-
argumentsWithoutPadding[8][3] - edgeYGap
588-
]]);
589-
done();
590-
});
546+
var xGap = mockWithPadding.data[0].xgap;
547+
var yGap = mockWithPadding.data[0].ygap;
548+
var xGapLeft = xGap / 2;
549+
var yGapTop = yGap / 2;
550+
551+
argumentsWithPadding = getContextStub.fillRect.calls.allArgs()
552+
.slice(getContextStub.fillRect.calls.allArgs().length - 25);
553+
554+
expect(argumentsWithPadding.length).toBe(25);
555+
556+
argumentsWithPadding.forEach(function(args, i) {
557+
expect(args[0]).toBe(argumentsWithoutPadding[i][0] + xGapLeft, i);
558+
expect(args[1]).toBe(argumentsWithoutPadding[i][1] + yGapTop, i);
559+
expect(args[2]).toBe(argumentsWithoutPadding[i][2] - xGap, i);
560+
expect(args[3]).toBe(argumentsWithoutPadding[i][3] - yGap, i);
561+
});
562+
})
563+
.catch(failTest)
564+
.then(done);
591565
});
592566
});
593567

0 commit comments

Comments
 (0)