From 381cfbc57730e83698587c8d9a6877e5d27f21a6 Mon Sep 17 00:00:00 2001 From: archmoj Date: Thu, 13 Aug 2020 14:59:02 -0400 Subject: [PATCH 1/4] fix issue 5058 - compute distinct positions using all traces --- src/traces/bar/cross_trace_calc.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/traces/bar/cross_trace_calc.js b/src/traces/bar/cross_trace_calc.js index 628bfb1396d..f8456fd2430 100644 --- a/src/traces/bar/cross_trace_calc.js +++ b/src/traces/bar/cross_trace_calc.js @@ -59,6 +59,9 @@ function crossTraceCalc(gd, plotinfo) { } var opts = { + xCat: xa.type === 'category' || xa.type === 'multicategory', + yCat: ya.type === 'category' || ya.type === 'multicategory', + mode: fullLayout.barmode, norm: fullLayout.barnorm, gap: fullLayout.bargap, @@ -172,14 +175,20 @@ function initBase(sa, calcTraces) { } function setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts) { + var sieve; + var sieveOpts = { + sepNegVal: false, + overlapNoMerge: !opts.norm + }; + + var categoryOverlay = opts.xCat || opts.yCat; + if(categoryOverlay) { + sieve = new Sieve(calcTraces, sieveOpts); + } + // update position axis and set bar offsets and widths for(var i = 0; i < calcTraces.length; i++) { - var calcTrace = calcTraces[i]; - - var sieve = new Sieve([calcTrace], { - sepNegVal: false, - overlapNoMerge: !opts.norm - }); + if(!categoryOverlay) sieve = new Sieve([calcTraces[i]], sieveOpts); // set bar offsets and widths, and update position axis setOffsetAndWidth(pa, sieve, opts); From 3e7f03e1832380f737c505bd9bf33e0c177e0d0c Mon Sep 17 00:00:00 2001 From: archmoj Date: Fri, 14 Aug 2020 13:53:42 -0400 Subject: [PATCH 2/4] add option to set minDiff to 1 from distinctVals --- src/lib/search.js | 8 ++++++-- src/traces/bar/cross_trace_calc.js | 19 +++++++------------ src/traces/bar/sieve.js | 5 ++++- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/lib/search.js b/src/lib/search.js index de1d634160a..6b24c77cf3c 100644 --- a/src/lib/search.js +++ b/src/lib/search.js @@ -71,7 +71,9 @@ exports.sorterDes = function(a, b) { return b - a; }; * just be off by a rounding error * return the distinct values and the minimum difference between any two */ -exports.distinctVals = function(valsIn) { +exports.distinctVals = function(valsIn, opts) { + var unitMinDiff = (opts || {}).unitMinDiff; + var vals = valsIn.slice(); // otherwise we sort the original array... vals.sort(exports.sorterAsc); // undefined listed in the end - also works on IE11 @@ -80,7 +82,9 @@ exports.distinctVals = function(valsIn) { if(vals[last] !== BADNUM) break; } - var minDiff = (vals[last] - vals[0]) || 1; + var minDiff = 1; + if(!unitMinDiff) minDiff = (vals[last] - vals[0]) || 1; + var errDiff = minDiff / (last || 1) / 10000; var newVals = []; var preV; diff --git a/src/traces/bar/cross_trace_calc.js b/src/traces/bar/cross_trace_calc.js index f8456fd2430..1bb5dda2f81 100644 --- a/src/traces/bar/cross_trace_calc.js +++ b/src/traces/bar/cross_trace_calc.js @@ -175,20 +175,15 @@ function initBase(sa, calcTraces) { } function setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts) { - var sieve; - var sieveOpts = { - sepNegVal: false, - overlapNoMerge: !opts.norm - }; - - var categoryOverlay = opts.xCat || opts.yCat; - if(categoryOverlay) { - sieve = new Sieve(calcTraces, sieveOpts); - } - // update position axis and set bar offsets and widths for(var i = 0; i < calcTraces.length; i++) { - if(!categoryOverlay) sieve = new Sieve([calcTraces[i]], sieveOpts); + var calcTrace = calcTraces[i]; + + var sieve = new Sieve([calcTrace], { + unitMinDiff: opts.xCat || opts.yCat, + sepNegVal: false, + overlapNoMerge: !opts.norm + }); // set bar offsets and widths, and update position axis setOffsetAndWidth(pa, sieve, opts); diff --git a/src/traces/bar/sieve.js b/src/traces/bar/sieve.js index 15052f6f1a9..7cc69f6e81e 100644 --- a/src/traces/bar/sieve.js +++ b/src/traces/bar/sieve.js @@ -48,7 +48,10 @@ function Sieve(traces, opts) { } this.positions = positions; - var dv = distinctVals(positions); + var dv = distinctVals(positions, { + unitMinDiff: opts.unitMinDiff + }); + this.distinctPositions = dv.vals; if(dv.vals.length === 1 && width1 !== Infinity) this.minDiff = width1; else this.minDiff = Math.min(dv.minDiff, width1); From 804be62c19efd0ad7b6f892316e91f5d37a81410 Mon Sep 17 00:00:00 2001 From: archmoj Date: Fri, 14 Aug 2020 14:18:30 -0400 Subject: [PATCH 3/4] fix issue 4510 for box plots --- src/traces/box/cross_trace_calc.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/traces/box/cross_trace_calc.js b/src/traces/box/cross_trace_calc.js index 478dea16abc..72b57856cfb 100644 --- a/src/traces/box/cross_trace_calc.js +++ b/src/traces/box/cross_trace_calc.js @@ -68,7 +68,10 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { if(!pointList.length) return; // box plots - update dPos based on multiple traces - var boxdv = Lib.distinctVals(pointList); + var boxdv = Lib.distinctVals(pointList, { + unitMinDiff: posAxis.type === 'category' || posAxis.type === 'multicategory' + }); + var dPos0 = boxdv.minDiff / 2; // check for forced minimum dtick From 081cb7759d23b16ebded73fff1de6c13e6f235d6 Mon Sep 17 00:00:00 2001 From: archmoj Date: Fri, 14 Aug 2020 14:32:59 -0400 Subject: [PATCH 4/4] add jasmine tests --- test/jasmine/tests/bar_test.js | 17 ++++++++++++ test/jasmine/tests/box_test.js | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js index 5b162e897af..8b835f36907 100644 --- a/test/jasmine/tests/bar_test.js +++ b/test/jasmine/tests/bar_test.js @@ -969,6 +969,23 @@ describe('Bar.crossTraceCalc (formerly known as setPositions)', function() { assertPointField(gd.calcdata, 'b', [[0, 0, 0, 0]]); }); + it('should set unit width for categories in overlay mode', function() { + var gd = mockBarPlot([{ + type: 'bar', + x: ['a', 'b', 'c'], + y: [2, 2, 2] + }, + { + type: 'bar', + x: ['a', 'c'], + y: [1, 1] + }], { + barmode: 'overlay' + }); + + expect(gd.calcdata[1][0].t.bardelta).toBe(1); + }); + describe('should relative-stack bar within the same trace that overlap under barmode=group', function() { it('- base case', function() { var gd = mockBarPlot([{ diff --git a/test/jasmine/tests/box_test.js b/test/jasmine/tests/box_test.js index 58ea50c048d..9ea11d6b406 100644 --- a/test/jasmine/tests/box_test.js +++ b/test/jasmine/tests/box_test.js @@ -1534,3 +1534,51 @@ describe('Test box calc', function() { }); }); }); + + +describe('Box crossTraceCalc', function() { + 'use strict'; + + function mockBoxPlot(dataWithoutTraceType, layout) { + var traceTemplate = { type: 'box' }; + + var dataWithTraceType = dataWithoutTraceType.map(function(trace) { + return Lib.extendFlat({}, traceTemplate, trace); + }); + + var gd = { + data: dataWithTraceType, + layout: layout || {}, + calcdata: [], + _context: {locale: 'en', locales: {}} + }; + + supplyAllDefaults(gd); + Plots.doCalcdata(gd); + + return gd; + } + + it('should set unit width for categories in overlay mode', function() { + var gd = mockBoxPlot([{ + y: [1, 2, 3] + }, + { + y: [null, null, null] + }, + { + y: [null, null, null] + }, + { + y: [4, 5, 6] + }], { + boxgap: 0, + xaxis: { + range: [-0.5, 3.5], + type: 'category' + } + }); + + expect(gd.calcdata[0][0].t.dPos).toBe(0.5); + }); +});