@@ -1661,13 +1661,12 @@ def view_limits(self, vmin, vmax):
1661
1661
return mtransforms .nonsingular (vmin , vmax )
1662
1662
1663
1663
1664
+ # @cbook.deprecated("3.1")
1664
1665
def closeto (x , y ):
1665
- if abs (x - y ) < 1e-10 :
1666
- return True
1667
- else :
1668
- return False
1666
+ return abs (x - y ) < 1e-10
1669
1667
1670
1668
1669
+ # @cbook.deprecated("3.1")
1671
1670
class Base (object ):
1672
1671
'this solution has some hacks to deal with floating point inaccuracies'
1673
1672
def __init__ (self , base ):
@@ -1766,6 +1765,49 @@ def scale_range(vmin, vmax, n=1, threshold=100):
1766
1765
return scale , offset
1767
1766
1768
1767
1768
+ class _Edge_integer :
1769
+ """
1770
+ Helper for MaxNLocator.
1771
+
1772
+ Take floating point precision limitations into account when calculating
1773
+ tick locations as integer multiples of a step.
1774
+ """
1775
+ def __init__ (self , step , offset ):
1776
+ """
1777
+ *step* is a positive floating-point interval between ticks.
1778
+ *offset* is the offset subtracted from the data limits
1779
+ prior to calculating tick locations.
1780
+ """
1781
+ if step <= 0 :
1782
+ raise ValueError ("'step' must be positive" )
1783
+ self .step = step
1784
+ self ._offset = abs (offset )
1785
+
1786
+ def closeto (self , ms , edge ):
1787
+ # Allow more slop when the offset is large compared to the step.
1788
+ if self ._offset > 0 :
1789
+ digits = np .log10 (self ._offset / self .step )
1790
+ tol = max (1e-10 , 10 ** (digits - 12 ))
1791
+ tol = min (0.4999 , tol )
1792
+ else :
1793
+ tol = 1e-10
1794
+ return abs (ms - edge ) < tol
1795
+
1796
+ def le (self , x ):
1797
+ 'Return the largest n: n*base <= x'
1798
+ d , m = _divmod (x , self .step )
1799
+ if self .closeto (m / self .step , 1 ):
1800
+ return (d + 1 )
1801
+ return d
1802
+
1803
+ def ge (self , x ):
1804
+ 'Return the smallest n: n*base >= x'
1805
+ d , m = _divmod (x , self .step )
1806
+ if self .closeto (m / self .step , 0 ):
1807
+ return d
1808
+ return (d + 1 )
1809
+
1810
+
1769
1811
class MaxNLocator (Locator ):
1770
1812
"""
1771
1813
Select no more than N intervals at nice locations.
@@ -1880,6 +1922,12 @@ def set_params(self, **kwargs):
1880
1922
self ._integer = kwargs ['integer' ]
1881
1923
1882
1924
def _raw_ticks (self , vmin , vmax ):
1925
+ """
1926
+ Generate a list of tick locations including the range *vmin* to
1927
+ *vmax*. In some applications, one or both of the end locations
1928
+ will not be needed, in which case they are trimmed off
1929
+ elsewhere.
1930
+ """
1883
1931
if self ._nbins == 'auto' :
1884
1932
if self .axis is not None :
1885
1933
nbins = np .clip (self .axis .get_tick_space (),
@@ -1892,7 +1940,7 @@ def _raw_ticks(self, vmin, vmax):
1892
1940
scale , offset = scale_range (vmin , vmax , nbins )
1893
1941
_vmin = vmin - offset
1894
1942
_vmax = vmax - offset
1895
- raw_step = (vmax - vmin ) / nbins
1943
+ raw_step = (_vmax - _vmin ) / nbins
1896
1944
steps = self ._extended_steps * scale
1897
1945
if self ._integer :
1898
1946
# For steps > 1, keep only integer values.
@@ -1911,20 +1959,29 @@ def _raw_ticks(self, vmin, vmax):
1911
1959
break
1912
1960
1913
1961
# This is an upper limit; move to smaller steps if necessary.
1914
- for i in range (istep ):
1915
- step = steps [istep - i ]
1962
+ # for i in range(istep):
1963
+ # step = steps[istep - i]
1964
+ for istep in range (istep , - 1 , - 1 ):
1965
+ step = steps [istep ]
1966
+
1916
1967
if (self ._integer and
1917
1968
np .floor (_vmax ) - np .ceil (_vmin ) >= self ._min_n_ticks - 1 ):
1918
1969
step = max (1 , step )
1919
1970
best_vmin = (_vmin // step ) * step
1920
1971
1921
- low = np .round (Base (step ).le (_vmin - best_vmin ) / step )
1922
- high = np .round (Base (step ).ge (_vmax - best_vmin ) / step )
1923
- ticks = np .arange (low , high + 1 ) * step + best_vmin + offset
1924
- nticks = ((ticks <= vmax ) & (ticks >= vmin )).sum ()
1972
+ # Find tick locations spanning the vmin-vmax range, taking into
1973
+ # account degradation of precision when there is a large offset.
1974
+ # The edge ticks beyond vmin and/or vmax are needed for the
1975
+ # "round_numbers" autolimit mode.
1976
+ edge = _Edge_integer (step , offset )
1977
+ low = edge .le (_vmin - best_vmin )
1978
+ high = edge .ge (_vmax - best_vmin )
1979
+ ticks = np .arange (low , high + 1 ) * step + best_vmin
1980
+ # Count only the ticks that will be displayed.
1981
+ nticks = ((ticks <= _vmax ) & (ticks >= _vmin )).sum ()
1925
1982
if nticks >= self ._min_n_ticks :
1926
1983
break
1927
- return ticks
1984
+ return ticks + offset
1928
1985
1929
1986
def __call__ (self ):
1930
1987
vmin , vmax = self .axis .get_view_interval ()
0 commit comments