@@ -237,16 +237,16 @@ class Line2D(Artist):
237
237
def __str__ (self ):
238
238
if self ._label != "" :
239
239
return "Line2D(%s)" % (self ._label )
240
- elif hasattr (self , '_x' ) and len (self ._x ) > 3 :
240
+ elif self ._x is None :
241
+ return "Line2D()"
242
+ elif len (self ._x ) > 3 :
241
243
return "Line2D((%g,%g),(%g,%g),...,(%g,%g))" \
242
244
% (self ._x [0 ], self ._y [0 ], self ._x [0 ],
243
245
self ._y [0 ], self ._x [- 1 ], self ._y [- 1 ])
244
- elif hasattr ( self , '_x' ) :
246
+ else :
245
247
return "Line2D(%s)" \
246
248
% ("," .join (["(%g,%g)" % (x , y ) for x , y
247
249
in zip (self ._x , self ._y )]))
248
- else :
249
- return "Line2D()"
250
250
251
251
def __init__ (self , xdata , ydata ,
252
252
linewidth = None , # all Nones default to rc
@@ -373,6 +373,14 @@ def __init__(self, xdata, ydata,
373
373
self ._yorig = np .asarray ([])
374
374
self ._invalidx = True
375
375
self ._invalidy = True
376
+ self ._x = None
377
+ self ._y = None
378
+ self ._xy = None
379
+ self ._path = None
380
+ self ._transformed_path = None
381
+ self ._subslice = False
382
+ self ._x_filled = None # used in subslicing; only x is needed
383
+
376
384
self .set_data (xdata , ydata )
377
385
378
386
def __getstate__ (self ):
@@ -598,7 +606,7 @@ def recache(self, always=False):
598
606
if always or self ._invalidx :
599
607
xconv = self .convert_xunits (self ._xorig )
600
608
if ma .isMaskedArray (self ._xorig ):
601
- x = ma .asarray (xconv , np .float_ )
609
+ x = ma .asarray (xconv , np .float_ ). filled ( np . nan )
602
610
else :
603
611
x = np .asarray (xconv , np .float_ )
604
612
x = x .ravel ()
@@ -607,7 +615,7 @@ def recache(self, always=False):
607
615
if always or self ._invalidy :
608
616
yconv = self .convert_yunits (self ._yorig )
609
617
if ma .isMaskedArray (self ._yorig ):
610
- y = ma .asarray (yconv , np .float_ )
618
+ y = ma .asarray (yconv , np .float_ ). filled ( np . nan )
611
619
else :
612
620
y = np .asarray (yconv , np .float_ )
613
621
y = y .ravel ()
@@ -622,24 +630,30 @@ def recache(self, always=False):
622
630
if len (x ) != len (y ):
623
631
raise RuntimeError ('xdata and ydata must be the same length' )
624
632
625
- x = x .reshape ((len (x ), 1 ))
626
- y = y .reshape ((len (y ), 1 ))
633
+ self ._xy = np .empty ((len (x ), 2 ), dtype = np .float_ )
634
+ self ._xy [:, 0 ] = x
635
+ self ._xy [:, 1 ] = y
627
636
628
- if ma .isMaskedArray (x ) or ma .isMaskedArray (y ):
629
- self ._xy = ma .concatenate ((x , y ), 1 )
630
- else :
631
- self ._xy = np .concatenate ((x , y ), 1 )
632
637
self ._x = self ._xy [:, 0 ] # just a view
633
638
self ._y = self ._xy [:, 1 ] # just a view
634
639
635
640
self ._subslice = False
636
- if (self .axes and len (x ) > 100 and self ._is_sorted (x ) and
641
+ if (self .axes and len (x ) > 1000 and self ._is_sorted (x ) and
637
642
self .axes .name == 'rectilinear' and
638
643
self .axes .get_xscale () == 'linear' and
639
644
self ._markevery is None and
640
645
self .get_clip_on () is True ):
641
646
self ._subslice = True
642
- if hasattr (self , '_path' ):
647
+ nanmask = np .isnan (x )
648
+ if nanmask .any ():
649
+ self ._x_filled = self ._x .copy ()
650
+ indices = np .arange (len (x ))
651
+ self ._x_filled [nanmask ] = np .interp (indices [nanmask ],
652
+ indices [~ nanmask ], self ._x [~ nanmask ])
653
+ else :
654
+ self ._x_filled = self ._x
655
+
656
+ if self ._path is not None :
643
657
interpolation_steps = self ._path ._interpolation_steps
644
658
else :
645
659
interpolation_steps = 1
@@ -650,13 +664,14 @@ def recache(self, always=False):
650
664
651
665
def _transform_path (self , subslice = None ):
652
666
"""
653
- Puts a TransformedPath instance at self._transformed_path,
667
+ Puts a TransformedPath instance at self._transformed_path;
654
668
all invalidation of the transform is then handled by the
655
669
TransformedPath instance.
656
670
"""
657
671
# Masked arrays are now handled by the Path class itself
658
672
if subslice is not None :
659
- _path = Path (self ._xy [subslice , :])
673
+ _steps = self ._path ._interpolation_steps
674
+ _path = Path (self ._xy [subslice , :], _interpolation_steps = _steps )
660
675
else :
661
676
_path = self ._path
662
677
self ._transformed_path = TransformedPath (_path , self .get_transform ())
@@ -682,10 +697,11 @@ def set_transform(self, t):
682
697
self .stale = True
683
698
684
699
def _is_sorted (self , x ):
685
- """return true if x is sorted"""
700
+ """return True if x is sorted in ascending order"""
701
+ # We don't handle the monotonically decreasing case.
686
702
if len (x ) < 2 :
687
- return 1
688
- return np .amin (x [1 :] - x [0 :- 1 ]) >= 0
703
+ return True
704
+ return np .nanmin (x [1 :] - x [:- 1 ]) >= 0
689
705
690
706
@allow_rasterization
691
707
def draw (self , renderer ):
@@ -697,13 +713,14 @@ def draw(self, renderer):
697
713
self .recache ()
698
714
self .ind_offset = 0 # Needed for contains() method.
699
715
if self ._subslice and self .axes :
700
- # Need to handle monotonically decreasing case also...
701
716
x0 , x1 = self .axes .get_xbound ()
702
- i0 , = self ._x .searchsorted ([x0 ], 'left' )
703
- i1 , = self ._x .searchsorted ([x1 ], 'right' )
717
+ i0 , = self ._x_filled .searchsorted ([x0 ], 'left' )
718
+ i1 , = self ._x_filled .searchsorted ([x1 ], 'right' )
704
719
subslice = slice (max (i0 - 1 , 0 ), i1 + 1 )
705
- self .ind_offset = subslice .start
706
- self ._transform_path (subslice )
720
+ # Don't remake the Path unless it will be sufficiently smaller.
721
+ if subslice .start > 100 or len (self ._x ) - subslice .stop > 100 :
722
+ self .ind_offset = subslice .start
723
+ self ._transform_path (subslice )
707
724
708
725
transf_path = self ._get_transformed_path ()
709
726
@@ -1432,7 +1449,7 @@ def __init__(self, line):
1432
1449
:class:`matplotlib.axes.Axes` instance and should have the
1433
1450
picker property set.
1434
1451
"""
1435
- if not hasattr ( line , ' axes' ) :
1452
+ if line . axes is None :
1436
1453
raise RuntimeError ('You must first add the line to the Axes' )
1437
1454
1438
1455
if line .get_picker () is None :
0 commit comments