Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6b54152

Browse filesBrowse files
authored
Merge pull request #1930 from plotly/big-exponents
Better exponent treatment beyond SI prefixes
2 parents e78ed04 + 52ac7f4 commit 6b54152
Copy full SHA for 6b54152

File tree

Expand file treeCollapse file tree

3 files changed

+141
-13
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+141
-13
lines changed

‎src/constants/numerical.js

Copy file name to clipboardExpand all lines: src/constants/numerical.js
+7-1Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,11 @@ module.exports = {
4747
/*
4848
* Are two values nearly equal? Compare to 1PPM
4949
*/
50-
ALMOST_EQUAL: 1 - 1e-6
50+
ALMOST_EQUAL: 1 - 1e-6,
51+
52+
/*
53+
* not a number, but for displaying numbers: the "minus sign" symbol is
54+
* wider than the regular ascii dash "-"
55+
*/
56+
MINUS_SIGN: '\u2212'
5157
};

‎src/plots/cartesian/axes.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/axes.js
+28-12Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ var ONEDAY = constants.ONEDAY;
2727
var ONEHOUR = constants.ONEHOUR;
2828
var ONEMIN = constants.ONEMIN;
2929
var ONESEC = constants.ONESEC;
30+
var MINUS_SIGN = constants.MINUS_SIGN;
3031

3132
var MID_SHIFT = require('../../constants/alignment').MID_SHIFT;
3233

@@ -1055,7 +1056,7 @@ function autoTickRound(ax) {
10551056

10561057
var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
10571058
if(Math.abs(rangeexp) > 3) {
1058-
if(ax.exponentformat === 'SI' || ax.exponentformat === 'B') {
1059+
if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
10591060
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
10601061
}
10611062
else ax._tickexponent = rangeexp;
@@ -1299,12 +1300,13 @@ function formatLog(ax, out, hover, extraPrecision, hideexp) {
12991300
out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision);
13001301
}
13011302
else if(isNumeric(dtick) || ((dtick.charAt(0) === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) {
1302-
if(['e', 'E', 'power'].indexOf(ax.exponentformat) !== -1) {
1303-
var p = Math.round(x);
1303+
var p = Math.round(x);
1304+
if(['e', 'E', 'power'].indexOf(ax.exponentformat) !== -1 ||
1305+
(isSIFormat(ax.exponentformat) && beyondSI(p))) {
13041306
if(p === 0) out.text = 1;
13051307
else if(p === 1) out.text = '10';
13061308
else if(p > 1) out.text = '10<sup>' + p + '</sup>';
1307-
else out.text = '10<sup>\u2212' + -p + '</sup>';
1309+
else out.text = '10<sup>' + MINUS_SIGN + -p + '</sup>';
13081310

13091311
out.fontSize *= 1.25;
13101312
}
@@ -1359,6 +1361,21 @@ function formatLinear(ax, out, hover, extraPrecision, hideexp) {
13591361
// also automatically switch to sci. notation
13601362
var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T'];
13611363

1364+
function isSIFormat(exponentFormat) {
1365+
return exponentFormat === 'SI' || exponentFormat === 'B';
1366+
}
1367+
1368+
// are we beyond the range of common SI prefixes?
1369+
// 10^-16 -> 1x10^-16
1370+
// 10^-15 -> 1f
1371+
// ...
1372+
// 10^14 -> 100T
1373+
// 10^15 -> 1x10^15
1374+
// 10^16 -> 1x10^16
1375+
function beyondSI(exponent) {
1376+
return exponent > 14 || exponent < -15;
1377+
}
1378+
13621379
function numFormat(v, ax, fmtoverride, hover) {
13631380
// negative?
13641381
var isNeg = v < 0,
@@ -1387,7 +1404,7 @@ function numFormat(v, ax, fmtoverride, hover) {
13871404
if(ax.hoverformat) tickformat = ax.hoverformat;
13881405
}
13891406

1390-
if(tickformat) return d3.format(tickformat)(v).replace(/-/g, '\u2212');
1407+
if(tickformat) return d3.format(tickformat)(v).replace(/-/g, MINUS_SIGN);
13911408

13921409
// 'epsilon' - rounding increment
13931410
var e = Math.pow(10, -tickRound) / 2;
@@ -1436,14 +1453,14 @@ function numFormat(v, ax, fmtoverride, hover) {
14361453

14371454
// add exponent
14381455
if(exponent && exponentFormat !== 'hide') {
1456+
if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power';
1457+
14391458
var signedExponent;
1440-
if(exponent < 0) signedExponent = '\u2212' + -exponent;
1459+
if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
14411460
else if(exponentFormat !== 'power') signedExponent = '+' + exponent;
14421461
else signedExponent = String(exponent);
14431462

1444-
if(exponentFormat === 'e' ||
1445-
((exponentFormat === 'SI' || exponentFormat === 'B') &&
1446-
(exponent > 12 || exponent < -15))) {
1463+
if(exponentFormat === 'e') {
14471464
v += 'e' + signedExponent;
14481465
}
14491466
else if(exponentFormat === 'E') {
@@ -1455,19 +1472,18 @@ function numFormat(v, ax, fmtoverride, hover) {
14551472
else if(exponentFormat === 'B' && exponent === 9) {
14561473
v += 'B';
14571474
}
1458-
else if(exponentFormat === 'SI' || exponentFormat === 'B') {
1475+
else if(isSIFormat(exponentFormat)) {
14591476
v += SIPREFIXES[exponent / 3 + 5];
14601477
}
14611478
}
14621479

14631480
// put sign back in and return
14641481
// replace standard minus character (which is technically a hyphen)
14651482
// with a true minus sign
1466-
if(isNeg) return '\u2212' + v;
1483+
if(isNeg) return MINUS_SIGN + v;
14671484
return v;
14681485
}
14691486

1470-
14711487
axes.subplotMatch = /^x([0-9]*)y([0-9]*)$/;
14721488

14731489
// getSubplots - extract all combinations of axes we need to make plots for

‎test/jasmine/tests/axes_test.js

Copy file name to clipboardExpand all lines: test/jasmine/tests/axes_test.js
+106Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,112 @@ describe('Test axes', function() {
19291929
});
19301930
}
19311931

1932+
it('reverts to "power" for SI/B exponentformat beyond the prefix range (linear case)', function() {
1933+
var textOut = mockCalc({
1934+
type: 'linear',
1935+
tickmode: 'linear',
1936+
exponentformat: 'B',
1937+
showexponent: 'all',
1938+
tick0: 0,
1939+
dtick: 1e13,
1940+
range: [8.5e13, 11.5e13]
1941+
});
1942+
1943+
expect(textOut).toEqual([
1944+
'90T', '100T', '110T'
1945+
]);
1946+
1947+
textOut = mockCalc({
1948+
type: 'linear',
1949+
tickmode: 'linear',
1950+
exponentformat: 'B',
1951+
showexponent: 'all',
1952+
tick0: 0,
1953+
dtick: 1e14,
1954+
range: [8.5e14, 11.5e14]
1955+
});
1956+
1957+
expect(textOut).toEqual([
1958+
'0.9×10<sup>15</sup>',
1959+
'1×10<sup>15</sup>',
1960+
'1.1×10<sup>15</sup>'
1961+
]);
1962+
1963+
textOut = mockCalc({
1964+
type: 'linear',
1965+
tickmode: 'linear',
1966+
exponentformat: 'SI',
1967+
showexponent: 'all',
1968+
tick0: 0,
1969+
dtick: 1e-16,
1970+
range: [8.5e-16, 11.5e-16]
1971+
});
1972+
1973+
expect(textOut).toEqual([
1974+
'0.9f', '1f', '1.1f'
1975+
]);
1976+
1977+
textOut = mockCalc({
1978+
type: 'linear',
1979+
tickmode: 'linear',
1980+
exponentformat: 'SI',
1981+
showexponent: 'all',
1982+
tick0: 0,
1983+
dtick: 1e-17,
1984+
range: [8.5e-17, 11.5e-17]
1985+
});
1986+
1987+
expect(textOut).toEqual([
1988+
'0.9×10<sup>\u221216</sup>',
1989+
'1×10<sup>\u221216</sup>',
1990+
'1.1×10<sup>\u221216</sup>'
1991+
]);
1992+
});
1993+
1994+
it('reverts to "power" for SI/B exponentformat beyond the prefix range (log case)', function() {
1995+
var textOut = mockCalc({
1996+
type: 'log',
1997+
tickmode: 'linear',
1998+
exponentformat: 'B',
1999+
showexponent: 'all',
2000+
tick0: 0,
2001+
dtick: 1,
2002+
range: [-18.5, 18.5]
2003+
});
2004+
2005+
expect(textOut).toEqual([
2006+
'10<sup>\u221218</sup>',
2007+
'10<sup>\u221217</sup>',
2008+
'10<sup>\u221216</sup>',
2009+
'1f', '10f', '100f', '1p', '10p', '100p', '1n', '10n', '100n',
2010+
'1μ', '10μ', '100μ', '0.001', '0.01', '0.1', '1', '10', '100',
2011+
'1000', '10k', '100k', '1M', '10M', '100M', '1B', '10B', '100B',
2012+
'1T', '10T', '100T',
2013+
'10<sup>15</sup>',
2014+
'10<sup>16</sup>',
2015+
'10<sup>17</sup>',
2016+
'10<sup>18</sup>'
2017+
]);
2018+
2019+
textOut = mockCalc({
2020+
type: 'log',
2021+
tickmode: 'linear',
2022+
exponentformat: 'SI',
2023+
showexponent: 'all',
2024+
tick0: 0,
2025+
dtick: 'D2',
2026+
range: [7.9, 12.1]
2027+
});
2028+
2029+
expect(textOut).toEqual([
2030+
'100M', '2', '5',
2031+
'1G', '2', '5',
2032+
'10G', '2', '5',
2033+
'100G', '2', '5',
2034+
'1T'
2035+
]);
2036+
});
2037+
19322038
it('provides a new date suffix whenever the suffix changes', function() {
19332039
var ax = {
19342040
type: 'date',

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.