1818def  streamplot (axes , x , y , u , v , density = 1 , linewidth = None , color = None ,
1919               cmap = None , norm = None , arrowsize = 1 , arrowstyle = '-|>' ,
2020               minlength = 0.1 , transform = None , zorder = None , start_points = None ,
21-                maxlength = 4.0 , integration_direction = 'both' ):
21+                maxlength = 4.0 , integration_direction = 'both' ,
22+                broken_streamlines = True ):
2223    """ 
2324    Draw streamlines of a vector flow. 
2425
@@ -70,6 +71,10 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
7071        Integrate the streamline in forward, backward or both directions. 
7172    data : indexable object, optional 
7273        DATA_PARAMETER_PLACEHOLDER 
74+     broken_streamlines : boolean, default: True 
75+         If False, forces streamlines to continue until they 
76+         leave the plot domain.  If True, they may be terminated if they 
77+         come too close to another streamline. 
7378
7479    Returns 
7580    ------- 
@@ -149,7 +154,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
149154        for  xm , ym  in  _gen_starting_points (mask .shape ):
150155            if  mask [ym , xm ] ==  0 :
151156                xg , yg  =  dmap .mask2grid (xm , ym )
152-                 t  =  integrate (xg , yg )
157+                 t  =  integrate (xg , yg ,  broken_streamlines )
153158                if  t  is  not None :
154159                    trajectories .append (t )
155160    else :
@@ -177,7 +182,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
177182            xg  =  np .clip (xg , 0 , grid .nx  -  1 )
178183            yg  =  np .clip (yg , 0 , grid .ny  -  1 )
179184
180-             t  =  integrate (xg , yg )
185+             t  =  integrate (xg , yg ,  broken_streamlines )
181186            if  t  is  not None :
182187                trajectories .append (t )
183188
@@ -294,19 +299,19 @@ def data2grid(self, xd, yd):
294299    def  grid2data (self , xg , yg ):
295300        return  xg  /  self .x_data2grid , yg  /  self .y_data2grid 
296301
297-     def  start_trajectory (self , xg , yg ):
302+     def  start_trajectory (self , xg , yg ,  broken_streamlines = True ):
298303        xm , ym  =  self .grid2mask (xg , yg )
299-         self .mask ._start_trajectory (xm , ym )
304+         self .mask ._start_trajectory (xm , ym ,  broken_streamlines )
300305
301306    def  reset_start_point (self , xg , yg ):
302307        xm , ym  =  self .grid2mask (xg , yg )
303308        self .mask ._current_xy  =  (xm , ym )
304309
305-     def  update_trajectory (self , xg , yg ):
310+     def  update_trajectory (self , xg , yg ,  broken_streamlines = True ):
306311        if  not  self .grid .within_grid (xg , yg ):
307312            raise  InvalidIndexError 
308313        xm , ym  =  self .grid2mask (xg , yg )
309-         self .mask ._update_trajectory (xm , ym )
314+         self .mask ._update_trajectory (xm , ym ,  broken_streamlines )
310315
311316    def  undo_trajectory (self ):
312317        self .mask ._undo_trajectory ()
@@ -396,17 +401,17 @@ def __init__(self, density):
396401    def  __getitem__ (self , args ):
397402        return  self ._mask [args ]
398403
399-     def  _start_trajectory (self , xm , ym ):
404+     def  _start_trajectory (self , xm , ym ,  broken_streamlines = True ):
400405        """Start recording streamline trajectory""" 
401406        self ._traj  =  []
402-         self ._update_trajectory (xm , ym )
407+         self ._update_trajectory (xm , ym ,  broken_streamlines )
403408
404409    def  _undo_trajectory (self ):
405410        """Remove current trajectory from mask""" 
406411        for  t  in  self ._traj :
407412            self ._mask [t ] =  0 
408413
409-     def  _update_trajectory (self , xm , ym ):
414+     def  _update_trajectory (self , xm , ym ,  broken_streamlines = True ):
410415        """ 
411416        Update current trajectory position in mask. 
412417
@@ -418,7 +423,10 @@ def _update_trajectory(self, xm, ym):
418423                self ._mask [ym , xm ] =  1 
419424                self ._current_xy  =  (xm , ym )
420425            else :
421-                 raise  InvalidIndexError 
426+                 if  broken_streamlines :
427+                     raise  InvalidIndexError 
428+                 else :
429+                     pass 
422430
423431
424432class  InvalidIndexError (Exception ):
@@ -457,9 +465,9 @@ def backward_time(xi, yi):
457465        dxi , dyi  =  forward_time (xi , yi )
458466        return  - dxi , - dyi 
459467
460-     def  integrate (x0 , y0 ):
468+     def  integrate (x0 , y0 ,  broken_streamlines = True ):
461469        """ 
462-         Return (N, 2)  grid-coordinates of trajectory based on starting point. 
470+         Return x, y  grid-coordinates of trajectory based on starting point. 
463471
464472        Integrate both forward and backward in time from starting point in 
465473        grid coordinates. 
@@ -472,17 +480,19 @@ def integrate(x0, y0):
472480        stotal , xy_traj  =  0. , []
473481
474482        try :
475-             dmap .start_trajectory (x0 , y0 )
483+             dmap .start_trajectory (x0 , y0 ,  broken_streamlines )
476484        except  InvalidIndexError :
477485            return  None 
478486        if  integration_direction  in  ['both' , 'backward' ]:
479-             s , xyt  =  _integrate_rk12 (x0 , y0 , dmap , backward_time , maxlength )
487+             s , xyt  =  _integrate_rk12 (x0 , y0 , dmap , backward_time , maxlength ,
488+                                      broken_streamlines )
480489            stotal  +=  s 
481490            xy_traj  +=  xyt [::- 1 ]
482491
483492        if  integration_direction  in  ['both' , 'forward' ]:
484493            dmap .reset_start_point (x0 , y0 )
485-             s , xyt  =  _integrate_rk12 (x0 , y0 , dmap , forward_time , maxlength )
494+             s , xyt  =  _integrate_rk12 (x0 , y0 , dmap , forward_time , maxlength ,
495+                                      broken_streamlines )
486496            stotal  +=  s 
487497            xy_traj  +=  xyt [1 :]
488498
@@ -508,7 +518,7 @@ class OutOfBounds(IndexError):
508518    pass 
509519
510520
511- def  _integrate_rk12 (x0 , y0 , dmap , f , maxlength ):
521+ def  _integrate_rk12 (x0 , y0 , dmap , f , maxlength ,  broken_streamlines = True ):
512522    """ 
513523    2nd-order Runge-Kutta algorithm with adaptive step size. 
514524
@@ -588,7 +598,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength):
588598            xi  +=  dx2 
589599            yi  +=  dy2 
590600            try :
591-                 dmap .update_trajectory (xi , yi )
601+                 dmap .update_trajectory (xi , yi ,  broken_streamlines )
592602            except  InvalidIndexError :
593603                break 
594604            if  stotal  +  ds  >  maxlength :
0 commit comments