Skip to content

Commit

Permalink
Merge pull request #6967 from my-tien/autotickangles-ticksonboundaries
Browse files Browse the repository at this point in the history
autotickangles for axes with tickson="boundaries" or showdividers=true
  • Loading branch information
archmoj authored Apr 16, 2024
2 parents a00e8c3 + 8648bb5 commit 3a6e2d7
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 37 deletions.
2 changes: 2 additions & 0 deletions draftlogs/6967_fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- 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)],
with thanks to @my-tien for the contribution!
65 changes: 35 additions & 30 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3748,27 +3748,54 @@ axes.drawLabels = function(gd, ax, opts) {
});
});

if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) {
// autotickangles
// if there are dividers or ticks on boundaries, the labels will be in between and
// we need to prevent overlap with the next divider/tick. Else the labels will be on
// the ticks and we need to prevent overlap with the next label.

// TODO should secondary labels also fall into this fix-overlap regime?
var preventOverlapWithTick = (ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary;

var vLen = vals.length;
var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);

var adjacent = preventOverlapWithTick ? tickSpacing / 2 : tickSpacing;
var opposite = preventOverlapWithTick ? ax.ticklen : 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 */);

if(preventOverlapWithTick) {
var gap = 2;
if(ax.ticks) gap += ax.tickwidth / 2;

// TODO should secondary labels also fall into this fix-overlap regime?

for(i = 0; i < lbbArray.length; i++) {
var xbnd = vals[i].xbnd;
var lbb = lbbArray[i];
if(
(xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) ||
(xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap)
) {
autoangle = 90;
autoangle = newAngle;
break;
}
}
} else {
var vLen = vals.length;
var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);

var ticklabelposition = ax.ticklabelposition || '';
var has = function(str) {
return ticklabelposition.indexOf(str) !== -1;
Expand All @@ -3779,29 +3806,7 @@ axes.drawLabels = function(gd, ax, opts) {
var isBottom = has('bottom');
var isAligned = isBottom || isLeft || isTop || isRight;
var pad = !isAligned ? 0 :
(ax.tickwidth || 0) + 2 * TEXTPAD;

// 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 */);
(ax.tickwidth || 0) + 2 * TEXTPAD;

for(i = 0; i < lbbArray.length - 1; i++) {
if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1], pad)) {
Expand Down
Binary file modified test/image/baselines/tickson_boundaries.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions test/jasmine/tests/axes_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4896,13 +4896,13 @@ describe('Test axes', function() {
function _assert(msg, exp) {
var tickLabels = d3SelectAll('.xtick > text');

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

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

Expand All @@ -4920,7 +4920,7 @@ describe('Test axes', function() {
})
.then(function() {
_assert('base - rotated', {
angle: [90, 90, 90]
angle: [30, 30, 30]
});

return Plotly.relayout(gd, 'xaxis.range', [-0.4, 1.4]);
Expand All @@ -4934,7 +4934,7 @@ describe('Test axes', function() {
})
.then(function() {
_assert('narrow range / wide ticks - rotated', {
angle: [90, 90]
angle: [30, 30]
});
})
.then(done, done.fail);
Expand Down
6 changes: 3 additions & 3 deletions test/jasmine/tests/cartesian_interact_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -779,13 +779,13 @@ describe('axis zoom/pan and main plot zoom', function() {

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

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

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

0 comments on commit 3a6e2d7

Please sign in to comment.