Skip to content

Commit

Permalink
closes plotly#858
Browse files Browse the repository at this point in the history
Add padding field to heatmap that allows users to define space
between bricks.
  • Loading branch information
Calvin Fernandez committed Aug 17, 2016
1 parent ad88139 commit a508d28
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 3 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"open": "0.0.5",
"prepend-file": "^1.3.0",
"prettysize": "0.0.3",
"sinon": "1.17.5",
"through2": "^2.0.0",
"uglify-js": "^2.6.1",
"watchify": "^3.7.0",
Expand Down
31 changes: 30 additions & 1 deletion src/traces/heatmap/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,36 @@ module.exports = extendFlat({},
'in the `z` data are filled in.'
].join(' ')
},

padding: {
b: {
valType: 'number',
role: 'info',
description: 'Sets the bottom padding (in px).',
dflt: 0,
min: 0
},
l: {
valType: 'number',
role: 'info',
description: 'Sets the left padding (in px).',
dflt: 0,
min: 0
},
r: {
valType: 'number',
role: 'info',
description: 'Sets the right padding (in px).',
dflt: 0,
min: 0
},
t: {
valType: 'number',
role: 'info',
description: 'Sets the right padding (in px).',
dflt: 0,
min: 0
}
},
_nestedModules: {
'colorbar': 'Colorbar'
}
Expand Down
5 changes: 5 additions & 0 deletions src/traces/heatmap/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
return;
}

coerce('padding.b');
coerce('padding.l');
coerce('padding.r');
coerce('padding.t');

coerce('text');
coerce('zsmooth');
coerce('connectgaps', hasColumns(traceOut) && (traceOut.zsmooth !== false));
Expand Down
25 changes: 24 additions & 1 deletion src/traces/heatmap/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,32 @@ function plotOne(gd, plotinfo, cd) {
rcount = 0,
gcount = 0,
bcount = 0,
brickWithPadding,
xb,
j,
xi,
v,
row,
c;

function applyBrickPadding(trace, x0, y0, x1, y1) {
if(trace.padding === null || trace.padding === undefined) {
return {
x0: x0,
y0: y0,
x1: x1,
y1: y1
};
} else {
return {
x0: x0 + trace.padding.l,
y0: y0 + trace.padding.t,
x1: x1 - trace.padding.r,
y1: y1 - trace.padding.b
};
}
}

function setColor(v, pixsize) {
if(v !== undefined) {
var c = s((v - min) / (max - min));
Expand Down Expand Up @@ -364,7 +383,11 @@ function plotOne(gd, plotinfo, cd) {
v = row[i];
c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0]));
context.fillStyle = 'rgba(' + c.join(',') + ')';
context.fillRect(xb[0], yb[0], (xb[1] - xb[0]), (yb[1] - yb[0]));
brickWithPadding = applyBrickPadding(trace, xb[0], yb[0], xb[1], yb[1]);
context.fillRect(brickWithPadding.x0,
brickWithPadding.y0,
(brickWithPadding.x1 - brickWithPadding.x0),
(brickWithPadding.y1 - brickWithPadding.y0));
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions test/image/mocks/heatmap_brick_padding.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"data": [
{
"z": [
[
1,
20,
30
],
[
20,
1,
60
],
[
30,
60,
1
]
],
"padding": {
"t": 5,
"l": 5,
"b": 5,
"r": 5
},
"type": "heatmap"
}
]
}
81 changes: 80 additions & 1 deletion test/jasmine/tests/heatmap_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var convertColumnXYZ = require('@src/traces/heatmap/convert_column_xyz');
var Heatmap = require('@src/traces/heatmap');

var d3 = require('d3');
var sinon = require('sinon');
var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var customMatchers = require('../assets/custom_matchers');
Expand Down Expand Up @@ -89,6 +90,34 @@ describe('heatmap supplyDefaults', function() {
expect(traceOut.visible).toBe(false);
});

it('should set paddings to 0 when not defined', function() {
traceIn = {
type: 'heatmap',
z: [[1, 2], [3, 4]]
};

supplyDefaults(traceIn, traceOut, defaultColor, layout);
expect(traceOut.padding.b).toBe(0);
expect(traceOut.padding.l).toBe(0);
expect(traceOut.padding.r).toBe(0);
expect(traceOut.padding.t).toBe(0);
});

it('should not step on defined paddings', function() {
traceIn = {
padding: {
b: 10
},
type: 'heatmap',
z: [[1, 2], [3, 4]]
};

supplyDefaults(traceIn, traceOut, defaultColor, layout);
expect(traceOut.padding.b).toBe(10);
expect(traceOut.padding.l).toBe(0);
expect(traceOut.padding.r).toBe(0);
expect(traceOut.padding.t).toBe(0);
});
});

describe('heatmap convertColumnXYZ', function() {
Expand Down Expand Up @@ -319,7 +348,16 @@ describe('heatmap calc', function() {
describe('heatmap plot', function() {
'use strict';

afterEach(destroyGraphDiv);
var sandbox;

beforeEach(function() {
sandbox = sinon.sandbox.create();
});

afterEach(function() {
destroyGraphDiv();
sandbox.restore();
});

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

done();
});
});

it('draws canvas with correct margins', function(done) {
var mockWithPadding = require('@mocks/heatmap_brick_padding.json'),
mockWithoutPadding = Lib.extendDeep({}, mockWithPadding),
gd = createGraphDiv(),
getContextStub = {
fillRect: sinon.stub()
},
originalCreateElement = document.createElement,
bottom = mockWithPadding.data[0].padding.b,
left = mockWithPadding.data[0].padding.l,
right = mockWithPadding.data[0].padding.r,
top = mockWithPadding.data[0].padding.t;

mockWithoutPadding.data[0].padding = null;
sandbox.stub(document, 'createElement', function(elementType) {
var element = originalCreateElement.call(document, elementType);
if(elementType === 'canvas') {
sinon.stub(element, 'getContext').returns(getContextStub);
}
return element;
});

var argumentsWithoutPadding = [],
argumentsWithPadding = [],
callCount;
Plotly.plot(gd, mockWithoutPadding.data, mockWithoutPadding.layout).then(function() {
callCount = getContextStub.fillRect.callCount;
argumentsWithoutPadding = getContextStub.fillRect.args.slice(0);
return Plotly.plot(gd, mockWithPadding.data, mockWithPadding.layout);
}).then(function() {
argumentsWithPadding = getContextStub.fillRect.args.slice(getContextStub.fillRect.args.length - callCount);
argumentsWithoutPadding.forEach(function(argumentWithoutPadding, index) {
var argumentWithPadding = argumentsWithPadding[index];
expect(argumentWithoutPadding[0] + left).toEqual(argumentWithPadding[0]);
expect(argumentWithoutPadding[1] + top).toEqual(argumentWithPadding[1]);
expect(argumentWithoutPadding[2] - right - left).toEqual(argumentWithPadding[2]);
expect(argumentWithoutPadding[3] - top - bottom).toEqual(argumentWithPadding[3]);
});
done();
});
});
});

Expand Down

0 comments on commit a508d28

Please sign in to comment.