diff --git a/draftlogs/7581_fix.md b/draftlogs/7581_fix.md new file mode 100644 index 00000000000..c3f1a5950dc --- /dev/null +++ b/draftlogs/7581_fix.md @@ -0,0 +1 @@ +- Fix KDE sampling precision in violin trace to eliminate floating-point drift and prevent density underrun/overrun ([#7581](https://github.com/plotly/plotly.js/pull/7581)) diff --git a/src/traces/violin/calc.js b/src/traces/violin/calc.js index 39fa549097b..504ba83302e 100644 --- a/src/traces/violin/calc.js +++ b/src/traces/violin/calc.js @@ -50,7 +50,8 @@ module.exports = function calc(gd, trace) { var kde = helpers.makeKDE(cdi, trace, vals); cdi.density = new Array(n); - for(var k = 0, t = span[0]; t < (span[1] + step / 2); k++, t += step) { + for(var k = 0; k < n; k++) { + var t = span[0] + k * step; var v = kde(t); cdi.density[k] = {v: v, t: t}; maxKDE = Math.max(maxKDE, v); diff --git a/test/image/baselines/box-violin-multicategory-on-val-axis.png b/test/image/baselines/box-violin-multicategory-on-val-axis.png index 6034395f8f8..4ad06427289 100644 Binary files a/test/image/baselines/box-violin-multicategory-on-val-axis.png and b/test/image/baselines/box-violin-multicategory-on-val-axis.png differ diff --git a/test/image/baselines/box-violin-x0-category-position.png b/test/image/baselines/box-violin-x0-category-position.png index 674aada673c..bd95ecaa157 100644 Binary files a/test/image/baselines/box-violin-x0-category-position.png and b/test/image/baselines/box-violin-x0-category-position.png differ diff --git a/test/image/baselines/box_quartile-methods.png b/test/image/baselines/box_quartile-methods.png index 80bdd6d814a..d866e65754a 100644 Binary files a/test/image/baselines/box_quartile-methods.png and b/test/image/baselines/box_quartile-methods.png differ diff --git a/test/image/baselines/point-selection2.png b/test/image/baselines/point-selection2.png index 706916de272..075089dced5 100644 Binary files a/test/image/baselines/point-selection2.png and b/test/image/baselines/point-selection2.png differ diff --git a/test/image/baselines/violin_bandwidth-edge-cases.png b/test/image/baselines/violin_bandwidth-edge-cases.png index 35a0ee8c2e8..438c174cfa7 100644 Binary files a/test/image/baselines/violin_bandwidth-edge-cases.png and b/test/image/baselines/violin_bandwidth-edge-cases.png differ diff --git a/test/image/baselines/violin_box_multiple_widths.png b/test/image/baselines/violin_box_multiple_widths.png index f2fd9f6a165..5d99db1de77 100644 Binary files a/test/image/baselines/violin_box_multiple_widths.png and b/test/image/baselines/violin_box_multiple_widths.png differ diff --git a/test/image/baselines/violin_box_overlay.png b/test/image/baselines/violin_box_overlay.png index 7150d01fb0b..93aecda7450 100644 Binary files a/test/image/baselines/violin_box_overlay.png and b/test/image/baselines/violin_box_overlay.png differ diff --git a/test/image/baselines/violin_grouped.png b/test/image/baselines/violin_grouped.png index 53a0c04842b..9b38db837ad 100644 Binary files a/test/image/baselines/violin_grouped.png and b/test/image/baselines/violin_grouped.png differ diff --git a/test/image/baselines/violin_grouped_horz-multicategory.png b/test/image/baselines/violin_grouped_horz-multicategory.png index b6cdb5ea622..0fd09d2c5fd 100644 Binary files a/test/image/baselines/violin_grouped_horz-multicategory.png and b/test/image/baselines/violin_grouped_horz-multicategory.png differ diff --git a/test/image/baselines/violin_log_scale.png b/test/image/baselines/violin_log_scale.png index 6f0d682c859..599466aa298 100644 Binary files a/test/image/baselines/violin_log_scale.png and b/test/image/baselines/violin_log_scale.png differ diff --git a/test/image/baselines/violin_negative_sides_w_points.png b/test/image/baselines/violin_negative_sides_w_points.png index 23c71791171..2c18823dc55 100644 Binary files a/test/image/baselines/violin_negative_sides_w_points.png and b/test/image/baselines/violin_negative_sides_w_points.png differ diff --git a/test/image/baselines/violin_non-linear.png b/test/image/baselines/violin_non-linear.png index 79d0eb4c103..e6fa2ab5fa3 100644 Binary files a/test/image/baselines/violin_non-linear.png and b/test/image/baselines/violin_non-linear.png differ diff --git a/test/image/baselines/violin_old-faithful.png b/test/image/baselines/violin_old-faithful.png index f1e091bf58c..c3ca9fb14f8 100644 Binary files a/test/image/baselines/violin_old-faithful.png and b/test/image/baselines/violin_old-faithful.png differ diff --git a/test/image/baselines/violin_one-sided.png b/test/image/baselines/violin_one-sided.png index a9a26d6a6f4..46fd4264833 100644 Binary files a/test/image/baselines/violin_one-sided.png and b/test/image/baselines/violin_one-sided.png differ diff --git a/test/image/baselines/violin_only_zeroes.png b/test/image/baselines/violin_only_zeroes.png index 4e51d7147f7..eecd79b37f2 100644 Binary files a/test/image/baselines/violin_only_zeroes.png and b/test/image/baselines/violin_only_zeroes.png differ diff --git a/test/image/baselines/violin_positive_and_negative.png b/test/image/baselines/violin_positive_and_negative.png index 2c5cc6bd469..3b7d2b880f3 100644 Binary files a/test/image/baselines/violin_positive_and_negative.png and b/test/image/baselines/violin_positive_and_negative.png differ diff --git a/test/image/baselines/violin_positive_sides_w_points.png b/test/image/baselines/violin_positive_sides_w_points.png index 8c18aef019f..502cb2dba83 100644 Binary files a/test/image/baselines/violin_positive_sides_w_points.png and b/test/image/baselines/violin_positive_sides_w_points.png differ diff --git a/test/image/baselines/violin_ridgeplot.png b/test/image/baselines/violin_ridgeplot.png index cb1de0b284c..b9fad0d42ca 100644 Binary files a/test/image/baselines/violin_ridgeplot.png and b/test/image/baselines/violin_ridgeplot.png differ diff --git a/test/image/baselines/violin_side-by-side.png b/test/image/baselines/violin_side-by-side.png index 972a767978f..67a879484d8 100644 Binary files a/test/image/baselines/violin_side-by-side.png and b/test/image/baselines/violin_side-by-side.png differ diff --git a/test/image/baselines/violin_style.png b/test/image/baselines/violin_style.png index b88c7a50e9e..ff5a64a3ba3 100644 Binary files a/test/image/baselines/violin_style.png and b/test/image/baselines/violin_style.png differ diff --git a/test/image/baselines/zorder_violin_box.png b/test/image/baselines/zorder_violin_box.png index d709d192f31..36285be7846 100644 Binary files a/test/image/baselines/zorder_violin_box.png and b/test/image/baselines/zorder_violin_box.png differ diff --git a/test/jasmine/tests/violin_test.js b/test/jasmine/tests/violin_test.js index c96a8dedd06..2e5c36090d0 100644 --- a/test/jasmine/tests/violin_test.js +++ b/test/jasmine/tests/violin_test.js @@ -401,6 +401,20 @@ describe('Test violin calc:', function() { expect(cd.length).toBe(1, '# of violins'); expect(cd[0].bandwidth).toBe(0, 'bandwidth'); }); + + it('should produce exactly n density samples for tiny or near-equal spans', function() { + var cd = _calc({ + type: 'violin', + x: [0, 0], + y: [0.5006312999999999, 0.5006313] + }); + var cdi = cd[0]; + + var dist = cdi.span[1] - cdi.span[0]; + var n = Math.ceil(dist / (cdi.bandwidth / 3)); + + expect(cdi.density.length).toBe(n); + }); }); describe('Test violin hover:', function() {