Skip to content

Better exponent treatment beyond SI prefixes #1930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/constants/numerical.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,11 @@ module.exports = {
/*
* Are two values nearly equal? Compare to 1PPM
*/
ALMOST_EQUAL: 1 - 1e-6
ALMOST_EQUAL: 1 - 1e-6,

/*
* not a number, but for displaying numbers: the "minus sign" symbol is
* wider than the regular ascii dash "-"
*/
MINUS_SIGN: '\u2212'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this is only used in cartesian tick labels (and hover text) and this does get reused in other places... but I suspect there are places in other plot types where we use a regular dash that should be replaced by this one.

};
40 changes: 28 additions & 12 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var ONEDAY = constants.ONEDAY;
var ONEHOUR = constants.ONEHOUR;
var ONEMIN = constants.ONEMIN;
var ONESEC = constants.ONESEC;
var MINUS_SIGN = constants.MINUS_SIGN;

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

Expand Down Expand Up @@ -1055,7 +1056,7 @@ function autoTickRound(ax) {

var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
if(Math.abs(rangeexp) > 3) {
if(ax.exponentformat === 'SI' || ax.exponentformat === 'B') {
if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
}
else ax._tickexponent = rangeexp;
Expand Down Expand Up @@ -1299,12 +1300,13 @@ function formatLog(ax, out, hover, extraPrecision, hideexp) {
out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision);
}
else if(isNumeric(dtick) || ((dtick.charAt(0) === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) {
if(['e', 'E', 'power'].indexOf(ax.exponentformat) !== -1) {
var p = Math.round(x);
var p = Math.round(x);
if(['e', 'E', 'power'].indexOf(ax.exponentformat) !== -1 ||
(isSIFormat(ax.exponentformat) && beyondSI(p))) {
if(p === 0) out.text = 1;
else if(p === 1) out.text = '10';
else if(p > 1) out.text = '10<sup>' + p + '</sup>';
else out.text = '10<sup>\u2212' + -p + '</sup>';
else out.text = '10<sup>' + MINUS_SIGN + -p + '</sup>';

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

function isSIFormat(exponentFormat) {
return exponentFormat === 'SI' || exponentFormat === 'B';
}

// are we beyond the range of common SI prefixes?
// 10^-16 -> 1x10^-16
// 10^-15 -> 1f
// ...
// 10^14 -> 100T
// 10^15 -> 1x10^15
// 10^16 -> 1x10^16
function beyondSI(exponent) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful.

return exponent > 14 || exponent < -15;
}

function numFormat(v, ax, fmtoverride, hover) {
// negative?
var isNeg = v < 0,
Expand Down Expand Up @@ -1387,7 +1404,7 @@ function numFormat(v, ax, fmtoverride, hover) {
if(ax.hoverformat) tickformat = ax.hoverformat;
}

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

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

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

var signedExponent;
if(exponent < 0) signedExponent = '\u2212' + -exponent;
if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
else if(exponentFormat !== 'power') signedExponent = '+' + exponent;
else signedExponent = String(exponent);

if(exponentFormat === 'e' ||
((exponentFormat === 'SI' || exponentFormat === 'B') &&
(exponent > 12 || exponent < -15))) {
if(exponentFormat === 'e') {
v += 'e' + signedExponent;
}
else if(exponentFormat === 'E') {
Expand All @@ -1455,19 +1472,18 @@ function numFormat(v, ax, fmtoverride, hover) {
else if(exponentFormat === 'B' && exponent === 9) {
v += 'B';
}
else if(exponentFormat === 'SI' || exponentFormat === 'B') {
else if(isSIFormat(exponentFormat)) {
v += SIPREFIXES[exponent / 3 + 5];
}
}

// put sign back in and return
// replace standard minus character (which is technically a hyphen)
// with a true minus sign
if(isNeg) return '\u2212' + v;
if(isNeg) return MINUS_SIGN + v;
return v;
}


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

// getSubplots - extract all combinations of axes we need to make plots for
Expand Down
106 changes: 106 additions & 0 deletions test/jasmine/tests/axes_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1929,6 +1929,112 @@ describe('Test axes', function() {
});
}

it('reverts to "power" for SI/B exponentformat beyond the prefix range (linear case)', function() {
var textOut = mockCalc({
type: 'linear',
tickmode: 'linear',
exponentformat: 'B',
showexponent: 'all',
tick0: 0,
dtick: 1e13,
range: [8.5e13, 11.5e13]
});

expect(textOut).toEqual([
'90T', '100T', '110T'
]);

textOut = mockCalc({
type: 'linear',
tickmode: 'linear',
exponentformat: 'B',
showexponent: 'all',
tick0: 0,
dtick: 1e14,
range: [8.5e14, 11.5e14]
});

expect(textOut).toEqual([
'0.9×10<sup>15</sup>',
'1×10<sup>15</sup>',
'1.1×10<sup>15</sup>'
]);

textOut = mockCalc({
type: 'linear',
tickmode: 'linear',
exponentformat: 'SI',
showexponent: 'all',
tick0: 0,
dtick: 1e-16,
range: [8.5e-16, 11.5e-16]
});

expect(textOut).toEqual([
'0.9f', '1f', '1.1f'
]);

textOut = mockCalc({
type: 'linear',
tickmode: 'linear',
exponentformat: 'SI',
showexponent: 'all',
tick0: 0,
dtick: 1e-17,
range: [8.5e-17, 11.5e-17]
});

expect(textOut).toEqual([
'0.9×10<sup>\u221216</sup>',
'1×10<sup>\u221216</sup>',
'1.1×10<sup>\u221216</sup>'
]);
});

it('reverts to "power" for SI/B exponentformat beyond the prefix range (log case)', function() {
var textOut = mockCalc({
type: 'log',
tickmode: 'linear',
exponentformat: 'B',
showexponent: 'all',
tick0: 0,
dtick: 1,
range: [-18.5, 18.5]
});

expect(textOut).toEqual([
'10<sup>\u221218</sup>',
'10<sup>\u221217</sup>',
'10<sup>\u221216</sup>',
'1f', '10f', '100f', '1p', '10p', '100p', '1n', '10n', '100n',
'1μ', '10μ', '100μ', '0.001', '0.01', '0.1', '1', '10', '100',
'1000', '10k', '100k', '1M', '10M', '100M', '1B', '10B', '100B',
'1T', '10T', '100T',
'10<sup>15</sup>',
'10<sup>16</sup>',
'10<sup>17</sup>',
'10<sup>18</sup>'
]);

textOut = mockCalc({
type: 'log',
tickmode: 'linear',
exponentformat: 'SI',
showexponent: 'all',
tick0: 0,
dtick: 'D2',
range: [7.9, 12.1]
});

expect(textOut).toEqual([
'100M', '2', '5',
'1G', '2', '5',
'10G', '2', '5',
'100G', '2', '5',
'1T'
]);
});

it('provides a new date suffix whenever the suffix changes', function() {
var ax = {
type: 'date',
Expand Down