Skip to content

Commit 9170f25

Browse files
committed
axis autotickangles property
when tickangles is set to "auto", this array will be searched for the first angle large enogh to prevent overlap between labels
1 parent 264617b commit 9170f25

File tree

4 files changed

+61
-4
lines changed

4 files changed

+61
-4
lines changed

src/plots/cartesian/axes.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3479,6 +3479,7 @@ axes.drawLabels = function(gd, ax, opts) {
34793479

34803480
var labelFns = opts.labelFns;
34813481
var tickAngle = opts.secondary ? 0 : ax.tickangle;
3482+
var autoTickAngles = ax.autotickangles;
34823483
var prevAngle = (ax._prevTickAngles || {})[cls];
34833484

34843485
var tickLabels = opts.layer.selectAll('g.' + cls)
@@ -3780,12 +3781,24 @@ 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+
const adjacent = tickSpacing;
3785+
const opposite = maxFontSize * 1.25;
3786+
const hypotenuse = Math.sqrt(Math.pow(adjacent, 2) + Math.pow(opposite, 2));
3787+
// sin(angle) = opposite / hypotenuse
3788+
const minAngle = Math.asin(opposite / hypotenuse) * (180 / Math.PI /* to degrees */);
3789+
3790+
var angle = autoTickAngles.find(angle => Math.abs(angle) >= minAngle);
3791+
if(angle === undefined) {
3792+
// no angle larger than minAngle, just pick the largest angle
3793+
angle = autoTickAngles.reduce(
3794+
(currentMax, nextAngle) => Math.abs(currentMax) < Math.abs(nextAngle) ? nextAngle : currentMax
3795+
, autoTickAngles[0]
3796+
);
3797+
}
37843798

3785-
// any overlap at all - set 30 degrees or 90 degrees
37863799
for(i = 0; i < lbbArray.length - 1; i++) {
37873800
if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1], pad)) {
3788-
autoangle = rotate90 ? 90 : 30;
3801+
autoangle = angle;
37893802
break;
37903803
}
37913804
}

src/plots/cartesian/layout_attributes.js

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

src/plots/cartesian/tick_label_defaults.js

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

43-
if(!options.noAng) coerce('tickangle');
43+
if(!options.noAng) {
44+
coerce('tickangle');
45+
coerce('autotickangles');
46+
}
4447

4548
if(axType !== 'category') {
4649
var tickFormat = coerce('tickformat');

test/plot-schema.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10699,6 +10699,21 @@
1069910699
},
1070010700
"role": "object"
1070110701
},
10702+
"autotickangles": {
10703+
"coerceNumber": true,
10704+
"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.",
10705+
"dflt": [
10706+
30,
10707+
90
10708+
],
10709+
"editType": "ticks",
10710+
"valType": "data_array"
10711+
},
10712+
"autotickanglessrc": {
10713+
"description": "Sets the source reference on Chart Studio Cloud for `autotickangles`.",
10714+
"editType": "none",
10715+
"valType": "string"
10716+
},
1070210717
"autotypenumbers": {
1070310718
"description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.",
1070410719
"dflt": "convert types",
@@ -12039,6 +12054,21 @@
1203912054
"editType": "plot",
1204012055
"valType": "boolean"
1204112056
},
12057+
"autotickangles": {
12058+
"coerceNumber": true,
12059+
"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.",
12060+
"dflt": [
12061+
30,
12062+
90
12063+
],
12064+
"editType": "ticks",
12065+
"valType": "data_array"
12066+
},
12067+
"autotickanglessrc": {
12068+
"description": "Sets the source reference on Chart Studio Cloud for `autotickangles`.",
12069+
"editType": "none",
12070+
"valType": "string"
12071+
},
1204212072
"autotypenumbers": {
1204312073
"description": "Using *strict* a numeric string in trace data is not converted to a number. Using *convert types* a numeric string in trace data may be treated as a number during automatic axis `type` detection. Defaults to layout.autotypenumbers.",
1204412074
"dflt": "convert types",

0 commit comments

Comments
 (0)