@@ -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 } ) ;
0 commit comments