Skip to content

Commit 8e3709f

Browse files
authored
Merge pull request #7249 from dberardi99/bugfix-for-issue-4787
Add "SI extended" formatting rule for the tick exponents
2 parents 0ad7c7f + beab33e commit 8e3709f

File tree

7 files changed

+202
-115
lines changed

7 files changed

+202
-115
lines changed

draftlogs/7249_add.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add "SI extended" formatting rule for tick exponents on axis labels, allowing values to be displayed with extended SI prefixes (e.g., femto, pico, atto) [[#7249](https://github.com/plotly/plotly.js/pull/7249)]

src/plots/cartesian/axes.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,7 +1557,8 @@ function autoTickRound(ax) {
15571557
var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
15581558
var minexponent = ax.minexponent === undefined ? 3 : ax.minexponent;
15591559
if(Math.abs(rangeexp) > minexponent) {
1560-
if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
1560+
if((isSIFormat(ax.exponentformat) && ax.exponentformat !== 'SI extended' && !beyondSI(rangeexp)) ||
1561+
(isSIFormat(ax.exponentformat) && ax.exponentformat === 'SI extended' && !beyondSIExtended(rangeexp))) {
15611562
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
15621563
} else ax._tickexponent = rangeexp;
15631564
}
@@ -1914,7 +1915,8 @@ function formatLog(ax, out, hover, extraPrecision, hideexp) {
19141915
var p = +parts[1];
19151916
var absP = Math.abs(p);
19161917
var exponentFormat = ax.exponentformat;
1917-
if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && beyondSI(p))) {
1918+
if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && exponentFormat !== 'SI extended' && beyondSI(p)) ||
1919+
(isSIFormat(exponentFormat) && exponentFormat === 'SI extended' && beyondSIExtended(p))) {
19181920
out.text = parts[0];
19191921
if(absP > 0) out.text += 'x10';
19201922
if(out.text === '1x10') out.text = '10';
@@ -2063,9 +2065,10 @@ function num2frac(num) {
20632065
// also automatically switch to sci. notation
20642066
var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T'];
20652067

2066-
function isSIFormat(exponentFormat) {
2067-
return exponentFormat === 'SI' || exponentFormat === 'B';
2068-
}
2068+
// extending SI prefixes
2069+
var SIPREFIXES_EXTENDED = ['q', 'r', 'y', 'z', 'a', ...SIPREFIXES, 'P', 'E', 'Z', 'Y', 'R', 'Q'];
2070+
2071+
const isSIFormat = (exponentFormat) => ['SI', 'SI extended','B'].includes(exponentFormat);
20692072

20702073
// are we beyond the range of common SI prefixes?
20712074
// 10^-16 -> 1x10^-16
@@ -2078,6 +2081,26 @@ function beyondSI(exponent) {
20782081
return exponent > 14 || exponent < -15;
20792082
}
20802083

2084+
2085+
// are we beyond the range of all SI prefixes?
2086+
// 10^-31 -> 1x10^-31
2087+
// 10^-30 -> 1q
2088+
// 10^-29 -> 10q
2089+
// ...
2090+
// 10^31 -> 10Q
2091+
// 10^32 -> 100Q
2092+
// 10^33 -> 1x10^33
2093+
function beyondSIExtended(exponent) {
2094+
return exponent > 32 || exponent < -30;
2095+
}
2096+
2097+
function shouldSwitchSIToPowerFormat(exponent, exponentFormat) {
2098+
if (!isSIFormat(exponentFormat)) return false;
2099+
if (exponentFormat === 'SI extended' && beyondSIExtended(exponent)) return true;
2100+
if (exponentFormat !== 'SI extended' && beyondSI(exponent)) return true;
2101+
return false;
2102+
}
2103+
20812104
function numFormat(v, ax, fmtoverride, hover) {
20822105
var isNeg = v < 0;
20832106
// max number of digits past decimal point to show
@@ -2153,7 +2176,7 @@ function numFormat(v, ax, fmtoverride, hover) {
21532176

21542177
// add exponent
21552178
if(exponent && exponentFormat !== 'hide') {
2156-
if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power';
2179+
if (shouldSwitchSIToPowerFormat(exponent, exponentFormat)) exponentFormat = 'power';
21572180

21582181
var signedExponent;
21592182
if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
@@ -2167,7 +2190,9 @@ function numFormat(v, ax, fmtoverride, hover) {
21672190
} else if(exponentFormat === 'B' && exponent === 9) {
21682191
v += 'B';
21692192
} else if(isSIFormat(exponentFormat)) {
2170-
v += SIPREFIXES[exponent / 3 + 5];
2193+
v += exponentFormat === 'SI extended'
2194+
? SIPREFIXES_EXTENDED[exponent / 3 + 10]
2195+
: SIPREFIXES[exponent / 3 + 5];
21712196
}
21722197
}
21732198

src/plots/cartesian/layout_attributes.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ module.exports = {
911911
},
912912
exponentformat: {
913913
valType: 'enumerated',
914-
values: ['none', 'e', 'E', 'power', 'SI', 'B'],
914+
values: ['none', 'e', 'E', 'power', 'SI', 'B', 'SI extended'],
915915
dflt: 'B',
916916
editType: 'ticks',
917917
description: [
@@ -922,7 +922,12 @@ module.exports = {
922922
'If *E*, 1E+9.',
923923
'If *power*, 1x10^9 (with 9 in a super script).',
924924
'If *SI*, 1G.',
925-
'If *B*, 1B.'
925+
'If *B*, 1B.',
926+
927+
'*SI* uses prefixes from "femto" f (10^-15) to "tera" T (10^12).',
928+
'*SI extended* covers instead the full SI range from "quecto" q (10^-30) to "quetta" Q (10^30).',
929+
'If *SI* or *SI extended* is used and the exponent is beyond the above ranges, the formatting rule',
930+
'will automatically be switched to the power notation.'
926931
].join(' ')
927932
},
928933
minexponent: {

src/traces/carpet/axis_attributes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ module.exports = {
234234
},
235235
exponentformat: {
236236
valType: 'enumerated',
237-
values: ['none', 'e', 'E', 'power', 'SI', 'B'],
237+
values: ['none', 'e', 'E', 'power', 'SI', 'B', 'SI extended'],
238238
dflt: 'B',
239239
editType: 'calc',
240240
description: [

test/image/baselines/20.png

1.13 KB
Loading

test/image/mocks/20.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
{
1010
"x": [2, 3, 4],
11-
"y": [40, 50, 60],
11+
"y": [4000000000000, 5000000000000, 6000000000000],
1212
"name": "yaxis2 data",
1313
"yaxis": "y2",
1414
"type": "scatter"
@@ -36,7 +36,10 @@
3636
},
3737
{
3838
"x": [6, 7, 8],
39-
"y": [4000000, 5000000, 6000000],
39+
"y": [
40+
400000000000000000000000000000000, 500000000000000000000000000000000,
41+
600000000000000000000000000000000
42+
],
4043
"name": "yaxis6 data",
4144
"yaxis": "y6",
4245
"type": "scatter"
@@ -111,6 +114,7 @@
111114
"tickfont": {
112115
"color": "#ff7f0e"
113116
},
117+
"exponentformat": "SI",
114118
"linecolor": "rgba(255,127,14,0.4)",
115119
"linewidth": 6,
116120
"anchor": "free",
@@ -189,7 +193,7 @@
189193
"tickfont": {
190194
"color": "#8c564b"
191195
},
192-
"exponentformat": "SI",
196+
"exponentformat": "SI extended",
193197
"linecolor": "rgba(140,86,75,0.5)",
194198
"anchor": "free",
195199
"side": "right",

0 commit comments

Comments
 (0)