From e1716c71b863685d87b0a533b251eeacb154693c Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 30 Apr 2019 19:22:51 -0400 Subject: [PATCH 01/21] heatmap: reorder data based on categoryarray --- src/traces/heatmap/calc.js | 2 +- src/traces/heatmap/clean_2d_array.js | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index 69633c278f3..c3e4a7feffb 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -71,7 +71,7 @@ module.exports = function calc(gd, trace) { y0 = trace.y0; dy = trace.dy; - z = clean2dArray(zIn, trace.transpose); + z = clean2dArray(zIn, trace.transpose, trace, xa, ya); if(isContour || trace.connectgaps) { trace._emptypoints = findEmpties(z); diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 74d6fa84db4..977b227d0aa 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -10,7 +10,7 @@ var isNumeric = require('fast-isnumeric'); -module.exports = function clean2dArray(zOld, transpose) { +module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { var rowlen, collen, getCollen, old2new, i, j; function cleanZvalue(v) { @@ -30,12 +30,21 @@ module.exports = function clean2dArray(zOld, transpose) { old2new = function(zOld, i, j) { return zOld[i][j]; }; } + var xMap = function(i) {return i;}; + var yMap = function(i) {return i;}; + if(ya.type === 'category') { + yMap = function(i) {return ya._categoriesMap[trace.y[i]];}; + } + if(xa.type === 'category') { + xMap = function(i) {return xa._categoriesMap[trace.x[i]];}; + } + var zNew = new Array(rowlen); for(i = 0; i < rowlen; i++) { collen = getCollen(zOld, i); zNew[i] = new Array(collen); - for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(old2new(zOld, i, j)); + for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(old2new(zOld, yMap(i), xMap(j))); } return zNew; From d8ef1d748728cf3f6b1c9dc8ca7829b1607b7185 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Wed, 1 May 2019 16:12:00 -0400 Subject: [PATCH 02/21] heatmap: :lock: down categoryorder and categoryarray with tests --- .../image/baselines/heatmap_categoryorder.png | Bin 0 -> 13842 bytes test/image/mocks/heatmap_categoryorder.json | 26 ++++++++++ test/jasmine/tests/heatmap_test.js | 45 ++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 test/image/baselines/heatmap_categoryorder.png create mode 100644 test/image/mocks/heatmap_categoryorder.json diff --git a/test/image/baselines/heatmap_categoryorder.png b/test/image/baselines/heatmap_categoryorder.png new file mode 100644 index 0000000000000000000000000000000000000000..d54b999e7ec8388e9bfa0ebb3a047e97f4cc75fc GIT binary patch literal 13842 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 NK}J=&Nb>hb{|6#4(%k?6 literal 0 HcmV?d00001 diff --git a/test/image/mocks/heatmap_categoryorder.json b/test/image/mocks/heatmap_categoryorder.json new file mode 100644 index 00000000000..c6e907f7b6f --- /dev/null +++ b/test/image/mocks/heatmap_categoryorder.json @@ -0,0 +1,26 @@ +{ + "layout": { + "xaxis": { + "type": "category", + "categoryorder": "category descending" + }, + "yaxis": { + "type": "category", + "categoryorder": "category descending" + }, + "height": 400, + "width": 400 + }, + "data": [{ + "type": "heatmap", + + "x": [3, 2, 1, 0], + "y": ["d", "c", "b", "a"], + "z": [ + [100, 75, 50, 0], + [90, 65, 40, 0], + [80, 55, 30, 0], + [0, 0, 0, 0] + ] + }] +} diff --git a/test/jasmine/tests/heatmap_test.js b/test/jasmine/tests/heatmap_test.js index 961db01764e..3fb0bfdcf20 100644 --- a/test/jasmine/tests/heatmap_test.js +++ b/test/jasmine/tests/heatmap_test.js @@ -491,6 +491,51 @@ describe('heatmap calc', function() { expect(out.y).toBeCloseToArray([0, 4, 8]); expect(out.z).toBeCloseTo2DArray([[1, 2, 3], [3, 1, 2]]); }); + + it('should handle axis categoryorder', function() { + var mock = require('@mocks/heatmap_categoryorder'); + var data = mock.data[0]; + var layout = mock.layout; + + // sort x axis categories + var mockLayout = Lib.extendDeep({}, layout); + var out = _calc(data, mockLayout); + mockLayout.xaxis.categoryorder = 'category ascending'; + var out1 = _calc(data, mockLayout); + + expect(out._xcategories).toEqual(out1._xcategories.reverse()); + // Check z data is also sorted + for(var i = 0; i < out.z.length; i++) { + expect(out1.z[i]).toEqual(out.z[i].reverse()); + } + + // sort y axis categories + mockLayout = Lib.extendDeep({}, layout); + out = _calc(data, mockLayout); + mockLayout.yaxis.categoryorder = 'category ascending'; + out1 = _calc(data, mockLayout); + + expect(out._ycategories).toEqual(out1._ycategories.reverse()); + // Check z data is also sorted + expect(out1.z).toEqual(out.z.reverse()); + }); + + it('should handle axis categoryarray', function() { + var mock = require('@mocks/heatmap_categoryorder'); + var data = mock.data[0]; + var layout = mock.layout; + + layout.xaxis.categoryorder = 'array'; + layout.xaxis.categoryarray = [2, 3, 0, 1]; + layout.yaxis.categoryorder = 'array'; + layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; + + var out = _calc(data, layout); + + expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); + expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); + expect(out.z[0][0]).toEqual(65); + }); }); describe('heatmap plot', function() { From 97356831fbfad45f6a366f85398621ecfa7fdee6 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Wed, 1 May 2019 16:37:40 -0400 Subject: [PATCH 03/21] heatmap: handle undefined axes in clean_2d_array --- src/traces/heatmap/clean_2d_array.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 977b227d0aa..275006b9222 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -32,10 +32,10 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { var xMap = function(i) {return i;}; var yMap = function(i) {return i;}; - if(ya.type === 'category') { + if(ya && ya.type === 'category') { yMap = function(i) {return ya._categoriesMap[trace.y[i]];}; } - if(xa.type === 'category') { + if(xa && xa.type === 'category') { xMap = function(i) {return xa._categoriesMap[trace.x[i]];}; } From 5798dd8648030ee02f67c2097b32a26fc261c794 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Wed, 1 May 2019 17:52:12 -0400 Subject: [PATCH 04/21] heatmap: properly sort categorical data when z data is a 1D array --- src/traces/heatmap/calc.js | 3 +++ src/traces/heatmap/clean_2d_array.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index c3e4a7feffb..c113d8deea0 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -64,6 +64,9 @@ module.exports = function calc(gd, trace) { } else { x = trace.x ? xa.makeCalcdata(trace, 'x') : []; y = trace.y ? ya.makeCalcdata(trace, 'y') : []; + + trace._x = x; + trace._y = y; } x0 = trace.x0; diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 275006b9222..eeface3fe44 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -33,10 +33,10 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { var xMap = function(i) {return i;}; var yMap = function(i) {return i;}; if(ya && ya.type === 'category') { - yMap = function(i) {return ya._categoriesMap[trace.y[i]];}; + yMap = function(i) {return trace._y[i];}; } if(xa && xa.type === 'category') { - xMap = function(i) {return xa._categoriesMap[trace.x[i]];}; + xMap = function(i) {return trace._x[i];}; } var zNew = new Array(rowlen); From 56c6a4df09df10dd6802326c425181bf9bdb673f Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Wed, 1 May 2019 19:40:59 -0400 Subject: [PATCH 05/21] heatmap: do not sort categorical axes for contour --- src/traces/heatmap/clean_2d_array.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index eeface3fe44..c0ce743ec81 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -32,11 +32,13 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { var xMap = function(i) {return i;}; var yMap = function(i) {return i;}; - if(ya && ya.type === 'category') { - yMap = function(i) {return trace._y[i];}; - } - if(xa && xa.type === 'category') { - xMap = function(i) {return trace._x[i];}; + if(trace.type !== 'contour') { + if(ya && ya.type === 'category') { + yMap = function(i) {return trace._y[i];}; + } + if(xa && xa.type === 'category') { + xMap = function(i) {return trace._x[i];}; + } } var zNew = new Array(rowlen); From f8783870a16dcac5921b2e16eaea0b1b6928f782 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Wed, 1 May 2019 20:00:10 -0400 Subject: [PATCH 06/21] heatmap: only sort categorical axes for trace type heatmap --- src/traces/heatmap/clean_2d_array.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index c0ce743ec81..267935d17d2 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -32,7 +32,7 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { var xMap = function(i) {return i;}; var yMap = function(i) {return i;}; - if(trace.type !== 'contour') { + if(trace.type === 'heatmap') { if(ya && ya.type === 'category') { yMap = function(i) {return trace._y[i];}; } From a34ca4576a173ae873f535afd1bb7da769fcaafe Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Wed, 1 May 2019 20:07:57 -0400 Subject: [PATCH 07/21] heatmap: fix - check trace exists before checking type --- src/traces/heatmap/clean_2d_array.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 267935d17d2..0ec3a664477 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -32,7 +32,7 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { var xMap = function(i) {return i;}; var yMap = function(i) {return i;}; - if(trace.type === 'heatmap') { + if(trace && trace.type === 'heatmap') { if(ya && ya.type === 'category') { yMap = function(i) {return trace._y[i];}; } From 6b065160b81a9d8d7182e7b1cd66572452572f31 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Thu, 2 May 2019 10:12:10 -0400 Subject: [PATCH 08/21] heatmap: handle categorical axis sorting except for (contour)carpet --- src/traces/heatmap/clean_2d_array.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 0ec3a664477..3ff4f693e54 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -32,12 +32,12 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { var xMap = function(i) {return i;}; var yMap = function(i) {return i;}; - if(trace && trace.type === 'heatmap') { + if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet') { if(ya && ya.type === 'category') { - yMap = function(i) {return trace._y[i];}; + if(trace._y.length) yMap = function(i) {return trace._y[i];}; } if(xa && xa.type === 'category') { - xMap = function(i) {return trace._x[i];}; + if(trace._x.length) xMap = function(i) {return trace._x[i];}; } } From 59edb5704f076b438e6b8460b3e03097d61748ab Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Fri, 3 May 2019 15:13:01 -0400 Subject: [PATCH 09/21] heatmap calc: improve style, test contour and heatmap --- src/traces/heatmap/calc.js | 9 +- src/traces/heatmap/clean_2d_array.js | 17 ++-- .../image/baselines/heatmap_categoryorder.png | Bin 13842 -> 13881 bytes test/image/mocks/heatmap_categoryorder.json | 2 +- test/jasmine/tests/contour_test.js | 62 +++++++++++++- test/jasmine/tests/heatmap_test.js | 78 +++++++++--------- 6 files changed, 115 insertions(+), 53 deletions(-) diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index c113d8deea0..5cd2243920f 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -62,11 +62,8 @@ module.exports = function calc(gd, trace) { y = trace._y; zIn = trace._z; } else { - x = trace.x ? xa.makeCalcdata(trace, 'x') : []; - y = trace.y ? ya.makeCalcdata(trace, 'y') : []; - - trace._x = x; - trace._y = y; + x = trace._x = trace.x ? xa.makeCalcdata(trace, 'x') : []; + y = trace._y = trace.y ? ya.makeCalcdata(trace, 'y') : []; } x0 = trace.x0; @@ -74,7 +71,7 @@ module.exports = function calc(gd, trace) { y0 = trace.y0; dy = trace.dy; - z = clean2dArray(zIn, trace.transpose, trace, xa, ya); + z = clean2dArray(zIn, trace, xa, ya); if(isContour || trace.connectgaps) { trace._emptypoints = findEmpties(z); diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 3ff4f693e54..43fc5056883 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -9,8 +9,9 @@ 'use strict'; var isNumeric = require('fast-isnumeric'); +var Lib = require('../../lib'); -module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { +module.exports = function clean2dArray(zOld, trace, xa, ya) { var rowlen, collen, getCollen, old2new, i, j; function cleanZvalue(v) { @@ -18,7 +19,7 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { return +v; } - if(transpose) { + if(trace && trace.transpose) { rowlen = 0; for(i = 0; i < zOld.length; i++) rowlen = Math.max(rowlen, zOld[i].length); if(rowlen === 0) return false; @@ -30,14 +31,14 @@ module.exports = function clean2dArray(zOld, transpose, trace, xa, ya) { old2new = function(zOld, i, j) { return zOld[i][j]; }; } - var xMap = function(i) {return i;}; - var yMap = function(i) {return i;}; + var xMap = Lib.identity; + var yMap = Lib.identity; if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet') { - if(ya && ya.type === 'category') { - if(trace._y.length) yMap = function(i) {return trace._y[i];}; + if(ya && ya.type === 'category' && trace._y.length) { + yMap = function(i) {return trace._y[i];}; } - if(xa && xa.type === 'category') { - if(trace._x.length) xMap = function(i) {return trace._x[i];}; + if(xa && xa.type === 'category' && trace._x.length) { + xMap = function(i) {return trace._x[i];}; } } diff --git a/test/image/baselines/heatmap_categoryorder.png b/test/image/baselines/heatmap_categoryorder.png index d54b999e7ec8388e9bfa0ebb3a047e97f4cc75fc..c4613c59db07f61709be475f0d9a738b3a76744a 100644 GIT binary patch literal 13881 zcmeIZcT|(v7dEOf4gnoVP(-8!0)kWtf^xGorTRO~37UQT_WiDg zRmSbxx1T`yzSGu4#ID&8hin&@lUe0RRf~1Z;mOI#g0;wf`(c=Ukb_)sJcb4v+{Qt9 ze!=aBxeBHH9e#n+4--|0fc|I&yU*sg4+_4*4S^|BGb_L;Pkxx`A20vc^nVukp9TK! zuz+$26^Xy>f-|%c_=^Hql1x{7&J!j*Wq5!MGZL=pcVFp+5(L()R0&gN7lL%!5@GN! zf%_m|1Lu9;SW~67FygO5xL6=;^cW;t<2mH!O+}?Ow6=0(rd##5!^e>!%x7cHj(A-XcO2x^mielD z!43u2OoAyNL4JmRAz&n6<8Rn~qi~t(EnS%!nJzP3Rge}E>Gtk=t;aGK8r_vCVc)}R zH&S2M{Q7lkkZ?Wk5gsj<-NCAI)i5@+hX?pm3+jgH4oh;Y()_QD^P&Mz>W<x7T6(Q>$N^ z=-OD_204Cb7C&r^kt@I;H=wThZD4wsMvhM$`&sc0-m^?}{L<_172(ayhy%+QVAwBH!Ejdcyd=}`K{`j=P z8w*J~KgrEpQq_pPw$3&S6O;3&!jDN;)vgnuEk5kntE za((tQ$45O1H$3pqk&{`8p|+`!*LYX3$3l8S{gefj9qSeco0`z+B5wjj1~J_Yz^$_qR;j%8=I$Fu5U=o_ktMskKH^0 z<3e%@`T2=Kq@>}hNI^6@`2EhU%SRz1>N41tBw;h?_1@dN_g+ z^Wp@>20j-JSwsW~xQn8O1=+A`4eA=5MuhQ6Zxyn~3UK!>Ql>w5Qu@~D{kQR=s^yMDd}%dSRAs2VUD z-^6bz3s3O|p>n-YWzy0gZ?j(V}Z`UnF zl(wP;2%Ov*>Wf;I!mk6=N;j&C`I&I2i zxopX@m_N8ua4I_ZCk;jcn=Q?{*j<{B3YT(@$R!F0MLX6o|7!-sg5{?giKFos0WANCM*6bjQ*WuWB+o0 zTbKA!X{Uj({Kd(*6%fohh=(g$75qU%9(Hayy{oVDGN=WgUNM^|J(1E_1FAR2it%aD- zqv3%=cc#E~>o3zt@t9;%)6{fL)$7U86Sn)1pDg`#WyR$dn`-z^oE?=3t?}93eBIB< zDJ?BMpkIFa{C!O0&5QB4k8sMB^&VrW;%J>W3u{AjW>|cvg)qr^Rq_YV6*QRAvPd9- zV~l1r^Xl}T9{L+=0b)CZcc!bhmD+VvP?C6p6R!F87bmz@Cw9q^_qsmPAC;jPE4@6A zN#eVlhdIlJ*D?{xFUDl4=8AxYy z@Sk{dm3n+3-bty{|5dW}w6(u7p9_hfpe?_BdGc*MYJJc-EZ3mqwe0KJ4+W{78Ddx8 zA>(B|Z-M-Ml1IbXIB8|RuNylA zx)OzSLa`E18Lr3N1UH+u5bMK-#xdnli|(pq+U6=dxhpUHWZqovex@D!@uPm##$;lq zQH3MgdGh?Dz(A){p~poO>5m-Y=HPg#l-)oVf;l4sh|W(+Ck0V6K^r*=7&SM|l8O(g zwM96w`Y;k_x}QzFH@Xr1Eai^+Y)=*nIH@C3CI%!#Z?vB2sh`N}y}5GBjfWKeKA1^# zaOlw^zydOU*%hl97BvA|U8KgPk__X~1rIXbp}$luDRB(2BUd%a#p2c~wUqGi&pxzP zV3Wy0&3$=i?0bkxX&%6FCOijx=TBymQ75fR-^RFFSS{*rTD@BuRVPw_2|jk=g%vEr z@5Rpg(js|lkw>v<+qJfh^|RbLH=ho#t|u4XI`3rYJg`VHgcz)4yF9-Frm~hCQ+#&t zwVOFZAo(Z)N^i;^f@!=a>k4)Hv|ZKp#6lz;6Eoi)g9&>>fxeCixHv5iu5jYYy;NpAG&QRo0oqvPyX0EwBWtwP;>BUj zHXX;?LomUkYcfobuPjBKPyi+i_dL3aip}vENfDLr>m(Y{{oFr2fh;xzBjaa(3oD4v zO%hJdPO>fCd{v{RKDa6i#`?ou{MCpR&+`3dy-T+JNyyAgM9ixc5#ZpDzgqA>Lq{bo(y3jb5j!n&T~#X7>8thIW49jJ`5gKQ4^Rt4 zJyjMWfa-!EZ5E24lw%f4DkGgW+#Zk)Ra+^8HS@Y80)C>o`LZJfbGM^sGQbeB;evO` z2M$1I`qP5m)h{}!WxF!dQ5ShH1_ ze)|JV?kL?ICn&v3hAvePJ+c2mS7yFB-a(o#Gx4@1l||XL!zWddznL>*A7Bn+#N!k- zkaLW!?Tj&K{XAl#Q5bgS8vMWewhCQ(IOr7)`nd(|ie?RHe<~8sjE~D$@D_rO%34M; zl|qX@YtG;bN?V*J^2VERo3n{~Y>6P=Yc0kli}8BtW<>;@+DuM05}w{82psi4jcruj z6C`4|ZPYS@+sq~b8~wrI*fLa*Y?v~Mj8F`%cRjY+nB^Misa~G9GvtQie^2Bpv{=8| zm>!&xiux_hG-kIw+LS?;UP*UtM4(l2e79;2&<{OFfsHc}HL$Qq6ELa1Yd%KojcP7?I#ia}=YyWY$}c+nH)R)zX<&iv*<2=%>l-JXHlm>_N{#7*|FN|CJ&sh-BrAcU6|cD?iD*9xu}h*3})FZo|0ZF4VEL zA&B#RB_P7wtsff=IXg^{^<2X?_-s2}s<=L|7L1M&HWPw1&-IE(%Rxli2Id^VVQxiW zYd>&LGWm9X@Z?%^qA+?#v??;_m|%p(tH3IyJmV_d`+O6A5ct3(&OoRGcr%1BNRBe& z{XxA)h%{9*ONStnu*ri#^_w~t!Z(^||C|XRmYK#?U?vtX?;b3^F;XuhO>pk@!_=Cx zmW-j`CAD=VVA29T?6Wq04-<=&uZ+HBMiwAkPk-kcDG9mdm@(ou;!Tz%yu3%%c8^Yu zC`*v{o48(EZ8BN#Z6i{5BL^9l-APY8%b$6vsg+IV_L@244fXJkZTa$+RK>T(R6I5& zVz~tcHAlzBtiONzO5Qg!GlRZ8udk-16%!YCoJq>%c~61ah<-bw5Bz&wym2Pnxv3V! zuw)|-RuTaj8yI_vwNzB)l>sM^v^M zP9OqM{?Af<40m?c8#F)&yF7kE)FzleYPim`Ek@AD>W={aQE>2KLrq_C_#c?PDZqmN z=Adw<4J@Q0C!?D|M~t6W|J$grZ+NAK(IkQhxI?ofch%->&hp{O@0JxnZb07{t~EB7 z_)vah#5CcI9j>rBP!9z^cT>_FVD00MMnpizG362#6TC}pae~dkgNJ-=$2x3b830tB zKA5<*6kE5A_helnQ$Jcx?diG9bml5yi?%CC4~L6TR#nvqW0R$c(f?`>O@qSc|RyLTUeuKlCx#`|T~F^)A0;uN*TNq&01mbp#xsT0{( z;*AkW6`q!sHmFRp{gAZ9O-)UGP$Sn}3~~_~d*mVDYk%(*0z12YjjQMuYda0ivHF$e z1sr+Q#sf-GP1c?5=;Xf_vdFl<+m3rV7A7wwX5Vvjgfs`HKPEV3qE8BsarSPB;6Ajc zjd=)VPGdT*KTN#UCw|K9R<{g95`<~z<4!9ib=-~Oy{PEeuzj1th7*w}RMR#Kak4#e z$b{&(|3JpYbQSv+hBSBpq*VT4;ek%*bjeNVXvx7&0aaDxR^$hbtsJ4A+{afwFM5~j zw0@|0Sb#oty!>6=3H*I>e{vpxCDS>8g5E968QVIm!4Qwd6058WivWl%DYF4s=0LoJGIgP*D(Rc?8v9LBqce!L1{#n1O`W|r1X;?h@z%y#pd&D;;}iO*&g zjuGOTCywX&ys9pkxO<41;<5h)P*RY-x`k_Y-Q!N_y#@N~(U-Fa_U$>dvt89Q3@SOh zR~BM%S!C-yaST^}BW5nq)UI?h`r29b38~dRU=z%~e(4W#11fWIFoj16nSv5ez-S`}GGCRo2fyF?IxB{&(`DUrBjw41 z9D?~`%U7PVD6<#rT(gJt* zHD8U@|0g?jg)TLPre+v4x~k-uEzhXpYII%qoa}(lH-iLmQGP<%HMxlDvK$bC%37Wg zgNOtUEQ1s3+^lj0gg|p^W*KTFF)DK=6*kev`p@|GJWYZFw_AEO4Vm(MnyTq0(yDd# zw6#}**tvXrr@GErd7}!PP<I9NNE-}J}_=J(V8;=_IK{GSUx~9^$dwa~& z>D>0x{9c`n2i;iL*yg`+#&CNwWKlg`101-2hY?^iH_m1`wJ)_He0kzWs$`doFXcfR=bJ)(s39Q!pHUO-R^T7jI>2+eY9}qUh!5o1ZWh z`9K+9lD?hG#lxd<{(SIZR;kM%X18_azNIWL8!d`VWo3F(M;_>rrTE44dqoqXI>vy)b2jvDR@AivJ1< zIcM1-3?CjW9vi5b=;u#ghK;X&FpIT(6I$iF2IPV%8ZK_`bN??$PKlO(!9?O4GnSYW zt2I}3FBE=ZKij}9cp{p_KooBH&N=ovB$7go06&iTAmb<2y6C*kJ+`*&c7x~|lJ0bHX*Q>zdR-#$=&4cKxcK-}&$Lrx zvUPG=fdWY(I)ADMzH%1LFtzjNqwe2_mO*HaKfuty=6!{2snV$5*?xvK+a5|Y2o1&P zdf!&gHg(WNHhAqmEUnG%k(8%edV><6Y6o!WtqXJ%fONv!Hvk@D|uqrAKwTEGhD}!qp^3RX z21wTa4(fBrgniz}LNQ+D$b^Y$*!Vrr#nQPU3KMj1jXBu0sSU*ycGfnWz<+4Zz16U+anTR^MFb=DH>m z=)IyJU7S_Gmb)#QuGjUF{9Uv43dla^{FZz7;Z0#&@F(v!B_Y&#t2CGMLNUW;ZpNc? z32HY|*7ogVSfSJcY{;A=$*eheYW9mMAo1^e-Q>+GZ$6Qrza^LJT&_1zZY$UCw6Qe7 zV!q+Xy-K5qyz~A0u{;#P`kFTOLAA{=ZDP~)k@5{ErEctPq|dbs%%@36y^>AwIOyusRz*kTT02PzsgP-Z<@8i{%GUa#{1olD zF*g!^AjtAG;0mw-3?D0AV0$GhD#tqq&*!ThzS(4~cAhsBI59d!^whQeNsEcMzb z)fv`3ZNLp@ylDlL^@~^&Ka8S%735eBiol&Cnt5KZ1R_~zneS3rejGwwSu9ukVuKL6 z%#lQ4w~LJw!u^5|fyF3uxbBBgXHC%P=K^CHc&oSeoDm=-J7EaJbfjmgaP-1S6$?tS2kW(Kb{e(E>}>5YcwYYJ(1&amU}>mAzJ zaDZdo^TZ+41*<&>Vp^D%x`S8~WW%F=(1lf+@MTu8oiCNjdLmnDD@rw{k)*tDTz8&a)v}EM zqVfk^$$_6E8S#f{%(U7x9Gbh?I@dLE2h7lfNPC|Z8|QjPnjmj>VTwcZhR@Bnffz?e zZ@JV1)FmSo&RYJgV6x!-7ej53aAD>TRlCexVm6gh?n zExle!$IU{3t(WE?GyQ9WbWux2%u6!xb_VmGmvjD)`!J6=R_}XR{Z*nj=jU+QLzza> z(6p-gCr3gtine)6mrg-i=rZ3`si-0k4a;+K^CvJ6JL?W*de%709~rta-iN=M5BrzX zD`<%2n9`fhHc)uk7`EW4qKN^|9|`Jtg41a>-qsEfvqp{wPVl~zXW$!Lb9{WIK+ZPQm%HW#kX^dj~g z&zW%mLCi~r2ebnP4CDZQRlt^J(_Bs+^Nj4gBT6R}G2j^Z`S0Z0U&2icW>}8*oaI}Z zahFZbsyt@_NYO`8l}kXcuawn(h>=lsID-$xOxC(y2vl)H8!pZ$1x>60N#Q>#*&WD+ zW?+;!KOwg%6f^n}|Iy(@WEJ9vb5cYOi;Bl9s3J>-ziD!&NhJvA6^jmsH@E=N5~S41 zD=+IL&V)?l%U2c^8H*p;?z!XFzVg~>-FukKLre0 zpD6iriHqy9d3{l^d-oV0Bql3s@;a22zon%G$j2xpf7)o^JDHV8v8U|QfJXbP%e9(p zJiy5<4WR4AmvoJBlB37LQDVC5+*ZCuLLLIS zg1*rmAPiGjfx(Mr`&blQ%(3d?KjLs!d~GpOv$#Y1N92Wg6QjOJw%%P~!^R4kSgpO68tSUfMv?a>u9MCKc`%)pcHpK@~p>G zOmRucL<>)9RDapEw!DhrS=xk{n3I&cfGX#y$&>Li9>vYCUa57Y$o?4*hoS^DZ_51+ zD3!_aSH&(+wD+)2i%dbCg-uzZ1i?+J{{EKYHfQxN}l zn4c%GR8;B%F?W4pSQ-Qsdyw+3d08i!#$1fLRfF*Q5CY^@ZMx&$hG5Qjt;sP$zNQql z9F~H_OI-_Ni7h|b7ENDlytB#pSBii8)07JGrLDcQGAsUk{6-E-o1vGVa0t$*#0>L4Haof?t5*ECfoA zYhx(X2bN9PEMbwc(M1+b6-4;&7>2N7Uqr8dJqR0r`zi~##4tSp_e>7Lr=pc6EHKRN z)()pH)2=Gd5JpQVrknB*5gOT#EpQlw`nLAQ^;%{rwl79%DLv^Z!lOvvh>_4Z2CQ*b znAN8Py?uE1uHV^ypOtBui?>Y)2T3uCY&ij{i@Kg_GKUuKyhvmXP;Ixh(}ylieM)}Q ztmXBUN0NZ}c3e76L;vKgCDTVnJTx-vnWeRjjr!tnoi&lbL5{4Y)DqCit`N2y%*pug zp=@~f{)exrRFU|D=+6(>CtjF0K~8NK9iG*@#~Sc;1orAaiQMFo{{`@NC)MLJ?uCP1 z%k3V}uJVO=v$ zNYiU0>`<^P*@{GKHQM}$Qp__>E9&s^^h{6I5r_g(DsN&=9~wc2Ki_|9tGk^h!|Sa* z(-O!BtO|Fq2RW1RgP?v1?e*;@1#lcj>WAv6mZsCgD6|XhZTwCVfxUv4*`BBE^L1^kaWF#x*DSI(S;Pzw-l9q}c2 zH*mXG)9y;UKd986_>3?6sXl#Ga|!w)i*46*`60zko!qRXmM0D$Xb;vkD|&mv!yOKe z!H^9aY7y!4#TiBTBw8k!7K+&sHLsc16cr#8U$gr7SZeRYRnb^QTS1nlW(+@Llo7u# z^WDEH1=zn2!nnSjzaIJ=Meq$9IoUuyD?nJfHWAL@+P4S3Yaq36L=J5w#0lu}>FD!> z9sUXLl!_TTA4e#?*Hdv-z-r@e8CkEJHbJr~wI0fQaY(}xH7(I+AtGu6TgI?v&(|kK zK|r*-Q=)@gGqx-?&SGgWWn+D~$E3IkMMhS!=J?CEpi31&dovF0ZO@?ZOLM)zxn(mt z5E|qo^pJli1#Zia%luKf*I7-97Wh$Az!-i1Dgwr(%t&UjMp3veD_%zOq=AoCRVy#S zK3YB!(KI*FhX+Yy6_95bdZQ&VGIaLN%GG%B>j}y#Dkt(596oJ|@X`#BHHp2k5x5{J}uORohxy$W%L0a`qb{z(zDhkmYBZ^QZoLje0z5apIXt zi0t#(K_%USrK;~^iw%dk#Kd%d`~3u0iERhzcXf63i0SF8Go9)Fo&E3ts>+`U&PPA; z!DXSC6!%$AIzAeiaZ6t~R$V>tU!`}0D8T{Y(84o%`~HDOeDEn@J0sz#kF6ff_m|Z* zG$MDlx7r4)oSoZ#!SUf zmUbwD40KeG@-gd9N5Q+B>)QP>wG(Z$%8~#1z7^G;Et`$%gK0r&I?{m}Nq`#}P94c2 z@lWN@D&~Ioifxlw4&mbx5)hbUq7Oneb0V;C^MvEsh2J^R+DC@0gjCH9w4If=!>RG# zELoRK&Y5qVvr5@;d_<@$EX=jV3aG$?l#vSe?JS-*E(H-~G+iRASRPo*wj)+mPeMn- zT#0TB4<;DHJtk|o6-OHzi&Hp?jw=YzU%5}T_uX2k_)#F)nWY zb~y^Y3fD997IQPD--uqSjA}FP^>dn?c-&vV1 zj89*tO953SZ9op1074+z{mT_e*V(8oTT+f~M+(|?_VS}gkEjzR8DBr5?CmN@=C-AW z$<2~g)T8+rEPAPgzzr`ewrZhY7w#@CJ>xO`#J_ptgM+fN@~2Pmno&?G7^`RMagLT% zii@I_feJqVkB3Pnj-YlBgdR$!o(p0I6^_YXtG#-~mTzd?WPvHkf!zpL!Ml?{z;M{! zS*vJJn<6EHIvI{;z7Df0($_&s$!sK#c1$$CUMomMPT;B7h-Noormh{b>%>=#W&$8p zbrYt$JH;(sH_Or~Xj0uHo4xtjpFKmr$b#IN>v08JoU6{d3LC#;;5kE<&#=TyqOxYLZ1SR$!9?^_zixi&^tCADT0R>g`#K} z_{L2?5AwjCIWNm=4M~>U_m^gEF=|QF?|>5A)?Pg`K~^c3o6n{O58N8g1odbIh4J~$ z>=~dxr`_#!4NwX>=pnnk(p~I2rwb|})jz&1Ijs%FAD!%7|2mlLd4Lkv0ey0_QeFKX z<}=tbn)S^sr!{u(mGCXC+#<_2o5ga0clSYeH1^@&QP*>P@+!Cf_T@9K*h+2VjrpGJEx_buXyF|{{uLN^&tQN literal 13842 zcmeHuWmJ@F7d8x|4k9{$fPlzDHylzzVdw@yIuA%IAR$OI3P>}wv=Snn(s3jdqy!0R zDQQqr`n$(-j<4@IertWdzF#kE;ToL#xpUupU;DcD6MRcmj_^F?c^n)ZLIrtg4ICU? z4E7gs7W|K2lVBGP4uqp1Eus0)crEpuifMr;-JPLpqA@qBx&0=Ru#w1*2r`-w9 zbk7av;iOlV9qyezli`tvDF$?#;K!dF4&mS;P|y6~bVzypU`OHae^J(vr~_cA!>MY>LW2FRR99`#!^9Hx)(4?*$r z^3J_JM-$tVDyn2_Ym1v-P{2WP-99BNCr3$HIqU+JpfWKr@woU@iRt4TUt3Aa+%}AD zK9)Sc@#POZja-dvKc~-R@pP*23a%y*UtH{0nW;jw}+_10k2 zWnAKmxZ!X~2}t~SJ4W;6EeSF%Ll#nYZ4RWov2ohrsZ~!3YgAMeSDSjKtisy}YDPgp zZR3a2XWS2#awdG&4af3zxD$CTLXz2Ual~hQT5*HzMk&VcY40nor~9LPJS<5>*Vfeg-vvFGP_p3c3gMz^}4E( zO_RMz&*E{H4=PTt&yZi9K=Bl#pf>n}+D>E;9jcl3S(IN0N-)A*@s3rFn)i_S6M08? znN{z#J!zghZ^9m#$1tfxS62(^K>6B*(bFwY$d5*h+$IK`&AnLUoaeO(W3 zaeo`P#l^7@AHVhNsEe;RcFev`EuCH+{ za5ka6Xpu5WKl)ODX4B$3T~`M`PITnzwCY4NonBvzh4GBa^Mul3%pQqMSo~-U#-;Jq z=Nj9ud$X=ei&=K;|0V`EyIk9ChG=E-ww3x+jL(8U7o0O+W)yfvgHx4qDiqahX+n=F zp%5S-l!URt_8pJ-oc7nfWpx_rZ43A*MKA`svbhEvfhdmV9DaBR^o$7;?BxE!6s_q9 zce(__Pe=_%HCyx-@Ld62n>R}0Lm7%u=&MEP9^Z{IJ!l)QJaHUYaqmF_lgtUt+hqkmlKQd@!4ma&>PdB6aE# z*XP(eM8~W*(xqi+k(sS{nqB+6Wk(wwZaz;Z_Xmr$3-w78 zsnM5%g9xDC!~LRBObt-sMX<;KmfX|GIw7>~7!NMX(L46yYH@-kFJGUNeDA|uW((u` zhoulecg$zt{6x2i+IzVh!E@9D10%T~d^8##4b+jKuLKn7uP*jwZjRn9?syYU#Yjgd zS?Msvl`85MpLWxeERpi3agCE0bgl`qV?2%V_Yf~aS;}UA!AY|rytx#d{T8(1kn@baQ zRYFaCH(VBPsX}?Tf}i+ZLa^X7tjXATcNL(-L^A6K8je*}A+e95ZT6-sLxsGST~{3K zEe2nzw;aoGFKClhm4t+22xvbkVa;nNE+K2@Ym4mgj`|2+c2A+N9E3HmH+6kxs@oB-B`OeWnk$oek+ShavL&8^+|4xM?h^SMqMbsN;<&F2%se;DUW1j8NMq>2tvTbB_fdb;eP?kaj@<^U&| z6ht}nyDb69J|6>j?_Ph7Abk5K z-oD)sd;j`{7MQR$_!ewVc`=dv$FQV;v$VKz2D+k?54A?+(tfL5Zj)0|@+vl{4zP%= zZ3CB|&P!1WHOpB*?VuX9JAZ*-PTnSXW8@Orzq3acn&!0`q6mY>szT*C@=rEvWD|~G zKi43h!UOhaUk+xckUolhzjwiW!QeFNA}M(Kch=m=28ZV)?Ui<8bGbQb(Oq52dwU@V z+e`G+A};sI)8gV7dwSLXinT)GZ&NXt-|rbn&&|)zQ{SZY452z zrPq;5JRY9SSViQf>FYu*PcFFy^u17_8mV(D9)B=wwAoC+T`lf$-E~zrI5IM!(fjaS zAR(3c_F^Be`=)ttSQtOA6g(q{;_o>45@9}^lI!$92J94)cCdEBa1lOpq>;Bp??y-^ z=%O&;bGWworR7pxlTmC^5*wdY4+rwiOZCjGJ8vVRV`H`iN<-5whs z&(zn~*AJpKeKi#&Yk(%m7$X$@sB3_>}V8OFQ(_KMYwCz z?{6J*2d@3Nf#P3Y5y^i_9PN6^fMMjza#jJS<73G7N(vI(3+o$2w6fT*0cYsiDogMZ zF}HWgN^wCy@JzI$;mqjnG+BI~k=b`QVT~r0F{{iBw$GLbT4r^B7Q~pUxN7QNsFEKHl?T zM0?#(svQzw$GA6Ljr+(>6f^>cqXb^<$8feVcd6PTJ`PMeHF|r7EWYjLoeF*!Pl$mh z4?Kgqv=4-aj)QHLno1wx%;TQprJ&Y|)nHVZMwRXbw61^gbADJK&Y+nhDSGvE6||a8 zl&wpzn=g?zLH~&HG~Y%M3}2M(EH#PS7s}^HWdqM_&YA|RuD`}_{rrMQj|%-XtFVa- z*4O*d{0u4js;-zpt$^UxtHpig&-cU_NV(-8roEs+eMBqUi66Nfgezw&@H`r&UC%g? zbcL+_g_OqEvh@^H!8+yRHliSjk7pHWk$4!w_*=y7KHVIUR)*z(yr}$9UZRw0;ko#8Mm2)Z%W`UsAAc^Iq6f{9 zK{|edGf4Tqfk>CS+{IAIPv_ypFsY*t#G3HvS02v|J?%*|QJ;MhSb4~^0WcXL4Slda zu1Ch9`-zo+`RC4A@y)wEoz`l!ZVv8H^6%A65Iau`AX?c`RE`t14`Rf;589&{mF9pi z1u%#lz#6wweF>1zvQ z+L708&J7tj#63QD>D~M1Qu9T1>(K;U)z8{D5n1xm5)W`;_<4?`BL!e@3y)E?g2l$> z;V9$Z941tQ8=m38@EJ5GYObP{)5VO~v<@~zxh8!M?M6!PJlWouYI^2!xVxrid*|y+ z+ar{V+S_|RaI^TTbz5-WZ;0gU*U1Rx)2r;Cw5CM@bJyYgj4+?9ch`8KOmTe>EXJZO zgp}8;HHhvuJ%*0x%-|UW!^<~X=H<39JvyV_V)gIy*hrpY*gbMhLBa5~+_>bR+c~NL z2A&*7gpQP3b%F%sd@4$H7!THWle&DLC`fOzHXrQh1o4uSkIDom**o#mA5~C)@dU~`Vo-OhCY=CVzwdfG3;#8xzMN1$dugA7^ z;tlsQX<6B`1vyQM{(nU;F#i~q{rjT=1T3QFGQ69viL2QfQE zt%WE^?c8iBCnLJxR9b|2XiIm*;NzoW-}q{O?K8|6FbmRgjg8^r^s7Sm|2R$=_1>Fc z21G+_6un$+Rl%mohZia_V@$tdrF!ii1d}*R*z0E{Hy=OB%MgUXxIn8xZbUPsxPhFcIowV4YLDE-l-?JFSAIiTc0Ee%fMn*=~7f?gRCL3vzXD?o50cJfJ;u!=| zuV=@?{v`j{4AoOf5o51-vvZ6DzNekx8n2eJz2X!)O3rKyA{ZW8x#jf`$y;~dmVxT} zbrjx)CR3i-JV{v6C|qI$!T&)h2>vqPC>80Egts_fXcD#e5SnxhCZyb{IF!lv!7N)< zq5d5Pl2F=8Pn5d7i(?-zJ?0)#+9~IjgvbD%=>Km#VgB}wK>ta61lweVut3o#bFXrtC{7?0wF8OXDU8Uy-9`%@UFzOYQL{>yIl*mIs&6Zx`im8YpepT7 zjEidP1NznzI8POAuiFRyJBpE_j}GqC8a~)>1GB0;Qp)}ClJ(ys4#LYM^qEa;D-I*Y zP9>N5MX3HAph1S~-pN$MJJO}r@|phN+g6-Lu_+1M%(3?hXE-WsX2|#g+FI^jg-y*hb2?{&2O-selF_>?;V6F8tEU<=kht9hU18C0TP@1_L1O!>&bd?1Xkn$* zR$=k7kgVff?m$!py`Vly1v1FC=!};=Jl_7GmhJQS?4KJiro?+F=4hZ)=xN1nkR3kc zz1He2yE4MITP(qD7bmE%BLf*!9x-(2Je}A}_uUQVzo$!szB0l1C*|@J3Z$tz;z(dN zv8gzmPxs{rryP>{w=Ds7@AzA&p=*sExphL0dGx*NJ$u$NGaQ|)Nm#FyWX0ilj z>rS2KHG~%m0CxLDt39eprI~EIaM#lvavH_*^1F0D^8TN;dMf*riQK5a$9gYchcn7K zdFYqc)TlB=OKvx$zI?Ivz=CQSey|IXv#Oeuq=eOvgOMRK{Xx--cB-lY6kim z=7=R*(3<{bU!4y;vzE@JhPkFU?i$rpyIcISRj9-6yO9Mq!O`PD2eS6;1s#!W6o}O3 zi|?Ofohk*tHD*jlTRT3C>bh$GhYyTQOtK(nXTrzFf8!C+?p!-Fp9qw^u$}}oqBY^p z*YVg8l(x@S9lyLq-Qkn3S&%>H+P=`20;p>j4S6`_Z?0L z1~e0eoh{M7$%a!hQj5CAW@RzRIJE%~&^GHbn*Wbpy&9R6C{sNPu@3w6Yh6vWm#H!> zS}5bby~@tUlnIumyE|0icqTeAkriOZ_@xgyEP%!V_o$ zfgo_eaTWdT9u%?$vbXZV!|dm#=zIJ2MTU;*F0|>N}|?|#Y7e$wPwHoB-ZYSi)a2O zjN*(8lS&eUqoZSgo!iFWaWDdeE1~p25W6qFxnls_+_(IDxnmrIcPt!Co4igd4M*tm zP*8ExN$p`+re>~&eKTNGg&k-70YM;V?DbVa60G0F)wP)t7q#&@Jz+rV*`Hcc|K2wfnsm!FqWanR0 zSbH%+D$1hwt#Z36tDA8?loPYMdT-AeS{W{OQ=`Q@cQLlLwKbYls~T_ve>hA21FU#e zFJlHo>YiYayZ7U72BRln*6}K*gNQk`u%>S_1dfwus~o1p_F9B{Z)}B0OG_WkUq%L< zsMwalU_1&2zMQ0Jj{TXS0ipby6Q)zm>8fxC#X6P_9mK z+94@!j}G4D9Z5)r=zNj59Q0bCIHOjJsQny!q`VgiU;3U)t8um} zUG>z0>0?DI<#WrObi(LwcJ!o!y-M5-b7?|~2PZbFkW3We&*L9CcgT42(4#z#-nmX-g?SbHCBy&HjMZ+UTo^jYOckDMlp{$wvh}T86=iHD}q%^hnY5 zhk6m3w$|E^XH^$QRVP*gl2ILcLM}wp?ti+ zu|5`E6waax3j_8+>T1?N4DxkIcowaSN?@=anj z(*Jsap~7|AqyW&*LodH0E-k2Zr>l|R?o@9%Qr_C`b(1k648PEZ9`imaCj|A`+e_JO zg_5kDyF6u_mt()fFx(*0bI;HB1<4055LeT2!)gnT3hrb z)Tc0G7uj*rg!8(M5Cs?6?Zzw~RyEKL8@Dm{wU{}JgwBy^TZbo+{E7RVcrz&Pb7cC*0d=*tfpu!(d!~Z>q%p(t1xy-{n&rf z*qAX@30LD^CRsN1-1I!Wr(pNw&*@IXiOod8Fnyo_IZgC=fv)p&pB%~=UmY*`-g@US zhMB@sPeCw$Phb}Qx>HUt_m#&|8nlzE+{D+$zs9?4_@OSpy>??7fK^Ma6jh=>6Eikg z;_+(ROg}&m1IBHylOhgm#K%eD{<_=z zzkppb5Fhz!LYd<*<=Wo24d-k%t6xT4%dRSV%t{@EO!jxS z>N!|R#s;mMgbzP=5E|Y2iLw7Xy0hV%uZyu#13$=I2HFh2p+km(o^1Zn(`bsfhDdE_ zpO4yVI`?%~0MY)E4la4w!HFNf5ai3lpL1i?>MKAwT@r!?&;Ejh3PHVSIIfeHejqqf zG-tt$Y03sz>Af9UvdQy^?#(y0%ol7-3*x&a#jB{T%j-)=b=)TJV*j>P@%Gd_>9m5dOc4mwN&)9d83Y8hEPGP)ps>G1cvjY2^MISi;iwyrIxk59 z^8LqbW8s^YFXrO;*fAP%WQtR1TB_`rbe{u-YaWZV?mL4zjC_2zfhw^2j^Q-!KbpY* z9a|{7)8BTYCnT6#lw2BK8GvSxy)MZvI=l+9a09WdP_KTFDdRgo$6T}YE#`_P=(LjXh0CE6ZIHf@JN^#SqN-fag zv1I^VPkAz*wPz{BFmQ3HdhD$m{Y_pt5-9ydH2{aq>s&RL($|L#{uU7p+*P}|`io@o z^pVVI51UC_)~cqAF&}~EUgwsOdpk!_Q#1CkS1#-dEh{T)bV|xoaz(? zip{njNUujwm&eqJ+rx~U>I?MHu{Tc|$347XNx~9^95Z6sbyU}@XCq>)u|yH65>^y& zGZn6?c%hs~2NZZ62A%r*aE%+PISuFkt3RUlxnZ}Cue>_nnSe6k(78$~AUt42t9S$Z zC{U?ErZ{)Rx1cBQ>RNdyjM-TkuFJuo@y;olnVEHjuo(gw`H>2GJcWq>Brcsn9~`FEGXs2ZB~wd+7T1~&=t z^-Jd(fogr@b~1$)d~%Vj} z#wiEMS9|VSRUGrt$G4|mAO7@j*v^JOC*IWY+^`(RYGNBaK7vircr8JSR$l)=M+&ON zPwp;9#GVFdrDhJtxxzcD^=599uwev4M@nDwzY;IE#DCKpc{8z{zts`9S2ox-0z*dqJedBPm zxa@RMOZG4_!jK7X{bdVKi2s>061OWgDGo&q$<{@(?nPbrCJJ({oA29FXzXo9%CakL z2lLto{z>ODHExHlir&+PO09et_Kq63m@#zBE3*X1_Kp^XEB(!HNeB$@A{g#-9Agw@ zpx*j#ptVbOpG9l@sqWcHpkhc~a^ZnLLctnxj^a0#dpwVaqT_wQqPDZr=l334o*~wm zzC*Dc>5+n}SUszvkZQ8&v7DRpovD5%frkar6?cat40L^Gy78qyd@heALmvq|T(B}r z_nEdDNjBQHTSY~bXbdp(E^@(%2X>i>-6J+l^PNV@rMZssh~4Zl3di zXFQ;yREt1`khS|S_(nIJyFidN!^~Ul>#YjCIhB3Lv6v#3GDYh1wT+yB7t8Mgs=HPa zW;1m%SNs5!E$}qSFGkgwv^+dJGu-iTMQNUF=ytDN+|5G+5B61_Jg4m(-nn(aLC}lO znzHK@#qMt{gl>(~pY8y~RS7V-!sz=1o!@-;wJ_fy$Jbu_xUOTJrye>H4 zP>iylI>WH#PV@T<>D=-`MXW8)@h*L79oi znBq}65#(o4jegz{Kl<`m`RQ-3y~(vfK@up?k?j#b^#W&i&t9nz2ylHc_n9Rci{Xt9 z{e>@09rb;}bIY0RtuoHh( zETD_SQLx?tOIcvHVy5M*<9BqSxP(sc$5hU+(i$7(Az6sYW~Dd)waq7}Cm{pPzHx}3 z;^pQWDH?cLCVAq(@=K~9GJJ}mX$@2$$1Q+;`Kv_dhg8J|qd07p9clbfg32q7o!(1p`=wJlI{X?QT{^1~ zv!+=1je8D#_=~O-VH8yol@w(1c5=|E+pq67@8?p&%@v1)r}uQ02Xq-v>zhhYWo2dN z|?d~5-Ort^lKt=Hf($MzK&dx0vW{FWa48Q+%>I(pE^tS-rnR(~Kd_q`g zI4sv-MTvr^-Ug`YA4~5riiu4gkKixV9sXUO*WNCl*^rud1*U!#_a8}O-|5Ng5Y)*O z-oj->>;A^!zxe=$@=M={f@UmlXbJr(NdC!Mf@N+!KB_Ma2rN=fFEd^%o;y9>x`Z~j zw0!sJ^R%2nwqo0V~2N?3fAXG^gH>4dvytvQN!@btNViR$YdG&A>vfh3n4+;TZ|s%*Y0> zj*Fmv95Tm;Fc4MYR7fBc9GBq#z!;F_Y5n?%d1B^9#N|t*XH{Ayear9)*Vcs)Q+SC2oEY^q3tKUdRD~nu zg;bJXNrIYn1cdwl_ytD!5FomEY@LX#Y#@-c6oJpgT7X2q_t&xU3Y(bvqs`8;2P@jF zIz=hlt0T8-UDx!F;1VwLpyqPE^zAj01-D`vkHzQEa{aeLk-MJL0p#XO{n;9o_w;I} z1F6OvI@$RVV7C%ypV0WQU?C(p*G^FPGN0zT9tAq-6kbRnVPI!5PNwntf*#1zQUYbR z<{Og@*d(B{>GasaZQ}X%XoF|f;n+}jl0ZyXR~Lq~-E*UX8Jji8U2~U?xGGF16?h@) z+_i_OBz_xW#n}sEufUkp4O`>@v&HwxD;WY1o?Jjl@bHq9R{gDUlbz7pFh`s_mI)+r7>4kJ*9Y#C?HzZZ3q#}>%1iRUMH=ZDx#pF5iR=W`sBkF z?+>WclVd6O0@HFqI59)!$?llLgc}nOjNf^a$8RAUfLmH;Wr#KI5p}&P9QgP~^E7;> z-t+zbrAJ3@ri7II@>r@DR6ZNC*o~DK5y0@H`yxTHG^m=Mo$PTu$*%_+>~y>{tOR&6 zOu@5eTpyM(1oSt58jhC$(-(<%og z_O%fa+&@de!3!eI(Ks;t&eC#g?0X^0;3X3gDPHUwA_yfIf`Q|0PX;Y`E9E5#3R=F< zfqg#(;sAHP?BAh>PMRB_NR`55rP+au@_2EYFe@V_nk|8GU7G!%nlHLAwCojBk} NK}J=&Nb>hb{|6#4(%k?6 diff --git a/test/image/mocks/heatmap_categoryorder.json b/test/image/mocks/heatmap_categoryorder.json index c6e907f7b6f..be1c521e398 100644 --- a/test/image/mocks/heatmap_categoryorder.json +++ b/test/image/mocks/heatmap_categoryorder.json @@ -14,7 +14,7 @@ "data": [{ "type": "heatmap", - "x": [3, 2, 1, 0], + "x": ["z", "y", "x", "w"], "y": ["d", "c", "b", "a"], "z": [ [100, 75, 50, 0], diff --git a/test/jasmine/tests/contour_test.js b/test/jasmine/tests/contour_test.js index b695fa3fa5d..72ee6ad6be1 100644 --- a/test/jasmine/tests/contour_test.js +++ b/test/jasmine/tests/contour_test.js @@ -183,17 +183,28 @@ describe('contour makeColorMap', function() { describe('contour calc', function() { 'use strict'; - function _calc(opts) { + function _calc(opts, layout) { var base = { type: 'contour' }; var trace = Lib.extendFlat({}, base, opts); var gd = { data: [trace] }; + if(layout) gd.layout = layout; supplyAllDefaults(gd); var fullTrace = gd._fullData[0]; + var fullLayout = gd._fullLayout; fullTrace._extremes = {}; + // we used to call ax.setScale during supplyDefaults, and this had a + // fallback to provide _categories and _categoriesMap. Now neither of + // those is true... anyway the right way to do this though is + // ax.clearCalc. + fullLayout.xaxis.clearCalc(); + fullLayout.yaxis.clearCalc(); + var out = Contour.calc(gd, fullTrace)[0]; out.trace = fullTrace; + out._xcategories = fullLayout.xaxis._categories; + out._ycategories = fullLayout.yaxis._categories; return out; } @@ -343,6 +354,55 @@ describe('contour calc', function() { }); }); }); + + ['contour'].forEach(function(traceType) { + it('should sort z data based on axis categoryorder for ' + traceType, function() { + var mock = require('@mocks/heatmap_categoryorder'); + var data = mock.data[0]; + data.type = traceType; + var layout = mock.layout; + + // sort x axis categories + var mockLayout = Lib.extendDeep({}, layout); + var out = _calc(data, mockLayout); + mockLayout.xaxis.categoryorder = 'category ascending'; + var out1 = _calc(data, mockLayout); + + expect(out._xcategories).toEqual(out1._xcategories.slice().reverse()); + // Check z data is also sorted + for(var i = 0; i < out.z.length; i++) { + expect(out1.z[i]).toEqual(out.z[i].slice().reverse()); + } + + // sort y axis categories + mockLayout = Lib.extendDeep({}, layout); + out = _calc(data, mockLayout); + mockLayout.yaxis.categoryorder = 'category ascending'; + out1 = _calc(data, mockLayout); + + expect(out._ycategories).toEqual(out1._ycategories.slice().reverse()); + // Check z data is also sorted + expect(out1.z).toEqual(out.z.slice().reverse()); + }); + + it('should sort z data based on axis categoryarray ' + traceType, function() { + var mock = require('@mocks/heatmap_categoryorder'); + var data = mock.data[0]; + data.type = traceType; + var layout = mock.layout; + + layout.xaxis.categoryorder = 'array'; + layout.xaxis.categoryarray = ['x', 'z', 'y', 'w']; + layout.yaxis.categoryorder = 'array'; + layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; + + var out = _calc(data, layout); + + expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); + expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); + expect(out.z[0][0]).toEqual(65); + }); + }); }); describe('contour plotting and editing', function() { diff --git a/test/jasmine/tests/heatmap_test.js b/test/jasmine/tests/heatmap_test.js index 3fb0bfdcf20..25d098fe199 100644 --- a/test/jasmine/tests/heatmap_test.js +++ b/test/jasmine/tests/heatmap_test.js @@ -492,49 +492,53 @@ describe('heatmap calc', function() { expect(out.z).toBeCloseTo2DArray([[1, 2, 3], [3, 1, 2]]); }); - it('should handle axis categoryorder', function() { - var mock = require('@mocks/heatmap_categoryorder'); - var data = mock.data[0]; - var layout = mock.layout; - - // sort x axis categories - var mockLayout = Lib.extendDeep({}, layout); - var out = _calc(data, mockLayout); - mockLayout.xaxis.categoryorder = 'category ascending'; - var out1 = _calc(data, mockLayout); - - expect(out._xcategories).toEqual(out1._xcategories.reverse()); - // Check z data is also sorted - for(var i = 0; i < out.z.length; i++) { - expect(out1.z[i]).toEqual(out.z[i].reverse()); - } + ['heatmap'].forEach(function(traceType) { // TODO: add heatmapgl here when plotly.js issue #3833 ? + it('should sort z data based on axis categoryorder for ' + traceType, function() { + var mock = require('@mocks/heatmap_categoryorder'); + var data = mock.data[0]; + data.type = traceType; + var layout = mock.layout; + + // sort x axis categories + var mockLayout = Lib.extendDeep({}, layout); + var out = _calc(data, mockLayout); + mockLayout.xaxis.categoryorder = 'category ascending'; + var out1 = _calc(data, mockLayout); + + expect(out._xcategories).toEqual(out1._xcategories.slice().reverse()); + // Check z data is also sorted + for(var i = 0; i < out.z.length; i++) { + expect(out1.z[i]).toEqual(out.z[i].slice().reverse()); + } - // sort y axis categories - mockLayout = Lib.extendDeep({}, layout); - out = _calc(data, mockLayout); - mockLayout.yaxis.categoryorder = 'category ascending'; - out1 = _calc(data, mockLayout); + // sort y axis categories + mockLayout = Lib.extendDeep({}, layout); + out = _calc(data, mockLayout); + mockLayout.yaxis.categoryorder = 'category ascending'; + out1 = _calc(data, mockLayout); - expect(out._ycategories).toEqual(out1._ycategories.reverse()); - // Check z data is also sorted - expect(out1.z).toEqual(out.z.reverse()); - }); + expect(out._ycategories).toEqual(out1._ycategories.slice().reverse()); + // Check z data is also sorted + expect(out1.z).toEqual(out.z.slice().reverse()); + }); - it('should handle axis categoryarray', function() { - var mock = require('@mocks/heatmap_categoryorder'); - var data = mock.data[0]; - var layout = mock.layout; + it('should sort z data based on axis categoryarray ' + traceType, function() { + var mock = require('@mocks/heatmap_categoryorder'); + var data = mock.data[0]; + data.type = traceType; + var layout = mock.layout; - layout.xaxis.categoryorder = 'array'; - layout.xaxis.categoryarray = [2, 3, 0, 1]; - layout.yaxis.categoryorder = 'array'; - layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; + layout.xaxis.categoryorder = 'array'; + layout.xaxis.categoryarray = ['x', 'z', 'y', 'w']; + layout.yaxis.categoryorder = 'array'; + layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; - var out = _calc(data, layout); + var out = _calc(data, layout); - expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); - expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); - expect(out.z[0][0]).toEqual(65); + expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); + expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); + expect(out.z[0][0]).toEqual(65); + }); }); }); From 22cfa82d3a20b7be084102a8c0682b4015a08076 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Sun, 5 May 2019 19:58:50 -0400 Subject: [PATCH 10/21] :lock: down category ordering in histogram2d(contour) with tests --- test/jasmine/tests/histogram2d_test.js | 75 +++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/test/jasmine/tests/histogram2d_test.js b/test/jasmine/tests/histogram2d_test.js index 5a529fb548f..d837b0e8c2f 100644 --- a/test/jasmine/tests/histogram2d_test.js +++ b/test/jasmine/tests/histogram2d_test.js @@ -116,16 +116,29 @@ describe('Test histogram2d', function() { describe('calc', function() { - function _calc(opts) { + function _calc(opts, layout) { var base = { type: 'histogram2d' }; var trace = Lib.extendFlat({}, base, opts); var gd = { data: [trace] }; + if(layout) gd.layout = layout; supplyAllDefaults(gd); var fullTrace = gd._fullData[0]; + var fullLayout = gd._fullLayout; + + fullTrace._extremes = {}; + + // we used to call ax.setScale during supplyDefaults, and this had a + // fallback to provide _categories and _categoriesMap. Now neither of + // those is true... anyway the right way to do this though is + // ax.clearCalc. + fullLayout.xaxis.clearCalc(); + fullLayout.yaxis.clearCalc(); var out = calc(gd, fullTrace); - delete out.trace; + out._xcategories = fullLayout.xaxis._categories; + out._ycategories = fullLayout.yaxis._categories; + return out; } @@ -157,6 +170,64 @@ describe('Test histogram2d', function() { [0, 0, 0, 1] ]); }); + + ['histogram2d', 'histogram2dcontour'].forEach(function(traceType) { + it('should sort z data based on axis categoryorder for ' + traceType, function() { + var mock = require('@mocks/heatmap_categoryorder'); + var mockCopy = Lib.extendDeep({}, mock); + var data = mockCopy.data[0]; + data.type = traceType; + var layout = mockCopy.layout; + + // sort x axis categories + var mockLayout = Lib.extendDeep({}, layout); + var out = _calc(data, mockLayout); + mockLayout.xaxis.categoryorder = 'category ascending'; + var out1 = _calc(data, mockLayout); + + expect(out._xcategories).toEqual(out1._xcategories.slice().reverse()); + // Check z data is also sorted + for(var i = 0; i < out.z.length; i++) { + expect(out1.z[i]).toEqual(out.z[i].slice().reverse()); + } + + // sort y axis categories + mockLayout = Lib.extendDeep({}, layout); + out = _calc(data, mockLayout); + mockLayout.yaxis.categoryorder = 'category ascending'; + out1 = _calc(data, mockLayout); + + expect(out._ycategories).toEqual(out1._ycategories.slice().reverse()); + // Check z data is also sorted + expect(out1.z).toEqual(out.z.slice().reverse()); + }); + + it('should sort z data based on axis categoryarray ' + traceType, function() { + var mock = require('@mocks/heatmap_categoryorder'); + var mockCopy = Lib.extendDeep({}, mock); + var data = mockCopy.data[0]; + data.type = traceType; + var layout = mockCopy.layout; + + layout.xaxis.categoryorder = 'array'; + layout.xaxis.categoryarray = ['x', 'z', 'y', 'w']; + layout.yaxis.categoryorder = 'array'; + layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; + + var out = _calc(data, layout); + layout.xaxis.categoryorder = 'array'; + layout.xaxis.categoryarray = ['x', 'z', 'y', 'w']; + layout.yaxis.categoryorder = 'array'; + layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; + + expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); + expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); + var offset = 0; + if(traceType === 'histogram2dcontour') offset = 1; + expect(out.z[0 + offset][0 + offset]).toEqual(0); + expect(out.z[0 + offset][3 + offset]).toEqual(1); + }); + }); }); describe('restyle / relayout interaction', function() { From 02e4089154d29fa24da0a36a73d9dc8c2724811b Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Sun, 5 May 2019 20:07:34 -0400 Subject: [PATCH 11/21] :lock: down category ordering in heatmapgl with tests --- test/jasmine/tests/heatmap_test.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/jasmine/tests/heatmap_test.js b/test/jasmine/tests/heatmap_test.js index 25d098fe199..db9c47376be 100644 --- a/test/jasmine/tests/heatmap_test.js +++ b/test/jasmine/tests/heatmap_test.js @@ -492,12 +492,13 @@ describe('heatmap calc', function() { expect(out.z).toBeCloseTo2DArray([[1, 2, 3], [3, 1, 2]]); }); - ['heatmap'].forEach(function(traceType) { // TODO: add heatmapgl here when plotly.js issue #3833 ? + ['heatmap', 'heatmapgl'].forEach(function(traceType) { it('should sort z data based on axis categoryorder for ' + traceType, function() { var mock = require('@mocks/heatmap_categoryorder'); - var data = mock.data[0]; + var mockCopy = Lib.extendDeep({}, mock); + var data = mockCopy.data[0]; data.type = traceType; - var layout = mock.layout; + var layout = mockCopy.layout; // sort x axis categories var mockLayout = Lib.extendDeep({}, layout); @@ -524,9 +525,10 @@ describe('heatmap calc', function() { it('should sort z data based on axis categoryarray ' + traceType, function() { var mock = require('@mocks/heatmap_categoryorder'); - var data = mock.data[0]; + var mockCopy = Lib.extendDeep({}, mock); + var data = mockCopy.data[0]; data.type = traceType; - var layout = mock.layout; + var layout = mockCopy.layout; layout.xaxis.categoryorder = 'array'; layout.xaxis.categoryarray = ['x', 'z', 'y', 'w']; From 5160948d6813b5de162fb6a68af44bd7be1bec9b Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Sun, 5 May 2019 20:20:54 -0400 Subject: [PATCH 12/21] test: tag @flaky test, :hocho: useless lines --- test/jasmine/tests/histogram2d_test.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/jasmine/tests/histogram2d_test.js b/test/jasmine/tests/histogram2d_test.js index d837b0e8c2f..64efbc0ed36 100644 --- a/test/jasmine/tests/histogram2d_test.js +++ b/test/jasmine/tests/histogram2d_test.js @@ -172,7 +172,7 @@ describe('Test histogram2d', function() { }); ['histogram2d', 'histogram2dcontour'].forEach(function(traceType) { - it('should sort z data based on axis categoryorder for ' + traceType, function() { + it('@flaky should sort z data based on axis categoryorder for ' + traceType, function() { var mock = require('@mocks/heatmap_categoryorder'); var mockCopy = Lib.extendDeep({}, mock); var data = mockCopy.data[0]; @@ -215,10 +215,6 @@ describe('Test histogram2d', function() { layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; var out = _calc(data, layout); - layout.xaxis.categoryorder = 'array'; - layout.xaxis.categoryarray = ['x', 'z', 'y', 'w']; - layout.yaxis.categoryorder = 'array'; - layout.yaxis.categoryarray = ['a', 'd', 'b', 'c']; expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); From e966030185491caa08982ce795baf2f82e8528ef Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Mon, 6 May 2019 18:57:12 -0400 Subject: [PATCH 13/21] categorical heatmap: handle missing categories, 1D data and hover --- src/traces/heatmap/clean_2d_array.js | 24 ++++++--- src/traces/heatmap/convert_column_xyz.js | 8 +++ src/traces/heatmap/hover.js | 4 ++ test/image/baselines/heatmap_columnar.png | Bin 0 -> 21638 bytes .../baselines/heatmap_shared_categories.png | Bin 0 -> 29620 bytes test/image/mocks/heatmap_columnar.json | 13 +++++ .../mocks/heatmap_shared_categories.json | 49 ++++++++++++++++++ test/jasmine/tests/heatmap_test.js | 20 ++++++- 8 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 test/image/baselines/heatmap_columnar.png create mode 100644 test/image/baselines/heatmap_shared_categories.png create mode 100644 test/image/mocks/heatmap_columnar.json create mode 100644 test/image/mocks/heatmap_shared_categories.json diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 43fc5056883..3e067bbacf4 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -31,21 +31,29 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { old2new = function(zOld, i, j) { return zOld[i][j]; }; } - var xMap = Lib.identity; - var yMap = Lib.identity; - if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet') { - if(ya && ya.type === 'category' && trace._y.length) { - yMap = function(i) {return trace._y[i];}; - } - if(xa && xa.type === 'category' && trace._x.length) { - xMap = function(i) {return trace._x[i];}; + function axisMapping(ax) { + if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet' && + ax && ax.type === 'category' && trace['_' + ax._id.charAt(0)].length) { + var axMapping = []; + for(i = 0; i < ax._categories.length; i++) { + axMapping.push((trace['_' + ax._id.charAt(0) + 'Map'] || trace[ax._id.charAt(0)]).indexOf(ax._categories[i])); + } + console.log('axMapping', axMapping); + return function(i) {return axMapping[i] === -1 ? undefined : axMapping[i];}; + } else { + return Lib.identity; } } + var xMap = axisMapping(xa); + var yMap = axisMapping(ya); + var zNew = new Array(rowlen); + if(ya && ya.type === 'category') rowlen = ya._categories.length; for(i = 0; i < rowlen; i++) { collen = getCollen(zOld, i); + if(xa && xa.type === 'category') collen = xa._categories.length; zNew[i] = new Array(collen); for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(old2new(zOld, yMap(i), xMap(j))); } diff --git a/src/traces/heatmap/convert_column_xyz.js b/src/traces/heatmap/convert_column_xyz.js index ace8224f877..ac441eb0778 100644 --- a/src/traces/heatmap/convert_column_xyz.js +++ b/src/traces/heatmap/convert_column_xyz.js @@ -65,4 +65,12 @@ module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, } if(hasColumnText) trace._text = text; if(hasColumnHoverText) trace._hovertext = hovertext; + + if(ax1 && ax1.type === 'category') { + trace['_' + var1Name + 'Map'] = col1vals.map(function(v) { return ax1._categories[v];}); + } + + if(ax2 && ax2.type === 'category') { + trace['_' + var2Name + 'Map'] = col2vals.map(function(v) { return ax2._categories[v];}); + } }; diff --git a/src/traces/heatmap/hover.js b/src/traces/heatmap/hover.js index 55e9f63d195..7c0deec8e0a 100644 --- a/src/traces/heatmap/hover.js +++ b/src/traces/heatmap/hover.js @@ -78,6 +78,10 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay } else { xl = xc ? xc[nx] : ((x[nx] + x[nx + 1]) / 2); yl = yc ? yc[ny] : ((y[ny] + y[ny + 1]) / 2); + + if(xa && xa.type === 'category') xl = x[nx]; + if(ya && ya.type === 'category') yl = y[ny]; + if(trace.zsmooth) { x0 = x1 = xa.c2p(xl); y0 = y1 = ya.c2p(yl); diff --git a/test/image/baselines/heatmap_columnar.png b/test/image/baselines/heatmap_columnar.png new file mode 100644 index 0000000000000000000000000000000000000000..65a278a36eea0343ac7f81a151397a795dcbe4ac GIT binary patch literal 21638 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 literal 0 HcmV?d00001 diff --git a/test/image/baselines/heatmap_shared_categories.png b/test/image/baselines/heatmap_shared_categories.png new file mode 100644 index 0000000000000000000000000000000000000000..10b085bd09d1f74df32f38e1f6da71457f02ffd3 GIT binary patch literal 29620 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 literal 0 HcmV?d00001 diff --git a/test/image/mocks/heatmap_columnar.json b/test/image/mocks/heatmap_columnar.json new file mode 100644 index 00000000000..8c75c673a0e --- /dev/null +++ b/test/image/mocks/heatmap_columnar.json @@ -0,0 +1,13 @@ +{ + "data": [{ + "type": "heatmap", + "x": ["a", "a", "a", "b", "b", "b", "c", "c", "c"], + "y": ["A", "B", "C", "A", "B", "C", "A", "B", "C"], + "z": [0, 50, 100, 50, 0, 255, 100, 510, 1010] + }], + "layout": { + "xaxis": { + "categoryorder": "category descending" + } + } +} diff --git a/test/image/mocks/heatmap_shared_categories.json b/test/image/mocks/heatmap_shared_categories.json new file mode 100644 index 00000000000..0230e40faa0 --- /dev/null +++ b/test/image/mocks/heatmap_shared_categories.json @@ -0,0 +1,49 @@ +{ + "data": [{ + "type": "heatmap", + "x": ["Team A", "Team B", "Team C"], + "xaxis": "x", + "y": ["Game Three", "Game Two", "Game One"], + "z": [ + [0.1, 0.3, 0.5], + [1, 0.8, 0.6], + [0.6, 0.4, 0.2] + ], + "yaxis": "y" + }, { + "type": "heatmap", + "x": ["Team B", "Team C"], + "xaxis": "x", + "y": ["Game Three", "Game Two", "Game One"], + "z": [ + [0.3, 0.5], + [0.8, 0.6], + [0.4, 0.2] + ], + "yaxis": "y2" + }], + "layout": { + "xaxis": { + "anchor": "y2", + "domain": [0, 1], + "type": "category", + "range": [-0.5, 2.5], + "autorange": true + }, + "yaxis": { + "anchor": "free", + "domain": [0.575, 1], + "position": 0, + "type": "category", + "range": [-0.5, 2.5], + "autorange": true + }, + "yaxis2": { + "anchor": "x", + "domain": [0, 0.425], + "type": "category", + "range": [-0.5, 2.5], + "autorange": true + } + } +} diff --git a/test/jasmine/tests/heatmap_test.js b/test/jasmine/tests/heatmap_test.js index db9c47376be..aa85f05d7bd 100644 --- a/test/jasmine/tests/heatmap_test.js +++ b/test/jasmine/tests/heatmap_test.js @@ -539,7 +539,7 @@ describe('heatmap calc', function() { expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); - expect(out.z[0][0]).toEqual(65); + expect(out.z[0][0]).toEqual(0); }); }); }); @@ -781,6 +781,24 @@ describe('heatmap hover', function() { }); }); + describe('with sorted categories', function() { + beforeAll(function(done) { + gd = createGraphDiv(); + + var mock = require('@mocks/heatmap_categoryorder.json'); + var mockCopy = Lib.extendDeep({}, mock); + + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done); + }); + afterAll(destroyGraphDiv); + + it('should find closest point (case 1) and should', function() { + var pt = _hover(gd, 3, 1)[0]; + expect(pt.index).toEqual([1, 3], 'have correct index'); + assertLabels(pt, 2.5, 0.5, 0); + }); + }); + describe('for xyz-column traces', function() { beforeAll(function(done) { gd = createGraphDiv(); From ae3ecf5fa81f1569c346d4aa397b85524f3d5a59 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Mon, 6 May 2019 19:02:19 -0400 Subject: [PATCH 14/21] categorical contour: fix jasmine test --- src/traces/heatmap/clean_2d_array.js | 1 - test/jasmine/tests/contour_test.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 3e067bbacf4..72a35ab5f2f 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -38,7 +38,6 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { for(i = 0; i < ax._categories.length; i++) { axMapping.push((trace['_' + ax._id.charAt(0) + 'Map'] || trace[ax._id.charAt(0)]).indexOf(ax._categories[i])); } - console.log('axMapping', axMapping); return function(i) {return axMapping[i] === -1 ? undefined : axMapping[i];}; } else { return Lib.identity; diff --git a/test/jasmine/tests/contour_test.js b/test/jasmine/tests/contour_test.js index 72ee6ad6be1..7b694037b09 100644 --- a/test/jasmine/tests/contour_test.js +++ b/test/jasmine/tests/contour_test.js @@ -400,7 +400,7 @@ describe('contour calc', function() { expect(out._xcategories).toEqual(layout.xaxis.categoryarray, 'xaxis should reorder'); expect(out._ycategories).toEqual(layout.yaxis.categoryarray, 'yaxis should reorder'); - expect(out.z[0][0]).toEqual(65); + expect(out.z[0][0]).toEqual(0); }); }); }); From e68c1706903cd59aa736f4d6387c729dd63551f7 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Mon, 6 May 2019 20:09:20 -0400 Subject: [PATCH 15/21] categorical 2dMap: fill missing category in input data with undefined --- src/traces/heatmap/clean_2d_array.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 72a35ab5f2f..726c417b10e 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -31,6 +31,11 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { old2new = function(zOld, i, j) { return zOld[i][j]; }; } + var padOld2new = function(zOld, i, j) { + if(i === undefined || j === undefined) return undefined; + return old2new(zOld, i, j); + }; + function axisMapping(ax) { if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet' && ax && ax.type === 'category' && trace['_' + ax._id.charAt(0)].length) { @@ -51,10 +56,13 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { if(ya && ya.type === 'category') rowlen = ya._categories.length; for(i = 0; i < rowlen; i++) { - collen = getCollen(zOld, i); - if(xa && xa.type === 'category') collen = xa._categories.length; + if(xa && xa.type === 'category') { + collen = xa._categories.length; + } else { + collen = getCollen(zOld, i); + } zNew[i] = new Array(collen); - for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(old2new(zOld, yMap(i), xMap(j))); + for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(padOld2new(zOld, yMap(i), xMap(j))); } return zNew; From ebf545ff33bd2f9fee38b516d73c891a2a2d1bee Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 7 May 2019 11:30:24 -0400 Subject: [PATCH 16/21] categorial 2dMap: replace undefined with BADNUM --- src/traces/heatmap/clean_2d_array.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 726c417b10e..61eb1d6165a 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -10,6 +10,7 @@ var isNumeric = require('fast-isnumeric'); var Lib = require('../../lib'); +var BADNUM = require('../../constants/numerical').BADNUM; module.exports = function clean2dArray(zOld, trace, xa, ya) { var rowlen, collen, getCollen, old2new, i, j; @@ -19,7 +20,7 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { return +v; } - if(trace && trace.transpose) { + if(trace.transpose) { rowlen = 0; for(i = 0; i < zOld.length; i++) rowlen = Math.max(rowlen, zOld[i].length); if(rowlen === 0) return false; @@ -39,11 +40,12 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { function axisMapping(ax) { if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet' && ax && ax.type === 'category' && trace['_' + ax._id.charAt(0)].length) { + var axLetter = ax._id.charAt(0); var axMapping = []; for(i = 0; i < ax._categories.length; i++) { - axMapping.push((trace['_' + ax._id.charAt(0) + 'Map'] || trace[ax._id.charAt(0)]).indexOf(ax._categories[i])); + axMapping.push((trace['_' + axLetter + 'Map'] || trace[axLetter]).indexOf(ax._categories[i])); } - return function(i) {return axMapping[i] === -1 ? undefined : axMapping[i];}; + return function(i) {return axMapping[i] === -1 ? BADNUM : axMapping[i];}; } else { return Lib.identity; } From 793c2a346e07d4dfa5504a20dd95d6fa47290ff0 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 7 May 2019 11:30:40 -0400 Subject: [PATCH 17/21] categorical 2dMap: update mock and baseline --- .../baselines/heatmap_shared_categories.png | Bin 29620 -> 27121 bytes .../mocks/heatmap_shared_categories.json | 9 +++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/test/image/baselines/heatmap_shared_categories.png b/test/image/baselines/heatmap_shared_categories.png index 10b085bd09d1f74df32f38e1f6da71457f02ffd3..93d8fb7a4c64725da91c2aa5c45ff1e18618fed6 100644 GIT binary patch literal 27121 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 literal 29620 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 diff --git a/test/image/mocks/heatmap_shared_categories.json b/test/image/mocks/heatmap_shared_categories.json index 0230e40faa0..c33fa64e298 100644 --- a/test/image/mocks/heatmap_shared_categories.json +++ b/test/image/mocks/heatmap_shared_categories.json @@ -9,7 +9,8 @@ [1, 0.8, 0.6], [0.6, 0.4, 0.2] ], - "yaxis": "y" + "yaxis": "y", + "coloraxis": "coloraxis" }, { "type": "heatmap", "x": ["Team B", "Team C"], @@ -20,7 +21,8 @@ [0.8, 0.6], [0.4, 0.2] ], - "yaxis": "y2" + "yaxis": "y2", + "coloraxis": "coloraxis" }], "layout": { "xaxis": { @@ -44,6 +46,9 @@ "type": "category", "range": [-0.5, 2.5], "autorange": true + }, + "coloraxis": { + "colorscale": "RdBu" } } } From 4d4b0b748d71fb6ef272369de06f4d00c6e45e43 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 7 May 2019 11:48:33 -0400 Subject: [PATCH 18/21] categorical 2dmap: fix flaky tests --- src/traces/heatmap/clean_2d_array.js | 2 +- test/jasmine/tests/contour_test.js | 10 ++++++---- test/jasmine/tests/histogram2d_test.js | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 61eb1d6165a..f185a89231e 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -20,7 +20,7 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { return +v; } - if(trace.transpose) { + if(trace && trace.transpose) { rowlen = 0; for(i = 0; i < zOld.length; i++) rowlen = Math.max(rowlen, zOld[i].length); if(rowlen === 0) return false; diff --git a/test/jasmine/tests/contour_test.js b/test/jasmine/tests/contour_test.js index 7b694037b09..7197f63c408 100644 --- a/test/jasmine/tests/contour_test.js +++ b/test/jasmine/tests/contour_test.js @@ -358,9 +358,10 @@ describe('contour calc', function() { ['contour'].forEach(function(traceType) { it('should sort z data based on axis categoryorder for ' + traceType, function() { var mock = require('@mocks/heatmap_categoryorder'); - var data = mock.data[0]; + var mockCopy = Lib.extendDeep({}, mock); + var data = mockCopy.data[0]; data.type = traceType; - var layout = mock.layout; + var layout = mockCopy.layout; // sort x axis categories var mockLayout = Lib.extendDeep({}, layout); @@ -387,9 +388,10 @@ describe('contour calc', function() { it('should sort z data based on axis categoryarray ' + traceType, function() { var mock = require('@mocks/heatmap_categoryorder'); - var data = mock.data[0]; + var mockCopy = Lib.extendDeep({}, mock); + var data = mockCopy.data[0]; data.type = traceType; - var layout = mock.layout; + var layout = mockCopy.layout; layout.xaxis.categoryorder = 'array'; layout.xaxis.categoryarray = ['x', 'z', 'y', 'w']; diff --git a/test/jasmine/tests/histogram2d_test.js b/test/jasmine/tests/histogram2d_test.js index 64efbc0ed36..8188745b4ae 100644 --- a/test/jasmine/tests/histogram2d_test.js +++ b/test/jasmine/tests/histogram2d_test.js @@ -172,7 +172,7 @@ describe('Test histogram2d', function() { }); ['histogram2d', 'histogram2dcontour'].forEach(function(traceType) { - it('@flaky should sort z data based on axis categoryorder for ' + traceType, function() { + it('should sort z data based on axis categoryorder for ' + traceType, function() { var mock = require('@mocks/heatmap_categoryorder'); var mockCopy = Lib.extendDeep({}, mock); var data = mockCopy.data[0]; From f180c799c15eb269e62a500d489adc3713bba935 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 7 May 2019 12:36:59 -0400 Subject: [PATCH 19/21] categorical 2dmap: do not use indexOf to find trace-level category mapping --- src/traces/heatmap/clean_2d_array.js | 11 ++++++----- src/traces/heatmap/convert_column_xyz.js | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index f185a89231e..36671800f42 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -33,7 +33,7 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { } var padOld2new = function(zOld, i, j) { - if(i === undefined || j === undefined) return undefined; + if(i === BADNUM || j === BADNUM) return BADNUM; return old2new(zOld, i, j); }; @@ -41,11 +41,12 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet' && ax && ax.type === 'category' && trace['_' + ax._id.charAt(0)].length) { var axLetter = ax._id.charAt(0); - var axMapping = []; - for(i = 0; i < ax._categories.length; i++) { - axMapping.push((trace['_' + axLetter + 'Map'] || trace[axLetter]).indexOf(ax._categories[i])); + var axMapping = {}; + var traceCategories = trace['_' + axLetter + 'CategoryMap'] || trace[axLetter]; + for(i = 0; i < traceCategories.length; i++) { + axMapping[traceCategories[i]] = i; } - return function(i) {return axMapping[i] === -1 ? BADNUM : axMapping[i];}; + return function(i) {return axMapping.hasOwnProperty(ax._categories[i]) ? axMapping[ax._categories[i]] : BADNUM;}; } else { return Lib.identity; } diff --git a/src/traces/heatmap/convert_column_xyz.js b/src/traces/heatmap/convert_column_xyz.js index ac441eb0778..6a5d73721bf 100644 --- a/src/traces/heatmap/convert_column_xyz.js +++ b/src/traces/heatmap/convert_column_xyz.js @@ -67,10 +67,10 @@ module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, if(hasColumnHoverText) trace._hovertext = hovertext; if(ax1 && ax1.type === 'category') { - trace['_' + var1Name + 'Map'] = col1vals.map(function(v) { return ax1._categories[v];}); + trace['_' + var1Name + 'CategoryMap'] = col1vals.map(function(v) { return ax1._categories[v];}); } if(ax2 && ax2.type === 'category') { - trace['_' + var2Name + 'Map'] = col2vals.map(function(v) { return ax2._categories[v];}); + trace['_' + var2Name + 'CategoryMap'] = col2vals.map(function(v) { return ax2._categories[v];}); } }; From 0c41776c7dcdf30eb596d5fcd5cc64d64d99087b Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 7 May 2019 15:25:36 -0400 Subject: [PATCH 20/21] categorical cartesian: convert numbers to strings for _categories --- src/plots/cartesian/set_convert.js | 2 +- test/jasmine/tests/axes_test.js | 2 +- test/jasmine/tests/calcdata_test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index 55bb4bdcb98..d700c83be06 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -133,7 +133,7 @@ module.exports = function setConvert(ax, fullLayout) { if(ax._categoriesMap[v] !== undefined) { return ax._categoriesMap[v]; } else { - ax._categories.push(v); + ax._categories.push(typeof v === 'number' ? String(v) : v); var curLength = ax._categories.length - 1; ax._categoriesMap[v] = curLength; diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 4409fad89b5..7507d27ea9e 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -3096,7 +3096,7 @@ describe('Test axes', function() { x: new Float32Array([3, 1, 2]), }, 'x', 'category'); expect(out).toEqual([0, 1, 2]); - expect(ax._categories).toEqual([3, 1, 2]); + expect(ax._categories).toEqual(['3', '1', '2']); }); it('- on a date axis', function() { diff --git a/test/jasmine/tests/calcdata_test.js b/test/jasmine/tests/calcdata_test.js index 0f6ae376d68..430bfc5e7ca 100644 --- a/test/jasmine/tests/calcdata_test.js +++ b/test/jasmine/tests/calcdata_test.js @@ -862,7 +862,7 @@ describe('calculated data and points', function() { xaxis: {type: 'category'} }); - expect(gd._fullLayout.xaxis._categories).toEqual(['a', 'b', 1]); + expect(gd._fullLayout.xaxis._categories).toEqual(['a', 'b', '1']); expect(gd._fullLayout.xaxis._categoriesMap).toEqual({ '1': 2, 'a': 0, From c7cd1f3e0034609d82a55229de8c3c023a483505 Mon Sep 17 00:00:00 2001 From: Antoine Roy-Gobeil Date: Tue, 7 May 2019 17:49:35 -0400 Subject: [PATCH 21/21] categorical 2dMap: remove hasOwnProperty() call --- src/traces/heatmap/clean_2d_array.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/traces/heatmap/clean_2d_array.js b/src/traces/heatmap/clean_2d_array.js index 36671800f42..13bc6432eeb 100644 --- a/src/traces/heatmap/clean_2d_array.js +++ b/src/traces/heatmap/clean_2d_array.js @@ -46,7 +46,10 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { for(i = 0; i < traceCategories.length; i++) { axMapping[traceCategories[i]] = i; } - return function(i) {return axMapping.hasOwnProperty(ax._categories[i]) ? axMapping[ax._categories[i]] : BADNUM;}; + return function(i) { + var ind = axMapping[ax._categories[i]]; + return ind + 1 ? ind : BADNUM; + }; } else { return Lib.identity; }