@@ -105,9 +105,12 @@ def secondary_sources(x0, n0, *, size=0.05, grid=None):
105105
106106
107107def loudspeakers (x0 , n0 , a0 = 0.5 , * , size = 0.08 , show_numbers = False , grid = None ,
108- ax = None ):
108+ ax = None , zorder = 2 ):
109109 """Draw loudspeaker symbols at given locations and angles.
110110
111+ The default ``zorder`` is changed to 2, which is the same as line plots
112+ (e.g. `level_contour()`).
113+
111114 Parameters
112115 ----------
113116 x0 : (N, 3) array_like
@@ -161,7 +164,7 @@ def loudspeakers(x0, n0, a0=0.5, *, size=0.08, show_numbers=False, grid=None,
161164
162165 # add collection of patches to current axis
163166 p = _mpl .collections .PatchCollection (
164- patches , edgecolor = '0' , facecolor = _np .tile (1 - a0 , 3 ))
167+ patches , edgecolor = '0' , facecolor = _np .tile (1 - a0 , 3 ), zorder = zorder )
165168 if ax is None :
166169 ax = _plt .gca ()
167170 ax .add_collection (p )
@@ -183,6 +186,48 @@ def _visible_secondarysources(x0, n0, grid):
183186 return x0 [idx , :], n0 [idx , :]
184187
185188
189+ def _plotting_plane (p , grid ):
190+ if p .ndim == 3 :
191+ if p .shape [2 ] == 1 :
192+ p = p [:, :, 0 ] # first axis: y; second axis: x
193+ plotting_plane = 'xy'
194+ elif p .shape [1 ] == 1 :
195+ p = p [:, 0 , :].T # first axis: z; second axis: y
196+ plotting_plane = 'yz'
197+ elif p .shape [0 ] == 1 :
198+ p = p [0 , :, :].T # first axis: z; second axis: x
199+ plotting_plane = 'xz'
200+ else :
201+ raise ValueError ("If p is 3D, one dimension must have length 1" )
202+ elif len (grid ) == 3 :
203+ if grid [2 ].ndim == 0 :
204+ plotting_plane = 'xy'
205+ elif grid [1 ].ndim == 0 :
206+ plotting_plane = 'xz'
207+ elif grid [0 ].ndim == 0 :
208+ plotting_plane = 'yz'
209+ else :
210+ raise ValueError (
211+ "If p is 2D and grid is 3D, one grid component must be scalar" )
212+ else :
213+ # 2-dimensional case
214+ plotting_plane = 'xy'
215+
216+ if plotting_plane == 'xy' :
217+ x , y = grid [[0 , 1 ]]
218+ elif plotting_plane == 'xz' :
219+ x , y = grid [[0 , 2 ]]
220+ elif plotting_plane == 'yz' :
221+ x , y = grid [[1 , 2 ]]
222+
223+ dx = 0.5 * _np .ptp (x ) / p .shape [0 ]
224+ dy = 0.5 * _np .ptp (y ) / p .shape [1 ]
225+
226+ extent = x .min () - dx , x .max () + dx , y .min () - dy , y .max () + dy
227+
228+ return p , extent , plotting_plane
229+
230+
186231def amplitude (p , grid , * , xnorm = None , cmap = 'coolwarm_clip' ,
187232 vmin = - 2.0 , vmax = 2.0 , xlabel = None , ylabel = None ,
188233 colorbar = True , colorbar_kwargs = {}, ax = None , ** kwargs ):
@@ -256,41 +301,7 @@ def amplitude(p, grid, *, xnorm=None, cmap='coolwarm_clip',
256301 if xnorm is not None :
257302 p = _util .normalize (p , grid , xnorm )
258303
259- if p .ndim == 3 :
260- if p .shape [2 ] == 1 :
261- p = p [:, :, 0 ] # first axis: y; second axis: x
262- plotting_plane = 'xy'
263- elif p .shape [1 ] == 1 :
264- p = p [:, 0 , :].T # first axis: z; second axis: y
265- plotting_plane = 'yz'
266- elif p .shape [0 ] == 1 :
267- p = p [0 , :, :].T # first axis: z; second axis: x
268- plotting_plane = 'xz'
269- else :
270- raise ValueError ("If p is 3D, one dimension must have length 1" )
271- elif len (grid ) == 3 :
272- if grid [2 ].ndim == 0 :
273- plotting_plane = 'xy'
274- elif grid [1 ].ndim == 0 :
275- plotting_plane = 'xz'
276- elif grid [0 ].ndim == 0 :
277- plotting_plane = 'yz'
278- else :
279- raise ValueError (
280- "If p is 2D and grid is 3D, one grid component must be scalar" )
281- else :
282- # 2-dimensional case
283- plotting_plane = 'xy'
284-
285- if plotting_plane == 'xy' :
286- x , y = grid [[0 , 1 ]]
287- elif plotting_plane == 'xz' :
288- x , y = grid [[0 , 2 ]]
289- elif plotting_plane == 'yz' :
290- x , y = grid [[1 , 2 ]]
291-
292- dx = 0.5 * _np .ptp (x ) / p .shape [0 ]
293- dy = 0.5 * _np .ptp (y ) / p .shape [1 ]
304+ p , extent , plotting_plane = _plotting_plane (p , grid )
294305
295306 if ax is None :
296307 ax = _plt .gca ()
@@ -300,8 +311,7 @@ def amplitude(p, grid, *, xnorm=None, cmap='coolwarm_clip',
300311 p = _np .clip (p , - 1e15 , 1e15 ) # clip to float64 range
301312
302313 im = ax .imshow (_np .real (p ), cmap = cmap , origin = 'lower' ,
303- extent = [x .min ()- dx , x .max ()+ dx , y .min ()- dy , y .max ()+ dy ],
304- vmax = vmax , vmin = vmin , ** kwargs )
314+ extent = extent , vmax = vmax , vmin = vmin , ** kwargs )
305315 if xlabel is None :
306316 xlabel = plotting_plane [0 ] + ' / m'
307317 if ylabel is None :
@@ -333,6 +343,38 @@ def level(p, grid, *, xnorm=None, power=False, cmap=None, vmax=3, vmin=-50,
333343 vmax = vmax , vmin = vmin , ** kwargs )
334344
335345
346+ def level_contour (p , grid , * , xnorm = None , power = False ,
347+ xlabel = None , ylabel = None , ax = None , ** kwargs ):
348+ """Two-dimensional contour plot of level (dB) of sound field.
349+
350+ Parameters
351+ ----------
352+ p, grid, xnorm, power, xlabel, ylabel, ax
353+ Same as in `level()`.
354+ **kwargs
355+ All further parameters are forwarded to
356+ :func:`matplotlib.pyplot.contour`.
357+
358+ """
359+ p = _np .asarray (p )
360+ grid = _util .as_xyz_components (grid )
361+ # normalize before converting to dB!
362+ if xnorm is not None :
363+ p = _util .normalize (p , grid , xnorm )
364+ p , extent , plotting_plane = _plotting_plane (p , grid )
365+ L = _util .db (p , power = power )
366+ if ax is None :
367+ ax = _plt .gca ()
368+ contour = ax .contour (L , extent = extent , ** kwargs )
369+ if xlabel is None :
370+ xlabel = plotting_plane [0 ] + ' / m'
371+ if ylabel is None :
372+ ylabel = plotting_plane [1 ] + ' / m'
373+ ax .set_xlabel (xlabel )
374+ ax .set_ylabel (ylabel )
375+ return contour
376+
377+
336378def particles (x , * , trim = None , ax = None , xlabel = 'x (m)' , ylabel = 'y (m)' ,
337379 edgecolors = None , marker = '.' , s = 15 , ** kwargs ):
338380 """Plot particle positions as scatter plot.
0 commit comments