Skip to content

Commit d553142

Browse files
committed
fix dynamic heatmap ordering
1 parent f996446 commit d553142

File tree

2 files changed

+80
-57
lines changed

2 files changed

+80
-57
lines changed

src/traces/heatmap/plot.js

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,36 @@ var Registry = require('../../registry');
1616
var Lib = require('../../lib');
1717
var Colorscale = require('../../components/colorscale');
1818
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
19-
var getUidsFromCalcData = require('../../plots/get_data').getUidsFromCalcData;
2019

2120
var maxRowLength = require('./max_row_length');
2221

2322
module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
24-
var uidLookup = getUidsFromCalcData(cdheatmaps);
23+
var heatmaps = heatmapLayer.selectAll('g.hm')
24+
.data(
25+
cdheatmaps.map(function(d) { return d[0]; }),
26+
function(cd) { return cd.trace.uid; }
27+
);
2528

26-
heatmapLayer.selectAll('.hm > image').each(function(d) {
27-
var oldTrace = d.trace || {};
29+
heatmaps.exit().remove();
2830

29-
if(!uidLookup[oldTrace.uid]) {
30-
d3.select(this.parentNode).remove();
31-
}
32-
});
31+
heatmaps.enter().append('g')
32+
.classed('hm', true);
3333

34-
for(var i = 0; i < cdheatmaps.length; i++) {
35-
plotOne(gd, plotinfo, cdheatmaps[i], heatmapLayer);
36-
}
34+
heatmaps.each(function(cd) {
35+
plotOne(gd, plotinfo, cd, d3.select(this));
36+
}).order();
3737
};
3838

39-
function plotOne(gd, plotinfo, cd, heatmapLayer) {
40-
var cd0 = cd[0];
41-
var trace = cd0.trace;
39+
function plotOne(gd, plotinfo, cd, plotGroup) {
40+
var trace = cd.trace;
4241
var xa = plotinfo.xaxis;
4342
var ya = plotinfo.yaxis;
44-
var id = 'hm' + trace.uid;
4543

46-
var z = cd0.z;
47-
var x = cd0.x;
48-
var y = cd0.y;
49-
var xc = cd0.xCenter;
50-
var yc = cd0.yCenter;
44+
var z = cd.z;
45+
var x = cd.x;
46+
var y = cd.y;
47+
var xc = cd.xCenter;
48+
var yc = cd.yCenter;
5149
var isContour = Registry.traceIs(trace, 'contour');
5250
var zsmooth = isContour ? 'best' : trace.zsmooth;
5351

@@ -111,8 +109,8 @@ function plotOne(gd, plotinfo, cd, heatmapLayer) {
111109
if(isContour) {
112110
xc = x;
113111
yc = y;
114-
x = cd0.xfill;
115-
y = cd0.yfill;
112+
x = cd.xfill;
113+
y = cd.yfill;
116114
}
117115

118116
// make an image that goes at most half a screen off either side, to keep
@@ -135,16 +133,11 @@ function plotOne(gd, plotinfo, cd, heatmapLayer) {
135133
// if image is entirely off-screen, don't even draw it
136134
var isOffScreen = (imageWidth <= 0 || imageHeight <= 0);
137135

138-
var plotgroup = heatmapLayer.selectAll('g.hm.' + id)
139-
.data(isOffScreen ? [] : [0]);
140-
141-
plotgroup.enter().append('g')
142-
.classed('hm', true)
143-
.classed(id, true);
144-
145-
plotgroup.exit().remove();
146-
147-
if(isOffScreen) return;
136+
if(isOffScreen) {
137+
var noImage = plotGroup.selectAll('image').data([]);
138+
noImage.exit().remove();
139+
return;
140+
}
148141

149142
// generate image data
150143

@@ -362,8 +355,8 @@ function plotOne(gd, plotinfo, cd, heatmapLayer) {
362355
gd._hmpixcount = (gd._hmpixcount||0) + pixcount;
363356
gd._hmlumcount = (gd._hmlumcount||0) + pixcount * avgColor.getLuminance();
364357

365-
var image3 = plotgroup.selectAll('image')
366-
.data(cd);
358+
var image3 = plotGroup.selectAll('image')
359+
.data([cd]);
367360

368361
image3.enter().append('svg:image').attr({
369362
xmlns: xmlnsNamespaces.svg,
@@ -377,8 +370,6 @@ function plotOne(gd, plotinfo, cd, heatmapLayer) {
377370
y: top,
378371
'xlink:href': canvas.toDataURL('image/png')
379372
});
380-
381-
image3.exit().remove();
382373
}
383374

384375
// get interpolated bin value. Returns {bin0:closest bin, frac:fractional dist to next, bin1:next bin}

test/jasmine/tests/heatmap_test.js

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -479,15 +479,20 @@ describe('heatmap calc', function() {
479479
describe('heatmap plot', function() {
480480
'use strict';
481481

482+
var gd;
483+
484+
beforeEach(function() {
485+
gd = createGraphDiv();
486+
});
487+
482488
afterEach(destroyGraphDiv);
483489

484490
it('should not draw traces that are off-screen', function(done) {
485-
var mock = require('@mocks/heatmap_multi-trace.json'),
486-
mockCopy = Lib.extendDeep({}, mock),
487-
gd = createGraphDiv();
491+
var mock = require('@mocks/heatmap_multi-trace.json');
492+
var mockCopy = Lib.extendDeep({}, mock);
488493

489494
function assertImageCnt(cnt) {
490-
var images = d3.selectAll('.hm').select('image');
495+
var images = d3.selectAll('.hm image');
491496

492497
expect(images.size()).toEqual(cnt);
493498
}
@@ -502,15 +507,44 @@ describe('heatmap plot', function() {
502507
return Plotly.relayout(gd, 'xaxis.autorange', true);
503508
}).then(function() {
504509
assertImageCnt(5);
510+
})
511+
.catch(failTest)
512+
.then(done);
513+
});
505514

506-
done();
507-
});
515+
it('keeps the correct ordering after hide and show', function(done) {
516+
function getIndices() {
517+
var out = [];
518+
d3.selectAll('.hm image').each(function(d) { out.push(d.trace.index); });
519+
return out;
520+
}
521+
522+
Plotly.newPlot(gd, [{
523+
type: 'heatmap',
524+
z: [[1, 2], [3, 4]]
525+
}, {
526+
type: 'heatmap',
527+
z: [[2, 1], [4, 3]],
528+
contours: {coloring: 'lines'}
529+
}])
530+
.then(function() {
531+
expect(getIndices()).toEqual([0, 1]);
532+
return Plotly.restyle(gd, 'visible', false, [0]);
533+
})
534+
.then(function() {
535+
expect(getIndices()).toEqual([1]);
536+
return Plotly.restyle(gd, 'visible', true, [0]);
537+
})
538+
.then(function() {
539+
expect(getIndices()).toEqual([0, 1]);
540+
})
541+
.catch(failTest)
542+
.then(done);
508543
});
509544

510545
it('should be able to restyle', function(done) {
511-
var mock = require('@mocks/13.json'),
512-
mockCopy = Lib.extendDeep({}, mock),
513-
gd = createGraphDiv();
546+
var mock = require('@mocks/13.json');
547+
var mockCopy = Lib.extendDeep({}, mock);
514548

515549
function getImageURL() {
516550
return d3.select('.hm > image').attr('href');
@@ -538,19 +572,18 @@ describe('heatmap plot', function() {
538572
imageURLs.push(getImageURL());
539573

540574
expect(imageURLs[1]).toEqual(imageURLs[3]);
541-
542-
done();
543-
});
575+
})
576+
.catch(failTest)
577+
.then(done);
544578
});
545579

546580
it('draws canvas with correct margins', function(done) {
547-
var mockWithPadding = require('@mocks/heatmap_brick_padding.json'),
548-
mockWithoutPadding = Lib.extendDeep({}, mockWithPadding),
549-
gd = createGraphDiv(),
550-
getContextStub = {
551-
fillRect: jasmine.createSpy()
552-
},
553-
originalCreateElement = document.createElement;
581+
var mockWithPadding = require('@mocks/heatmap_brick_padding.json');
582+
var mockWithoutPadding = Lib.extendDeep({}, mockWithPadding);
583+
var getContextStub = {
584+
fillRect: jasmine.createSpy()
585+
};
586+
var originalCreateElement = document.createElement;
554587

555588
mockWithoutPadding.data[0].xgap = 0;
556589
mockWithoutPadding.data[0].ygap = 0;
@@ -591,7 +624,6 @@ describe('heatmap plot', function() {
591624
});
592625

593626
it('can change z values with connected gaps', function(done) {
594-
var gd = createGraphDiv();
595627
Plotly.newPlot(gd, [{
596628
type: 'heatmap', connectgaps: true,
597629
z: [[1, 2], [null, 4], [1, 2]]
@@ -615,7 +647,7 @@ describe('heatmap plot', function() {
615647
.then(function() {
616648
expect(gd.calcdata[0][0].z).toEqual([[1, 2], [2, 4], [1, 2]]);
617649
})
618-
.catch(fail)
650+
.catch(failTest)
619651
.then(done);
620652
});
621653
});

0 commit comments

Comments
 (0)