Skip to content

Commit a508d28

Browse files
author
Calvin Fernandez
committed
closes #858
Add padding field to heatmap that allows users to define space between bricks.
1 parent ad88139 commit a508d28

File tree

6 files changed

+170
-3
lines changed

6 files changed

+170
-3
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
"open": "0.0.5",
110110
"prepend-file": "^1.3.0",
111111
"prettysize": "0.0.3",
112+
"sinon": "1.17.5",
112113
"through2": "^2.0.0",
113114
"uglify-js": "^2.6.1",
114115
"watchify": "^3.7.0",

src/traces/heatmap/attributes.js

+30-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,36 @@ module.exports = extendFlat({},
7676
'in the `z` data are filled in.'
7777
].join(' ')
7878
},
79-
79+
padding: {
80+
b: {
81+
valType: 'number',
82+
role: 'info',
83+
description: 'Sets the bottom padding (in px).',
84+
dflt: 0,
85+
min: 0
86+
},
87+
l: {
88+
valType: 'number',
89+
role: 'info',
90+
description: 'Sets the left padding (in px).',
91+
dflt: 0,
92+
min: 0
93+
},
94+
r: {
95+
valType: 'number',
96+
role: 'info',
97+
description: 'Sets the right padding (in px).',
98+
dflt: 0,
99+
min: 0
100+
},
101+
t: {
102+
valType: 'number',
103+
role: 'info',
104+
description: 'Sets the right padding (in px).',
105+
dflt: 0,
106+
min: 0
107+
}
108+
},
80109
_nestedModules: {
81110
'colorbar': 'Colorbar'
82111
}

src/traces/heatmap/defaults.js

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
2828
return;
2929
}
3030

31+
coerce('padding.b');
32+
coerce('padding.l');
33+
coerce('padding.r');
34+
coerce('padding.t');
35+
3136
coerce('text');
3237
coerce('zsmooth');
3338
coerce('connectgaps', hasColumns(traceOut) && (traceOut.zsmooth !== false));

src/traces/heatmap/plot.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,32 @@ function plotOne(gd, plotinfo, cd) {
238238
rcount = 0,
239239
gcount = 0,
240240
bcount = 0,
241+
brickWithPadding,
241242
xb,
242243
j,
243244
xi,
244245
v,
245246
row,
246247
c;
247248

249+
function applyBrickPadding(trace, x0, y0, x1, y1) {
250+
if(trace.padding === null || trace.padding === undefined) {
251+
return {
252+
x0: x0,
253+
y0: y0,
254+
x1: x1,
255+
y1: y1
256+
};
257+
} else {
258+
return {
259+
x0: x0 + trace.padding.l,
260+
y0: y0 + trace.padding.t,
261+
x1: x1 - trace.padding.r,
262+
y1: y1 - trace.padding.b
263+
};
264+
}
265+
}
266+
248267
function setColor(v, pixsize) {
249268
if(v !== undefined) {
250269
var c = s((v - min) / (max - min));
@@ -364,7 +383,11 @@ function plotOne(gd, plotinfo, cd) {
364383
v = row[i];
365384
c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0]));
366385
context.fillStyle = 'rgba(' + c.join(',') + ')';
367-
context.fillRect(xb[0], yb[0], (xb[1] - xb[0]), (yb[1] - yb[0]));
386+
brickWithPadding = applyBrickPadding(trace, xb[0], yb[0], xb[1], yb[1]);
387+
context.fillRect(brickWithPadding.x0,
388+
brickWithPadding.y0,
389+
(brickWithPadding.x1 - brickWithPadding.x0),
390+
(brickWithPadding.y1 - brickWithPadding.y0));
368391
}
369392
}
370393
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"data": [
3+
{
4+
"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+
]
20+
],
21+
"padding": {
22+
"t": 5,
23+
"l": 5,
24+
"b": 5,
25+
"r": 5
26+
},
27+
"type": "heatmap"
28+
}
29+
]
30+
}

test/jasmine/tests/heatmap_test.js

+80-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var convertColumnXYZ = require('@src/traces/heatmap/convert_column_xyz');
66
var Heatmap = require('@src/traces/heatmap');
77

88
var d3 = require('d3');
9+
var sinon = require('sinon');
910
var createGraphDiv = require('../assets/create_graph_div');
1011
var destroyGraphDiv = require('../assets/destroy_graph_div');
1112
var customMatchers = require('../assets/custom_matchers');
@@ -89,6 +90,34 @@ describe('heatmap supplyDefaults', function() {
8990
expect(traceOut.visible).toBe(false);
9091
});
9192

93+
it('should set paddings to 0 when not defined', function() {
94+
traceIn = {
95+
type: 'heatmap',
96+
z: [[1, 2], [3, 4]]
97+
};
98+
99+
supplyDefaults(traceIn, traceOut, defaultColor, layout);
100+
expect(traceOut.padding.b).toBe(0);
101+
expect(traceOut.padding.l).toBe(0);
102+
expect(traceOut.padding.r).toBe(0);
103+
expect(traceOut.padding.t).toBe(0);
104+
});
105+
106+
it('should not step on defined paddings', function() {
107+
traceIn = {
108+
padding: {
109+
b: 10
110+
},
111+
type: 'heatmap',
112+
z: [[1, 2], [3, 4]]
113+
};
114+
115+
supplyDefaults(traceIn, traceOut, defaultColor, layout);
116+
expect(traceOut.padding.b).toBe(10);
117+
expect(traceOut.padding.l).toBe(0);
118+
expect(traceOut.padding.r).toBe(0);
119+
expect(traceOut.padding.t).toBe(0);
120+
});
92121
});
93122

94123
describe('heatmap convertColumnXYZ', function() {
@@ -319,7 +348,16 @@ describe('heatmap calc', function() {
319348
describe('heatmap plot', function() {
320349
'use strict';
321350

322-
afterEach(destroyGraphDiv);
351+
var sandbox;
352+
353+
beforeEach(function() {
354+
sandbox = sinon.sandbox.create();
355+
});
356+
357+
afterEach(function() {
358+
destroyGraphDiv();
359+
sandbox.restore();
360+
});
323361

324362
it('should not draw traces that are off-screen', function(done) {
325363
var mock = require('@mocks/heatmap_multi-trace.json'),
@@ -381,7 +419,48 @@ describe('heatmap plot', function() {
381419

382420
done();
383421
});
422+
});
423+
424+
it('draws canvas with correct margins', function(done) {
425+
var mockWithPadding = require('@mocks/heatmap_brick_padding.json'),
426+
mockWithoutPadding = Lib.extendDeep({}, mockWithPadding),
427+
gd = createGraphDiv(),
428+
getContextStub = {
429+
fillRect: sinon.stub()
430+
},
431+
originalCreateElement = document.createElement,
432+
bottom = mockWithPadding.data[0].padding.b,
433+
left = mockWithPadding.data[0].padding.l,
434+
right = mockWithPadding.data[0].padding.r,
435+
top = mockWithPadding.data[0].padding.t;
436+
437+
mockWithoutPadding.data[0].padding = null;
438+
sandbox.stub(document, 'createElement', function(elementType) {
439+
var element = originalCreateElement.call(document, elementType);
440+
if(elementType === 'canvas') {
441+
sinon.stub(element, 'getContext').returns(getContextStub);
442+
}
443+
return element;
444+
});
384445

446+
var argumentsWithoutPadding = [],
447+
argumentsWithPadding = [],
448+
callCount;
449+
Plotly.plot(gd, mockWithoutPadding.data, mockWithoutPadding.layout).then(function() {
450+
callCount = getContextStub.fillRect.callCount;
451+
argumentsWithoutPadding = getContextStub.fillRect.args.slice(0);
452+
return Plotly.plot(gd, mockWithPadding.data, mockWithPadding.layout);
453+
}).then(function() {
454+
argumentsWithPadding = getContextStub.fillRect.args.slice(getContextStub.fillRect.args.length - callCount);
455+
argumentsWithoutPadding.forEach(function(argumentWithoutPadding, index) {
456+
var argumentWithPadding = argumentsWithPadding[index];
457+
expect(argumentWithoutPadding[0] + left).toEqual(argumentWithPadding[0]);
458+
expect(argumentWithoutPadding[1] + top).toEqual(argumentWithPadding[1]);
459+
expect(argumentWithoutPadding[2] - right - left).toEqual(argumentWithPadding[2]);
460+
expect(argumentWithoutPadding[3] - top - bottom).toEqual(argumentWithPadding[3]);
461+
});
462+
done();
463+
});
385464
});
386465
});
387466

0 commit comments

Comments
 (0)