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' , broken_streamlines = True ):
2222 """
2323 Draw streamlines of a vector flow.
2424
@@ -70,6 +70,9 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
7070 Integrate the streamline in forward, backward or both directions.
7171 data : indexable object, optional
7272 DATA_PARAMETER_PLACEHOLDER
73+ broken_streamlines : If False, forces streamlines to continue until they
74+ leave the plot domain. If True, they may be terminated if they
75+ come too close to another streamline. Default is True.
7376
7477 Returns
7578 -------
@@ -149,7 +152,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
149152 for xm , ym in _gen_starting_points (mask .shape ):
150153 if mask [ym , xm ] == 0 :
151154 xg , yg = dmap .mask2grid (xm , ym )
152- t = integrate (xg , yg )
155+ t = integrate (xg , yg , broken_streamlines )
153156 if t is not None :
154157 trajectories .append (t )
155158 else :
@@ -177,7 +180,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
177180 xg = np .clip (xg , 0 , grid .nx - 1 )
178181 yg = np .clip (yg , 0 , grid .ny - 1 )
179182
180- t = integrate (xg , yg )
183+ t = integrate (xg , yg , broken_streamlines )
181184 if t is not None :
182185 trajectories .append (t )
183186
@@ -294,19 +297,19 @@ def data2grid(self, xd, yd):
294297 def grid2data (self , xg , yg ):
295298 return xg / self .x_data2grid , yg / self .y_data2grid
296299
297- def start_trajectory (self , xg , yg ):
300+ def start_trajectory (self , xg , yg , broken_streamlines = True ):
298301 xm , ym = self .grid2mask (xg , yg )
299- self .mask ._start_trajectory (xm , ym )
302+ self .mask ._start_trajectory (xm , ym , broken_streamlines )
300303
301304 def reset_start_point (self , xg , yg ):
302305 xm , ym = self .grid2mask (xg , yg )
303306 self .mask ._current_xy = (xm , ym )
304307
305- def update_trajectory (self , xg , yg ):
308+ def update_trajectory (self , xg , yg , broken_streamlines = True ):
306309 if not self .grid .within_grid (xg , yg ):
307310 raise InvalidIndexError
308311 xm , ym = self .grid2mask (xg , yg )
309- self .mask ._update_trajectory (xm , ym )
312+ self .mask ._update_trajectory (xm , ym , broken_streamlines )
310313
311314 def undo_trajectory (self ):
312315 self .mask ._undo_trajectory ()
@@ -396,17 +399,17 @@ def __init__(self, density):
396399 def __getitem__ (self , args ):
397400 return self ._mask [args ]
398401
399- def _start_trajectory (self , xm , ym ):
402+ def _start_trajectory (self , xm , ym , broken_streamlines = True ):
400403 """Start recording streamline trajectory"""
401404 self ._traj = []
402- self ._update_trajectory (xm , ym )
405+ self ._update_trajectory (xm , ym , broken_streamlines )
403406
404407 def _undo_trajectory (self ):
405408 """Remove current trajectory from mask"""
406409 for t in self ._traj :
407410 self ._mask [t ] = 0
408411
409- def _update_trajectory (self , xm , ym ):
412+ def _update_trajectory (self , xm , ym , broken_streamlines = True ):
410413 """
411414 Update current trajectory position in mask.
412415
@@ -418,7 +421,10 @@ def _update_trajectory(self, xm, ym):
418421 self ._mask [ym , xm ] = 1
419422 self ._current_xy = (xm , ym )
420423 else :
421- raise InvalidIndexError
424+ if broken_streamlines :
425+ raise InvalidIndexError
426+ else :
427+ pass
422428
423429
424430class InvalidIndexError (Exception ):
@@ -457,9 +463,9 @@ def backward_time(xi, yi):
457463 dxi , dyi = forward_time (xi , yi )
458464 return - dxi , - dyi
459465
460- def integrate (x0 , y0 ):
466+ def integrate (x0 , y0 , broken_streamlines = True ):
461467 """
462- Return (N, 2) grid-coordinates of trajectory based on starting point.
468+ Return x, y grid-coordinates of trajectory based on starting point.
463469
464470 Integrate both forward and backward in time from starting point in
465471 grid coordinates.
@@ -472,17 +478,17 @@ def integrate(x0, y0):
472478 stotal , xy_traj = 0. , []
473479
474480 try :
475- dmap .start_trajectory (x0 , y0 )
481+ dmap .start_trajectory (x0 , y0 , broken_streamlines )
476482 except InvalidIndexError :
477483 return None
478484 if integration_direction in ['both' , 'backward' ]:
479- s , xyt = _integrate_rk12 (x0 , y0 , dmap , backward_time , maxlength )
485+ s , xyt = _integrate_rk12 (x0 , y0 , dmap , backward_time , maxlength , broken_streamlines )
480486 stotal += s
481487 xy_traj += xyt [::- 1 ]
482488
483489 if integration_direction in ['both' , 'forward' ]:
484490 dmap .reset_start_point (x0 , y0 )
485- s , xyt = _integrate_rk12 (x0 , y0 , dmap , forward_time , maxlength )
491+ s , xyt = _integrate_rk12 (x0 , y0 , dmap , forward_time , maxlength , broken_streamlines )
486492 stotal += s
487493 xy_traj += xyt [1 :]
488494
@@ -508,7 +514,7 @@ class OutOfBounds(IndexError):
508514 pass
509515
510516
511- def _integrate_rk12 (x0 , y0 , dmap , f , maxlength ):
517+ def _integrate_rk12 (x0 , y0 , dmap , f , maxlength , broken_streamlines = True ):
512518 """
513519 2nd-order Runge-Kutta algorithm with adaptive step size.
514520
@@ -588,7 +594,7 @@ def _integrate_rk12(x0, y0, dmap, f, maxlength):
588594 xi += dx2
589595 yi += dy2
590596 try :
591- dmap .update_trajectory (xi , yi )
597+ dmap .update_trajectory (xi , yi , broken_streamlines )
592598 except InvalidIndexError :
593599 break
594600 if stotal + ds > maxlength :
0 commit comments