Skip to content

Commit 5495fd2

Browse files
ketcharagilar
authored andcommitted
Implement proposed enhancement from #8388.
1 parent abdc7c4 commit 5495fd2

File tree

2 files changed

+26
-20
lines changed

2 files changed

+26
-20
lines changed

lib/matplotlib/pyplot.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2909,14 +2909,14 @@ def streamplot(
29092909
x, y, u, v, density=1, linewidth=None, color=None, cmap=None,
29102910
norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1,
29112911
transform=None, zorder=None, start_points=None, maxlength=4.0,
2912-
integration_direction='both', *, data=None):
2912+
integration_direction='both', broken_streamlines=True, *, data=None):
29132913
__ret = gca().streamplot(
29142914
x, y, u, v, density=density, linewidth=linewidth, color=color,
29152915
cmap=cmap, norm=norm, arrowsize=arrowsize,
29162916
arrowstyle=arrowstyle, minlength=minlength,
29172917
transform=transform, zorder=zorder, start_points=start_points,
29182918
maxlength=maxlength,
2919-
integration_direction=integration_direction,
2919+
integration_direction=integration_direction, broken_streamlines=broken_streamlines,
29202920
**({"data": data} if data is not None else {}))
29212921
sci(__ret.lines)
29222922
return __ret

lib/matplotlib/streamplot.py

+24-18
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
def 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

424430
class 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

Comments
 (0)