Skip to content
Merged
1 change: 1 addition & 0 deletions draftlogs/7249_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +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)]
39 changes: 32 additions & 7 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,8 @@ function autoTickRound(ax) {
var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
var minexponent = ax.minexponent === undefined ? 3 : ax.minexponent;
if(Math.abs(rangeexp) > minexponent) {
if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
if((isSIFormat(ax.exponentformat) && ax.exponentformat !== 'SI extended' && !beyondSI(rangeexp)) ||
(isSIFormat(ax.exponentformat) && ax.exponentformat === 'SI extended' && !beyondSIExtended(rangeexp))) {
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
} else ax._tickexponent = rangeexp;
}
Expand Down Expand Up @@ -1914,7 +1915,8 @@ function formatLog(ax, out, hover, extraPrecision, hideexp) {
var p = +parts[1];
var absP = Math.abs(p);
var exponentFormat = ax.exponentformat;
if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && beyondSI(p))) {
if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && exponentFormat !== 'SI extended' && beyondSI(p)) ||
(isSIFormat(exponentFormat) && exponentFormat === 'SI extended' && beyondSIExtended(p))) {
out.text = parts[0];
if(absP > 0) out.text += 'x10';
if(out.text === '1x10') out.text = '10';
Expand Down Expand Up @@ -2063,9 +2065,10 @@ function num2frac(num) {
// also automatically switch to sci. notation
var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T'];

function isSIFormat(exponentFormat) {
return exponentFormat === 'SI' || exponentFormat === 'B';
}
// extending SI prefixes
var SIPREFIXES_EXTENDED = ['q', 'r', 'y', 'z', 'a', ...SIPREFIXES, 'P', 'E', 'Z', 'Y', 'R', 'Q'];

const isSIFormat = (exponentFormat) => ['SI', 'SI extended','B'].includes(exponentFormat);

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


// are we beyond the range of all SI prefixes?
// 10^-31 -> 1x10^-31
// 10^-30 -> 1q
// 10^-29 -> 10q
// ...
// 10^31 -> 10Q
// 10^32 -> 100Q
// 10^33 -> 1x10^33
function beyondSIExtended(exponent) {
return exponent > 32 || exponent < -30;
}

function shouldSwitchSIToPowerFormat(exponent, exponentFormat) {
if (!isSIFormat(exponentFormat)) return false;
if (exponentFormat === 'SI extended' && beyondSIExtended(exponent)) return true;
if (exponentFormat !== 'SI extended' && beyondSI(exponent)) return true;
return false;
}

function numFormat(v, ax, fmtoverride, hover) {
var isNeg = v < 0;
// max number of digits past decimal point to show
Expand Down Expand Up @@ -2153,7 +2176,7 @@ function numFormat(v, ax, fmtoverride, hover) {

// add exponent
if(exponent && exponentFormat !== 'hide') {
if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power';
if (shouldSwitchSIToPowerFormat(exponent, exponentFormat)) exponentFormat = 'power';

var signedExponent;
if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
Expand All @@ -2167,7 +2190,9 @@ function numFormat(v, ax, fmtoverride, hover) {
} else if(exponentFormat === 'B' && exponent === 9) {
v += 'B';
} else if(isSIFormat(exponentFormat)) {
v += SIPREFIXES[exponent / 3 + 5];
v += exponentFormat === 'SI extended'
? SIPREFIXES_EXTENDED[exponent / 3 + 10]
: SIPREFIXES[exponent / 3 + 5];
}
}

Expand Down
9 changes: 7 additions & 2 deletions src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@ module.exports = {
},
exponentformat: {
valType: 'enumerated',
values: ['none', 'e', 'E', 'power', 'SI', 'B'],
values: ['none', 'e', 'E', 'power', 'SI', 'B', 'SI extended'],
dflt: 'B',
editType: 'ticks',
description: [
Expand All @@ -922,7 +922,12 @@ module.exports = {
'If *E*, 1E+9.',
'If *power*, 1x10^9 (with 9 in a super script).',
'If *SI*, 1G.',
'If *B*, 1B.'
'If *B*, 1B.',

'*SI* uses prefixes from "femto" f (10^-15) to "tera" T (10^12).',
'*SI extended* covers instead the full SI range from "quecto" q (10^-30) to "quetta" Q (10^30).',
'If *SI* or *SI extended* is used and the exponent is beyond the above ranges, the formatting rule',
'will automatically be switched to the power notation.'
].join(' ')
},
minexponent: {
Expand Down
2 changes: 1 addition & 1 deletion src/traces/carpet/axis_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ module.exports = {
},
exponentformat: {
valType: 'enumerated',
values: ['none', 'e', 'E', 'power', 'SI', 'B'],
values: ['none', 'e', 'E', 'power', 'SI', 'B', 'SI extended'],
dflt: 'B',
editType: 'calc',
description: [
Expand Down
Binary file modified test/image/baselines/20.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 7 additions & 3 deletions test/image/mocks/20.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
{
"x": [2, 3, 4],
"y": [40, 50, 60],
"y": [4000000000000, 5000000000000, 6000000000000],
"name": "yaxis2 data",
"yaxis": "y2",
"type": "scatter"
Expand Down Expand Up @@ -36,7 +36,10 @@
},
{
"x": [6, 7, 8],
"y": [4000000, 5000000, 6000000],
"y": [
400000000000000000000000000000000, 500000000000000000000000000000000,
600000000000000000000000000000000
],
"name": "yaxis6 data",
"yaxis": "y6",
"type": "scatter"
Expand Down Expand Up @@ -111,6 +114,7 @@
"tickfont": {
"color": "#ff7f0e"
},
"exponentformat": "SI",
"linecolor": "rgba(255,127,14,0.4)",
"linewidth": 6,
"anchor": "free",
Expand Down Expand Up @@ -189,7 +193,7 @@
"tickfont": {
"color": "#8c564b"
},
"exponentformat": "SI",
"exponentformat": "SI extended",
"linecolor": "rgba(140,86,75,0.5)",
"anchor": "free",
"side": "right",
Expand Down
Loading