|
7 | 7 | import json |
8 | 8 | import re |
9 | 9 | import warnings |
10 | | -from collections.abc import Mapping, MutableSequence |
| 10 | +from collections.abc import Iterable, Mapping, MutableSequence |
11 | 11 | from importlib.resources import files |
12 | 12 | from pathlib import Path |
13 | 13 | from subprocess import TimeoutExpired |
14 | 14 | from tempfile import NamedTemporaryFile |
15 | | -from typing import Any, Optional |
| 15 | +from typing import Any, Optional, Union |
16 | 16 |
|
17 | 17 | import matplotlib.pyplot as plt |
18 | 18 | import numpy as np |
@@ -307,30 +307,92 @@ def __call__( |
307 | 307 |
|
308 | 308 |
|
309 | 309 | def plot_all_geometry( |
310 | | - trace_image: Imager, *, colorbar=True, figsize=None |
| 310 | + trace_image: Imager, |
| 311 | + *, |
| 312 | + colorbar: bool = True, |
| 313 | + figsize: Optional[tuple] = None, |
| 314 | + engines: Optional[Iterable] = None, |
311 | 315 | ) -> Mapping[model.GeometryEngine, Any]: |
312 | 316 | """Convenience function for plotting all available geometry types.""" |
313 | | - width_ratios = [1.0] * len(model.GeometryEngine) |
| 317 | + if engines is None: |
| 318 | + engines = model.GeometryEngine |
| 319 | + engines = list(engines) |
| 320 | + width_ratios = [1.0] * len(engines) |
314 | 321 | if colorbar: |
315 | 322 | width_ratios.append(0.1) |
316 | 323 |
|
317 | | - (fig, axx) = plt.subplots( |
| 324 | + (fig, all_ax) = plt.subplots( |
318 | 325 | ncols=len(width_ratios), |
319 | 326 | layout="constrained", |
320 | 327 | figsize=figsize, |
321 | 328 | gridspec_kw=dict(width_ratios=width_ratios), |
322 | 329 | ) |
323 | 330 | result = {} |
324 | | - cbar: list[Any] = [False] * len(model.GeometryEngine) |
| 331 | + all_cbar: list[Any] = [False] * len(engines) |
325 | 332 | if colorbar: |
326 | | - cbar[:0] = [axx[-1]] |
| 333 | + all_cbar[:0] = [all_ax[-1]] |
327 | 334 |
|
328 | | - for g, ax, cb in zip(model.GeometryEngine, axx, cbar): |
| 335 | + for ax, g, cb in zip(all_ax, engines, all_cbar): |
329 | 336 | try: |
330 | 337 | result[g] = trace_image(ax, geometry=g, colorbar=cb) |
331 | 338 | except Exception as e: |
332 | 339 | warnings.warn(f"Failed to trace {g} geometry: {e!s}", stacklevel=1) |
333 | 340 | return result |
334 | 341 |
|
335 | 342 |
|
| 343 | +def centered_image( |
| 344 | + center, |
| 345 | + xdir, |
| 346 | + outdir, |
| 347 | + width: Union[float, tuple[float, float]], |
| 348 | + **kwargs: Any, |
| 349 | +) -> model.ImageInput: |
| 350 | + """ |
| 351 | + Create an ImageInput with a centered view based on the given parameters. |
| 352 | +
|
| 353 | + Parameters |
| 354 | + ---------- |
| 355 | + center : array_like |
| 356 | + The center coordinate (real space) of the image. |
| 357 | + xdir : array_like |
| 358 | + The direction along the rendered x-axis. |
| 359 | + outdir : array_like |
| 360 | + The direction out of the page in the result. |
| 361 | + width : float or tuple of two floats or array_like with shape (2,) |
| 362 | + If a single float is provided, the image is square and that value is |
| 363 | + used for both the x (horizontal) and y (vertical) dimensions. If a |
| 364 | + tuple or array-like with two elements is |
| 365 | + provided, the first element specifies the width along the x-axis and |
| 366 | + the second element specifies the width along the y-axis. |
| 367 | + **kwargs |
| 368 | + Additional keyword arguments passed to the ImageInput constructor. |
| 369 | +
|
| 370 | + Returns |
| 371 | + ------- |
| 372 | + model.ImageInput |
| 373 | + The input to ``visualize`` to generate the centered image. |
| 374 | + """ |
| 375 | + center = np.asarray(center) |
| 376 | + xdir = np.asarray(xdir) |
| 377 | + ydir = np.cross(outdir, xdir) |
| 378 | + |
| 379 | + if isinstance(width, float): |
| 380 | + wx, wy = width, width |
| 381 | + elif len(width) == 2: |
| 382 | + wx, wy = width |
| 383 | + else: |
| 384 | + raise ValueError("width must be a float or a length-2 tuple") |
| 385 | + |
| 386 | + offset = xdir * (wx / 2) + ydir * (wy / 2) |
| 387 | + lower_left = (center - offset).tolist() |
| 388 | + upper_right = (center + offset).tolist() |
| 389 | + |
| 390 | + return model.ImageInput( |
| 391 | + lower_left=lower_left, |
| 392 | + upper_right=upper_right, |
| 393 | + rightward=xdir.tolist(), |
| 394 | + **kwargs, |
| 395 | + ) |
| 396 | + |
| 397 | + |
336 | 398 | _register_cmaps() |
0 commit comments