@@ -105,9 +105,12 @@ def secondary_sources(x0, n0, *, size=0.05, grid=None):
105
105
106
106
107
107
def loudspeakers (x0 , n0 , a0 = 0.5 , * , size = 0.08 , show_numbers = False , grid = None ,
108
- ax = None ):
108
+ ax = None , zorder = 2 ):
109
109
"""Draw loudspeaker symbols at given locations and angles.
110
110
111
+ The default ``zorder`` is changed to 2, which is the same as line plots
112
+ (e.g. `level_contour()`).
113
+
111
114
Parameters
112
115
----------
113
116
x0 : (N, 3) array_like
@@ -161,7 +164,7 @@ def loudspeakers(x0, n0, a0=0.5, *, size=0.08, show_numbers=False, grid=None,
161
164
162
165
# add collection of patches to current axis
163
166
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 )
165
168
if ax is None :
166
169
ax = _plt .gca ()
167
170
ax .add_collection (p )
@@ -183,6 +186,48 @@ def _visible_secondarysources(x0, n0, grid):
183
186
return x0 [idx , :], n0 [idx , :]
184
187
185
188
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
+
186
231
def amplitude (p , grid , * , xnorm = None , cmap = 'coolwarm_clip' ,
187
232
vmin = - 2.0 , vmax = 2.0 , xlabel = None , ylabel = None ,
188
233
colorbar = True , colorbar_kwargs = {}, ax = None , ** kwargs ):
@@ -256,41 +301,7 @@ def amplitude(p, grid, *, xnorm=None, cmap='coolwarm_clip',
256
301
if xnorm is not None :
257
302
p = _util .normalize (p , grid , xnorm )
258
303
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 )
294
305
295
306
if ax is None :
296
307
ax = _plt .gca ()
@@ -300,8 +311,7 @@ def amplitude(p, grid, *, xnorm=None, cmap='coolwarm_clip',
300
311
p = _np .clip (p , - 1e15 , 1e15 ) # clip to float64 range
301
312
302
313
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 )
305
315
if xlabel is None :
306
316
xlabel = plotting_plane [0 ] + ' / m'
307
317
if ylabel is None :
@@ -333,6 +343,38 @@ def level(p, grid, *, xnorm=None, power=False, cmap=None, vmax=3, vmin=-50,
333
343
vmax = vmax , vmin = vmin , ** kwargs )
334
344
335
345
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
+
336
378
def particles (x , * , trim = None , ax = None , xlabel = 'x (m)' , ylabel = 'y (m)' ,
337
379
edgecolors = None , marker = '.' , s = 15 , ** kwargs ):
338
380
"""Plot particle positions as scatter plot.
0 commit comments