Source code for steps.API_1.visual.PartitionViewer

####################################################################################
#
#    STEPS - STochastic Engine for Pathway Simulation
#    Copyright (C) 2007-2023 Okinawa Institute of Science and Technology, Japan.
#    Copyright (C) 2003-2006 University of Antwerp, Belgium.
#    
#    See the file AUTHORS for details.
#    This file is part of STEPS.
#    
#    STEPS is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License version 3,
#    as published by the Free Software Foundation.
#    
#    STEPS is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU General Public License for more details.
#    
#    You should have received a copy of the GNU General Public License
#    along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#################################################################################   
###
from __future__ import print_function

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
from PyQt5.QtWidgets import QMainWindow
import numpy as np
import pyqtgraph.opengl as gl
import random
from steps.API_1.geom import INDEX_DTYPE
from steps.API_1.geom import UNKNOWN_TET
from steps.API_1.geom import UNKNOWN_TRI
[docs]def createColorMap(partitions): """ Genrate a random color map for a partition. Parameter: * partitions Partitions for a list of elements. The partition can be either a list, or a dict. Return: Return a dict color_map, for each partion in partitions, color_map[partition] gives a randomly generated color. """ if isinstance(partitions, list): color_map = {} for part in partitions: if part not in color_map.keys(): if part is None: color_map[part] = [1.0, 0.0, 0.0, 1.0] else: color_map[part] = [random.random(), random.random(), random.random(), 0.3] return color_map elif isinstance(partitions, dict): color_map = {} for part in partitions.values(): if part not in color_map.keys(): if part is None: color_map[part] = [1.0, 0.0, 0.0, 1.0] else: color_map[part] = [random.random(), random.random(), random.random(), 0.3] return color_map print("Unknown partitioning data, no color map is generated.") return None
[docs]class TetPartitionDisplay(QMainWindow): """ Partition Display Parameters: * mesh STEPS Tetmesh * tet_partitions Partition of tetrahedrons * title Display title * x X cordinate of the display * y Y cordinate of the display * w Width of the display * h Height of the display * scale Scaling between STEPS mesurement and display pixel * color_map Color map for each partition, it is a Python dict with key-value pairs as color_map[partition_id] = [red, green, blue, alpha], where partition_id is the data stored in tet_partitions. The color is defined by four parameters [red, green, blue, alpha], each with range from 0.0 to 1.0. If the partition name is not in the color map a random color will be generated. A partition assigned to None will always be colored as nontransparent red [1.0, 0.0, 0.0, 1.0]. * morph_sections NEURON hoc morphology data of the mesh * show_list List of sections to be visualized """ def __init__(self, mesh, tet_partitions, title = "TetPartitionDisplay", x = 100, y = 100, w = 800, h = 600, scale = 1e6, color_map = None, morph_sections = None, show_list = None): """ Constructor. """ QtGui.QMainWindow.__init__(self) self.setGeometry(x, y, w, h) self.widget = gl.GLViewWidget(self) self.setCentralWidget(self.widget) self.widget_mapping = {} self.mesh = mesh self.tet_part_table = {} for tet in range(len(tet_partitions)): if tet_partitions[tet] not in self.tet_part_table.keys(): self.tet_part_table[tet_partitions[tet]] = [] self.tet_part_table[tet_partitions[tet]].append(tet) self.scale = scale self.setWindowTitle(title) self.bound_min = [v * self.scale for v in mesh.getBoundMin()] self.bound_max = [v * self.scale for v in mesh.getBoundMax()] self.center = [0.0, 0.0, 0.0] axis = [0, 1, 2] new_center = [(self.bound_min[i] + self.bound_max[i]) / 2.0 for i in axis] pan_dist = [new_center[i] - self.center[i] for i in axis] self.widget.pan(pan_dist[0], pan_dist[1], pan_dist[2]) dist_x = self.bound_max[0] - self.bound_min[0] dist_y = self.bound_max[1] - self.bound_min[1] dist_z = self.bound_max[2] - self.bound_min[2] if dist_y >= dist_x and dist_y >= dist_z: self.main_axis = 1 self.widget.setCameraPosition(distance=dist_y * 2) elif dist_z >= dist_x and dist_z >= dist_y: self.widget.setCameraPosition(distance=dist_z * 2) self.main_axis = 2 else: self.widget.setCameraPosition(distance=dist_x * 2) self.main_axis = 0 self.center = new_center if color_map is None: self.color_map = {} else: self.color_map = color_map for tet_part in self.tet_part_table: if tet_part is None: self.color_map[None] = [1.0, 0.0, 0.0, 1.0] else: if tet_part not in self.color_map: color = [random.random(), random.random(), random.random(), 0.3] self.color_map[tet_part] = color part = TetPartitionMesh(self, mesh, self.tet_part_table[tet_part], color = self.color_map[tet_part]) self.widget.addItem(part) if tet_part not in self.widget_mapping: self.widget_mapping[tet_part] = [] self.widget_mapping[tet_part].append(part) if morph_sections is not None: for sec in morph_sections.values(): c = None if sec["name"] not in self.color_map: print("Section ", sec["name"], " does not have defined color, generate random one instead.") c = [random.random(), random.random(), random.random(), 0.3] self.color_map[sec["name"]] = c else: c = self.color_map[sec["name"]] data = [] points = sec["points"] npoints = len(points) for i in range(npoints - 1): p0 = points[i] p1 = points[i+1] data.append([p0[0], p0[1], p0[2]]) data.append([p1[0], p1[1], p1[2]]) data = np.array(data) data.shape = -1, 3 line_color = [c[0], c[1], c[2], 1.0] section = gl.GLLinePlotItem(pos = data, color = line_color, width = 3, mode = "lines") self.widget.addItem(section) if sec["name"] not in self.widget_mapping: self.widget_mapping[sec["name"]] = [] self.widget_mapping[sec["name"]].append(section) self.show() def showSections(self, section_list): for w in self.widget.items: w.hide() for sec in section_list: if sec not in self.widget_mapping: print("Section ", sec, " does not have visualization compoment.") continue for w in self.widget_mapping[sec]: w.show() def _updateView(self): axis = [0, 1, 2] new_center = [(self.bound_min[i] + self.bound_max[i]) / 2.0 for i in axis] pan_dist = [new_center[i] - self.center[i] for i in axis] self.widget.pan(pan_dist[0], pan_dist[1], pan_dist[2]) dist_x = self.bound_max[0] - self.bound_min[0] dist_y = self.bound_max[1] - self.bound_min[1] dist_z = self.bound_max[2] - self.bound_min[2] if dist_y >= dist_x and dist_y >= dist_z: self.main_axis = 1 self.widget.setCameraPosition(distance=dist_y * 2) elif dist_z >= dist_x and dist_z >= dist_y: self.widget.setCameraPosition(distance=dist_z * 2) self.main_axis = 2 else: self.widget.setCameraPosition(distance=dist_x * 2) self.main_axis = 0 self.center = new_center
class TetPartitionMesh(gl.GLMeshItem): """ Static mesh component for a compartment Parameters: * display Parent display * steps_mesh STEPS mesh * tet_list Tetrahedron list of a section of the mesh * color Color of the component """ def __init__(self, display, mesh, tet_list, color = None): """ Constructor """ self.display = display self.mesh = mesh if not color: color = [random.random(), random.random(), random.random(), 0.3] surface_tris = [] for tet in tet_list: tris = mesh.getTetTriNeighb(tet) for tri in tris: if tri == UNKNOWN_TRI: continue neighb_tets = mesh.getTriTetNeighb(tri) for neighb_tet in neighb_tets: if neighb_tet == UNKNOWN_TET or neighb_tet not in tet_list: surface_tris.append(tri) surface_tris = np.array(surface_tris, dtype = INDEX_DTYPE) v_set_size = mesh.getTriVerticesSetSizeNP(surface_tris) tris_data = np.zeros(surface_tris.size * 3, dtype = INDEX_DTYPE) v_set = np.zeros(v_set_size, dtype = INDEX_DTYPE) verts_data = np.zeros(v_set_size * 3) mesh.getTriVerticesMappingSetNP(surface_tris, tris_data, v_set) mesh.getBatchVerticesNP(v_set, verts_data) verts_data *= display.scale tris_data.shape = -1, 3 verts_data.shape = -1, 3 mesh_data = gl.MeshData(vertexes=verts_data, faces = tris_data) gl.GLMeshItem.__init__(self, meshdata=mesh_data, smooth=False, computeNormals =True, shader='balloon', glOptions='additive') self.setColor(color)
[docs]class TriPartitionDisplay(QMainWindow): """ Partition Display Parameters: * mesh STEPS Tetmesh * tri_partitions Partition of triangles * title Display title * x X cordinate of the display * y Y cordinate of the display * w Width of the display * h Height of the display * scale Scaling between STEPS mesurement and display pixel * color_map Color map for each partition, it is a Python dict with key-value pairs as color_map[partition_id] = [red, green, blue, alpha], where partition_id is the data stored in tet_partitions. The color is defined by four parameters [red, green, blue, alpha], each with range from 0.0 to 1.0. If the partition name is not in the color map a random color will be generated. A partition assigned to None will always be colored as nontransparent red [1.0, 0.0, 0.0, 1.0]. * morph_sections NEURON hoc morphology data of the mesh * show_list List of sections to be visualized """ def __init__(self, mesh, tri_partitions, title = "TriPartitionDisplay", x = 100, y = 100, w = 800, h = 600, scale = 1e6, color_map = None, morph_sections = None, show_list = None): """ Constructor. """ QtGui.QMainWindow.__init__(self) self.setGeometry(x, y, w, h) self.widget = gl.GLViewWidget(self) self.setCentralWidget(self.widget) self.widget_mapping = {} self.mesh = mesh self.tri_part_table = {} for tri in tri_partitions: if tri_partitions[tri] not in self.tri_part_table.keys(): self.tri_part_table[tri_partitions[tri]] = [] self.tri_part_table[tri_partitions[tri]].append(tri) self.scale = scale self.setWindowTitle(title) self.bound_min = [v * self.scale for v in mesh.getBoundMin()] self.bound_max = [v * self.scale for v in mesh.getBoundMax()] self.center = [0.0, 0.0, 0.0] axis = [0, 1, 2] new_center = [(self.bound_min[i] + self.bound_max[i]) / 2.0 for i in axis] pan_dist = [new_center[i] - self.center[i] for i in axis] self.widget.pan(pan_dist[0], pan_dist[1], pan_dist[2]) dist_x = self.bound_max[0] - self.bound_min[0] dist_y = self.bound_max[1] - self.bound_min[1] dist_z = self.bound_max[2] - self.bound_min[2] if dist_y >= dist_x and dist_y >= dist_z: self.main_axis = 1 self.widget.setCameraPosition(distance=dist_y * 2) elif dist_z >= dist_x and dist_z >= dist_y: self.widget.setCameraPosition(distance=dist_z * 2) self.main_axis = 2 else: self.widget.setCameraPosition(distance=dist_x * 2) self.main_axis = 0 self.center = new_center if color_map == None: self.color_map = {} else: self.color_map = color_map for tri_part in self.tri_part_table: if tri_part == None: self.color_map[None] = [1.0, 0.0, 0.0, 1.0] else: if tri_part not in self.color_map: color = [random.random(), random.random(), random.random(), 0.3] self.color_map[tri_part] = color part = TriPartitionMesh(self, mesh, self.tri_part_table[tri_part], color = self.color_map[tri_part]) self.widget.addItem(part) if tri_part not in self.widget_mapping: self.widget_mapping[tri_part] = [] self.widget_mapping[tri_part].append(part) if morph_sections != None: for sec in morph_sections.values(): c = None if sec["name"] not in self.color_map: print("Section ", sec["name"], " does not have defined color, generate random one instead.") c = [random.random(), random.random(), random.random(), 0.3] self.color_map[sec["name"]] = c else: c = self.color_map[sec["name"]] data = [] points = sec["points"] npoints = len(points) for i in range(npoints - 1): p0 = points[i] p1 = points[i+1] data.append([p0[0], p0[1], p0[2]]) data.append([p1[0], p1[1], p1[2]]) data = np.array(data) data.shape = -1, 3 line_color = [c[0], c[1], c[2], 1.0] section = gl.GLLinePlotItem(pos = data, color = line_color, width = 3, mode = "lines") self.widget.addItem(section) if sec["name"] not in self.widget_mapping: self.widget_mapping[sec["name"]] = [] self.widget_mapping[sec["name"]].append(section) self.show() def showSections(self, section_list): for w in self.widget.items: w.hide() for sec in section_list: if sec not in self.widget_mapping: print("Section ", sec, " does not have visualization compoment.") continue for w in self.widget_mapping[sec]: w.show() def _updateView(self): axis = [0, 1, 2] new_center = [(self.bound_min[i] + self.bound_max[i]) / 2.0 for i in axis] pan_dist = [new_center[i] - self.center[i] for i in axis] self.widget.pan(pan_dist[0], pan_dist[1], pan_dist[2]) dist_x = self.bound_max[0] - self.bound_min[0] dist_y = self.bound_max[1] - self.bound_min[1] dist_z = self.bound_max[2] - self.bound_min[2] if dist_y >= dist_x and dist_y >= dist_z: self.main_axis = 1 self.widget.setCameraPosition(distance=dist_y * 2) elif dist_z >= dist_x and dist_z >= dist_y: self.widget.setCameraPosition(distance=dist_z * 2) self.main_axis = 2 else: self.widget.setCameraPosition(distance=dist_x * 2) self.main_axis = 0 self.center = new_center
class TriPartitionMesh(gl.GLMeshItem): """ Static mesh component for a compartment Parameters: * display Parent display * steps_mesh STEPS mesh * tri_list Triangle list of a section of the mesh * color Color of the component """ def __init__(self, display, mesh, tri_list, color = None): """ Constructor """ self.display = display self.mesh = mesh if not color: color = [random.random(), random.random(), random.random(), 0.3] surface_tris = np.array(tri_list, dtype = INDEX_DTYPE) v_set_size = mesh.getTriVerticesSetSizeNP(surface_tris) tris_data = np.zeros(surface_tris.size * 3, dtype = INDEX_DTYPE) v_set = np.zeros(v_set_size, dtype = INDEX_DTYPE) verts_data = np.zeros(v_set_size * 3) mesh.getTriVerticesMappingSetNP(surface_tris, tris_data, v_set) mesh.getBatchVerticesNP(v_set, verts_data) verts_data *= display.scale tris_data.shape = -1, 3 verts_data.shape = -1, 3 mesh_data = gl.MeshData(vertexes=verts_data, faces = tris_data) gl.GLMeshItem.__init__(self, meshdata=mesh_data, smooth=False, computeNormals =True, shader='balloon', glOptions='additive') self.setColor(color)