Source code for tyssue.draw.ipv_draw

"""3D visualisation inside the notebook."""

import warnings

import numpy as np
import pandas as pd
from ipywidgets import interact
from matplotlib import colormaps

from ..config.draw import sheet_spec
from ..utils.utils import get_sub_eptm, spec_updater

try:
    import ipyvolume as ipv
except ImportError:
    print(
        """
This module needs ipyvolume to work.
You can install it with:
$ conda install -c conda-forge ipyvolume
"""
    )


[docs]def browse_history( history, coords=["x", "y", "z"], start=None, stop=None, size=None, **draw_specs_kw ): times = history.slice(start, stop, size) num_frames = times.size draw_specs = sheet_spec() spec_updater(draw_specs, draw_specs_kw) sheet = history.retrieve(0) ipv.clear() fig, meshes = sheet_view(sheet, coords, **draw_specs_kw) lim_inf = sheet.vert_df[sheet.coords].min().min() lim_sup = sheet.vert_df[sheet.coords].max().max() ipv.xyzlim(lim_inf, lim_sup) def set_frame(i=0): fig.animation = 0 t = times[i] meshes = _get_meshes(history.retrieve(t), coords, draw_specs) update_view(fig, meshes) ipv.show() interact(set_frame, i=(0, num_frames - 1))
[docs]def update_view(fig, meshes): for old, new in zip(fig.meshes, meshes): old.x = new.x old.y = new.y old.z = new.z old.color = new.color old.triangles = new.triangles old.lines = new.lines
[docs]def sheet_view(sheet, coords=["x", "y", "z"], **draw_specs_kw): """ Creates a javascript renderer of the edge lines to be displayed in Jupyter Notebooks Returns ------- fig: a :class:`ipyvolume.widgets.Figure` widget mesh: a :class:`ipyvolume.widgets.Mesh` mesh widget """ # ipv.style.use(["dark", "minimal"]) draw_specs = sheet_spec() spec_updater(draw_specs, draw_specs_kw) fig = ipv.gcf() fig.meshes = fig.meshes + _get_meshes(sheet, coords, draw_specs) box_size = max(*(np.ptp(sheet.vert_df[u]) for u in sheet.coords)) border = 0.05 * box_size lim_inf = sheet.vert_df[sheet.coords].min().min() - border lim_sup = sheet.vert_df[sheet.coords].max().max() + border ipv.xyzlim(lim_inf, lim_sup) return fig, fig.meshes
[docs]def view_ipv(sheet, coords=["x", "y", "z"], **edge_specs): """ Creates a javascript renderer of the edge lines to be displayed in Jupyter Notebooks Returns ------- fig: a :class:`ipyvolume.widgets.Figure` widget mesh: a :class:`ipyvolume.widgets.Mesh` mesh widget """ warnings.warn("`view_ipv` is deprecated, use the more generic `sheet_view`") mesh = edge_mesh(sheet, coords, **edge_specs) fig = ipv.gcf() fig.meshes = fig.meshes + [mesh] box_size = max(*(np.ptp(sheet.vert_df[u]) for u in sheet.coords)) border = 0.05 * box_size lim_inf = sheet.vert_df[sheet.coords].min().min() - border lim_sup = sheet.vert_df[sheet.coords].max().max() + border ipv.xyzlim(lim_inf, lim_sup) return fig, mesh
[docs]def edge_mesh(sheet, coords, **edge_specs): """ Creates a ipyvolume Mesh of the edge lines to be displayed in Jupyter Notebooks Returns ------- mesh: a :class:`ipyvolume.widgets.Mesh` mesh widget """ spec = sheet_spec()["edge"] spec.update(**edge_specs) if callable(spec["color"]): spec["color"] = spec["color"](sheet) if isinstance(spec["color"], str): color = spec["color"] elif hasattr(spec["color"], "__len__"): color = _wire_color_from_sequence(spec, sheet)[:, :3] u, v, w = coords mesh = ipv.Mesh( x=sheet.vert_df[u], y=sheet.vert_df[v], z=sheet.vert_df[w], lines=sheet.edge_df[["srce", "trgt"]].astype(dtype=np.uint32), color=color, ) return mesh
[docs]def face_mesh(sheet, coords, **face_draw_specs): """ Creates a ipyvolume Mesh of the face polygons """ Ne, Nf = sheet.Ne, sheet.Nf if callable(face_draw_specs["color"]): face_draw_specs["color"] = face_draw_specs["color"](sheet) if isinstance(face_draw_specs["color"], str): color = face_draw_specs["color"] elif hasattr(face_draw_specs["color"], "__len__"): color = _face_color_from_sequence(face_draw_specs, sheet)[:, :3] if "visible" in sheet.face_df.columns: edges = sheet.edge_df[sheet.upcast_face(sheet.face_df["visible"])].index _sheet = get_sub_eptm(sheet, edges) if _sheet is not None: sheet = _sheet if isinstance(color, np.ndarray): faces = sheet.face_df["face_o"].values.astype(np.uint32) edges = edges.values.astype(np.uint32) indexer = np.concatenate([faces, edges + Nf, edges + Ne + Nf]) color = color.take(indexer, axis=0) epsilon = face_draw_specs.get("epsilon", 0) up_srce = sheet.edge_df[["s" + c for c in coords]] up_trgt = sheet.edge_df[["t" + c for c in coords]] Ne, Nf = sheet.Ne, sheet.Nf if epsilon > 0: up_face = sheet.edge_df[["f" + c for c in coords]].values up_srce = (up_srce - up_face) * (1 - epsilon) + up_face up_trgt = (up_trgt - up_face) * (1 - epsilon) + up_face mesh_ = np.concatenate( [sheet.face_df[coords].values, up_srce.values, up_trgt.values] ) triangles = np.vstack( [sheet.edge_df["face"], np.arange(Ne) + Nf, np.arange(Ne) + Ne + Nf] ).T.astype(dtype=np.uint32) mesh = ipv.Mesh( x=mesh_[:, 0], y=mesh_[:, 1], z=mesh_[:, 2], triangles=triangles, color=color ) return mesh
def _wire_color_from_sequence(edge_spec, sheet): """ """ color_ = edge_spec["color"] cmap = colormaps[edge_spec.get("colormap", "viridis")] if color_.shape in [(sheet.Nv, 3), (sheet.Nv, 4)]: return np.asarray(color_) if color_.shape == (sheet.Nv,): if np.ptp(color_) < 1e-10: return np.ones((sheet.Nv, 3)) * 0.7 return cmap((color_ - color_.min()) / np.ptp(color_)) if color_.shape in [(sheet.Ne, 3), (sheet.Ne, 4)]: color_ = pd.DataFrame(color_, index=sheet.edge_df.index) color_["srce"] = sheet.edge_df["srce"] color_ = color_.groupby("srce").mean().values return color_ if color_.shape == (sheet.Ne,): color_ = pd.DataFrame(color_, index=sheet.edge_df.index) color_["srce"] = sheet.edge_df["srce"] color_ = color_.groupby("srce").mean().values.ravel() if np.ptp(color_) < 1e-10: warnings.warn("Attempting to draw a colormap " "with a uniform value") return np.ones((sheet.Nv, 3)) * 0.7 return cmap((color_ - color_.min()) / np.ptp(color_)) else: raise ValueError("The 'color' value of the spec doesn't have a correct shape.") def _face_color_from_sequence(face_spec, sheet): color_ = face_spec["color"] cmap = colormaps[face_spec.get("colormap", "viridis")] Nf, Ne = sheet.Nf, sheet.Ne color_min, color_max = face_spec.get("color_range", (color_.min(), color_.max())) face_mesh_shape = Nf + 2 * Ne if color_.shape in [(sheet.Nf, 3), (sheet.Nf, 4)]: return np.concatenate([color_, color_, color_]) elif color_.shape == (sheet.Nf,): if np.ptp(color_) < 1e-10: # warnings.warn("Attempting to draw a colormap with a uniform value") return np.ones((face_mesh_shape, 3)) * 0.5 normed = (color_ - color_min) / (color_max - color_min) up_color = sheet.upcast_face(normed).values return cmap(np.concatenate([normed, up_color, up_color])) else: raise ValueError( "shape of `face_spec['color']` must be either (Nf, 3), (Nf, 4) or (Nf,)" ) def _get_meshes(sheet, coords, draw_specs): meshes = [] edge_spec = draw_specs["edge"] if edge_spec["visible"]: edges = edge_mesh(sheet, coords, **edge_spec) meshes.append(edges) else: edges = None face_spec = draw_specs["face"] if face_spec["visible"]: faces = face_mesh(sheet, coords, **face_spec) meshes.append(faces) else: faces = None return meshes