Skip to content

Commit 3a6e2d7

Browse files
authored
Merge pull request #6967 from my-tien/autotickangles-ticksonboundaries
autotickangles for axes with tickson="boundaries" or showdividers=true
2 parents a00e8c3 + 8648bb5 commit 3a6e2d7

File tree

5 files changed

+44
-37
lines changed

5 files changed

+44
-37
lines changed

draftlogs/6967_fix.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Fix applying `autotickangles` on axes with `showdividers` as well as cases where `tickson` is set to "boundaries" [[#6967](https://github.com/plotly/plotly.js/pull/6967)],
2+
with thanks to @my-tien for the contribution!

src/plots/cartesian/axes.js

+35-30
Original file line numberDiff line numberDiff line change
@@ -3748,27 +3748,54 @@ axes.drawLabels = function(gd, ax, opts) {
37483748
});
37493749
});
37503750

3751-
if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) {
3751+
// autotickangles
3752+
// if there are dividers or ticks on boundaries, the labels will be in between and
3753+
// we need to prevent overlap with the next divider/tick. Else the labels will be on
3754+
// the ticks and we need to prevent overlap with the next label.
3755+
3756+
// TODO should secondary labels also fall into this fix-overlap regime?
3757+
var preventOverlapWithTick = (ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary;
3758+
3759+
var vLen = vals.length;
3760+
var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);
3761+
3762+
var adjacent = preventOverlapWithTick ? tickSpacing / 2 : tickSpacing;
3763+
var opposite = preventOverlapWithTick ? ax.ticklen : maxFontSize * 1.25 * maxLines;
3764+
var hypotenuse = Math.sqrt(Math.pow(adjacent, 2) + Math.pow(opposite, 2));
3765+
var maxCos = adjacent / hypotenuse;
3766+
var autoTickAnglesRadians = ax.autotickangles.map(
3767+
function(degrees) { return degrees * Math.PI / 180; }
3768+
);
3769+
var angleRadians = autoTickAnglesRadians.find(
3770+
function(angle) { return Math.abs(Math.cos(angle)) <= maxCos; }
3771+
);
3772+
if(angleRadians === undefined) {
3773+
// no angle with smaller cosine than maxCos, just pick the angle with smallest cosine
3774+
angleRadians = autoTickAnglesRadians.reduce(
3775+
function(currentMax, nextAngle) {
3776+
return Math.abs(Math.cos(currentMax)) < Math.abs(Math.cos(nextAngle)) ? currentMax : nextAngle;
3777+
}
3778+
, autoTickAnglesRadians[0]
3779+
);
3780+
}
3781+
var newAngle = angleRadians * (180 / Math.PI /* to degrees */);
3782+
3783+
if(preventOverlapWithTick) {
37523784
var gap = 2;
37533785
if(ax.ticks) gap += ax.tickwidth / 2;
37543786

3755-
// TODO should secondary labels also fall into this fix-overlap regime?
3756-
37573787
for(i = 0; i < lbbArray.length; i++) {
37583788
var xbnd = vals[i].xbnd;
37593789
var lbb = lbbArray[i];
37603790
if(
37613791
(xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) ||
37623792
(xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap)
37633793
) {
3764-
autoangle = 90;
3794+
autoangle = newAngle;
37653795
break;
37663796
}
37673797
}
37683798
} else {
3769-
var vLen = vals.length;
3770-
var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);
3771-
37723799
var ticklabelposition = ax.ticklabelposition || '';
37733800
var has = function(str) {
37743801
return ticklabelposition.indexOf(str) !== -1;
@@ -3779,29 +3806,7 @@ axes.drawLabels = function(gd, ax, opts) {
37793806
var isBottom = has('bottom');
37803807
var isAligned = isBottom || isLeft || isTop || isRight;
37813808
var pad = !isAligned ? 0 :
3782-
(ax.tickwidth || 0) + 2 * TEXTPAD;
3783-
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 */);
3809+
(ax.tickwidth || 0) + 2 * TEXTPAD;
38053810

38063811
for(i = 0; i < lbbArray.length - 1; i++) {
38073812
if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1], pad)) {
634 Bytes
Loading

test/jasmine/tests/axes_test.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -4896,13 +4896,13 @@ describe('Test axes', function() {
48964896
function _assert(msg, exp) {
48974897
var tickLabels = d3SelectAll('.xtick > text');
48984898

4899-
expect(tickLabels.size()).toBe(exp.angle.length, msg + ' - # of tick labels');
4899+
expect(tickLabels.size()).withContext(msg + ' - # of tick labels').toBe(exp.angle.length);
49004900

49014901
tickLabels.each(function(_, i) {
49024902
var t = d3Select(this).attr('transform');
49034903
var rotate = (t.split('rotate(')[1] || '').split(')')[0];
49044904
var angle = rotate.split(',')[0];
4905-
expect(Number(angle)).toBe(exp.angle[i], msg + ' - node ' + i);
4905+
expect(Number(angle)).withContext(msg + ' - node ' + i).toBeCloseTo(exp.angle[i], 2);
49064906
});
49074907
}
49084908

@@ -4920,7 +4920,7 @@ describe('Test axes', function() {
49204920
})
49214921
.then(function() {
49224922
_assert('base - rotated', {
4923-
angle: [90, 90, 90]
4923+
angle: [30, 30, 30]
49244924
});
49254925

49264926
return Plotly.relayout(gd, 'xaxis.range', [-0.4, 1.4]);
@@ -4934,7 +4934,7 @@ describe('Test axes', function() {
49344934
})
49354935
.then(function() {
49364936
_assert('narrow range / wide ticks - rotated', {
4937-
angle: [90, 90]
4937+
angle: [30, 30]
49384938
});
49394939
})
49404940
.then(done, done.fail);

test/jasmine/tests/cartesian_interact_test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -779,13 +779,13 @@ describe('axis zoom/pan and main plot zoom', function() {
779779

780780
function _assertLabels(msg, exp) {
781781
var tickLabels = d3Select(gd).selectAll('.xtick > text');
782-
expect(tickLabels.size()).toBe(exp.angle.length, msg + ' - # of tick labels');
782+
expect(tickLabels.size()).withContext(msg + ' - # of tick labels').toBe(exp.angle.length);
783783

784784
tickLabels.each(function(_, i) {
785785
var t = d3Select(this).attr('transform');
786786
var rotate = (t.split('rotate(')[1] || '').split(')')[0];
787787
var angle = rotate.split(',')[0];
788-
expect(Number(angle)).toBe(exp.angle[i], msg + ' - node ' + i);
788+
expect(Number(angle)).withContext(msg + ' - node ' + i).toBeCloseTo(exp.angle[i], 2);
789789
});
790790

791791
var tickLabels2 = d3Select(gd).selectAll('.xtick2 > text');
@@ -813,7 +813,7 @@ describe('axis zoom/pan and main plot zoom', function() {
813813
})
814814
.then(function() {
815815
return _run('drag to wide-range -> rotates labels', [-340, 0], {
816-
angle: [90, 90, 90, 90, 90, 90, 90],
816+
angle: [30, 30, 30, 30, 30, 30, 30],
817817
y: [430, 430]
818818
});
819819
})

0 commit comments

Comments
 (0)