From 2bf70eccd7775ee6c4dddfe006581d00bdd7a785 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Tue, 10 Sep 2024 15:28:26 +0100 Subject: [PATCH] feat(vispy): simplify mesh creation Move the calculations for constant plot types to the cells, and make soma thicker to make it easy to see. The mesh creator is now much more specific and does not need to know what type of plot it's working on. It simply takes a list and spits out a mesh. --- pyneuroml/plot/PlotMorphologyVispy.py | 74 ++++++++++++--------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/pyneuroml/plot/PlotMorphologyVispy.py b/pyneuroml/plot/PlotMorphologyVispy.py index e865f0c1..a65c91b0 100644 --- a/pyneuroml/plot/PlotMorphologyVispy.py +++ b/pyneuroml/plot/PlotMorphologyVispy.py @@ -387,7 +387,8 @@ def plot_interactive_3D( - "detailed": show detailed morphology taking into account each segment's width - - "constant": show morphology, but use constant line widths + - "constant": show morphology, but use "min_width" for line widths; the + soma is made thicker to make it easy to see - "schematic": only plot each unbranched segment group as a straight line, not following each segment - "point": show all cells as points @@ -900,9 +901,7 @@ def plot_interactive_3D( if not nogui: if pbar is not None: pbar.finish() - create_mesh( - meshdata, plot_type, current_view, min_width, save_mesh_to=save_mesh_to - ) + create_mesh(meshdata, current_view, save_mesh_to=save_mesh_to) if pynml_in_jupyter: display(current_canvas) else: @@ -994,10 +993,9 @@ def plot_3D_cell_morphology( :param plot_type: type of plot, one of: - "detailed": show detailed morphology taking into account each segment's - width. This is not performant, because a new line is required for - each segment. To only be used for cells with small numbers of - segments - - "constant": show morphology, but use constant line widths + width. + - "constant": show morphology, but use min_width for line widths; the + soma is made 5 times thicker to make it easier to see. This is only applicable for neuroml.Cell cells (ones with some morphology) @@ -1108,6 +1106,21 @@ def plot_3D_cell_morphology( r1 = p.diameter / 2 r2 = d.diameter / 2 + # ensure larger than provided minimum width + if r1 < min_width: + r1 = min_width + if r2 < min_width: + r2 = min_width + + # if plot is a constant type, fix the widths + if plot_type == "constant": + r1 = min_width + r2 = min_width + # let soma be thicker so that it can be easily made out + if seg.id in soma_segs: + r1 = r1 * 5 + r2 = r2 * 5 + segment_spec = { "marker_size": None, "marker_color": None, @@ -1167,9 +1180,7 @@ def plot_3D_cell_morphology( logger.debug(f"meshdata added: {meshdata[-1]}") if not nogui: - create_mesh( - meshdata, plot_type, current_view, min_width, save_mesh_to=save_mesh_to - ) + create_mesh(meshdata, current_view, save_mesh_to=save_mesh_to) if pynml_in_jupyter: display(current_canvas) else: @@ -1184,7 +1195,7 @@ def plot_3D_schematic( segment_groups: Optional[List[SegmentGroup]] = None, offset: Optional[Tuple[float, float, float]] = (0.0, 0.0, 0.0), labels: bool = False, - width: float = 5.0, + width: float = 1.0, verbose: bool = False, nogui: bool = False, title: str = "", @@ -1254,7 +1265,7 @@ def plot_3D_schematic( - "origin": automatically added at origin - "bottom left": automatically added at bottom left - :type axes_pos: [float, float, float] or [int, int, int] or None or str + :type axes_pos: (float, float, float) or (int, int, int) or None or str :param theme: theme to use (light/dark) :type theme: str :param color: color to use for segment groups with some special values: @@ -1268,10 +1279,6 @@ def plot_3D_schematic( :param meshdata: dictionary used to store mesh related data for vispy visualisation :type meshdata: dict - :param mesh_precision: what decimal places to use to group meshes into - instances: more precision means more detail (meshes), means less - performance (passed to :py:func:`round` and so may be negative) - :type mesh_precision: int :param upright: bool only applicable for single cells: Makes cells "upright" (along Y axis) by calculating its PCA, rotating it so it is along the Y axis, and transforming cell co-ordinates to align along the rotated first principal @@ -1360,6 +1367,11 @@ def plot_3D_schematic( (last_dist.x, last_dist.y, last_dist.z), ) + seg_width = width + + if first_seg.id in soma_segs and last_seg.id in soma_segs: + seg_width = width * 5 + branch_color = color if color is None: branch_color = get_next_hex_color() @@ -1380,8 +1392,8 @@ def plot_3D_schematic( if offset is not None: meshdata.append( ( - f"{first_prox.diameter/2}", - f"{last_dist.diameter/2}", + seg_width, + seg_width, f"{length}", first_prox, last_dist, @@ -1392,8 +1404,8 @@ def plot_3D_schematic( else: meshdata.append( ( - f"{first_prox.diameter/2}", - f"{last_dist.diameter/2}", + seg_width, + seg_width, f"{length}", first_prox, last_dist, @@ -1402,9 +1414,7 @@ def plot_3D_schematic( ) if not nogui: - create_mesh( - meshdata, "Detailed", current_view, width, save_mesh_to=save_mesh_to - ) + create_mesh(meshdata, current_view, save_mesh_to=save_mesh_to) if pynml_in_jupyter: display(current_canvas) else: @@ -1561,9 +1571,7 @@ def create_mesh( Optional[Tuple[float, float, float]], ] ], - plot_type: str, current_view: ViewBox, - min_width: float, save_mesh_to: Optional[str], ): """Internal function to create a mesh from the mesh data @@ -1572,12 +1580,8 @@ def create_mesh( :param meshdata: meshdata to plot: list with: [(r1, r2, length, prox, dist, color, offset)] - :param plot_type: type of plot - :type plot_type: str :param current_view: vispy viewbox to use :type current_view: ViewBox - :param min_width: minimum width of tubes - :type min_width: float :param save_mesh_to: name of file to save mesh object to :type save_mesh_to: str or None """ @@ -1611,16 +1615,6 @@ def create_mesh( if offset is None: offset = (0.0, 0.0, 0.0) - # actual plotting bits - if plot_type == "constant": - r1 = min_width - r2 = min_width - - if r1 < min_width: - r1 = min_width - if r2 < min_width: - r2 = min_width - seg_mesh = None # 1: for points, we set the prox/dist to None since they only have # positions.