Skip to content

Commit d57a844

Browse files
authored
Merge pull request #2657 from plotly/react-interp2d
remove cached `trace._interpz` from heatmaps/contour maps with gaps
2 parents 28aed69 + 29bc81e commit d57a844

File tree

5 files changed

+97
-23
lines changed

5 files changed

+97
-23
lines changed

src/traces/contourcarpet/calc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function heatmappishCalc(gd, trace) {
8484
z = trace._z = clean2dArray(trace._z || trace.z, trace.transpose);
8585

8686
trace._emptypoints = findEmpties(z);
87-
trace._interpz = interp2d(z, trace._emptypoints, trace._interpz);
87+
interp2d(z, trace._emptypoints);
8888

8989
// create arrays of brick boundaries, to be used by autorange and heatmap.plot
9090
var xlen = maxRowLength(z),

src/traces/heatmap/calc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ module.exports = function calc(gd, trace) {
7777

7878
if(isContour || trace.connectgaps) {
7979
trace._emptypoints = findEmpties(z);
80-
trace._interpz = interp2d(z, trace._emptypoints, trace._interpz);
80+
interp2d(z, trace._emptypoints);
8181
}
8282
}
8383

src/traces/heatmap/interp2d.js

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,37 @@
1010

1111
var Lib = require('../../lib');
1212

13-
var INTERPTHRESHOLD = 1e-2,
14-
NEIGHBORSHIFTS = [[-1, 0], [1, 0], [0, -1], [0, 1]];
13+
var INTERPTHRESHOLD = 1e-2;
14+
var NEIGHBORSHIFTS = [[-1, 0], [1, 0], [0, -1], [0, 1]];
1515

1616
function correctionOvershoot(maxFractionalChange) {
1717
// start with less overshoot, until we know it's converging,
1818
// then ramp up the overshoot for faster convergence
1919
return 0.5 - 0.25 * Math.min(1, maxFractionalChange * 0.5);
2020
}
2121

22-
module.exports = function interp2d(z, emptyPoints, savedInterpZ) {
23-
// fill in any missing data in 2D array z using an iterative
24-
// poisson equation solver with zero-derivative BC at edges
25-
// amazingly, this just amounts to repeatedly averaging all the existing
26-
// nearest neighbors (at least if we don't take x/y scaling into account)
27-
var maxFractionalChange = 1,
28-
i,
29-
thisPt;
30-
31-
if(Array.isArray(savedInterpZ)) {
32-
for(i = 0; i < emptyPoints.length; i++) {
33-
thisPt = emptyPoints[i];
34-
z[thisPt[0]][thisPt[1]] = savedInterpZ[thisPt[0]][thisPt[1]];
35-
}
36-
}
37-
else {
38-
// one pass to fill in a starting value for all the empties
39-
iterateInterp2d(z, emptyPoints);
40-
}
22+
/*
23+
* interp2d: Fill in missing data from a 2D array using an iterative
24+
* poisson equation solver with zero-derivative BC at edges.
25+
* Amazingly, this just amounts to repeatedly averaging all the existing
26+
* nearest neighbors, at least if we don't take x/y scaling into account,
27+
* which is the right approach here where x and y may not even have the
28+
* same units.
29+
*
30+
* @param {array of arrays} z
31+
* The 2D array to fill in. Will be mutated here. Assumed to already be
32+
* cleaned, so all entries are numbers except gaps, which are `undefined`.
33+
* @param {array of arrays} emptyPoints
34+
* Each entry [i, j, neighborCount] for empty points z[i][j] and the number
35+
* of neighbors that are *not* missing. Assumed to be sorted from most to
36+
* least neighbors, as produced by heatmap/find_empties.
37+
*/
38+
module.exports = function interp2d(z, emptyPoints) {
39+
var maxFractionalChange = 1;
40+
var i;
41+
42+
// one pass to fill in a starting value for all the empties
43+
iterateInterp2d(z, emptyPoints);
4144

4245
// we're don't need to iterate lone empties - remove them
4346
for(i = 0; i < emptyPoints.length; i++) {

test/jasmine/tests/contour_test.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,4 +458,46 @@ describe('contour plotting and editing', function() {
458458
.catch(fail)
459459
.then(done);
460460
});
461+
462+
it('can change z values with gaps', function(done) {
463+
Plotly.newPlot(gd, [{
464+
type: 'contour',
465+
z: [[1, 2], [null, 4], [1, 2]]
466+
}])
467+
.then(function() {
468+
expect(gd.calcdata[0][0].z).toEqual([[1, 2], [2, 4], [1, 2]]);
469+
expect(gd.calcdata[0][0].zmask).toEqual([[1, 1], [0, 1], [1, 1]]);
470+
471+
return Plotly.react(gd, [{
472+
type: 'contour',
473+
z: [[6, 5], [8, 7], [null, 10]]
474+
}]);
475+
})
476+
.then(function() {
477+
expect(gd.calcdata[0][0].z).toEqual([[6, 5], [8, 7], [9, 10]]);
478+
expect(gd.calcdata[0][0].zmask).toEqual([[1, 1], [1, 1], [0, 1]]);
479+
480+
return Plotly.react(gd, [{
481+
type: 'contour',
482+
z: [[1, 2], [null, 4], [1, 2]]
483+
}]);
484+
})
485+
.then(function() {
486+
expect(gd.calcdata[0][0].z).toEqual([[1, 2], [2, 4], [1, 2]]);
487+
expect(gd.calcdata[0][0].zmask).toEqual([[1, 1], [0, 1], [1, 1]]);
488+
489+
return Plotly.react(gd, [{
490+
type: 'contour',
491+
// notice that this one is the same as the previous, except that
492+
// a previously present value was removed...
493+
z: [[1, 2], [null, 4], [1, null]]
494+
}]);
495+
})
496+
.then(function() {
497+
expect(gd.calcdata[0][0].z).toEqual([[1, 2], [2, 4], [1, 2.5]]);
498+
expect(gd.calcdata[0][0].zmask).toEqual([[1, 1], [0, 1], [1, 0]]);
499+
})
500+
.catch(fail)
501+
.then(done);
502+
});
461503
});

test/jasmine/tests/heatmap_test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,35 @@ describe('heatmap plot', function() {
587587
.catch(failTest)
588588
.then(done);
589589
});
590+
591+
it('can change z values with connected gaps', function(done) {
592+
var gd = createGraphDiv();
593+
Plotly.newPlot(gd, [{
594+
type: 'heatmap', connectgaps: true,
595+
z: [[1, 2], [null, 4], [1, 2]]
596+
}])
597+
.then(function() {
598+
expect(gd.calcdata[0][0].z).toEqual([[1, 2], [2, 4], [1, 2]]);
599+
600+
return Plotly.react(gd, [{
601+
type: 'heatmap', connectgaps: true,
602+
z: [[6, 5], [8, 7], [null, 10]]
603+
}]);
604+
})
605+
.then(function() {
606+
expect(gd.calcdata[0][0].z).toEqual([[6, 5], [8, 7], [9, 10]]);
607+
608+
return Plotly.react(gd, [{
609+
type: 'heatmap', connectgaps: true,
610+
z: [[1, 2], [null, 4], [1, 2]]
611+
}]);
612+
})
613+
.then(function() {
614+
expect(gd.calcdata[0][0].z).toEqual([[1, 2], [2, 4], [1, 2]]);
615+
})
616+
.catch(fail)
617+
.then(done);
618+
});
590619
});
591620

592621
describe('heatmap hover', function() {

0 commit comments

Comments
 (0)