Skip to content

Commit

Permalink
Merge pull request #6790 from my-tien/autotickangles
Browse files Browse the repository at this point in the history
Add autotickangles property
  • Loading branch information
archmoj authored Jan 26, 2024
2 parents 24b6f75 + b293a11 commit b31d45b
Show file tree
Hide file tree
Showing 29 changed files with 152 additions and 35 deletions.
1 change: 1 addition & 0 deletions draftlogs/6790_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `autotickangles` to cartesian and radial axes [[#6790](https://github.com/plotly/plotly.js/pull/6790)], with thanks to @my-tien for the contribution!
6 changes: 5 additions & 1 deletion src/components/colorbar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear');

var font = layout.font;
var opts = {outerTicks: false, font: font};
var opts = {
noAutotickangles: true,
outerTicks: false,
font: font
};
if(ticklabelposition.indexOf('inside') !== -1) {
opts.bgColor = 'black'; // could we instead use the average of colors in the scale?
}
Expand Down
1 change: 1 addition & 0 deletions src/components/colorbar/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,7 @@ function mockColorBarAxis(gd, opts, zrange) {
var axisOptions = {
letter: letter,
font: fullLayout.font,
noAutotickangles: letter === 'y',
noHover: true,
noTickson: true,
noTicklabelmode: true,
Expand Down
36 changes: 28 additions & 8 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3472,13 +3472,13 @@ axes.drawLabels = function(gd, ax, opts) {

var fullLayout = gd._fullLayout;
var axId = ax._id;
var axLetter = axId.charAt(0);
var cls = opts.cls || axId + 'tick';

var vals = opts.vals.filter(function(d) { return d.text; });

var labelFns = opts.labelFns;
var tickAngle = opts.secondary ? 0 : ax.tickangle;

var prevAngle = (ax._prevTickAngles || {})[cls];

var tickLabels = opts.layer.selectAll('g.' + cls)
Expand Down Expand Up @@ -3719,21 +3719,22 @@ axes.drawLabels = function(gd, ax, opts) {
// check for auto-angling if x labels overlap
// don't auto-angle at all for log axes with
// base and digit format
if(vals.length && axLetter === 'x' && !isNumeric(tickAngle) &&
if(vals.length && ax.autotickangles &&
(ax.type !== 'log' || String(ax.dtick).charAt(0) !== 'D')
) {
autoangle = 0;
autoangle = ax.autotickangles[0];

var maxFontSize = 0;
var lbbArray = [];
var i;

var maxLines = 1;
tickLabels.each(function(d) {
maxFontSize = Math.max(maxFontSize, d.fontSize);

var x = ax.l2p(d.x);
var thisLabel = selectTickLabel(this);
var bb = Drawing.bBox(thisLabel.node());
maxLines = Math.max(maxLines, svgTextUtils.lineCount(thisLabel));

lbbArray.push({
// ignore about y, just deal with x overlaps
Expand Down Expand Up @@ -3780,12 +3781,31 @@ axes.drawLabels = function(gd, ax, opts) {
var pad = !isAligned ? 0 :
(ax.tickwidth || 0) + 2 * TEXTPAD;

var rotate90 = (tickSpacing < maxFontSize * 2.5) || ax.type === 'multicategory' || ax._name === 'realaxis';
// autotickangles
var adjacent = tickSpacing;
var opposite = maxFontSize * 1.25 * maxLines;
var hypotenuse = Math.sqrt(Math.pow(adjacent, 2) + Math.pow(opposite, 2));
var maxCos = adjacent / hypotenuse;
var autoTickAnglesRadians = ax.autotickangles.map(
function(degrees) { return degrees * Math.PI / 180; }
);
var angleRadians = autoTickAnglesRadians.find(
function(angle) { return Math.abs(Math.cos(angle)) <= maxCos; }
);
if(angleRadians === undefined) {
// no angle with smaller cosine than maxCos, just pick the angle with smallest cosine
angleRadians = autoTickAnglesRadians.reduce(
function(currentMax, nextAngle) {
return Math.abs(Math.cos(currentMax)) < Math.abs(Math.cos(nextAngle)) ? currentMax : nextAngle;
}
, autoTickAnglesRadians[0]
);
}
var newAngle = angleRadians * (180 / Math.PI /* to degrees */);

// any overlap at all - set 30 degrees or 90 degrees
for(i = 0; i < lbbArray.length - 1; i++) {
if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1], pad)) {
autoangle = rotate90 ? 90 : 30;
autoangle = newAngle;
break;
}
}
Expand All @@ -3807,7 +3827,7 @@ axes.drawLabels = function(gd, ax, opts) {
// by rotating 90 degrees, do not attempt to re-fix its label overlaps
// as this can lead to infinite redraw loops!
if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) {
autoangle = 90;
autoangle = prevAngle;
seq.push(function() {
positionLabels(tickLabels, prevAngle);
});
Expand Down
14 changes: 14 additions & 0 deletions src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,20 @@ module.exports = {
'vertically.'
].join(' ')
},
autotickangles: {
valType: 'info_array',
freeLength: true,
items: {
valType: 'angle'
},
dflt: [0, 30, 90],
editType: 'ticks',
description: [
'When `tickangle` is set to *auto*, it will be set to the first',
'angle in this array that is large enough to prevent label',
'overlap.'
].join(' ')
},
tickprefix: {
valType: 'string',
dflt: '',
Expand Down
3 changes: 2 additions & 1 deletion src/plots/cartesian/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
visibleDflt: visibleDflt,
reverseDflt: reverseDflt,
autotypenumbersDflt: autotypenumbersDflt,
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId],
noAutotickangles: axLetter === 'y'
};

coerce('uirevision', layoutOut.uirevision);
Expand Down
7 changes: 6 additions & 1 deletion src/plots/cartesian/tick_label_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ module.exports = function handleTickLabelDefaults(containerIn, containerOut, coe
coerce('ticklabelstep');
}

if(!options.noAng) coerce('tickangle');
if(!options.noAng) {
var tickAngle = coerce('tickangle');
if(!options.noAutotickangles && tickAngle === 'auto') {
coerce('autotickangles');
}
}

if(axType !== 'category') {
var tickFormat = coerce('tickformat');
Expand Down
1 change: 1 addition & 0 deletions src/plots/gl3d/layout/axis_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) {
letter: axName[0],
data: options.data,
showGrid: true,
noAutotickangles: true,
noTickson: true,
noTicklabelmode: true,
noTicklabelstep: true,
Expand Down
2 changes: 2 additions & 0 deletions src/plots/polar/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ var radialAxisAttrs = {
].join(' ')
},

autotickangles: axesAttrs.autotickangles,

side: {
valType: 'enumerated',
// TODO add 'center' for `showline: false` radial axes
Expand Down
3 changes: 2 additions & 1 deletion src/plots/polar/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ function handleDefaults(contIn, contOut, coerce, opts) {
color: dfltFontColor,
size: dfltFontSize,
family: dfltFontFamily
}
},
noAutotickangles: axName === 'angularaxis'
});

handleTickMarkDefaults(axIn, axOut, coerceAxis, {outerTicks: true});
Expand Down
1 change: 1 addition & 0 deletions src/plots/smith/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ function handleDefaults(contIn, contOut, coerce, opts) {
}

handleTickLabelDefaults(axIn, axOut, coerceAxis, axOut.type, {
noAutotickangles: true,
noTicklabelstep: true,
noAng: !isRealAxis,
noExp: true,
Expand Down
2 changes: 1 addition & 1 deletion src/plots/ternary/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut

handleTickValueDefaults(containerIn, containerOut, coerce, 'linear');
handlePrefixSuffixDefaults(containerIn, containerOut, coerce, 'linear');
handleTickLabelDefaults(containerIn, containerOut, coerce, 'linear');
handleTickLabelDefaults(containerIn, containerOut, coerce, 'linear', { noAutotickangles: true });
handleTickMarkDefaults(containerIn, containerOut, coerce,
{ outerTicks: true });

Expand Down
1 change: 1 addition & 0 deletions src/traces/carpet/ab_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function mimickAxisDefaults(traceIn, traceOut, fullLayout, dfltColor) {
var axOut = Template.newContainer(traceOut, axName);

var defaultOptions = {
noAutotickangles: true,
noTicklabelstep: true,
tickfont: 'x',
id: axLetter + 'axis',
Expand Down
5 changes: 4 additions & 1 deletion src/traces/indicator/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
coerceGaugeAxis('visible');
traceOut._range = coerceGaugeAxis('range', traceOut._range);

var opts = {outerTicks: true};
var opts = {
noAutotickangles: true,
outerTicks: true
};
handleTickValueDefaults(axisIn, axisOut, coerceGaugeAxis, 'linear');
handlePrefixSuffixDefaults(axisIn, axisOut, coerceGaugeAxis, 'linear', opts);
handleTickLabelDefaults(axisIn, axisOut, coerceGaugeAxis, 'linear', opts);
Expand Down
1 change: 1 addition & 0 deletions src/traces/indicator/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ function mockAxis(gd, opts, zrange) {
var axisOptions = {
letter: 'x',
font: fullLayout.font,
noAutotickangles: true,
noHover: true,
noTickson: true
};
Expand Down
Binary file modified test/image/baselines/10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/automargin-zoom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/domain_ref_axis_types.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/finance_subplots_categories.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/grid_subplot_types.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/period_positioning6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/polar_polygon-grids.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/tick-increment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/ticklabelposition-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/waterfall_and_bar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 14 additions & 13 deletions test/image/mocks/10.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
-0.255461150807681,
-0.25597595662203515
],
"name": "Trial 1",
"name": "Trial 1 Very Long<br>multiline label",
"boxpoints": "all",
"pointpos": -1.5,
"jitter": 0,
Expand Down Expand Up @@ -45,7 +45,7 @@
2.2237747316232417,
2.0456528234898133
],
"name": "Trial 2",
"name": "Trial 2 Very Long<br>multiline label",
"boxpoints": "all",
"pointpos": -1.5,
"jitter": 0,
Expand Down Expand Up @@ -77,7 +77,7 @@
1.114484992405051,
0.577777449231605
],
"name": "Trial 3",
"name": "Trial 3 Very Long<br>multiline label",
"boxpoints": "all",
"pointpos": -1.5,
"jitter": 0,
Expand Down Expand Up @@ -109,7 +109,7 @@
3.01289053296517,
2.8335761244537614
],
"name": "Trial 4",
"name": "Trial 4 Very Long<br>multiline label",
"boxpoints": "all",
"pointpos": -1.5,
"jitter": 0,
Expand Down Expand Up @@ -141,7 +141,7 @@
5.687207835036456,
5.718713550485276
],
"name": "Trial 5",
"name": "Trial 5 Very Long<br>multiline label",
"boxpoints": "all",
"pointpos": -1.5,
"jitter": 0,
Expand Down Expand Up @@ -173,7 +173,7 @@
6.146408206163261,
6.726224574612897
],
"name": "Trial 6",
"name": "Trial 6 Very Long<br>multiline label",
"boxpoints": "all",
"pointpos": -1.5,
"jitter": 0,
Expand All @@ -194,12 +194,12 @@
},
{
"x": [
"Trial 1",
"Trial 2",
"Trial 3",
"Trial 4",
"Trial 5",
"Trial 6"
"Trial 1 Very Long<br>multiline label",
"Trial 2 Very Long<br>multiline label",
"Trial 3 Very Long<br>multiline label",
"Trial 4 Very Long<br>multiline label",
"Trial 5 Very Long<br>multiline label",
"Trial 6 Very Long<br>multiline label"
],
"y": [
-0.16783142774745008,
Expand Down Expand Up @@ -262,7 +262,8 @@
"showticklabels": true,
"tick0": 0,
"dtick": 1,
"tickangle": 0,
"tickangle": "auto",
"autotickangles": [5, -25],
"anchor": "y",
"autorange": true
},
Expand Down
29 changes: 22 additions & 7 deletions test/jasmine/tests/axes_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4267,16 +4267,22 @@ describe('Test axes', function() {
var op = parts[0];

var method = {
'=': 'toBe',
'=': 'toBeCloseTo',
'~=': 'toBeWithin',
grew: 'toBeGreaterThan',
shrunk: 'toBeLessThan',
initial: 'toBe'
initial: 'toBeCloseTo'
}[op];

var val = op === 'initial' ? initialSize[k] : previousSize[k];
var msgk = msg + ' ' + k + (parts[1] ? ' |' + parts[1] : '');
var args = op === '~=' ? [val, 1.1, msgk] : [val, msgk, ''];
var args = [val];
if(op === '~=') {
args.push(1.1);
} else if(method === 'toBeCloseTo') {
args.push(3);
}
args.push(msgk);

expect(actual[k])[method](args[0], args[1], args[2]);
}
Expand Down Expand Up @@ -4312,7 +4318,7 @@ describe('Test axes', function() {
width: 600, height: 600
})
.then(function() {
expect(gd._fullLayout.xaxis._tickAngles.xtick).toBe(30);
expect(gd._fullLayout.xaxis._tickAngles.xtick).toBeCloseTo(30, 3);

var gs = gd._fullLayout._size;
initialSize = Lib.extendDeep({}, gs);
Expand Down Expand Up @@ -4484,13 +4490,22 @@ describe('Test axes', function() {
var op = parts[0];

var method = {
'=': 'toBe',
'=': 'toBeCloseTo',
'~=': 'toBeWithin',
grew: 'toBeGreaterThan',
shrunk: 'toBeLessThan',
initial: 'toBeCloseTo'
}[op];

var val = initialSize[k];
var msgk = msg + ' ' + k + (parts[1] ? ' |' + parts[1] : '');
var args = op === '~=' ? [val, 1.1, msgk] : [val, msgk, ''];
var args = [val];
if(op === '~=') {
args.push(1.1);
} else if(method === 'toBeCloseTo') {
args.push(3);
}
args.push(msgk);

expect(actual[k])[method](args[0], args[1], args[2]);
}
Expand Down Expand Up @@ -4525,7 +4540,7 @@ describe('Test axes', function() {
width: 600, height: 600
})
.then(function() {
expect(gd._fullLayout.xaxis._tickAngles.xtick).toBe(30);
expect(gd._fullLayout.xaxis._tickAngles.xtick).toBeCloseTo(30, 3);

var gs = gd._fullLayout._size;
initialSize = Lib.extendDeep({}, gs);
Expand Down
5 changes: 4 additions & 1 deletion test/jasmine/tests/plots_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,10 @@ describe('Test Plots with automargin and minreducedwidth/height', function() {
assert('height', '100');
})
.then(function() {
return Plotly.relayout(gd, 'minreducedwidth', 100);
// force tickangle to 90 so when we increase the width the x axis labels
// don't revert to 30 degrees, giving us a larger height
// this is a cool effect, but not what we're testing here!
return Plotly.relayout(gd, {minreducedwidth: 100, 'xaxis.tickangle': 90});
})
.then(function() {
assert('width', '100');
Expand Down
Loading

0 comments on commit b31d45b

Please sign in to comment.