@@ -979,7 +979,7 @@ axes.ti
E7F5
ckText = function(ax, x, hover) {
979
979
980
980
// Setup ticks and grid lines boundaries
981
981
// at 1/2 a 'category' to the left/bottom
982
- if ( ax . tickson === 'boundaries' ) {
982
+ if ( ax . tickson === 'boundaries' || ax . showdividers ) {
983
983
var inbounds = function ( v ) {
984
984
var p = ax . l2p ( v ) ;
985
985
return p >= 0 && p <= ax . _length ? v : null ;
@@ -1583,6 +1583,7 @@ axes.draw = function(gd, arg, opts) {
1583
1583
plotinfo . yaxislayer . selectAll ( '.' + ya . _id + 'tick' ) . remove ( ) ;
1584
1584
if ( xa . type === 'multicategory' ) {
1585
1585
plotinfo . xaxislayer . selectAll ( '.' + xa . _id + 'tick2' ) . remove ( ) ;
1586
+ plotinfo . xaxislayer . selectAll ( '.' + xa . _id + 'divider' ) . remove ( ) ;
1586
1587
}
1587
1588
if ( plotinfo . gridlayer ) plotinfo . gridlayer . selectAll ( 'path' ) . remove ( ) ;
1588
1589
if ( plotinfo . zerolinelayer ) plotinfo . zerolinelayer . selectAll ( 'path' ) . remove ( ) ;
@@ -1642,36 +1643,21 @@ axes.drawOne = function(gd, ax, opts) {
1642
1643
ax . _selections = { } ;
1643
1644
1644
1645
var transFn = axes . makeTransFn ( ax ) ;
1645
-
1646
+ var tickVals ;
1646
1647
// We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
1647
1648
// The key case here is removing zero lines when the axis bound is zero
1648
1649
var valsClipped ;
1649
- var tickVals ;
1650
- var gridVals ;
1651
1650
1652
1651
if ( ax . tickson === 'boundaries' && vals . length ) {
1653
- // valsBoundaries is not used for labels;
1654
- // no need to worry about the other tickTextObj keys
1655
- var valsBoundaries = [ ] ;
1656
- var _push = function ( d , bndIndex ) {
1657
- var xb = d . xbnd [ bndIndex ] ;
1658
- if ( xb !== null ) {
1659
- valsBoundaries . push ( Lib . extendFlat ( { } , d , { x : xb } ) ) ;
1660
- }
1661
- } ;
1662
- for ( i = 0 ; i < vals . length ; i ++ ) _push ( vals [ i ] , 0 ) ;
1663
- _push ( vals [ i - 1 ] , 1 ) ;
1664
-
1665
- valsClipped = axes . clipEnds ( ax , valsBoundaries ) ;
1666
- tickVals = ax . ticks === 'inside' ? valsClipped : valsBoundaries ;
1667
- gridVals = valsClipped ;
1652
+ var boundaryVals = getBoundaryVals ( ax , vals ) ;
1653
+ valsClipped = axes . clipEnds ( ax , boundaryVals ) ;
1654
+ tickVals = ax . ticks === 'inside' ? valsClipped : boundaryVals ;
1668
1655
} else {
1669
1656
valsClipped = axes . clipEnds ( ax , vals ) ;
1670
1657
tickVals = ax . ticks === 'inside' ? valsClipped : vals ;
1671
- gridVals = valsClipped ;
1672
1658
}
1673
1659
1674
- ax . _valsClipped = valsClipped ;
1660
+ var gridVals = ax . _gridVals = valsClipped ;
1675
1661
1676
1662
if ( ! fullLayout . _hasOnlyLargeSploms ) {
1677
1663
// keep track of which subplots (by main conteraxis) we've already
@@ -1759,56 +1745,35 @@ axes.drawOne = function(gd, ax, opts) {
1759
1745
} ) ;
1760
1746
1761
1747
if ( ax . type === 'multicategory' ) {
1762
- seq . push ( function ( ) {
1763
- // TODO?
1764
- // drawDividers()
1765
-
1766
- var secondaryLabelVals = [ ] ;
1767
- var lookup = { } ;
1768
- for ( i = 0 ; i < vals . length ; i ++ ) {
1769
- var d = vals [ i ] ;
1770
- if ( lookup [ d . text2 ] ) {
1771
- lookup [ d . text2 ] . push ( d . x ) ;
1772
- } else {
1773
- lookup [ d . text2 ] = [ d . x ] ;
1774
- }
1775
- }
1776
- for ( var k in lookup ) {
1777
- secondaryLabelVals . push ( tickTextObj ( ax , Lib . interp ( lookup [ k ] , 0.5 ) , k ) ) ;
1778
- }
1748
+ var labelLength = 0 ;
1779
1749
1780
- var labelHeight = 0 ;
1781
- ax . _selections [ ax . _id + 'tick' ] . each ( function ( ) {
1782
- var thisLabel = selectTickLabel ( this ) ;
1783
-
1784
- // TODO Drawing.bBox doesn't work when labels are rotated
1785
- // var bb = Drawing.bBox(thisLabel.node());
1786
- var bb = thisLabel . node ( ) . getBoundingClientRect ( ) ;
1787
- labelHeight = Math . max ( labelHeight , bb . height ) ;
1788
- } ) ;
1789
-
1790
- var secondarayPosition ;
1791
- if ( ax . side === 'bottom' ) {
1792
- secondarayPosition = mainLinePosition + labelHeight + 2 ;
1793
- } else {
1794
- secondarayPosition = mainLinePosition - labelHeight - 2 ;
1795
- if ( ax . tickfont ) {
1796
- secondarayPosition -= ( ax . tickfont . size * LINE_SPACING ) ;
1797
- }
1798
- }
1799
-
1800
- var secondaryLabelFns = axes . makeLabelFns ( ax , secondarayPosition ) ;
1750
+ seq . push ( function ( ) {
1751
+ labelLength += getLabelLevelSpan ( ax . _selections [ axId + 'tick' ] ) + 2 ;
1752
+ labelLength += ax . _lastangle ? ax . tickfont . size * LINE_SPACING : 0 ;
1753
+ var secondaryPosition = mainLinePosition + labelLength * tickSigns [ 2 ] ;
1754
+ var secondaryLabelFns = axes . makeLabelFns ( ax , secondaryPosition ) ;
1801
1755
1802
1756
return axes . drawLabels ( gd , ax , {
1803
- vals : secondaryLabelVals ,
1757
+ vals : getSecondaryLabelVals ( ax , vals ) ,
1804
1758
layer : mainAxLayer ,
1805
- cls : ax . _id + 'tick2' ,
1759
+ cls : axId + 'tick2' ,
1806
1760
transFn : transFn ,
1807
1761
labelXFn : secondaryLabelFns . labelXFn ,
1808
1762
labelYFn : secondaryLabelFns . labelYFn ,
1809
1763
labelAnchorFn : secondaryLabelFns . labelAnchorFn ,
1810
1764
} ) ;
1811
1765
} ) ;
1766
+
1767
+ seq . push ( function ( ) {
1768
+ labelLength += getLabelLevelSpan ( ax . _selections [ axId + 'tick2' ] ) + 2 ;
1769
+
1770
+ return drawDividers ( gd , ax , {
1771
+ vals : getDividerVals ( ax , vals ) ,
1772
+ layer : mainAxLayer ,
1773
+ path : axes . makeTickPath ( ax , mainLinePosition , tickSigns [ 2 ] , labelLength ) ,
1774
+ transFn : transFn
1775
+ } ) ;
1776
+ } ) ;
1812
1777
}
1813
1778
1814
1779
function extendRange ( range , newRange ) {
@@ -1937,6 +1902,87 @@ axes.drawOne = function(gd, ax, opts) {
1937
1902
return Lib . syncOrAsync ( seq ) ;
1938
1903
} ;
1939
1904
1905
+ function getBoundaryVals ( ax , vals ) {
1906
+ var out = [ ] ;
1907
+ var i ;
1908
+
1909
+ // boundaryVals are never used for labels;
1910
+ // no need to worry about the other tickTextObj keys
1911
+ var _push = function ( d , bndIndex ) {
1912
+ var xb = d . xbnd [ bndIndex ] ;
1913
+ if ( xb !== null ) {
1914
+ out . push ( Lib . extendFlat ( { } , d , { x : xb } ) ) ;
1915
+ }
1916
+ } ;
1917
+
1918
+ for ( i = 0 ; i < vals . length ; i ++ ) {
1919
+ _push ( vals [ i ] , 0 ) ;
1920
+ }
1921
+ _push ( vals [ i - 1 ] , 1 ) ;
1922
+
1923
+ return out ;
1924
+ }
1925
+
1926
+ function getSecondaryLabelVals ( ax , vals ) {
1927
+ var out = [ ] ;
1928
+ var lookup = { } ;
1929
+
1930
+ for ( var i = 0 ; i < vals . length ; i ++ ) {
1931
+ var d = vals [ i ] ;
1932
+ if ( lookup [ d . text2 ] ) {
1933
+ lookup [ d . text2 ] .push ( d . x ) ;
1934
+ } else {
1935
+ lookup [ d . text2 ] = [ d . x ] ;
1936
+ }
1937
+ }
1938
+
1939
+ for ( var k in lookup ) {
1940
+ out . push ( tickTextObj ( ax , Lib . interp ( lookup [ k ] , 0.5 ) , k ) ) ;
1941
+ }
1942
+
1943
+ return out ;
1944
+ }
1945
+
1946
+ function getDividerVals ( ax , vals ) {
1947
+ var out = [ ] ;
1948
+ var i , current ;
1949
+
1950
+ // never used for labels;
1951
+ // no need to worry about the other tickTextObj keys
1952
+ var _push = function ( d , bndIndex ) {
1953
+ var xb = d . xbnd [ bndIndex ] ;
1954
+ if ( xb !== null ) {
1955
+ out . push ( Lib . extendFlat ( { } , d , { x : xb } ) ) ;
1956
+ }
1957
+ } ;
1958
+
1959
+ for ( i = 0 ; i < vals . length ; i ++ ) {
1960
+ var d = vals [ i ] ;
1961
+ if ( d . text2 !== current ) {
1962
+ _push ( d , 0 ) ;
1963
+ }
1964
+ current = d . text2 ;
1965
+ }
1966
+ _push ( vals [ i - 1 ] , 1 ) ;
1967
+
1968
+ return out ;
1969
+ }
1970
+
1971
+ function getLabelLevelSpan ( tickLabels ) {
1972
+ var out = 2 ;
1973
+
1974
+ tickLabels . each ( function ( ) {
1975
+ var thisLabel = selectTickLabel ( this ) ;
1976
+
1977
+ // TODO Drawing.bBox doesn't work when labels are rotated
1978
+ // var bb = Drawing.bBox(thisLabel.node());
1979
+ var bb = thisLabel . node ( ) . getBoundingClientRect ( ) ;
1980
+ out = Math . max ( out , bb . height ) ;
1981
+ } ) ;
1982
+
1983
+ return out ;
1984
+ }
1985
+
1940
1986
/**
1941
1987
* Which direction do the 'ax.side' values, and free ticks go?
1942
1988
*
@@ -1988,12 +2034,15 @@ axes.makeTransFn = function(ax) {
1988
2034
* - {number} linewidth
1989
2035
* @param {number } shift along direction of ticklen
1990
2036
* @param {1 or -1 } sng tick sign
2037
+ * @param {number (optional) } len tick length
1991
2038
* @return {string }
1992
2039
*/
1993
- axes . makeTickPath = function ( ax , shift , sgn ) {
2040
+ axes . makeTickPath = function ( ax , shift , sgn , len ) {
2041
+ len = len !== undefined ? len : ax . ticklen ;
2042
+
1994
2043
var axLetter = ax . _id . charAt ( 0 ) ;
1995
2044
var pad = ( ax . linewidth || 1 ) / 2 ;
1996
- var len = ax . ticklen ;
2045
+
1997
2046
return axLetter === 'x' ?
1998
2047
'M0,' + ( shift + pad * sgn ) + 'v' + ( len * sgn ) :
1999
2048
'M' + ( shift + pad * sgn ) + ',0h' + ( len * sgn ) ;
@@ -2181,7 +2230,6 @@ axes.drawGrid = function(gd, ax, opts) {
2181
2230
* - {string} zerolinecolor
2182
2231
* - {number (optional)} _gridWidthCrispRound
2183
2232
* @param {object } opts
2184
- * - {array of object} vals (calcTicks output-like)
2185
2233
* - {d3 selection} layer
2186
2234
* - {object} counterAxis (full axis object corresponding to counter axis)
2187
2235
* - {string or fn} path
@@ -2392,10 +2440,12 @@ axes.drawLabels = function(gd, ax, opts) {
2392
2440
2393
2441
var autoangle = 0 ;
2394
2442
2395
- if ( ax . tickson === 'boundaries' && cls === ax . _id + 'tick' ) {
2443
+ if ( ( ax . tickson === 'boundaries' || ax . showdividers ) && cls === ax . _id + 'tick' ) {
2396
2444
var gap = 2 ;
2397
2445
if ( ax . ticks ) gap += ax . tickwidth / 2 ;
2398
2446
2447
+ // TODO should secondary labels also fall into this fix-overlap regime?
2448
+
2399
2449
for ( i = 0 ; i < lbbArray . length ; i ++ ) {
2400
2450
var xbnd = vals [ i ] . xbnd ;
2401
2451
var lbb = lbbArray [ i ] ;
@@ -2437,6 +2487,41 @@ axes.drawLabels = function(gd, ax, opts) {
2437
2487
return done ;
2438
2488
} ;
2439
2489
2490
+ /**
2491
+ * Draw axis dividers
2492
+ *
2493
+ * @param {DOM element } gd
2494
+ * @param {object } ax (full) axis object
2495
+ * - {string} _id
2496
+ * - {string} showdividers
2497
+ * - {number} dividerwidth
2498
+ * - {string} dividercolor
2499
+ * @param {object } opts
2500
+ * - {array of object} vals (calcTicks output-like)
2501
+ * - {d3 selection} layer
2502
+ * - {fn} path
2503
+ * - {fn} transFn
2504
+ */
2505
+ function drawDividers ( gd , ax , opts ) {
2506
+ var cls = ax . _id + 'divider' ;
2507
+ var vals = opts . vals ;
2508
+
2509
+ var dividers = opts . layer . selectAll ( 'path.' + cls )
2510
+ . data ( ax . showdividers ? vals : [ ] , makeDataFn ( ax ) ) ;
2511
+
2512
+ dividers . exit ( ) . remove ( ) ;
2513
+
2514
+ dividers . enter ( ) . insert ( 'path' , ':first-child' )
2515
+ . classed ( cls , 1 )
2516
+ . classed ( 'crisp' , 1 )
2517
+ . call ( Color . stroke , ax . dividercolor )
2518
+ . style ( 'stroke-width' , Drawing . crispRound ( gd , ax . dividerwidth , 1 ) + 'px' ) ;
2519
+
2520
+ dividers
2521
+ . attr ( 'transform' , opts . transFn )
2522
+ . attr ( 'd' , opts . path ) ;
2523
+ }
2524
+
2440
2525
function drawTitle ( gd , ax ) {
2441
2526
var fullLayout = gd . _fullLayout ;
2442
2527
var axId = ax . _id ;
@@ -2519,7 +2604,7 @@ axes.shouldShowZeroLine = function(gd, ax, counterAxis) {
2519
2604
( rng [ 0 ] * rng [ 1 ] <= 0 ) &&
2520
2605
ax . zeroline &&
2521
2606
( ax . type === 'linear' || ax . type === '-' ) &&
2522
- ax . _valsClipped . length &&
2607
+ ax . _gridVals . length &&
2523
2608
(
2524
2609
clipEnds ( ax , 0 ) ||
2525
2610
! anyCounterAxLineAtZero ( gd , ax , counterAxis , rng ) ||
0 commit comments