@@ -1163,6 +1163,20 @@ function formatCategory(ax, out) {
11631163 var tt = ax . _categories [ Math . round ( out . x ) ] ;
11641164 if ( tt === undefined ) tt = '' ;
11651165 out . text = String ( tt ) ;
1166+
1167+ // Setup ticks and grid lines boundaries
1168+ // at 1/2 a 'category' to the left/bottom
1169+ if ( ax . tickson === 'boundaries' ) {
1170+ var inbounds = function ( v ) {
1171+ var p = ax . l2p ( v ) ;
1172+ return p >= 0 && p <= ax . _length ? v : null ;
1173+ } ;
1174+
1175+ out . xbnd = [
1176+ inbounds ( out . x - 0.5 ) ,
1177+ inbounds ( out . x + ax . dtick - 0.5 )
1178+ ] ;
1179+ }
11661180}
11671181
11681182function formatLinear ( ax , out , hover , extraPrecision , hideexp ) {
@@ -1610,14 +1624,41 @@ axes.drawOne = function(gd, ax, opts) {
16101624 var subplotsWithAx = axes . getSubplots ( gd , ax ) ;
16111625
16121626 var vals = ax . _vals = axes . calcTicks ( ax ) ;
1613- // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
1614- // The key case here is removing zero lines when the axis bound is zero
1615- var valsClipped = ax . _valsClipped = axes . clipEnds ( ax , vals ) ;
16161627
16171628 if ( ! ax . visible ) return ;
16181629
16191630 var transFn = axes . makeTransFn ( ax ) ;
16201631
1632+ // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
1633+ // The key case here is removing zero lines when the axis bound is zero
1634+ var valsClipped ;
1635+ var tickVals ;
1636+ var gridVals ;
1637+
1638+ if ( ax . tickson === 'boundaries' && vals . length ) {
1639+ // valsBoundaries is not used for labels;
1640+ // no need to worry about the other tickTextObj keys
1641+ var valsBoundaries = [ ] ;
1642+ var _push = function ( d , bndIndex ) {
1643+ var xb = d . xbnd [ bndIndex ] ;
1644+ if ( xb !== null ) {
1645+ valsBoundaries . push ( Lib . extendFlat ( { } , d , { x : xb } ) ) ;
1646+ }
1647+ } ;
1648+ for ( i = 0 ; i < vals . length ; i ++ ) _push ( vals [ i ] , 0 ) ;
1649+ _push ( vals [ i - 1 ] , 1 ) ;
1650+
1651+ valsClipped = axes . clipEnds ( ax , valsBoundaries ) ;
1652+ tickVals = ax . ticks === 'inside' ? valsClipped : valsBoundaries ;
1653+ gridVals = valsClipped ;
1654+ } else {
1655+ valsClipped = axes . clipEnds ( ax , vals ) ;
1656+ tickVals = ax . ticks === 'inside' ? valsClipped : vals ;
1657+ gridVals = valsClipped ;
1658+ }
1659+
1660+ ax . _valsClipped = valsClipped ;
1661+
16211662 if ( ! fullLayout . _hasOnlyLargeSploms ) {
16221663 // keep track of which subplots (by main conteraxis) we've already
16231664 // drawn grids for, so we don't overdraw overlaying subplots
@@ -1637,7 +1678,7 @@ axes.drawOne = function(gd, ax, opts) {
16371678 'M' + counterAxis . _offset + ',0h' + counterAxis . _length ;
16381679
16391680 axes . drawGrid ( gd , ax , {
1640- vals : valsClipped ,
1681+ vals : gridVals ,
16411682 layer : plotinfo . gridlayer . select ( '.' + axId ) ,
16421683 path : gridPath ,
16431684 transFn : transFn
@@ -1652,7 +1693,6 @@ axes.drawOne = function(gd, ax, opts) {
16521693 }
16531694
16541695 var tickSigns = axes . getTickSigns ( ax ) ;
1655- var tickVals = ax . ticks === 'inside' ? valsClipped : vals ;
16561696 var tickSubplots = [ ] ;
16571697
16581698 if ( ax . ticks ) {
@@ -1920,8 +1960,9 @@ axes.makeTickPath = function(ax, shift, sgn) {
19201960axes . makeLabelFns = function ( ax , shift , angle ) {
19211961 var axLetter = ax . _id . charAt ( 0 ) ;
19221962 var pad = ( ax . linewidth || 1 ) / 2 ;
1963+ var ticksOnOutsideLabels = ax . tickson !== 'boundaries' && ax . ticks === 'outside' ;
19231964
1924- var labelStandoff = ax . ticks === 'outside' ? ax . ticklen : 0 ;
1965+ var labelStandoff = ticksOnOutsideLabels ? ax . ticklen : 0 ;
19251966 var labelShift = 0 ;
19261967
19271968 if ( angle && ax . ticks === 'outside' ) {
@@ -1930,7 +1971,7 @@ axes.makeLabelFns = function(ax, shift, angle) {
19301971 labelShift = ax . ticklen * Math . sin ( rad ) ;
19311972 }
19321973
1933- if ( ax . showticklabels && ( ax . ticks === 'outside' || ax . showline ) ) {
1974+ if ( ax . showticklabels && ( ticksOnOutsideLabels || ax . showline ) ) {
19341975 labelStandoff += 0.2 * ax . tickfont . size ;
19351976 }
19361977
@@ -2018,7 +2059,6 @@ axes.drawTicks = function(gd, ax, opts) {
20182059 ticks . attr ( 'transform' , opts . transFn ) ;
20192060} ;
20202061
2021-
20222062/**
20232063 * Draw axis grid
20242064 *
@@ -2151,8 +2191,6 @@ axes.drawLabels = function(gd, ax, opts) {
21512191 var tickLabels = opts . layer . selectAll ( 'g.' + cls )
21522192 . data ( ax . showticklabels ? vals : [ ] , makeDataFn ( ax ) ) ;
21532193
2154- var maxFontSize = 0 ;
2155- var autoangle = 0 ;
21562194 var labelsReady = [ ] ;
21572195
21582196 tickLabels . enter ( ) . append ( 'g' )
@@ -2187,10 +2225,6 @@ axes.drawLabels = function(gd, ax, opts) {
21872225
21882226 tickLabels . exit ( ) . remove ( ) ;
21892227
2190- tickLabels . each ( function ( d ) {
2191- maxFontSize = Math . max ( maxFontSize , d . fontSize ) ;
2192- } ) ;
2193-
21942228 ax . _tickLabels = tickLabels ;
21952229
21962230 // TODO ??
@@ -2273,16 +2307,20 @@ axes.drawLabels = function(gd, ax, opts) {
22732307 // check for auto-angling if x labels overlap
22742308 // don't auto-angle at all for log axes with
22752309 // base and digit format
2276- if ( axLetter === 'x' && ! isNumeric ( ax . tickangle ) &&
2310+ if ( vals . length && axLetter === 'x' && ! isNumeric ( ax . tickangle ) &&
22772311 ( ax . type !== 'log' || String ( ax . dtick ) . charAt ( 0 ) !== 'D' )
22782312 ) {
2313+ var maxFontSize = 0 ;
22792314 var lbbArray = [ ] ;
2315+ var i ;
22802316
22812317 tickLabels . each ( function ( d ) {
22822318 var s = d3 . select ( this ) ;
22832319 var thisLabel = s . select ( '.text-math-group' ) ;
22842320 if ( thisLabel . empty ( ) ) thisLabel = s . select ( 'text' ) ;
22852321
2322+ maxFontSize = Math . max ( maxFontSize , d . fontSize ) ;
2323+
22862324 var x = ax . l2p ( d . x ) ;
22872325 var bb = Drawing . bBox ( thisLabel . node ( ) ) ;
22882326
@@ -2298,21 +2336,38 @@ axes.drawLabels = function(gd, ax, opts) {
22982336 } ) ;
22992337 } ) ;
23002338
2301- for ( var i = 0 ; i < lbbArray . length - 1 ; i ++ ) {
2302- if ( Lib . bBoxIntersect ( lbbArray [ i ] , lbbArray [ i + 1 ] ) ) {
2303- // any overlap at all - set 30 degrees
2304- autoangle = 30 ;
2305- break ;
2339+ var autoangle = 0 ;
2340+
2341+ if ( ax . tickson === 'boundaries' ) {
2342+ var gap = 2 ;
2343+ if ( ax . ticks ) gap += ax . tickwidth / 2 ;
2344+
2345+ for ( i = 0 ; i < lbbArray . length ; i ++ ) {
2346+ var xbnd = vals [ i ] . xbnd ;
2347+ var lbb = lbbArray [ i ] ;
2348+ if (
2349+ ( xbnd [ 0 ] !== null && ( lbb . left - ax . l2p ( xbnd [ 0 ] ) ) < gap ) ||
2350+ ( xbnd [ 1 ] !== null && ( ax . l2p ( xbnd [ 1 ] ) - lbb . right ) < gap )
2351+ ) {
2352+ autoangle = 90 ;
2353+ break ;
2354+ }
2355+ }
2356+ } else {
2357+ var vLen = vals . length ;
2358+ var tickSpacing = Math . abs ( ( vals [ vLen - 1 ] . x - vals [ 0 ] . x ) * ax . _m ) / ( vLen - 1 ) ;
2359+ var fitBetweenTicks = tickSpacing < maxFontSize * 2.5 ;
2360+
2361+ // any overlap at all - set 30 degrees or 90 degrees
2362+ for ( i = 0 ; i < lbbArray . length - 1 ; i ++ ) {
2363+ if ( Lib . bBoxIntersect ( lbbArray [ i ] , lbbArray [ i + 1 ] ) ) {
2364+ autoangle = fitBetweenTicks ? 90 : 30 ;
2365+ break ;
2366+ }
23062367 }
23072368 }
23082369
23092370 if ( autoangle ) {
2310- var tickspacing = Math . abs (
2311- ( vals [ vals . length - 1 ] . x - vals [ 0 ] . x ) * ax . _m
2312- ) / ( vals . length - 1 ) ;
2313- if ( tickspacing < maxFontSize * 2.5 ) {
2314- autoangle = 90 ;
2315- }
23162371 positionLabels ( tickLabels , autoangle ) ;
23172372 }
23182373 ax . _lastangle = autoangle ;
0 commit comments