Visualization & Plotting
Ossify supports 2D plotting (via matplotlib) and 3D interactive rendering (via PyVista, install with pip install ossify[viz]).
Overview
| Function Category | Functions | Purpose |
|---|---|---|
| Cell Plotting | plot_cell_2d, plot_cell_multiview |
2D integrated visualization of complete cells |
| Layer Plotting | plot_morphology_2d, plot_annotations_2d, plot_skeleton, plot_points |
2D individual layer visualization |
| Figure Management | single_panel_figure, multi_panel_figure, add_scale_bar |
Layout and annotation utilities |
| Lineups | plot_lineup, plot_lineup_grid, LineupGroup, add_layer_lines |
Side-by-side cell arrays for comparison |
| Projections and Rotations | Rotation, RotateCell |
Build custom projection callables |
| 3D Cell Plotting | plot_cell_3d |
3D integrated visualization of complete cells |
| 3D Layer Plotting | plot_morphology_3d, plot_annotations_3d, plot_mesh_3d, plot_graph_3d |
3D individual layer visualization |
Cell Plotting
plot_cell_2d
plot_cell_2d
plot_cell_2d(cell: Cell, color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, alpha: Optional[Union[str, ndarray, float]] = 1.0, alpha_norm: Optional[Tuple[float, float]] = None, linewidth: Optional[Union[str, ndarray, float]] = 1.0, linewidth_norm: Optional[Tuple[float, float]] = None, widths: Optional[tuple] = (1, 50), root_marker: bool = False, root_size: float = 100.0, root_color: Optional[Union[str, tuple]] = None, synapses: Literal['pre', 'post', 'both', True, False] = False, pre_anno: str = 'pre_syn', pre_color: Optional[Union[str, tuple]] = None, pre_palette: Union[str, dict] = 'coolwarm', pre_color_norm: Optional[Tuple[float, float]] = None, syn_alpha: float = 1, syn_color_scale: Optional[Literal['log']] = None, syn_size: Optional[Union[str, ndarray, float]] = None, syn_size_norm: Optional[Tuple[float, float]] = None, syn_size_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, syn_sizes: Optional[ndarray] = (1, 30), post_anno: str = 'post_syn', post_color: Optional[Union[str, tuple]] = None, post_palette: Union[str, dict] = 'coolwarm', post_color_norm: Optional[Tuple[float, float]] = None, projection: Union[str, Callable] = 'xy', rotation_angle: Optional[Union[float, int, Literal['best']]] = None, rotation_axis: Optional[Union[str, ndarray]] = None, offset_h: float = 0.0, offset_v: float = 0.0, invert_y: bool = True, ax: Optional[Axes] = None, units_per_inch: Optional[float] = None, dpi: Optional[float] = None, despine: bool = True, **syn_kwargs) -> Axes
Comprehensive 2D visualization of complete cells with skeleton, annotations, and flexible styling.
Usage Examples
import ossify
import matplotlib.pyplot as plt
# Load cell with skeleton and synapses
cell = ossify.load_cell("neuron.osy")
# Basic plot with compartment coloring
fig, ax = ossify.plot_cell_2d(
cell,
color="compartment", # Color by axon/dendrite
projection="xy", # XY projection
synapses=True # Show both pre/post synapses
)
# Advanced styling with custom parameters
fig, ax = ossify.plot_cell_2d(
cell,
color="strahler_order", # Hierarchical coloring
palette="viridis", # Scientific colormap
linewidth="radius", # Width by branch radius
widths=(1, 20), # Min/max linewidth range
alpha=0.8, # Semi-transparent
root_as_sphere=True, # Mark root location
root_size=150, # Root marker size
synapses="pre", # Only presynaptic sites
pre_color="red", # Synapse color
syn_size=30, # Synapse marker size
projection="zy", # Side view
units_per_inch=50000, # 50,000 nm per inch
despine=True # Clean axes
)
plt.show()
Color and Style Options
# Discrete compartment coloring
plot_cell_2d(cell, color="compartment", palette={"axon": "red", "dendrite": "blue"})
# Continuous property coloring
plot_cell_2d(cell, color="distance_to_root", palette="plasma", color_norm=(0, 500000))
# Multi-property styling
plot_cell_2d(
cell,
color="strahler_order", # Color by hierarchy
alpha="confidence", # Transparency by confidence
linewidth="thickness", # Width by morphology
alpha_norm=(0.3, 1.0), # Alpha range
linewidth_norm=(0.5, 5.0), # Width range
widths=(2, 40) # Final width scaling
)
Synapse Visualization
# Separate pre/post synapse styling
plot_cell_2d(
cell,
synapses="both",
pre_anno="pre_syn", # Presynaptic annotation layer
pre_color="red", # Presynaptic color
pre_palette="Reds", # If coloring by property
post_anno="post_syn", # Postsynaptic annotation layer
post_color="blue", # Postsynaptic color
post_palette="Blues", # If coloring by property
syn_size="activity", # Size by synapse activity
syn_sizes=(10, 100), # Size range
syn_alpha=0.7 # Synapse transparency
)
plot_cell_multiview
plot_cell_multiview
plot_cell_multiview(cell: Cell, layout: Literal['stacked', 'side_by_side', 'three_panel'] = 'three_panel', color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, alpha: Optional[Union[str, ndarray, float]] = 1.0, alpha_norm: Optional[Tuple[float, float]] = None, linewidth: Optional[Union[str, ndarray, float]] = 1.0, linewidth_norm: Optional[Tuple[float, float]] = None, widths: Optional[tuple] = (1, 50), root_marker: bool = False, root_size: float = 100.0, root_color: Optional[Union[str, tuple]] = None, synapses: Literal['pre', 'post', 'both', True, False] = False, pre_anno: str = 'pre_syn', pre_color: Optional[Union[str, tuple]] = None, pre_palette: Union[str, dict] = 'coolwarm', pre_color_norm: Optional[Tuple[float, float]] = None, syn_alpha: float = 1, syn_color_scale: Optional[Literal['log']] = None, syn_size: Optional[Union[str, ndarray, float]] = None, syn_size_norm: Optional[Tuple[float, float]] = None, syn_size_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, syn_sizes: Optional[ndarray] = (1, 30), post_anno: str = 'post_syn', post_color: Optional[Union[str, tuple]] = None, post_palette: Union[str, dict] = 'coolwarm', post_color_norm: Optional[Tuple[float, float]] = None, invert_y: bool = True, despine: bool = True, units_per_inch: float = 100000, dpi: Optional[float] = None, **syn_kwargs) -> dict
Multi-panel visualization showing different anatomical projections.
Usage Examples
# Three-panel L-shaped layout (default)
fig, axes = ossify.plot_cell_multiview(
cell,
layout="three_panel", # xy (bottom-left), xz (top-left), zy (bottom-right)
color="compartment",
units_per_inch=100000, # Scale factor
gap_inches=0.3 # Panel spacing
)
# Side-by-side comparison (xy | zy)
fig, axes = ossify.plot_cell_multiview(
cell,
layout="side_by_side",
color="strahler_order",
palette="cmc.hawaii",
synapses=True,
units_per_inch=75000
)
# Stacked layout (xz over xy)
fig, axes = ossify.plot_cell_multiview(
cell,
layout="stacked",
color="distance_to_root",
palette="magma",
root_as_sphere=True
)
# Access individual axes
xy_ax = axes["xy"]
xz_ax = axes["xz"]
zy_ax = axes["zy"]
# Add titles or annotations
xy_ax.set_title("Horizontal View (XY)")
zy_ax.set_title("Side View (ZY)")
Layer Plotting
plot_morphology_2d
plot_morphology_2d
plot_morphology_2d(cell: Union[Cell, SkeletonLayer], color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, alpha: Optional[Union[str, ndarray, float]] = 1.0, alpha_norm: Optional[Tuple[float, float]] = None, alpha_extent: Optional[Tuple[float, float]] = None, linewidth: Optional[Union[str, ndarray, float]] = 1.0, linewidth_norm: Optional[Tuple[float, float]] = None, widths: Optional[tuple] = (1, 50), projection: Union[str, Callable] = 'xy', rotation_angle: Optional[Union[float, int, Literal['best']]] = None, rotation_axis: Optional[Union[str, ndarray]] = None, offset_h: float = 0.0, offset_v: float = 0.0, root_marker: bool = False, root_size: float = 100.0, root_color: Optional[Union[str, tuple]] = None, zorder: int = 2, invert_y: bool = True, ax: Optional[Axes] = None) -> Axes
Plot 2D skeleton with flexible styling options.
Parameters:
-
cell(Cell or SkeletonLayer) –Cell or SkeletonLayer to plot
-
projection(str or Callable, default:"xy") –Projection function or string mapping 3d points to a 2d projection.
-
color(str, np.ndarray, or tuple, default:None) –Color specification - can be feature name, array of values, or matplotlib color
-
palette(str or dict, default:"coolwarm") –Colormap for mapping array values to colors
-
color_norm(tuple of float, default:None) –(min, max) tuple for color normalization, in the original (pre-transform) value space.
-
color_scale(log, default:"log") –Value transform applied before colormap projection.
"log"log-transforms feature values so the colormap is distributed linearly in log-space. color_norm bounds remain in original units and are converted internally. Mirrors :func:plot_morphology_3d. -
alpha(str, np.ndarray, or float, default:1.0) –Alpha specification - can be feature name, array, or single value. Feature names / arrays are rescaled to
alpha_extent. -
alpha_norm(tuple of float, default:None) –(min, max) clip range for alpha values in original feature units.
-
alpha_extent(tuple of float, default:None) –(min, max)output range for rescaled alpha values. Default(0.0, 1.0)— i.e., the dimmest vertex is fully transparent. Pass e.g.(0.1, 1.0)to keep low-end vertices faintly visible. -
linewidth(str, np.ndarray, or float, default:1.0) –Linewidth specification - can be feature name, array, or single value
-
linewidth_norm(tuple of float, default:None) –(min, max) tuple for linewidth normalization
-
widths(tuple, default:(1, 50)) –(min, max) tuple for final linewidth scaling
-
ax(Axes, default:None) –Matplotlib axes
Returns:
-
Axes–Matplotlib axes with skeleton plotted
Flexible 2D plotting of skeleton morphology with advanced styling.
Usage Examples
# Basic skeleton plotting
ax = ossify.plot_morphology_2d(
cell.skeleton, # Can use Cell or SkeletonLayer
projection="xy"
)
# Advanced styling
ax = ossify.plot_morphology_2d(
cell,
color="branch_order", # Color by topological property
palette="Set1", # Discrete colormap
alpha="confidence", # Transparency by data quality
linewidth="diameter", # Width by branch diameter
linewidth_norm=(0.1, 10.0), # Diameter range (μm)
widths=(0.5, 25), # Plot width range (points)
projection="xz", # Sagittal view
offset_h=1000, # Horizontal offset (nm)
offset_v=500, # Vertical offset (nm)
zorder=3, # Drawing order
invert_y=True, # Invert Y axis
ax=existing_axis # Plot on existing axes
)
plot_annotations_2d
plot_annotations_2d
plot_annotations_2d(annotation: PointCloudLayer, color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, alpha: float = 1, size: Optional[Union[str, ndarray, float]] = None, size_norm: Optional[Tuple[float, float]] = None, size_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, sizes: Optional[ndarray] = (1, 30), projection: Union[str, Callable] = 'xy', rotation_angle: Optional[Union[float, int, Literal['best']]] = None, rotation_axis: Optional[Union[str, ndarray]] = None, rotation_center: Optional[ndarray] = None, offset_h: float = 0.0, offset_v: float = 0.0, invert_y: bool = True, ax: Optional[Axes] = None, **kwargs) -> Axes
Plot a 2D scatter of a :class:PointCloudLayer annotation.
Parameters:
-
annotation(PointCloudLayer) –Annotation layer to plot. Raw
np.ndarrayis also accepted, but feature-name resolution then requires the layer. -
color(str, np.ndarray, or tuple, default:None) –Color specification. A string matching a feature name resolves to a per-point value array mapped through palette. Otherwise treated as a matplotlib color.
-
palette(str or dict, default:"coolwarm") –Colormap or dict for scalar color mapping.
-
color_norm(tuple of float, default:None) –(min, max)clip range, in original (pre-transform) units. -
color_scale(log, default:"log") –Value transform applied before colormap projection. Mirrors :func:
plot_annotations_3d. -
alpha(float, default:1) –Marker opacity.
-
size(str, np.ndarray, or float, default:None) –Marker size specification. Feature name, per-point array, or scalar.
-
size_norm(tuple of float, default:None) –(min, max)clip range for size mapping, in original units. -
size_scale((log, sqrt, cbrt), default:"log") –Value transform applied before size normalization.
"sqrt"is useful when the feature is a cross-sectional area;"cbrt"when the feature is a volume. Mirrors :func:plot_annotations_3d. -
sizes(tuple of float, default:(1, 30)) –(min_size, max_size)output range for size rescaling. Default(1, 30).
Returns:
-
Axes–Matplotlib axes with the annotation rendered.
Scatter plots for point cloud annotations with flexible styling.
Usage Examples
# Basic annotation plotting
ax = ossify.plot_annotations_2d(
cell.annotations["pre_syn"],
projection="xy"
)
# Advanced styling
ax = ossify.plot_annotations_2d(
cell.annotations["synapses"],
color="activity_level", # Color by annotation property
palette="plasma", # Continuous colormap
color_norm=(0, 1), # Value range for coloring
size="strength", # Size by property
size_norm=(0.1, 2.0), # Property range
sizes=(5, 80), # Marker size range (points²)
alpha=0.8, # Transparency
projection="zy", # Side projection
offset_h=500, # Position offset
offset_v=0,
ax=ax, # Add to existing plot
edgecolors='black', # Marker edge color
linewidths=0.5 # Edge width
)
plot_skeleton & plot_points
Lower-level functions for precise control:
plot_skeleton
plot_skeleton(skel: SkeletonLayer, projection: Union[str, Callable] = 'xy', rotation_angle: Optional[Union[float, int, Literal['best']]] = None, rotation_axis: Optional[Union[str, ndarray]] = None, colors: Optional[ndarray] = None, alpha: Optional[ndarray] = None, linewidths: Optional[ndarray] = None, offset_h: float = 0.0, offset_v: float = 0.0, zorder: int = 2, invert_y: bool = True, ax: Optional[Axes] = None) -> Axes
Plot skeleton with explicit arrays for styling.
Parameters:
-
skel(SkeletonLayer) –SkeletonLayer to plot
-
projection(str or Callable, default:"xy") –Projection function or string
-
colors(ndarray, default:None) –(N, 3) or (N, 4) RGB/RGBA color array for vertices
-
alpha(ndarray, default:None) –(N,) alpha values for vertices
-
linewidths(ndarray, default:None) –(N,) linewidth values for vertices
-
offset_h(float, default:0.0) –Horizontal offset for projection
-
offset_v(float, default:0.0) –Vertical offset for projection
-
zorder(int, default:2) –Drawing order for line collection
-
invert_y(bool, default:True) –Whether to automatically invert y-axis for projections containing 'y'
-
ax(Axes, default:None) –Matplotlib axes
Returns:
-
Axes–Matplotlib axes with skeleton plotted
plot_points
plot_points(points: ndarray, sizes: Optional[ndarray] = None, colors: Optional[ndarray] = None, palette: Union[str, Dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, projection: Union[str, Callable] = 'xy', rotation_angle: Optional[Union[float, int, Literal['best']]] = None, rotation_axis: Optional[Union[str, ndarray]] = None, rotation_center: Optional[ndarray] = None, offset_h: float = 0.0, offset_v: float = 0.0, invert_y: bool = True, ax: Optional[Axes] = None, zorder: int = 2, **scatter_kws) -> Axes
Direct Array Styling
# Direct control with pre-computed arrays
skeleton = cell.skeleton
# Prepare styling arrays
colors = ossify.strahler_number(skeleton) # Color values
alphas = np.ones(skeleton.n_vertices) * 0.8 # Uniform transparency
widths = skeleton.get_feature("radius") * 2 # Width by radius
# Plot with explicit arrays
ax = ossify.plot_skeleton(
skeleton,
projection="xy",
colors=colors, # (N, 3) RGB or (N, 4) RGBA array
alpha=alphas, # (N,) alpha values
linewidths=widths, # (N,) linewidth values
zorder=2
)
# Plot points with arrays
points = cell.annotations["pre_syn"].vertices
sizes = np.random.uniform(10, 50, len(points))
colors = np.random.choice(['red', 'blue'], len(points))
ax = ossify.plot_points(
points=points,
sizes=sizes,
colors=colors,
projection="xy",
ax=ax,
zorder=3
)
Figure Management
single_panel_figure
single_panel_figure
single_panel_figure(data_bounds_min: ndarray, data_bounds_max: ndarray, units_per_inch: float, despine: bool = True, dpi: Optional[float] = None) -> Tuple[Figure, Axes]
Create a single panel figure with precise unit-based sizing.
Parameters:
-
data_bounds_min(ndarray) –2-element array [x_min, y_min] of data bounds
-
data_bounds_max(ndarray) –2-element array [x_max, y_max] of data bounds
-
units_per_inch(float) –Number of data units per inch for scaling
-
despine(bool, default:True) –Whether to remove axis spines and ticks for clean appearance
-
dpi(float, default:None) –Dots per inch for figure resolution. If None, uses matplotlib default.
Returns:
-
tuple of (plt.Figure, plt.Axes)–Figure and axes objects with correct unit scaling
Examples:
Create figures with precise unit-based sizing for consistent scaling.
Usage Examples
# Calculate data bounds
bounds_min = cell.skeleton.bbox[0] # [x_min, y_min, z_min]
bounds_max = cell.skeleton.bbox[1] # [x_max, y_max, z_max]
# Create figure with specific scale
fig, ax = ossify.single_panel_figure(
data_bounds_min=bounds_min[:2], # [x_min, y_min]
data_bounds_max=bounds_max[:2], # [x_max, y_max]
units_per_inch=50000, # 50,000 nm per inch
despine=True, # Remove axes
dpi=300 # High resolution
)
# Plot data on precisely-sized axes
ossify.plot_morphology_2d(cell, ax=ax)
# The figure size automatically matches data aspect ratio
print(f"Figure size: {fig.get_size_inches()}")
multi_panel_figure
multi_panel_figure
multi_panel_figure(data_bounds_min: ndarray, data_bounds_max: ndarray, units_per_inch: float, layout: Literal['side_by_side', 'stacked', 'three_panel'], gap_inches: float = 0.5, despine: bool = True, dpi: Optional[float] = None) -> Tuple[Figure, Dict[str, Axes]]
Create multi-panel figure with precise unit-based sizing and alignment.
Parameters:
-
data_bounds_min(ndarray) –3-element array [x_min, y_min, z_min] of data bounds
-
data_bounds_max(ndarray) –3-element array [x_max, y_max, z_max] of data bounds
-
units_per_inch(float) –Number of data units per inch for scaling
-
layout((side_by_side, stacked, three_panel), default:"side_by_side") –Layout configuration: - "side_by_side": xy | zy (horizontal) - "stacked": xz over xy (vertical) - "three_panel": L-shaped (xy bottom-left, xz top-left, zy bottom-right)
-
gap_inches(float, default:0.5) –Gap between panels in inches
-
despine(bool, default:True) –Whether to remove axis spines and ticks for clean appearance
-
dpi(float, default:None) –Dots per inch for figure resolution. If None, uses matplotlib default.
Returns:
-
tuple of (plt.Figure, dict of plt.Axes)–Figure and dictionary of axes keyed by projection. - "side_by_side": {"xy": xy_ax, "zy": zy_ax} - "stacked": {"xz": xz_ax, "xy": xy_ax} - "three_panel": {"xy": xy_ax, "xz": xz_ax, "zy": zy_ax}
Examples:
Create aligned multi-panel layouts with consistent scaling.
Usage Examples
# Three-panel layout with precise alignment
fig, axes = ossify.multi_panel_figure(
data_bounds_min=cell.skeleton.bbox[0],
data_bounds_max=cell.skeleton.bbox[1],
units_per_inch=75000,
layout="three_panel",
gap_inches=0.4, # Panel spacing
despine=True,
dpi=300
)
# Plot different projections
for projection, ax in axes.items():
ossify.plot_morphology_2d(
cell,
projection=projection,
color="compartment",
ax=ax
)
ax.set_title(f"{projection.upper()} View")
plt.suptitle("Multi-View Neuron Analysis")
add_scale_bar
add_scale_bar
add_scale_bar(ax: Axes, length: float, position: Tuple[float, float] = (0.05, 0.05), color: str = 'black', linewidth: float = 10.0, orientation: Literal['h', 'v', 'horizontal', 'vertical'] = 'h', feature: Optional[str] = None, feature_offset: float = 0.01, fontsize: float = 10) -> None
Add a scale bar to an axis with precise positioning.
Parameters:
-
ax(Axes) –Matplotlib axes to add scale bar to
-
length(float) –Length of scale bar in data units
-
position(tuple of float, default:(0.05, 0.05)) –Starting position as fraction of axis dimensions (x_frac, y_frac). (0, 0) is bottom-left, (1, 1) is top-right.
-
color(str, default:"black") –Color of the scale bar line
-
linewidth(float, default:3.0) –Width of the scale bar line in points
-
feature(str, default:None) –Text feature for the scale bar (e.g., "100 μm")
-
feature_offset(float, default:0.01) –Vertical offset for feature as fraction of axis height
-
fontsize(float, default:10) –Font size for scale bar feature
Examples:
Add calibrated scale bars to plots.
Usage Examples
# Add scale bars to plots
fig, ax = ossify.single_panel_figure(
bounds_min, bounds_max,
units_per_inch=100000 # 100,000 nm per inch
)
ossify.plot_morphology_2d(cell, ax=ax)
# Horizontal scale bar (bottom-left)
ossify.add_scale_bar(
ax,
length=50000, # 50 μm in nm
position=(0.05, 0.05), # Fractional position
feature="50 μm", # Text feature
color="black",
linewidth=8,
fontsize=12
)
# Vertical scale bar (top-right)
ossify.add_scale_bar(
ax,
length=25000, # 25 μm
position=(0.9, 0.7),
orientation="vertical", # or "v"
feature="25 μm",
feature_offset=0.02, # feature spacing
color="white", # For dark backgrounds
linewidth=6
)
Advanced Plotting Workflows
Publication Figure Pipeline
def create_publication_figure(cell, output_path="figure.pdf"):
\"\"\"Create a publication-ready multi-panel figure\"\"\"
# Calculate compartmentalization
is_axon = ossify.label_axon_from_synapse_flow(cell)
compartment = np.where(is_axon, "Axon", "Dendrite")
cell.skeleton.add_feature(compartment, "compartment")
# Create multi-panel layout
fig, axes = ossify.plot_cell_multiview(
cell,
layout="three_panel",
color="compartment",
palette={"Axon": "#d62728", "Dendrite": "#1f77b4"}, # Red/Blue
linewidth="radius",
widths=(1, 15),
synapses="both",
pre_color="#ff7f0e", # Orange presynaptic
post_color="#2ca02c", # Green postsynaptic
syn_size=25,
units_per_inch=100000, # 100k nm/inch
gap_inches=0.3,
despine=True,
dpi=300
)
# Add scale bars
for proj, ax in axes.items():
if proj == "xy":
ossify.add_scale_bar(ax, 100000, (0.05, 0.05), "100 μm", fontsize=14)
elif proj == "xz":
ossify.add_scale_bar(ax, 50000, (0.05, 0.05), "50 μm", fontsize=12)
elif proj == "zy":
ossify.add_scale_bar(ax, 75000, (0.05, 0.05), "75 μm", fontsize=12)
# Add panel features
axes["xy"].text(0.02, 0.98, "A", transform=axes["xy"].transAxes,
fontsize=20, fontweight='bold', va='top')
axes["xz"].text(0.02, 0.98, "B", transform=axes["xz"].transAxes,
fontsize=20, fontweight='bold', va='top')
axes["zy"].text(0.02, 0.98, "C", transform=axes["zy"].transAxes,
fontsize=20, fontweight='bold', va='top')
# Add figure title and save
fig.suptitle("Neuronal Compartmentalization Analysis", fontsize=16, y=0.95)
plt.tight_layout()
plt.savefig(output_path, dpi=300, bbox_inches='tight')
return fig
# Generate publication figure
fig = create_publication_figure(cell, "neuron_analysis.pdf")
Interactive Analysis Workflow
def plot_analysis_comparison(cell, algorithms=['flow', 'spectral']):
\"\"\"Compare different analysis algorithms visually\"\"\"
results = {}
# Run different algorithms
if 'flow' in algorithms:
results['flow'] = ossify.label_axon_from_synapse_flow(cell)
if 'spectral' in algorithms:
results['spectral'] = ossify.label_axon_from_spectral_split(cell)
# Create comparison plot
n_methods = len(results)
fig, axes = plt.subplots(1, n_methods, figsize=(6*n_methods, 6))
if n_methods == 1:
axes = [axes]
for i, (method, is_axon) in enumerate(results.items()):
compartment = np.where(is_axon, "Axon", "Dendrite")
ossify.plot_morphology_2d(
cell,
color=compartment,
palette={"Axon": "red", "Dendrite": "blue"},
ax=axes[i]
)
axes[i].set_title(f"{method.capitalize()} Method")
axes[i].set_aspect('equal')
plt.tight_layout()
return fig, results
# Compare methods
fig, results = plot_analysis_comparison(cell)
# Calculate agreement
if len(results) > 1:
methods = list(results.keys())
agreement = (results[methods[0]] == results[methods[1]]).mean()
print(f"Agreement between methods: {agreement:.1%}")
Custom Styling Functions
def create_custom_colormap():
\"\"\"Create custom colormap for morphological data\"\"\"
from matplotlib.colors import LinearSegmentedColormap
colors = ['#440154', '#31688e', '#35b779', '#fde725'] # Viridis-like
n_bins = 256
cmap = LinearSegmentedColormap.from_list('custom', colors, N=n_bins)
return cmap
def style_by_distance_to_branch(cell, ax):
\"\"\"Style skeleton by distance to nearest branch point\"\"\"
skeleton = cell.skeleton
branch_points = skeleton.branch_points
# Calculate distance to nearest branch for each vertex
distances = []
for vertex in skeleton.vertex_index:
dist_to_branches = skeleton.distance_between(
[vertex], branch_points, limit=100000
)
distances.append(dist_to_branches.min() if len(dist_to_branches) > 0 else np.inf)
distances = np.array(distances)
# Plot with custom styling
ossify.plot_morphology_2d(
cell,
color=distances,
palette=create_custom_colormap(),
color_norm=(0, np.percentile(distances[distances < np.inf], 95)),
linewidth=2,
ax=ax
)
return distances
# Apply custom styling
fig, ax = plt.subplots(figsize=(10, 8))
distances = style_by_distance_to_branch(cell, ax)
ax.set_title("Distance to Nearest Branch Point")
Plotting Design Principles
Unit Consistency: All plotting functions support precise unit control for consistent scaling across figures.
Flexible Styling: Multiple ways to specify colors, sizes, and transparency - from simple constants to complex property mappings.
Layer Integration: Seamless integration between different data layers in combined plots.
Publication Ready: Built-in support for high-DPI output, precise scaling, and clean styling.
Best Practices
- Scale Bars: Always include scale bars for spatial data
- Color Schemes: Use perceptually uniform colormaps (viridis, plasma, cividis) for continuous data
- Resolution: Set
dpi=300for publication figures - Aspect Ratios: Use
units_per_inchfor consistent scaling across different data sizes - Layering: Use
zorderto control drawing order when combining multiple plot elements
Lineups
Lineups arrange multiple cells side-by-side on a single axis — useful for comparing morphologies across a small group, or for figure panels that show several example cells. See the visualization guide for a walkthrough.
plot_lineup
plot_lineup
plot_lineup(cells: List[Cell], projection: Union[str, Callable] = 'xy', color: Optional[Union[str, ndarray, tuple, List]] = None, palette: Union[str, dict, List[Union[str, dict]]] = 'coolwarm', color_norm: Optional[Union[Tuple[float, float], List]] = None, alpha: Optional[Union[str, ndarray, float, List]] = 1.0, alpha_norm: Optional[Union[Tuple[float, float], List]] = None, alpha_extent: Optional[Union[Tuple[float, float], List]] = None, linewidth: Optional[Union[str, ndarray, float, List]] = 1.0, linewidth_norm: Optional[Union[Tuple[float, float], List]] = None, widths: Optional[Union[tuple, List]] = (1, 50), root_marker: Union[bool, List[bool]] = False, root_size: Union[float, List[float]] = 100.0, root_color: Optional[Union[str, tuple, List]] = None, gap: float = 0.0, align: Literal['natural', 'soma', 'point'] = 'natural', alignment_point: Optional[Union[ndarray, List[ndarray]]] = None, invert_y: bool = True, ax: Optional[Axes] = None, units_per_inch: Optional[float] = None, dpi: Optional[float] = None, despine: bool = True) -> Axes
Plot multiple cells side-by-side in a single figure.
Parameters:
-
cells(List[Cell]) –Cells to plot.
-
projection(str or Callable, default:"xy") –Shared projection for all cells.
-
color(str, np.ndarray, tuple, or List, default:None) –Per-cell or shared color specification.
-
palette(str, dict, or List, default:"coolwarm") –Per-cell or shared colormap.
-
color_norm(tuple or List, default:None) –Per-cell or shared (min, max) color normalization.
-
alpha(str, np.ndarray, float, or List, default:1.0) –Per-cell or shared alpha specification.
-
alpha_norm(tuple or List, default:None) –Per-cell or shared alpha normalization range.
-
alpha_extent(tuple or List, default:None) –Per-cell or shared alpha output range.
-
linewidth(str, np.ndarray, float, or List, default:1.0) –Per-cell or shared linewidth specification.
-
linewidth_norm(tuple or List, default:None) –Per-cell or shared linewidth normalization.
-
widths(tuple or List, default:(1, 50)) –Per-cell or shared (min, max) linewidth scaling.
-
root_marker(bool or List[bool], default:False) –Per-cell or shared root marker flag.
-
root_size(float or List[float], default:100.0) –Per-cell or shared root marker size.
-
root_color(str, tuple, or List, default:None) –Per-cell or shared root marker color.
-
gap(float, default:0.0) –Horizontal gap between cells.
-
align("natural", "soma", or "point", default:"natural") –Vertical alignment mode.
-
alignment_point(ndarray or List[ndarray], default:None) –Required when align="point". A single 3D point (broadcast) or one per cell.
-
invert_y(bool, default:True) –Whether to invert the y axis.
-
ax(Axes, default:None) –Existing axes to plot onto. If None, a new figure is created.
-
units_per_inch(float, default:None) –Data units per inch for auto-sizing the figure. Used only when ax=None.
-
dpi(float, default:None) –Figure DPI. Used only when ax=None and units_per_inch is given.
-
despine(bool, default:True) –Whether to remove spines when creating a new figure.
Returns:
-
Axes–Axes with all cells plotted.
Raises:
-
ValueError–If cells is empty, if a list param has the wrong length, or if align="point" but alignment_point is None.
plot_lineup_grid
plot_lineup_grid
plot_lineup_grid(groups: List[LineupGroup], *, projection: Union[str, Callable] = 'xy', align: Literal['natural', 'soma', 'point'] = 'natural', inter_cell_gap: float = 0.0, inter_group_gap: float = 0.0, row_max_cells: Optional[int] = None, row_max_width: Optional[float] = None, row_gap: float = 0.0, layer_lines: Optional[Dict[float, Optional[str]]] = None, layer_line_kwargs: Optional[dict] = None, group_label_offset: float = 0.0, group_label_kwargs: Optional[dict] = None, alignment_points: Optional[List[List[ndarray]]] = None, invert_y: bool = True, units_per_inch: Optional[float] = None, dpi: Optional[float] = None, despine: bool = True, ax: Optional[Axes] = None) -> Axes
Plot a grid of cell groups with per-group styling and multi-row layout.
Each group's cells are placed contiguously, with optional gaps between
cells and between groups. When row_max_cells or row_max_width
is set, groups wrap to new rows as units — a single group is never
split across rows. Group labels float above each group's plotted cells
(locally per group, not globally per row). Optional layer guide lines
can be drawn across the full plot.
Parameters:
-
groups(list of LineupGroup) –Groups of cells with shared styling. Build these by spreading named style dicts into the constructor; see :class:
LineupGroupfor examples. -
projection(str or Callable, default:"xy") –Shared projection for all cells.
-
align((natural, soma, point), default:"natural") –Vertical alignment mode.
"natural"preserves each cell's y coordinate (anatomical depth);"soma"aligns each cell's soma to the row's reference y;"point"aligns the per-cellalignment_pointsto the row reference. -
inter_cell_gap(float, default:0.0) –Horizontal spacing between adjacent cells within a group, in data units.
-
inter_group_gap(float, default:0.0) –Extra horizontal spacing between adjacent groups, in data units.
-
row_max_cells(int, default:None) –Wrap to a new row before the running cell count exceeds this.
-
row_max_width(float, default:None) –Wrap to a new row before the running row width (data units) exceeds this. When both
row_max_cellsandrow_max_widthare given, whichever fires first triggers the wrap. -
row_gap(float, default:0.0) –Vertical spacing between rows, in data units.
-
layer_lines(dict of float -> str or None, default:None) –{y: label}mapping passed to :func:add_layer_lines. PassNoneas a value to draw the line without a label. -
layer_line_kwargs(dict, default:None) –Extra kwargs forwarded to :func:
add_layer_lines. -
group_label_offset(float, default:0.0) –Vertical offset from each group's projected top edge to its label, in data units. Direction is "above on screen": for y-inverted plots (the default) the label sits at
min_plotted_y - group_label_offset; otherwise atmax_plotted_y + group_label_offset. -
group_label_kwargs(dict, default:None) –Extra keyword arguments forwarded to :meth:
Axes.textfor the group labels (e.g.fontsize,color,fontweight). -
alignment_points(list of list of np.ndarray, default:None) –Required when
align="point".alignment_points[gi][ci]is the 3D anchor point for cellciin groupgi. -
invert_y(bool, default:True) –Invert the y-axis for string projections containing
"y". Mirrors the behavior of other plot functions. -
units_per_inch(optional, default:None) –Passed to :func:
single_panel_figurewhenaxisNoneandunits_per_inchis given. -
dpi(optional, default:None) –Passed to :func:
single_panel_figurewhenaxisNoneandunits_per_inchis given. -
despine(optional, default:None) –Passed to :func:
single_panel_figurewhenaxisNoneandunits_per_inchis given. -
ax(Axes, default:None) –Existing axes to render into. A new figure is created when
None.
Returns:
-
Axes–Axes with all cells, layer lines, and group labels drawn.
LineupGroup
LineupGroup
dataclass
LineupGroup(cells: List[Cell], label: Optional[str] = None, rotation_angle: Optional[Union[float, int, Literal['best'], List]] = None, rotation_axis: Optional[Union[str, ndarray, List]] = None, color: Optional[Union[str, ndarray, tuple, List]] = None, palette: Union[str, dict, List] = 'coolwarm', color_norm: Optional[Union[Tuple[float, float], List]] = None, color_scale: Optional[Union[Literal['log'], List]] = None, alpha: Union[str, ndarray, float, List] = 1.0, alpha_norm: Optional[Union[Tuple[float, float], List]] = None, alpha_extent: Optional[Union[Tuple[float, float], List]] = None, linewidth: Union[str, ndarray, float, List] = 1.0, linewidth_norm: Optional[Union[Tuple[float, float], List]] = None, widths: Optional[Union[tuple, List]] = (1, 50), root_marker: Union[bool, List[bool]] = False, root_size: Union[float, List[float]] = 100.0, root_color: Optional[Union[str, tuple, List]] = None, synapses: Literal['pre', 'post', 'both', True, False] = False, pre_anno: str = 'pre_syn', pre_color: Optional[Union[str, tuple]] = None, pre_palette: Union[str, dict] = 'coolwarm', pre_color_norm: Optional[Tuple[float, float]] = None, post_anno: str = 'post_syn', post_color: Optional[Union[str, tuple]] = None, post_palette: Union[str, dict] = 'coolwarm', post_color_norm: Optional[Tuple[float, float]] = None, syn_alpha: float = 1.0, syn_color_scale: Optional[Literal['log']] = None, syn_size: Optional[Union[str, ndarray, float]] = None, syn_size_norm: Optional[Tuple[float, float]] = None, syn_size_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, syn_sizes: Optional[ndarray] = (1, 30), extra_kwargs: Optional[Dict[str, Any]] = dict())
A labeled bag of cells with shared styling, used by :func:plot_lineup_grid.
Each style field mirrors the matching keyword on :func:plot_cell_2d.
Per-cell fields (color, palette, alpha, linewidth, the
root markers, etc.) accept either a scalar (broadcast across all cells
in the group) or a list with one entry per cell. Synapse-related
fields are uniform across the group.
Parameters:
-
cells(list of Cell) –The cells in this group.
-
label(str, default:None) –Title displayed above the group's cells.
Nonesuppresses it.
Examples:
Define named styles once, then build groups:
>>> L2A = dict(color="compartment", palette={SWC_AXON: "tab:blue",
... SWC_DENDRITE: "navy"})
>>> L2B = dict(color="compartment", palette={SWC_AXON: "lightblue",
... SWC_DENDRITE: "steelblue"})
>>> groups = [
... LineupGroup(l2a_cells, label="L2a", **L2A),
... LineupGroup(l2b_cells, label="L2b", **L2B),
... ]
>>> plot_lineup_grid(groups=groups, row_max_width=2000,
... layer_lines={0: "L1", 250: "L2/3", 500: "L4"})
Mix styles within a single group via per-cell broadcasts:
>>> LineupGroup(cells, label="Comparison",
... color=["compartment"] * len(cells),
... palette=[L2A["palette"], L3A["palette"], L2A["palette"]],
... alpha=[1.0, 1.0, 0.3])
add_layer_lines
add_layer_lines
add_layer_lines(ax: Axes, layer_lines: Dict[float, Optional[str]], color: str = 'gray', linestyle: str = '--', linewidth: float = 0.5, label_fontsize: float = 9.0, label_pad: float = 0.01, label_kwargs: Optional[dict] = None, line_kwargs: Optional[dict] = None) -> Axes
Add horizontal layer reference lines with optional left-margin labels.
Parameters:
-
ax(Axes) –Axes to annotate.
-
layer_lines(dict of float -> str or None) –Mapping from y-coordinate (in data units) to a label string. Pass
Noneas the value to draw the line without a label. -
color(str, default:"gray") –Color for both lines and labels.
-
linestyle(str, default:"--") –Line style for the reference lines.
-
linewidth(float, default:0.5) –Width of the reference lines in points.
-
label_fontsize(float, default:9.0) –Font size for labels.
-
label_pad(float, default:0.01) –Horizontal pad between the axis edge and each label, expressed as a fraction of the axes width.
-
label_kwargs(dict, default:None) –Extra keyword arguments forwarded to :meth:
Axes.textfor the labels. Overrides any of the defaults above. -
line_kwargs(dict, default:None) –Extra keyword arguments forwarded to :meth:
Axes.axhline. Overrides any of the defaults above.
Returns:
-
Axes–The same axes, with lines and labels added.
Examples:
Projections and Rotations
The projection keyword of every 2D plotting function accepts either a
string label (e.g. "xy") or a callable mapping (N, 3) → (N, 2).
Rotation and RotateCell are factories that build such callables for
common rotated-view use cases. See the
visualization guide for examples.
Rotation
Rotation
Rotation(center: ndarray, axis: Union[ndarray, Literal['x', 'y', 'z']], angle: float, new_center: Optional[ndarray] = None, invert_y: bool = True) -> Callable[[ndarray], ndarray]
Return a projection callable that rotates 3D points and projects to 2D.
The callable accepts an (N, 3) array of 3D points, applies a rotation about the given axis and center, projects to 2D (xy plane), optionally inverts y, and optionally translates the center to a new 2D location.
Compatible with the projection parameter of all ossify plotting
functions.
Parameters:
-
center(ndarray) –3D point about which the rotation is performed, shape (3,). The rotation center is a fixed point of the transform.
-
axis(ndarray or x | y | z) –Rotation axis. String labels are converted to unit vectors.
-
angle(float) –Rotation angle in degrees.
-
new_center(ndarray, default:None) –If provided, shifts the 2D output so that the projected rotation center appears at this 2D location. Shape (2,). Useful for centering a cell at the origin (
[0, 0]) or at a specific layout position. -
invert_y(bool, default:True) –If True, negates the y coordinate of the projected output, matching the image-space convention (y increases downward) used by the standard string projections.
Returns:
Examples:
Rotate 90° about the z-axis, centered at the soma:
Center the projected cell at the plot origin:
RotateCell
RotateCell
RotateCell(cell: Cell, axis: Union[ndarray, Literal['x', 'y', 'z'], Literal['best'], None] = None, angle: Union[float, Literal['best'], None] = None, center: Optional[ndarray] = None, new_center: Optional[ndarray] = None, invert_y: bool = True) -> Callable[[ndarray], ndarray]
Return a projection callable for a cell, with automatic center and PCA modes.
A high-level wrapper around :func:Rotation that extracts the rotation
center from the cell's soma location and supports PCA-based automatic
angle optimization.
Parameters:
-
cell(Cell) –The cell to build a projection for. Used to extract the default rotation center and skeleton vertices for PCA.
-
axis(ndarray or x | y | z or best or None, default:None) –Rotation axis. String labels "x"/"y"/"z" are converted to unit vectors.
"best"orNonetriggers full PCA: the minimum- variance axis of the skeleton is used (see Notes). -
angle(float or best or None, default:None) –Rotation angle in degrees, or
"best"to find the optimal angle about the given axis via PCA.Noneis treated as 0. -
center(ndarray, default:None) –3D rotation center. Defaults to
cell.skeleton.root_location. -
new_center(ndarray, default:None) –2D display position for the rotation center after projection. Passed through to :func:
Rotation. -
invert_y(bool, default:True) –Passed through to :func:
Rotation.
Returns:
Notes
Both PCA modes share the same two-step algorithm via
_best_angle_for_axis:
- Axis given, angle="best": project skeleton vertices onto the plane perpendicular to the given axis, run 2D PCA, and return the angle that aligns the principal axis with x.
- axis="best" or None: run 3D PCA first to find the minimum-variance direction (PC3), use that as the rotation axis, then apply the same constrained angle-finding step.
Examples:
Rotate about y to the best orientation:
Fully automatic best view:
3D Cell Plotting
Installation
3D plotting requires PyVista. Install with pip install ossify[viz].
plot_cell_3d
plot_cell_3d
plot_cell_3d(cell: Cell, color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, opacity: float = 1.0, line_width: float = 2.0, tube_radius: Optional[Union[float, str, ndarray]] = None, tube_radius_scale: Optional[float] = None, tube_radius_norm: Optional[Tuple[float, float]] = None, tube_radii: Optional[Tuple[float, float]] = None, root_marker: bool = False, mesh: bool = False, mesh_color: Optional[Union[str, ndarray, tuple]] = None, mesh_palette: Union[str, dict] = 'coolwarm', mesh_color_norm: Optional[Tuple[float, float]] = None, mesh_color_scale: Optional[Literal['log']] = None, mesh_opacity: float = 0.3, mesh_show_edges: bool = False, synapses: Literal['pre', 'post', 'both', True, False] = False, pre_anno: str = 'pre_syn', pre_color: Optional[Union[str, ndarray, tuple]] = None, pre_palette: Union[str, dict] = 'coolwarm', pre_color_norm: Optional[Tuple[float, float]] = None, post_anno: str = 'post_syn', post_color: Optional[Union[str, ndarray, tuple]] = None, post_palette: Union[str, dict] = 'coolwarm', post_color_norm: Optional[Tuple[float, float]] = None, syn_opacity: float = 1.0, syn_color_scale: Optional[Literal['log']] = None, syn_size: Optional[Union[str, ndarray, float]] = None, syn_size_norm: Optional[Tuple[float, float]] = None, syn_size_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, syn_sizes: Optional[Tuple[float, float]] = None, plotter: Optional[Plotter] = None) -> Plotter
Render a :class:Cell — skeleton and optional annotations — in 3D.
Parameters:
-
cell(Cell) –Cell to render.
-
color(str, np.ndarray, or tuple, default:None) –Skeleton color specification (see :func:
plot_morphology_3d). -
palette(str or dict, default:"coolwarm") –Colormap for skeleton color mapping. Any name from the
matplotlib colormap registry <https://matplotlib.org/stable/gallery/color/colormap_reference.html>_ is accepted, including colormaps registered by third-party packages (e.g. cmocean, colorcet, cmcrameri) if they are installed. A dict maps discrete values to colors. -
color_norm(tuple of float, default:None) –(min, max)normalization range for skeleton color. -
color_scale(log, default:"log") –Value transform applied before skeleton colormap projection (see :func:
plot_morphology_3d). -
opacity(float, default:1.0) –Skeleton opacity.
-
line_width(float, default:2.0) –Skeleton line width (used when
tube_radiusisNone). -
tube_radius(float, str, or np.ndarray, default:None) –Skeleton tube radius (see :func:
plot_morphology_3d). -
tube_radius_scale(float, default:None) –Multiplicative unit-conversion factor (e.g.
1/1000for nm → µm). -
tube_radius_norm(tuple of float, default:None) –(min, max)input range / cap for per-vertex tube radii. -
tube_radii(tuple of float, default:None) –(min_radius, max_radius)output range for per-vertex tube radii. -
root_marker(bool, default:False) –If
True, mark the root vertex with a sphere. -
mesh(bool, default:False) –If
Trueand cell has a mesh layer, render the mesh surface. -
mesh_color(str, np.ndarray, or tuple, default:None) –Color specification for the mesh surface (see :func:
plot_mesh_3d). -
mesh_palette(str or dict, default:"coolwarm") –Colormap for mesh scalar color mapping (see palette).
-
mesh_color_norm(tuple of float, default:None) –(min, max)clipping range for mesh color mapping. -
mesh_color_scale(log, default:"log") –Value transform for mesh color mapping (see :func:
plot_mesh_3d). -
mesh_opacity(float, default:0.3) –Opacity of the mesh surface. Defaults to semi-transparent so the skeleton remains visible underneath.
-
mesh_show_edges(bool, default:False) –If
True, draw wireframe edges on the mesh surface. -
synapses((pre, post, both), default:"pre") –Which synapse layers to render.
Falserenders none;Trueor"both"renders both pre- and post-synaptic;"pre"/"post"renders only that side. -
pre_anno(str, default:"pre_syn") –Name of the pre-synaptic annotation layer in cell.
-
pre_color(str, np.ndarray, or tuple, default:None) –Color specification for the pre-synaptic layer.
-
pre_palette(str or dict, default:"coolwarm") –Colormap for pre-synaptic scalar color mapping (see palette).
-
pre_color_norm(tuple of float, default:None) –(min, max)clipping range for pre-synaptic color mapping. -
post_anno(str, default:"post_syn") –Name of the post-synaptic annotation layer in cell.
-
post_color(str, np.ndarray, or tuple, default:None) –Color specification for the post-synaptic layer.
-
post_palette(str or dict, default:"coolwarm") –Colormap for post-synaptic scalar color mapping (see palette).
-
post_color_norm(tuple of float, default:None) –(min, max)clipping range for post-synaptic color mapping. -
syn_opacity(float, default:1.0) –Opacity for all synapse spheres.
-
syn_color_scale(log, default:"log") –Value transform for synapse color mapping (see :func:
plot_annotations_3d). -
syn_size(str, np.ndarray, or float, default:None) –Sphere radius for all synapse layers.
-
syn_size_norm(tuple of float, default:None) –(min, max)clipping range for synapse size mapping. -
syn_size_scale((log, sqrt, cbrt), default:"log") –Value transform for synapse size mapping (see :func:
plot_annotations_3d). -
syn_sizes(tuple of float, default:None) –(min_radius, max_radius)output range for synapse sphere radii, in the same world units as the cell vertices. WhenNone(default), estimated from the synapse bounding box so spheres are visible regardless of coordinate scale (nm/µm/voxels). -
plotter(Plotter, default:None) –Existing plotter to add actors to.
Returns:
-
Plotter–Plotter with the cell rendered.
3D Layer Plotting
plot_morphology_3d
plot_morphology_3d
plot_morphology_3d(cell: Union[Cell, SkeletonLayer], color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, opacity: Optional[Union[str, ndarray, float]] = 1.0, line_width: float = 2.0, tube_radius: Optional[Union[float, str, ndarray]] = None, tube_radius_scale: Optional[float] = None, tube_radius_norm: Optional[Tuple[float, float]] = None, tube_radii: Optional[Tuple[float, float]] = None, root_marker: bool = False, root_radius: Optional[float] = None, root_color: Optional[Union[str, tuple]] = None, plotter: Optional[Plotter] = None) -> Plotter
Plot a skeleton in 3D with flexible color and radius styling.
Parameters:
-
cell(Cell or SkeletonLayer) –Cell or skeleton to render.
-
color(str, np.ndarray, or tuple, default:None) –Color specification. A string that matches a feature name is resolved to a per-vertex value array and mapped through palette. A string that does not match a feature is treated as a matplotlib color name. An array of shape
(N,)is mapped through palette. An(N, 3)or(N, 4)array is used directly as RGB/RGBA. -
palette(str or dict, default:"coolwarm") –Colormap for mapping scalar color values to RGB. Any name from the
matplotlib colormap registry <https://matplotlib.org/stable/gallery/color/colormap_reference.html>_ is accepted, including colormaps registered by third-party packages (e.g. cmocean, colorcet, cmcrameri) if they are installed. A dict maps discrete values to colors. -
color_norm(tuple of float, default:None) –(min, max)normalization range for continuous color mapping, in the original (pre-transform) value space. -
color_scale(log, default:"log") –Value transform applied before colormap projection.
"log"log-transforms the feature values so the colormap is distributed linearly in log-space. color_norm bounds are still specified in the original value space. -
opacity(str, np.ndarray, or float, default:1.0) –Opacity of the skeleton. Feature name, per-vertex array, or scalar.
-
line_width(float, default:2.0) –Line width in pixels. Used only when
tube_radiusisNone. -
tube_radius(float, str, or np.ndarray, default:None) –Render as tubes with this radius. A scalar float gives a uniform tube; a string resolves to a feature name; an array specifies per-vertex radii directly.
-
tube_radius_scale(float, default:None) –Multiplicative scale factor applied to all tube radius values after feature/array resolution. Useful for unit conversion — e.g.
tube_radius_scale=1/1000when the feature is in nm but the skeleton vertices are in µm. Applied before tube_radius_norm and tube_radii rescaling. -
tube_radius_norm(tuple of float, default:None) –(min, max)clip range for per-vertex tube radii. Values outside this range are clamped. When tube_radii is also given, the clipped values are further remapped to that output range; otherwise the original scale is preserved (clip only). Ignored when tube_radius is a scalar float. -
tube_radii(tuple of float, default:None) –(min_radius, max_radius)output range for per-vertex tube radii after normalization. Ignored when tube_radius is a scalar float. -
root_marker(bool, default:False) –If
True, place a sphere at the root vertex. -
root_radius(float, default:None) –Radius for the root marker sphere. Falls back to tube_radius if it is a scalar float, then to
1.0. -
root_color(str or tuple, default:None) –Color for the root marker sphere. Defaults to the root vertex's mapped color when
None. -
plotter(Plotter, default:None) –Existing plotter to add actors to.
Returns:
-
Plotter–Plotter with the skeleton rendered.
plot_annotations_3d
plot_annotations_3d
plot_annotations_3d(annotation: PointCloudLayer, color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, opacity: float = 1.0, size: Optional[Union[str, ndarray, float]] = None, size_norm: Optional[Tuple[float, float]] = None, size_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, sizes: Optional[Tuple[float, float]] = None, plotter: Optional[Plotter] = None) -> Plotter
Render a :class:PointCloudLayer annotation as 3D spheres.
Parameters:
-
annotation(PointCloudLayer) –Annotation layer to render.
-
color(str, np.ndarray, or tuple, default:None) –Color specification. A string matching a feature name resolves to a per-point value array mapped through palette. Otherwise treated as a matplotlib color.
-
palette(str or dict, default:"coolwarm") –Colormap for scalar color mapping. Any name from the
matplotlib colormap registry <https://matplotlib.org/stable/gallery/color/colormap_reference.html>_ is accepted, including colormaps registered by third-party packages (e.g. cmocean, colorcet, cmcrameri) if they are installed. A dict maps discrete values to colors. -
color_norm(tuple of float, default:None) –(min, max)clipping range for color mapping, in the original (pre-transform) value space. -
color_scale(log, default:"log") –Value transform applied before colormap projection.
"log"log-transforms the feature values so the colormap is distributed linearly in log-space. color_norm bounds are still specified in the original value space and are converted internally. -
opacity(float, default:1.0) –Overall opacity.
-
size(str, np.ndarray, or float, default:None) –Sphere radius specification. A string resolves to a feature name. An array or float is used directly or rescaled to sizes.
-
size_norm(tuple of float, default:None) –(min, max)clipping range for size mapping, in the original (pre-transform) value space. -
size_scale((log, sqrt, cbrt), default:"log") –Value transform applied before size normalization.
"log"log-transforms values (linear spacing in log-space);"sqrt"takes the square root (useful when the feature is a cross-sectional area and radius ∝ √area);"cbrt"takes the cube root (useful when the feature is a volume and radius ∝ ∛volume). size_norm bounds are always specified in the original value space. -
sizes(tuple of float, default:None) –(min_radius, max_radius)output range for size rescaling, in the same world units as the annotation vertices. WhenNone(default), estimated from the annotation's bounding box (approximately 0.1 %–0.8 % of the smallest spatial extent) so spheres are visible regardless of coordinate scale (nm/µm/voxels). Pass an explicit tuple to make markers bolder. -
plotter(Plotter, default:None) –Existing plotter to add actors to.
Returns:
-
Plotter–Plotter with the annotation rendered.
plot_mesh_3d
plot_mesh_3d
plot_mesh_3d(mesh: MeshLayer, color: Optional[Union[str, ndarray, tuple]] = None, palette: Union[str, dict] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, color_scale: Optional[Literal['log']] = None, opacity: float = 1.0, show_edges: bool = False, plotter: Optional[Plotter] = None) -> Plotter
Render a :class:MeshLayer as a 3D surface using PyVista.
Parameters:
-
mesh(MeshLayer) –Mesh to render.
-
color(str, np.ndarray, or tuple, default:None) –Color specification. May be:
- A matplotlib color string (e.g.
"steelblue") for a uniform surface color. - A tuple
(r, g, b)or(r, g, b, a)for a uniform RGBA color. - A 1-D array of length
n_verticesfor per-vertex scalar coloring (mapped through palette). - A 2-D
(n_vertices, 3)uint8 RGB array for direct per-vertex colors. - A feature name string present in the mesh to use those values for coloring.
If
None, PyVista's default surface color is used. - A matplotlib color string (e.g.
-
palette(str or dict, default:"coolwarm") –Colormap for scalar color mapping. Any name from the
matplotlib colormap registry <https://matplotlib.org/stable/gallery/color/colormap_reference.html>_ is accepted, including colormaps registered by third-party packages (e.g. cmocean, colorcet, cmcrameri) if they are installed. -
color_norm(tuple of float, default:None) –(min, max)normalization range for scalar color mapping. Values outside this range are clamped. -
color_scale(log, default:"log") –Transform applied to scalar values before color mapping.
"log"applies a natural-log transform; color_norm bounds are interpreted in the original (pre-transform) space and converted internally. -
opacity(float, default:1.0) –Surface opacity. Values less than 1.0 make the mesh semi-transparent, useful when rendering alongside a skeleton.
-
show_edges(bool, default:False) –If
True, draw the mesh wireframe edges on top of the surface. -
plotter(Plotter, default:None) –Existing plotter to add the mesh to. If
None, a new plotter is created.
Returns:
-
Plotter–Plotter with the mesh surface rendered.
plot_graph_3d
plot_graph_3d
plot_graph_3d(graph: GraphLayer, node_color: Optional[Union[str, ndarray, tuple]] = None, node_palette: Union[str, dict] = 'coolwarm', node_color_norm: Optional[Tuple[float, float]] = None, node_color_scale: Optional[Literal['log']] = None, node_size: Optional[Union[str, ndarray, float]] = None, node_size_norm: Optional[Tuple[float, float]] = None, node_size_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, node_sizes: Optional[Tuple[float, float]] = None, node_opacity: float = 1.0, show_nodes: bool = True, edge_color: Optional[Union[str, ndarray, tuple]] = None, edge_palette: Union[str, dict] = 'coolwarm', edge_color_norm: Optional[Tuple[float, float]] = None, edge_color_scale: Optional[Literal['log']] = None, edge_radius: Optional[Union[str, float]] = None, edge_radius_norm: Optional[Tuple[float, float]] = None, edge_radius_scale: Optional[Literal['log', 'sqrt', 'cbrt']] = None, edge_radii: Optional[Tuple[float, float]] = None, edge_opacity: float = 1.0, line_width: float = 2.0, show_edges: bool = True, plotter: Optional[Plotter] = None) -> Plotter
Render a :class:GraphLayer as sphere glyphs at nodes with edges as
lines or tubes.
All color and size features are per-vertex. When a feature name is given for an edge property, PyVista interpolates that value along each tube, creating a smooth gradient between the two endpoint values.
Parameters:
-
graph(GraphLayer) –Graph to render.
-
node_color(str, np.ndarray, or tuple, default:None) –Color specification for node spheres. A feature name string resolves to per-vertex values mapped through node_palette. Otherwise treated as a matplotlib color.
-
node_palette(str or dict, default:"coolwarm") –Colormap for node scalar color mapping. Any name from the
matplotlib colormap registry <https://matplotlib.org/stable/gallery/color/colormap_reference.html>_ is accepted, including colormaps registered by third-party packages (e.g. cmocean, colorcet, cmcrameri) if they are installed. A dict maps discrete values to colors. -
node_color_norm(tuple of float, default:None) –(min, max)clipping range for node color mapping, in the original (pre-transform) value space. -
node_color_scale(log, default:"log") –Value transform before node colormap projection.
"log"log-transforms values; node_color_norm stays in original space. -
node_size(str, np.ndarray, or float, default:None) –Sphere radius for each node. A feature name string resolves to per-vertex values rescaled to node_sizes.
-
node_size_norm(tuple of float, default:None) –(min, max)clipping range for node size mapping, in the original (pre-transform) value space. -
node_size_scale((log, sqrt, cbrt), default:"log") –Value transform before node size normalization.
-
node_sizes(tuple of float, default:None) –(min_radius, max_radius)output range for node sphere radii, in the same world units as the vertex coordinates. WhenNone(default), a sensible range is estimated from the graph's bounding box (approximately 0.5 %–3 % of the smallest spatial extent). -
node_opacity(float, default:1.0) –Opacity for node spheres.
-
show_nodes(bool, default:True) –If
False, suppress node sphere rendering. -
edge_color(str, np.ndarray, or tuple, default:None) –Color specification for edges. A feature name string resolves to per-vertex values; each tube is then colored by smoothly interpolating between the two endpoint values. Otherwise treated as a uniform matplotlib color.
-
edge_palette(str or dict, default:"coolwarm") –Colormap for edge scalar color mapping (see node_palette).
-
edge_color_norm(tuple of float, default:None) –(min, max)clipping range for edge color mapping. -
edge_color_scale(log, default:"log") –Value transform before edge colormap projection.
-
edge_radius(str or float, default:None) –Tube radius for edges. A scalar float gives a uniform tube radius. A feature name string resolves to per-vertex values rescaled to edge_radii; the tube radius interpolates between the two endpoint values along each edge. When
None, edges are rendered as lines of line_width pixels. -
edge_radius_norm(tuple of float, default:None) –(min, max)clipping range for edge radius mapping. -
edge_radius_scale((log, sqrt, cbrt), default:"log") –Value transform before edge radius normalization.
-
edge_radii(tuple of float, default:None) –(min_radius, max_radius)output range for per-vertex edge radii, in world units. WhenNone(default), estimated from the graph's bounding box (approximately 0.1 %–1 % of the smallest extent). -
edge_opacity(float, default:1.0) –Opacity for edges.
-
line_width(float, default:2.0) –Line width in pixels. Used only when edge_radius is
None. -
show_edges(bool, default:True) –If
False, suppress edge rendering. -
plotter(Plotter, default:None) –Existing plotter to add actors to.
Returns:
-
Plotter–Plotter with the graph rendered.
3D Utilities
add_colorbar_3d
add_colorbar_3d
add_colorbar_3d(plotter: Plotter, palette: Union[str, Colormap] = 'coolwarm', color_norm: Optional[Tuple[float, float]] = None, label: Optional[str] = None, position_x: float = 0.85, position_y: float = 0.05, width: float = 0.1, height: float = 0.9, n_labels: int = 5, fmt: str = '%.3g', orientation: Literal['vertical', 'horizontal'] = 'vertical', n_colors: int = 256, **scalar_bar_kwargs) -> Plotter
Add a colorbar to a 3D plotter.
Because ossify's plot functions pre-map scalars to RGB before they
reach VTK, PyVista has no mapper to build a scalar bar from. This
helper creates a :class:vtkScalarBarActor directly from a
:class:vtkLookupTable populated by the matplotlib colormap, then
attaches it as a 2D viewport overlay. The actor has no spatial
bounds, so the scene camera is unaffected.
Parameters:
-
plotter(Plotter) –Plotter to add the colorbar to.
-
palette(str or Colormap, default:"coolwarm") –Colormap name (any matplotlib-registered name) or Colormap object.
-
color_norm(tuple of float, default:None) –(min, max)range for the colorbar. WhenNone, the bar spans[0, 1]. -
label(str, default:None) –Title text displayed alongside the colorbar.
-
position_x(float, default:0.85) –Horizontal position of the colorbar (0 = left edge, 1 = right edge).
-
position_y(float, default:0.05) –Vertical position of the colorbar bottom (0 = bottom, 1 = top).
-
width(float, default:0.1) –Width of the colorbar as a fraction of the window.
-
height(float, default:0.9) –Height of the colorbar as a fraction of the window.
-
n_labels(int, default:5) –Number of tick labels along the bar.
-
fmt(str, default:"%.3g") –printf-style format string for tick labels. -
orientation((vertical, horizontal), default:"vertical") –Bar orientation.
-
n_colors(int, default:256) –Number of colors sampled from palette into the lookup table.
-
**scalar_bar_kwargs–Extra keyword arguments passed to :class:
vtkScalarBarActor. Keys are converted fromsnake_casetoSetPascalCase; unknown keys are silently ignored.
Returns:
-
Plotter–The same plotter, with the colorbar added.
Examples:
orbit_3d
orbit_3d
orbit_3d(plotter: Plotter, output: Optional[str] = None, n_frames: int = 60, elevation: float = 0.0, factor: float = 2.0, framerate: int = 24, viewup: Optional[Tuple[float, float, float]] = None, window_size: Optional[Tuple[int, int]] = None, close: bool = True) -> Plotter
Orbit the camera around the scene, optionally saving to a file.
Generates a circular 360° orbital path around the viewup axis and either plays the orbit interactively or writes frames to a GIF / MP4 file.
Parameters:
-
plotter(Plotter) –Plotter with actors already added.
-
output(str, default:None) –Path to write the animation to (
.gifor.mp4). Requiresimageio(already bundled with the[viz]extra). WhenNone, the orbit is displayed interactively (or runs off-screen silently in headless contexts). -
n_frames(int, default:60) –Number of frames in the full 360° orbit. Higher values give a smoother animation; lower values give a smaller file. 60 frames @ 24 fps is a 2.5-second loop.
-
elevation(float, default:0.0) –Camera elevation angle in degrees above the orbital plane, applied once before path generation. Positive values tilt the camera up; negative values tilt it down.
-
factor(float, default:2.0) –Orbit radius factor relative to the scene bounding box. Larger values move the camera further from the scene center. Note that PyVista's :meth:
Plotter.generate_orbital_pathcomputes the radius from the scene's x and y extents only (ignoring z), so very-tall-in-z scenes may need a larger factor to avoid the camera path passing through the data. -
framerate(int, default:24) –Frames per second for file output. Ignored when output is
None. -
viewup(tuple of float, default:None) –The axis around which the camera orbits and the camera's "up" direction during the orbit.
[0, 0, 1](z-up, PyVista's default) orbits in the xy plane around the z axis.[0, 1, 0]or[0, -1, 0]orbits around the y axis (common for cells where y is the depth/anatomical axis). WhenNone, the plotter's current camera up vector is used. -
window_size(tuple of int, default:None) –(width, height)in pixels for rendered output frames. Larger sizes produce higher-resolution GIFs/MP4s at the cost of file size. WhenNone, uses the plotter's existing window size (typically 1024 × 768 for newly-constructed plotters). For publication-quality output, try(1920, 1440)or larger. -
close(bool, default:True) –If
True, close the plotter after the orbit completes (default for one-shot animation rendering). Passclose=Falseto keep the plotter alive — e.g. to add more actors and orbit again, or to capture screenshots after the orbit.
Returns:
-
Plotter–The plotter passed in. By default the plotter is closed and not reusable; pass
close=Falseto keep it alive.
Examples:
Interactive orbit:
Save a high-res GIF orbiting around the depth axis: