Skip to content

Commit 0d1709b

Browse files
committed
fix heatmap gap algo: uniform gaps, all bricks centered on their data
1 parent 2802c79 commit 0d1709b

File tree

3 files changed

+41
-117
lines changed

3 files changed

+41
-117
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
}
-494 Bytes
Loading

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)