From 98ec02ac243902c406df98539ff6b7f6563db42f Mon Sep 17 00:00:00 2001 From: Davis Rens <166565828+davrens101@users.noreply.github.com> Date: Sun, 1 Nov 2026 18:38:43 -0800 Subject: [PATCH 1/2] Prevent double-precision underflow in findEmpties for deep empty regions (#8108) --- draftlogs/pending_fix.md | 1 + src/traces/heatmap/find_empties.js | 12 +++++++++--- test/jasmine/tests/heatmap_test.js | 27 +++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 draftlogs/pending_fix.md diff --git a/draftlogs/pending_fix.md b/draftlogs/pending_fix.md new file mode 100644 index 00000000000..57c73b28cb0 --- /dev/null +++ b/draftlogs/pending_fix.md @@ -0,0 +1 @@ +- Prevent double-precision underflow in `findEmpties` for deep empty (NaN) regions [[#8108](https://github.com/plotly/plotly.js/issues/8108)] diff --git a/src/traces/heatmap/find_empties.js b/src/traces/heatmap/find_empties.js index 88c75894fcd..406759f3fc2 100644 --- a/src/traces/heatmap/find_empties.js +++ b/src/traces/heatmap/find_empties.js @@ -23,6 +23,7 @@ module.exports = function findEmpties(z) { var thisPt; var p; var neighborCount; + var neighborSum; var newNeighborHash; var foundNewNeighbors; @@ -67,12 +68,17 @@ module.exports = function findEmpties(z) { i = thisPt[0]; j = thisPt[1]; - neighborCount = ((neighborHash[[i - 1, j]] || blank)[2] + + neighborSum = (neighborHash[[i - 1, j]] || blank)[2] + (neighborHash[[i + 1, j]] || blank)[2] + (neighborHash[[i, j - 1]] || blank)[2] + - (neighborHash[[i, j + 1]] || blank)[2]) / 20; + (neighborHash[[i, j + 1]] || blank)[2]; + + if(neighborSum) { + neighborCount = neighborSum / 20; + // neighborSum / 20 can underflow to 0 in float64 when deep empty regions decay + // past ~249 layers, but this cell does have neighbours: keep a strictly positive weight + if(!neighborCount) neighborCount = Number.MIN_VALUE; - if(neighborCount) { newNeighborHash[thisPt] = [i, j, neighborCount]; noNeighborList.splice(p, 1); foundNewNeighbors = true; diff --git a/test/jasmine/tests/heatmap_test.js b/test/jasmine/tests/heatmap_test.js index ceb527f4132..557549629ff 100644 --- a/test/jasmine/tests/heatmap_test.js +++ b/test/jasmine/tests/heatmap_test.js @@ -4,6 +4,7 @@ var Lib = require('../../../src/lib'); var setConvert = require('../../../src/plots/cartesian/set_convert'); var convertColumnXYZ = require('../../../src/traces/heatmap/convert_column_xyz'); +var findEmpties = require('../../../src/traces/heatmap/find_empties'); var Heatmap = require('../../../src/traces/heatmap'); var d3Select = require('../../strict-d3').select; @@ -1115,3 +1116,29 @@ describe('heatmap hover', function() { }); }); }); + +describe('findEmpties', function() { + 'use strict'; + + it('should fill deep empty regions without floating-point underflow (#8108)', function() { + // 5 rows, column 0 is defined, columns 1..250 are undefined (empty). + // Depth of 250 causes geometric weight decay (1/20)^249 to underflow double precision. + var z = []; + var r; + var i; + var j; + var nanCount = 250; + for(i = 0; i < 5; i++) { + r = [1]; + for(j = 0; j < nanCount; j++) r.push(undefined); + z.push(r); + } + + var empties; + expect(function() { + empties = findEmpties(z); + }).not.toThrow(); + + expect(empties.length).toBe(5 * nanCount); + }); +}); From 7374ddd386442132122df271e8b330e5ad788ae5 Mon Sep 17 00:00:00 2001 From: Davis Rens <166565828+davrens101@users.noreply.github.com> Date: Sun, 1 Nov 2026 18:39:53 -0800 Subject: [PATCH 2/2] Link PR #8162 in draftlog --- draftlogs/8162_fix.md | 1 + draftlogs/pending_fix.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 draftlogs/8162_fix.md delete mode 100644 draftlogs/pending_fix.md diff --git a/draftlogs/8162_fix.md b/draftlogs/8162_fix.md new file mode 100644 index 00000000000..533a28c2830 --- /dev/null +++ b/draftlogs/8162_fix.md @@ -0,0 +1 @@ +- Prevent double-precision underflow in `findEmpties` for deep empty (NaN) regions [[#8162](https://github.com/plotly/plotly.js/pull/8162)] diff --git a/draftlogs/pending_fix.md b/draftlogs/pending_fix.md deleted file mode 100644 index 57c73b28cb0..00000000000 --- a/draftlogs/pending_fix.md +++ /dev/null @@ -1 +0,0 @@ -- Prevent double-precision underflow in `findEmpties` for deep empty (NaN) regions [[#8108](https://github.com/plotly/plotly.js/issues/8108)]