Skip to content

dd

Additional plotting parameters for Matplotlib 2d backend.

Source code in navis/plotting/settings.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
@dataclass
class Matplotlib2dSettings(BasePlottingSettings):
    """Additional plotting parameters for Matplotlib 2d backend."""

    _name = "matplotlib backend"

    method: Literal["2d", "3d", "3d_complex"] = "2d"
    group_neurons: bool = False
    autoscale: bool = True
    orthogonal: bool = True
    scalebar: Union[int, float, str, pint.Quantity] = False
    volume_outlines: bool = False
    rasterize: bool = False
    view: Tuple[str, str] = ("x", "y")
    figsize: Optional[Tuple[float, float]] = None
    ax: Optional[mpl.axes.Axes] = None
    mesh_shade: bool = False
    non_view_axes3d: Literal["hide", "show", "fade"] = "hide"

    depth_coloring: bool = False
    depth_scale: bool = True

Convert single color to color palette.

Source code in navis/plotting/dd.py
855
856
857
858
859
860
861
def color_to_cmap(color):
    """Convert single color to color palette."""
    color = mcl.to_rgb(color)

    colors = [[color[0], color[1], color[2], 0], [color[0], color[1], color[2], 1]]

    return mcl.LinearSegmentedColormap.from_list("Palette", colors, N=256)

Project points using a projection matrix.

This was previously done using the analagous function mpl_toolkits.mplot3d.proj3d.proj_points but that is deprecated.

Source code in navis/plotting/dd.py
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
def proj_points(points, M):
    """Project points using a projection matrix.

    This was previously done using the analagous function
    mpl_toolkits.mplot3d.proj3d.proj_points but that is deprecated.
    """
    xs, ys, zs = zip(*points)
    vec = np.array([xs, ys, zs, np.ones_like(xs)])

    vecw = np.dot(M, vec)
    w = vecw[3]
    # clip here..
    txs, tys, tzs = vecw[0] / w, vecw[1] / w, vecw[2] / w

    return np.column_stack((txs, tys, tzs))

Turn lists of node IDs into coordinates.

Will use navis-fastcore if available.

PARAMETER DESCRIPTION
x
        Must contain the nodes

TYPE: TreeNeuron

node_colors
        A color for each node in `x.nodes`. If provided, will
        also return a list of colors sorted to match coordinates.

TYPE: numpy.ndarray DEFAULT: None

modifier
        Use e.g. to modify/invert x/y/z axes.

TYPE: ints DEFAULT: (1, 1, 1)

RETURNS DESCRIPTION
coords

[(x, y, z), (x, y, z), ... ]

TYPE: list of tuples

colors

If node_colors provided will return a copy of it sorted to match coords.

TYPE: list of colors

Source code in navis/plotting/plot_utils.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def segments_to_coords(
    x: core.TreeNeuron,
    modifier: Optional[Tuple[float, float, float]] = (1, 1, 1),
    node_colors: Optional[np.ndarray] = None,
) -> List[np.ndarray]:
    """Turn lists of node IDs into coordinates.

    Will use navis-fastcore if available.

    Parameters
    ----------
    x :             TreeNeuron
                    Must contain the nodes
    node_colors :   numpy.ndarray, optional
                    A color for each node in `x.nodes`. If provided, will
                    also return a list of colors sorted to match coordinates.
    modifier :      ints, optional
                    Use e.g. to modify/invert x/y/z axes.

    Returns
    -------
    coords :        list of tuples
                    [(x, y, z), (x, y, z), ... ]
    colors :        list of colors
                    If `node_colors` provided will return a copy of it sorted
                    to match `coords`.

    """
    colors = None

    if utils.fastcore is not None:
        if node_colors is None:
            coords = utils.fastcore.segment_coords(
                x.nodes.node_id.values,
                x.nodes.parent_id.values,
                x.nodes[["x", "y", "z"]].values,
            )
        else:
            coords, colors = utils.fastcore.segment_coords(
                x.nodes.node_id.values,
                x.nodes.parent_id.values,
                x.nodes[["x", "y", "z"]].values,
                node_colors=node_colors,
            )
    else:
        # Using a dictionary here is orders of manitude faster than .loc[]!
        locs: Dict[int, Tuple[float, float, float]]
        # Oddly, this is also the fastest way to generate the dictionary
        nodes = x.nodes
        locs = {
            i: (x, y, z)
            for i, x, y, z in zip(
                nodes.node_id.values, nodes.x.values, nodes.y.values, nodes.z.values
            )
        }  # type: ignore
        # locs = {r.node_id: (r.x, r.y, r.z) for r in x.nodes.itertuples()}  # type: ignore
        coords = [np.array([locs[tn] for tn in s]) for s in x.segments]

        if not isinstance(node_colors, type(None)):
            ilocs = dict(zip(x.nodes.node_id.values, np.arange(x.nodes.shape[0])))
            colors = [node_colors[[ilocs[tn] for tn in s]] for s in x.segments]

    modifier = np.asarray(modifier)
    if (modifier != 1).any():
        for seg in coords:
            np.multiply(seg, modifier, out=seg)

    if colors is not None:
        return coords, colors

    return coords

Return pairs of child->parent node coordinates.

PARAMETER DESCRIPTION
x
    Must contain the nodes.

TYPE: TreeNeuron

modifier
    Use to modify/invert x/y/z axes.

TYPE: ints DEFAULT: (1, 1, 1)

RETURNS DESCRIPTION
coords

[[[x1, y1, z1], [x2, y2, z2]], [[x3, y3, y4], [x4, y4, z4]]]

TYPE: np.array

Source code in navis/plotting/plot_utils.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def tn_pairs_to_coords(
    x: core.TreeNeuron, modifier: Optional[Tuple[float, float, float]] = (1, 1, 1)
) -> np.ndarray:
    """Return pairs of child->parent node coordinates.

    Parameters
    ----------
    x :         TreeNeuron
                Must contain the nodes.
    modifier :  ints, optional
                Use to modify/invert x/y/z axes.

    Returns
    -------
    coords :    np.array
                `[[[x1, y1, z1], [x2, y2, z2]], [[x3, y3, y4], [x4, y4, z4]]]`

    """
    if not isinstance(modifier, np.ndarray):
        modifier = np.array(modifier)

    nodes = x.nodes[x.nodes.parent_id >= 0]

    tn_co = nodes.loc[:, ["x", "y", "z"]].values
    parent_co = (
        x.nodes.set_index("node_id").loc[nodes.parent_id.values, ["x", "y", "z"]].values
    )

    coords = np.append(tn_co, parent_co, axis=1)

    if any(modifier != 1):
        coords *= modifier

    return coords.reshape((coords.shape[0], 2, 3))

Update axis bounds and remove default points (0,0,0) and (1,1,1).

Source code in navis/plotting/dd.py
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
def update_axes3d_bounds(ax):
    """Update axis bounds and remove default points (0,0,0) and (1,1,1)."""
    # Collect data points present in the figure
    points = []
    for c in ax.collections:
        if isinstance(c, Line3DCollection):
            for s in c._segments3d:
                points.append(s)
        elif isinstance(c, Poly3DCollection):
            points.append(c._vec[:3, :].T)
        elif isinstance(c, (Path3DCollection, Patch3DCollection)):
            points.append(np.array(c._offsets3d).T)

    if not len(points):
        return

    points = np.vstack(points)

    # If this is the first set of points, we need to overwrite the defaults
    # That should happen automatically but for some reason doesn't for 3d axes
    if not getattr(ax, "had_data", False):
        mn = points.min(axis=0)
        mx = points.max(axis=0)
        new_xybounds = np.array([[mn[0], mn[1]], [mx[0], mx[1]]])
        new_zzbounds = np.array([[mn[2], mn[2]], [mx[2], mx[2]]])
        ax.xy_dataLim.set_points(new_xybounds)
        ax.zz_dataLim.set_points(new_zzbounds)
        ax.xy_viewLim.set_points(new_xybounds)
        ax.zz_viewLim.set_points(new_zzbounds)
        ax.had_data = True
    else:
        ax.auto_scale_xyz(
            points[:, 0].tolist(),
            points[:, 1].tolist(),
            points[:, 2].tolist(),
            had_data=True,
        )