# -*- coding: utf-8 -*-
"""
GraphVisual
===========
Module providing Graph visuals for rendering graphs.
Note
----
This module is providing vispy visuals only.
See :mod:`PlotGraph3d` module for plotting.
"""
__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.visuals as visuals
import ClearMap.Analysis.Graphs.GraphRendering as gr
import ClearMap.Visualization.Color as col
###############################################################################
# ## Graph visuals
###############################################################################
default_color = (0.8, 0.1, 0.1, 1.0)
[docs]
class GraphLineVisual(visuals.LineVisual):
"""Displays a graph in 3d using tube rendering for the edges
"""
def __init__(self, graph,
coordinates=None,
color=None, vertex_colors=None, edge_colors=None,
width=None, mode='gl'):
connectivity = graph.edge_connectivity()
if coordinates is None:
name = 'coordinates'
else:
name = coordinates
coordinates = graph.vertex_property(name)
if vertex_colors is not None or edge_colors is not None:
color = None
if color is None:
if edge_colors is None and vertex_colors is None:
color = default_color
elif vertex_colors is not None:
if isinstance(vertex_colors, np.ndarray) and vertex_colors.ndim == 2:
color = vertex_colors
else:
color = col.color(vertex_colors, alpha=True)
elif edge_colors is not None:
if isinstance(edge_colors, np.ndarray) and edge_colors.ndim == 2:
# need a vertex pair for every edge if the color is different
coordinates = coordinates[connectivity.flatten()]
connectivity = np.arange(coordinates.shape[0])
connectivity = connectivity.reshape((-1, 2))
indices = np.arange(len(edge_colors))
indices = np.array([indices, indices]).T.flatten()
color = edge_colors[indices]
else:
color = col.color(edge_colors, alpha=True)
else:
color = col.color(color, alpha=True)
if width is None:
width = 1
visuals.LineVisual.__init__(self, coordinates, connect=connectivity,
color=color, width=width, method=mode)
[docs]
class GraphMeshVisual(visuals.mesh.MeshVisual):
"""Displays a graph in 3d using tube rendering for the edges"""
def __init__(self, graph,
coordinates=None, radii=None,
n_tube_points=8, default_radius=1,
color=None, vertex_colors=None, edge_colors=None,
mode='triangles', shading='smooth'):
if vertex_colors is not None or edge_colors is not None:
color = None
if color is None and vertex_colors is None:
color = default_color
if graph.has_edge_geometry():
name = coordinates if coordinates is not None else 'coordinates'
coordinates, indices = graph.edge_geometry(name=name, return_indices=True, as_list=False)
# calculate mesh
try:
name = radii if radii is not None else 'radii'
radii = graph.edge_geometry(name=name, return_indices=False, as_list=False)
except: # FIXME: broad
radii = self.use_default_radii(coordinates, default_radius)
else:
coordinates = graph.vertex_coordinates()
indices = graph.edge_connectivity().flatten()
coordinates = np.vstack(coordinates[indices])
try:
radii = graph.vertex_radii()
radii = radii[indices]
except: # FIXME: broad
radii = self.use_default_radii(coordinates, default_radius)
n_edges = graph.n_edges
indices = np.array([2*np.arange(0, n_edges), 2*np.arange(1, n_edges+1)]).T
if vertex_colors is not None:
connectivity = graph.edge_connectivity()
edge_colors = (vertex_colors[connectivity[:, 0]] + vertex_colors[connectivity[:, 1]])/2.0
vertices, faces, vertex_colors = gr.mesh_tube_from_coordinates_and_radii(coordinates, radii, indices,
n_tube_points=n_tube_points,
edge_colors=edge_colors,
processes=None)
visuals.mesh.MeshVisual.__init__(self, vertices, faces,
color=color, vertex_colors=vertex_colors,
shading=shading, mode=mode)
[docs]
def use_default_radii(self, coordinates, default_radius):
print(f'No radii found in the graph, using uniform radii = {default_radius}!')
radii = np.full(coordinates.shape[0], default_radius)
return radii
###############################################################################
# ## Tests
###############################################################################
def _test():
import numpy as np
import vispy
import ClearMap.Analysis.Graphs.GraphGt as ggt
import ClearMap.Visualization.Vispy.Plot3d as p3d
import ClearMap.Visualization.Vispy.GraphVisual as gv
# reload(gv)
g = ggt.Graph()
g.add_vertex(5)
g.add_edge([[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]])
g.set_vertex_coordinates(20*np.random.rand(5, 3))
v = vispy.scene.visuals.create_visual_node(gv.GraphLineVisual)
p = v(g, parent=p3d.initialize_view().scene)
p3d.center(p)
v = vispy.scene.visuals.create_visual_node(gv.GraphMeshVisual)
p = v(g, parent=p3d.initialize_view().scene)
p3d.center(p)