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