Source code for xpcsviewer.module.saxs2d

"""
2D SAXS scattering pattern visualization.

Provides visualization of integrated 2D small-angle X-ray scattering patterns
with support for log/linear scaling, colormap selection, and image rotation.

Functions:
    plot: Display 2D SAXS pattern with beam center overlay.
"""

from xpcsviewer.backends._conversions import ensure_numpy
from xpcsviewer.utils.logging_config import get_logger

logger = get_logger(__name__)


[docs] def plot( xfile, pg_hdl=None, plot_type="log", cmap="jet", rotate=False, autolevel=False, autorange=False, vmin=None, vmax=None, ): """Display a 2-D SAXS detector image in a PyQtGraph ImageView. Renders the detector image from *xfile* with optional log scaling, custom colour mapping, and intensity clamping. A beam-centre ROI marker is drawn automatically. Args: xfile: XpcsFile containing ``saxs_2d`` and ``saxs_2d_log`` image arrays plus beam centre coordinates (``bcx``, ``bcy``). pg_hdl: PyQtGraph ImageView handle for rendering. plot_type: Image intensity scaling. ``"log"`` uses the pre-computed log-scaled image; any other value uses the linear image. cmap: Colour-map name passed to ``pg_hdl.set_colormap``. ``None`` keeps the current colour map. rotate: If True, transpose the image (unused in current implementation but returned for caller state tracking). autolevel: Automatically scale intensity levels to the data range. autorange: Force the view to fit the full image. When False the previous view range is preserved unless the image shape changed. vmin: Manual lower intensity clamp. Applied only when *autolevel* is False. vmax: Manual upper intensity clamp. Applied only when *autolevel* is False. Returns: bool: The *rotate* flag, echoed back for caller convenience. Example: >>> rotate = plot(xfile, pg_hdl=viewer, plot_type="log", ... cmap="viridis", vmin=0, vmax=100) """ logger.info(f"Starting SAXS2D plot for {getattr(xfile, 'label', 'unknown file')}") logger.debug( f"Plot parameters: plot_type='{plot_type}', cmap='{cmap}', rotate={rotate}, autolevel={autolevel}, autorange={autorange}" ) center = (xfile.bcx, xfile.bcy) img = xfile.saxs_2d_log if plot_type == "log" else xfile.saxs_2d logger.debug(f"Image data: shape={img.shape}, center=({xfile.bcx}, {xfile.bcy})") if cmap is not None: pg_hdl.set_colormap(cmap) prev_img = pg_hdl.image shape_changed = prev_img is None or prev_img.shape != img.shape do_autorange = autorange or shape_changed # Save view range if keeping it if not do_autorange: view_range = pg_hdl.view.viewRange() # Set new image - ensure NumPy at PyQtGraph boundary pg_hdl.setImage(ensure_numpy(img), autoLevels=autolevel, autoRange=do_autorange) # Restore view range if we skipped auto-ranging if not do_autorange: pg_hdl.view.setRange(xRange=view_range[0], yRange=view_range[1], padding=0) # Restore levels if needed if not autolevel and vmin is not None and vmax is not None: pg_hdl.setLevels(vmin, vmax) # Restore intensity levels (if needed) if not autolevel and vmin is not None and vmax is not None: pg_hdl.setLevels(vmin, vmax) if center is not None: pg_hdl.add_roi(sl_type="Center", center=center, label="Center") logger.info("SAXS2D plot completed successfully") return rotate