Skip to content

Commit b31d45b

Browse files
authored
Merge pull request #6790 from my-tien/autotickangles
Add autotickangles property
2 parents 24b6f75 + b293a11 commit b31d45b

29 files changed

+152
-35
lines changed

draftlogs/6790_add.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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!

src/components/colorbar/defaults.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
109109
handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear');
110110

111111
var font = layout.font;
112-
var opts = {outerTicks: false, font: font};
112+
var opts = {
113+
noAutotickangles: true,
114+
outerTicks: false,
115+
font: font
116+
};
113117
if(ticklabelposition.indexOf('inside') !== -1) {
114118
opts.bgColor = 'black'; // could we instead use the average of colors in the scale?
115119
}

src/components/colorbar/draw.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,7 @@ function mockColorBarAxis(gd, opts, zrange) {
10011001
var axisOptions = {
10021002
letter: letter,
10031003
font: fullLayout.font,
1004+
noAutotickangles: letter === 'y',
10041005
noHover: true,
10051006
noTickson: true,
10061007
noTicklabelmode: true,

src/plots/cartesian/axes.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,13 +3472,13 @@ axes.drawLabels = function(gd, ax, opts) {
34723472

34733473
var fullLayout = gd._fullLayout;
34743474
var axId = ax._id;
3475-
var axLetter = axId.charAt(0);
34763475
var cls = opts.cls || axId + 'tick';
34773476

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

34803479
var labelFns = opts.labelFns;
34813480
var tickAngle = opts.secondary ? 0 : ax.tickangle;
3481+
34823482
var prevAngle = (ax._prevTickAngles || {})[cls];
34833483

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

37273727
var maxFontSize = 0;
37283728
var lbbArray = [];
37293729
var i;
3730-
3730+
var maxLines = 1;
37313731
tickLabels.each(function(d) {
37323732
maxFontSize = Math.max(maxFontSize, d.fontSize);
37333733

37343734
var x = ax.l2p(d.x);
37353735
var thisLabel = selectTickLabel(this);
37363736
var bb = Drawing.bBox(thisLabel.node());
3737+
maxLines = Math.max(maxLines, svgTextUtils.lineCount(thisLabel));
37373738

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

3783-
var rotate90 = (tickSpacing < maxFontSize * 2.5) || ax.type === 'multicategory' || ax._name === 'realaxis';
3784+
// autotickangles
3785+
var adjacent = tickSpacing;
3786+
var opposite = maxFontSize * 1.25 * maxLines;
3787+
var hypotenuse = Math.sqrt(Math.pow(adjacent, 2) + Math.pow(opposite, 2));
3788+
var maxCos = adjacent / hypotenuse;
3789+
var autoTickAnglesRadians = ax.autotickangles.map(
3790+
function(degrees) { return degrees * Math.PI / 180; }
3791+
);
3792+
var angleRadians = autoTickAnglesRadians.find(
3793+
function(angle) { return Math.abs(Math.cos(angle)) <= maxCos; }
3794+
);
3795+
if(angleRadians === undefined) {
3796+
// no angle with smaller cosine than maxCos, just pick the angle with smallest cosine
3797+
angleRadians = autoTickAnglesRadians.reduce(
3798+
function(currentMax, nextAngle) {
3799+
return Math.abs(Math.cos(currentMax)) < Math.abs(Math.cos(nextAngle)) ? currentMax : nextAngle;
3800+
}
3801+
, autoTickAnglesRadians[0]
3802+
);
3803+
}
3804+
var newAngle = angleRadians * (180 / Math.PI /* to degrees */);
37843805

3785-
// any overlap at all - set 30 degrees or 90 degrees
37863806
for(i = 0; i < lbbArray.length - 1; i++) {
37873807
if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1], pad)) {
3788-
autoangle = rotate90 ? 90 : 30;
3808+
autoangle = newAngle;
37893809
break;
37903810
}
37913811
}
@@ -3807,7 +3827,7 @@ axes.drawLabels = function(gd, ax, opts) {
38073827
// by rotating 90 degrees, do not attempt to re-fix its label overlaps
38083828
// as this can lead to infinite redraw loops!
38093829
if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) {
3810-
autoangle = 90;
3830+
autoangle = prevAngle;
38113831
seq.push(function() {
38123832
positionLabels(tickLabels, prevAngle);
38133833
});

src/plots/cartesian/layout_attributes.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,20 @@ module.exports = {
809809
'vertically.'
810810
].join(' ')
811811
},
812+
autotickangles: {
813+
valType: 'info_array',
814+
freeLength: true,
815+
items: {
816+
valType: 'angle'
817+
},
818+
dflt: [0, 30, 90],
819+
editType: 'ticks',
820+
description: [
821+
'When `tickangle` is set to *auto*, it will be set to the first',
822+
'angle in this array that is large enough to prevent label',
823+
'overlap.'
824+
].join(' ')
825+
},
812826
tickprefix: {
813827
valType: 'string',
814828
dflt: '',

src/plots/cartesian/layout_defaults.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
242242
visibleDflt: visibleDflt,
243243
reverseDflt: reverseDflt,
244244
autotypenumbersDflt: autotypenumbersDflt,
245-
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
245+
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId],
246+
noAutotickangles: axLetter === 'y'
246247
};
247248

248249
coerce('uirevision', layoutOut.uirevision);

src/plots/cartesian/tick_label_defaults.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@ module.exports = function handleTickLabelDefaults(containerIn, containerOut, coe
4040
coerce('ticklabelstep');
4141
}
4242

43-
if(!options.noAng) coerce('tickangle');
43+
if(!options.noAng) {
44+
var tickAngle = coerce('tickangle');
45+
if(!options.noAutotickangles && tickAngle === 'auto') {
46+
coerce('autotickangles');
47+
}
48+
}
4449

4550
if(axType !== 'category') {
4651
var tickFormat = coerce('tickformat');

src/plots/gl3d/layout/axis_defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) {
4141
letter: axName[0],
4242
data: options.data,
4343
showGrid: true,
44+
noAutotickangles: true,
4445
noTickson: true,
4546
noTicklabelmode: true,
4647
noTicklabelstep: true,

src/plots/polar/layout_attributes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ var radialAxisAttrs = {
105105
].join(' ')
106106
},
107107

108+
autotickangles: axesAttrs.autotickangles,
109+
108110
side: {
109111
valType: 'enumerated',
110112
// TODO add 'center' for `showline: false` radial axes

src/plots/polar/layout_defaults.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ function handleDefaults(contIn, contOut, coerce, opts) {
167167
color: dfltFontColor,
168168
size: dfltFontSize,
169169
family: dfltFontFamily
170-
}
170+
},
171+
noAutotickangles: axName === 'angularaxis'
171172
});
172173

173174
handleTickMarkDefaults(axIn, axOut, coerceAxis, {outerTicks: true});

0 commit comments

Comments
 (0)