Source code for ClearMap.Visualization.Vispy.Plot3d

# -*- coding: utf-8 -*-
"""
Plot3d Module
=============

Plotting routines for 3d display of data.

Note
----
This module is using vispy.
"""
__author__ = 'Christoph Kirst <ckirst@rockefeller.edu>'
__license__ = 'MIT License <https://www.opensource.org/licenses/mit-license.php>'
__copyright__ = 'Copyright (c) 2017 by Christoph Kirst, The Rockefeller University, New York City'

import numpy as np

import vispy

#try:
#  vispy.use(app='PyQt5');  #avoid use of ipynb_webgl
#except:
#  print('qt')
#  try:
#    vispy.use(app='PySide')
#  except:
#    print('no')
#    print(vispy.app.backends.BACKENDMAP)

import vispy.app
import vispy.scene
#vispy.app.run();


import ClearMap.Visualization.Vispy.arbitrary_rotation_camera as ttc
import ClearMap.Visualization.Vispy.VolumeVisual as volume_visual

import ClearMap.Visualization.Color as col

import ClearMap.IO.IO as io

###############################################################################
# ## 3d plotting
###############################################################################


[docs] def list_line_plot_3d(coordinates, view=None, title=None, center_view=True, **kwargs): """ Plot lines between coordinates in 3d. Arguments --------- coordinates : array Coordinate nx3 array. title : str or None Window title. view : view or None Add plot to this view. if given. Returns ------- view : view The view of the plot. """ # visual Plot3D = vispy.scene.visuals.create_visual_node(vispy.visuals.LinePlotVisual) # view title = title if title is not None else 'list_line_plot_3d' view = initialize_view(view, title=title) # style style = dict(width=2.0, color='red', symbol='o', edge_color='w', face_color='blue') style.update(**kwargs) # plot plot = Plot3D(coordinates, parent=view.scene, **style) plot.set_gl_state('translucent', blend=True, depth_test=True) if center_view: _center_view(view) return plot
[docs] def list_plot_3d(coordinates, view=None, title=None, center_view=True, color=None, **kwargs): """ Scatter plot of points in 3d. Arguments --------- coordinates : array Coordinatenx3 array. title : str or None Window title. view : view or None Add plot to this view. if given. Returns ------- view : view The view of the plot. """ # visual Scatter3D = vispy.scene.visuals.create_visual_node(vispy.visuals.MarkersVisual) # view title = title if title is not None else 'list_plot_3d' view = initialize_view(view, title=title) # style if color and 'face_color' not in kwargs.keys(): kwargs.update(face_color=color) if color and 'edge_color' not in kwargs.keys(): kwargs.update(edge_color=color) style = dict(face_color='blue', symbol='o', size=10, edge_width=0.5, edge_color='blue') style.update(**kwargs) # plot scatter_plot = Scatter3D(parent=view.scene) scatter_plot.set_gl_state('translucent', blend=True, depth_test=True) scatter_plot.set_data(coordinates, **style) if center_view: _center_view(view) return scatter_plot
[docs] def plot_3d(source, colormap=None, view=None, title=None, center_view=True, **kwargs): """Plot 3d volume. Arguments --------- source : array The 3d volume. title : str or None Window title. view : view or None Add plot to this view. if given. Returns ------- view : view The view of the plot. """ # visual VolumePlot3D = vispy.scene.visuals.create_visual_node(volume_visual.VolumeVisual) # view title = title if title is not None else 'plot_3d' # center = (np.array(source.shape) // 2) view = initialize_view(view, title=title, fov=0, depth_value=10**8) #, center=center) # style style = dict(cmap=grays_alpha(), method='translucent', relative_step_size=0.5) style.update(**kwargs) # source source = io.as_source(source)[:] if source.dtype == bool: source = source.view(dtype='uint8') # orient source = source.transpose([2, 1, 0]) # plot plot = VolumePlot3D(source, parent=view.scene, **style) if center_view: _center_view(view) return plot
[docs] def plot_mesh_3d(coordinates, faces, view=None, shading='smooth', color=None, face_colors=None, vertex_colors=None, mode='triangles', center_view=True, title=None, **kwargs): """Plot a 3d mesh. Arguments --------- coordinates : array Coordinate nx3 array. faces : array Indices of triangular faces, nx3 array. title : str or None Window title. view : view or None Add plot to this view. if given. Returns ------- view : view The view of the plot. """ # build visuals MeshVisual = vispy.scene.visuals.create_visual_node(vispy.visuals.mesh.MeshVisual) # view title = title if title is not None else 'plot_mesh_3d' view = initialize_view(view, title=title) # print view if color is None and face_colors is None and vertex_colors is None: color = 'red' p = MeshVisual(coordinates, faces, parent=view.scene, shading=shading, color=color, face_colors=face_colors, vertex_colors=vertex_colors, mode=mode, **kwargs) if center_view: view.camera.center = np.mean(coordinates, axis=0) return p
[docs] def plot_regular_polygon(center, sides=4, title=None, view=None, color='red', border_color=None, border_width=1, radius=1.0, **kwargs): # build visuals PolyVisual = vispy.scene.visuals.create_visual_node(vispy.visuals.RegularPolygonVisual) # view title = title if title is not None else 'plot_cube' view = initialize_view(view, title=title) p = PolyVisual(center, color=color, border_color=border_color, border_width=border_width, radius=radius, sides=sides, parent=view.scene, **kwargs) if center_view: view.camera.center = center return p
[docs] def plot_box(lower, upper, face_color=(1, 0, 0, 0.5), line_color=None, line_width=1, line_padding=0, shading='smooth', mode='triangles', title=None, view=None, center_view=True, **kwargs): """Plots a box in 3d.""" # TODO: return compound visual # view title = title if title is not None else 'plot_box' view = initialize_view(view, title=title) # corners corners_base = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0], [0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]] corners = [[lower[d] if c[d] == 0 else upper[d] for d in range(3)] for c in corners_base] corners = np.array(corners, dtype=float) visuals = [] if face_color is not None: # build visuals MeshVisual = vispy.scene.visuals.create_visual_node(vispy.visuals.mesh.MeshVisual) faces = [[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [4, 5, 6], [4, 6, 7], [3, 0, 4], [3, 4, 7], [2, 3, 7], [2, 7, 6], [3, 2, 1], [3, 1, 0]] faces = np.array(faces) mv = MeshVisual(corners, faces, parent=view.scene, shading=shading, color=face_color, mode=mode, **kwargs) visuals.append(mv) if line_color is not None: # visual LineVisual = vispy.scene.visuals.create_visual_node(vispy.visuals.LineVisual) lines = np.array([[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]]) if line_padding != 0: corners = [[lower[d] - line_padding if c[d] == 0 else upper[d] + line_padding for d in range(3)] for c in corners_base] corners = np.array(corners, dtype=float) lv = LineVisual(corners, parent=view.scene, connect=lines, color=line_color, width=line_width) visuals.append(lv) if center_view: view.camera.center = 0.5 * (np.array(lower) + np.array(upper)) return view
############################################################################### # ## Helpers ############################################################################### # TODO: vispy.color.ColorMap([col1,col2,...]) is much easier!
[docs] def single_color_colormap(color=(1, 1, 1), alpha=0.075, inverse_alpha=False): color = tuple(col.color(color, alpha=alpha)) if inverse_alpha: opacity = lambda t: 1-t else: opacity = lambda t: t class SingleColor(vispy.color.colormap.BaseColormap): glsl_map = """ vec4 grays(float t) { return vec4(%g * t, %g * t, %g * t, %g * %s); } """ % (color + ('(1-t)' if inverse_alpha else 't',)) def map(self, t): if isinstance(t, np.ndarray): return np.hstack([color[0] * t, color[1] * t, color[2] * t, color[3] * opacity(t)]).astype(np.float32) else: return np.array([color[0] * t, color[1] * t, color[2] * t, color[3] * opacity(t)], dtype=np.float32) return SingleColor()
[docs] def grays_alpha(alpha=0.075, inverse=False): return single_color_colormap(color=(1, 1, 1), alpha=alpha, inverse_alpha=inverse)
[docs] def get_view(view): """ Return the view of the argument. Arguments --------- view : view The vispy window. Returns ------- view : view The vispy view of the plot. """ while not isinstance(view, vispy.scene.widgets.viewbox.ViewBox) and hasattr(view, 'parent'): view = view.parent return view
[docs] def add_axes(view): """ Add axes to a plot. Arguments --------- view : view The vispy window. Returns ------- view : view The vispy view of the plot. """ view = initialize_view(view) axes = vispy.scene.visuals.XYZAxis(parent=view.scene) return axes
[docs] def set_light_to_camera(mesh): """ Set the light direction to the crrent camera position. Arguments --------- mesh : mesh A vispy mesh. """ v = get_view(mesh) a = np.deg2rad(v.camera.azimuth) e = np.deg2rad(v.camera.elevation) direction = (np.cos(a) * np.cos(e), np.sin(a) * np.cos(e), np.sin(e)) # direction = (np.sin(a) * np.cos(e), np.cos(a) * np.cos(e), np.sin(e)) direction = tuple(-d for d in direction) mesh.light_dir = direction
[docs] def set_background(view, color): """ Set the background color of the view. Arguments --------- view : view The vispy window. color : color specification The color for the background. Returns ------- view : view The vispy view of the plot. """ view = get_view(view) view.canvas.bgcolor = col.color(color, alpha=True, as_int=False) return view
[docs] def center_view(view): """ Center the camera in a plot. Arguments --------- view : view The vispy window. Returns ------- view : view The vispy view of the plot. """ view = get_view(view) bounds = view.get_scene_bounds() center = [(b[1] - b[0])*0.5 for b in bounds] view.camera.center = center
_center_view = center_view # FIXME: why
[docs] def get_view_parameter(view): view = get_view(view) return view.camera.get_state()
[docs] def set_view_parameter(view, parameter): view = get_view(view) view.camera.set_state(parameter)
[docs] def initialize_view(view=None, title=None, show=True, fov=None, distance=None, elevation=None, azimuth=None, center=None, depth_value=None, bg_color='white'): """Return a default view.""" if view is None: # build canvas canvas = vispy.scene.SceneCanvas(keys='interactive', title=title, show=show, bgcolor=bg_color) view = canvas.central_widget.add_view(camera=ttc.ArbitraryRotationCamera()) # view.camera = 'turntable' # WARNING: This overwrites the cam defined as argument to the ctor above if fov is not None: view.camera.fov = fov if distance is not None: view.camera.distance = distance if elevation is not None: view.camera.elevation = elevation if azimuth is not None: view.camera.azimuth = azimuth if center is not None: view.camera.center = center if depth_value is not None: view.camera.depth_value = depth_value else: view = get_view(view) return view
[docs] def save(location, view, transparent=None, *args, **kwargs): """Save the current view to a file.""" canvas = get_view(view).canvas img = canvas.render(*args, **kwargs) if transparent is not None: t = np.logical_and(img[:, :, 0] >= transparent, img[:, :, 1] >= transparent) t = np.logical_and(img[:, :, 2] >= transparent, t) img[:, :, 3][t] = 0 img = img.transpose([0, 1, 2]) else: img = img[:, :, :3].T return io.write(location, img)
############################################################################### # ## Tests ############################################################################### def _test(): import numpy as np import ClearMap.Visualization.Vispy.Plot3d as p3d # reload(p3d) # plot coordinates = np.random.rand(40, 3) p1 = p3d.list_plot_3d(coordinates, title='test') # plot something on top coordinates = np.random.rand(30, 3) p2 = p3d.list_line_plot_3d(coordinates, color='white', face_color='red', view=p1) # analysis:ignore # volumetric plot import numpy as np shape = (31, 31, 31) binary = np.zeros(shape, dtype=bool) grid = np.meshgrid(*[range(s) for s in shape], indexing='ij') center = tuple(s/2 for s in shape) distance = np.sum([(g-c)**2 for g, c in zip(grid, center)], axis=0) binary[distance <= 10**2] = True p3 = p3d.plot_3d(binary) # analysis:ignore # reload(p3d) v = p3d.plot_box((0, 0, 0), (1, 2, 3), face_color=(1, 0, 0, 0), line_color='white', line_width=2, line_padding=0.015) print(v)