upxo.pxtal.twinned_simple_3d.viz_3d module

viz_3d.py

Visualization utilities for the twinned simple 3D pipeline.

upxo.pxtal.twinned_simple_3d.viz_3d.plot_ipf_slice(lgi_3d: numpy.ndarray, all_quats: Dict[int, numpy.ndarray], axis: int = 2, slice_idx: int | None = None, sample_direction: Tuple[float, float, float] = (0.0, 0.0, 1.0), figsize: Tuple[float, float] = (7.0, 7.0), dpi: int = 150, title: str | None = None)[source]

Plot an IPF-coloured 2D slice through a 3D grain structure.

Parameters:
  • lgi_3d (ndarray (nx, ny, nz), int)

  • all_quats (dict {int: ndarray(4,)})

  • axis (int) – 0=X, 1=Y, 2=Z. Default 2.

  • slice_idx (int or None) – Defaults to mid-slice if None.

  • sample_direction (tuple (3,))

  • figsize (figure size and resolution)

  • dpi (figure size and resolution)

  • title (str or None)

upxo.pxtal.twinned_simple_3d.viz_3d.plot_ipf_triangle_key(ax=None, n: int = 200, corner_labels: Tuple[str, str, str] = ('[001]', '[011]', '[111]'), figsize: Tuple[float, float] = (4.0, 4.0))[source]

Plot the IPF colour-key triangle matching plot_ipf_slice()’s colour formula.

Parameters:
  • ax (matplotlib Axes or None) – Plots into an existing axes (e.g. for GUI embedding) if given; otherwise creates its own standalone figure.

  • n (int) – Grid resolution (n x n before triangular masking).

  • corner_labels ((str, str, str)) – Labels for the [001]/[011]/[111] corners.

  • figsize ((float, float)) – Only used when ax is None.

upxo.pxtal.twinned_simple_3d.viz_3d.render_lgi_3d(lgi: numpy.ndarray, voxel_size=1.0, title: str | None = None, cmap: str = 'tab20', rng_seed: int = 0, show_edges: bool = False)[source]

Render a 3D grain-label field in a separate PyVista window, each grain shown in a distinct colour via a qualitative colormap.

Shared by TwinnedSimple3DBase.plot_temporal_slice_3d (which builds lgi from a raw MC state array and must transpose it first – see that method) and the GUI’s “View Grain Structure” button, which passes TwinnedSimple3DBase.lgi directly.

Parameters:
  • lgi (ndarray (nx, ny, nz), int) – Grain label field, already in PyVista’s (nx, ny, nz) axis convention – the same convention render_3d() expects for lgi_twinned. NOT the raw pxt.gs[...].s (nz, ny, nx) order.

  • voxel_size (float or (float, float, float)) – Physical voxel edge length(s). Isotropic spacing if a bare float.

  • title (str or None) – Window title text; defaults to a grain-count summary.

  • cmap (str) – Qualitative PyVista/matplotlib colormap name.

  • rng_seed (int) – Seed for the ID -> colour-position shuffle. cc3d/regionprops-style labelling assigns numerically close IDs to spatially close grains; without shuffling, tab20’s discrete cycle repeats faster than IDs vary, so neighbouring grains land on near-identical colours and visually merge. Scattering IDs across the colour range keeps real grain boundaries distinct.

  • show_edges (bool) – Draw voxel-cell edges as a wireframe overlay on the mesh.

upxo.pxtal.twinned_simple_3d.viz_3d.render_lgi_3d_compare(lgi_old: numpy.ndarray, lgi_new: numpy.ndarray, voxel_size_old=1.0, voxel_size_new=1.0, title_old: str | None = None, title_new: str | None = None, cmap: str = 'tab20', rng_seed: int = 0, show_edges: bool = True)[source]

Render two grain-label fields side by side in a single PyVista window (two linked-camera subplots) – e.g. an original vs. a stretched/ rescaled grain structure on Transformations’ “Introduce non-equiaxiality to SGS” page, so the two are visually comparable at a glance instead of two separate, independently-sized windows.

Parameters:
  • lgi_old (ndarray (nx, ny, nz), int) – Grain label fields, already in PyVista’s (nx, ny, nz) axis convention – see render_lgi_3d().

  • lgi_new (ndarray (nx, ny, nz), int) – Grain label fields, already in PyVista’s (nx, ny, nz) axis convention – see render_lgi_3d().

  • voxel_size_old (float or (float, float, float)) – Physical voxel edge length(s) for each structure.

  • voxel_size_new (float or (float, float, float)) – Physical voxel edge length(s) for each structure.

  • title_old (str or None) – Per-panel title text; default to a grain-count summary.

  • title_new (str or None) – Per-panel title text; default to a grain-count summary.

  • cmap (str) – Qualitative PyVista/matplotlib colormap name.

  • rng_seed (int) – Seed for the ID -> colour-position shuffle (see render_lgi_3d()); the same seed is used for both panels.

  • show_edges (bool) – Draw voxel-cell edges as a wireframe overlay on both meshes.

upxo.pxtal.twinned_simple_3d.viz_3d.render_lgi_3d_by_size(lgi: numpy.ndarray, size_thresholds: Tuple[float, float], voxel_size=1.0, title: str | None = None, band_colors: Tuple[str, str, str] | None = None, band_opacities: Tuple[float, float, float] = (1.0, 0.6, 0.3))[source]

Render a 3D grain-label field in a separate PyVista window, grains coloured and opacity-banded by their own voxel count into 3 size domains – e.g. to compare a grain structure before vs. after cleaning (small-grain merging measurably shifts the size distribution, which a uniform per-grain colouring can’t show).

Parameters:
  • lgi (ndarray (nx, ny, nz), int) – Grain label field, already in PyVista’s (nx, ny, nz) axis convention (matching render_3d()’s convention – transpose first if coming from the pipeline’s native (nz, ny, nx) array; see render_lgi_3d()’s docstring).

  • size_thresholds ((float, float)) – (t1, t2) voxel-count cut points, t1 < t2. Domain 1: grains with voxel_count < t1. Domain 2: t1 <= voxel_count < t2. Domain 3: voxel_count >= t2.

  • voxel_size (float or (float, float, float)) – Physical voxel edge length(s). Isotropic if a bare float.

  • title (str or None) – Window title text; defaults to a per-domain grain-count summary.

  • band_colors ((str, str, str) or None) – PyVista colour names for domains 1/2/3. Defaults to _DEFAULT_SIZE_BAND_COLORS (steelblue, seagreen, firebrick).

  • band_opacities ((float, float, float)) – Opacity per domain. Defaults to (1.0, 0.60, 0.30) – smallest grains fully opaque, largest grains most transparent, so small grains (the ones cleaning actually acts on) stay visible instead of being hidden behind the bulk of the structure.

upxo.pxtal.twinned_simple_3d.viz_3d.render_3d(lgi_twinned: numpy.ndarray, twin_role: Dict[int, str], role_opacity: Dict[str, float] | None = None, role_color: Dict[str, str] | None = None, nonhost_cmap: str | None = 'Greens')[source]

3D PyVista voxel render with per-role solid colour and opacity.

Host, primary_twin and secondary_twin roles are rendered with solid colours so add_legend() shows correct swatches. Non-host grains are rendered with per-grain scalar colouring using nonhost_cmap so individual matrix grains are visible rather than a uniform grey.

Parameters:
  • lgi_twinned (ndarray (nx, ny, nz), int)

  • twin_role (dict {int: str}) – Role map: ‘host’ | ‘primary_twin’ | ‘secondary_twin’ | ‘non_host’.

  • role_opacity (dict {str: float} or None) – Per-role opacity. Defaults to _DEFAULT_ROLE_OPACITY.

  • role_color (dict {str: str} or None) – Per-role PyVista colour name for solid-coloured roles. Defaults to _DEFAULT_ROLE_COLOR.

  • nonhost_cmap (str or None) – Matplotlib / PyVista colormap name for non-host grains. Each grain is mapped to a unique hue so individual matrix grains are distinguishable. The colormap should not overlap with the solid role colours (steelblue, darkorange, crimson). Defaults to 'Greens'. Pass None to fall back to the solid role_color['non_host'] colour instead.