Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve plotting #157

Merged
merged 55 commits into from
Aug 29, 2024
Merged

Improve plotting #157

merged 55 commits into from
Aug 29, 2024

Conversation

schlegelp
Copy link
Collaborator

@schlegelp schlegelp commented Aug 27, 2024

This PR represents a medium-sized rework of the plotting system.

First up the unpleasant part as there are a few (minor) breaking changes:

  • the synapse_layout parameter was renamed to cn_layout (matching e.g. other parameters such as cn_colors)
  • negative views in navis.plot2d (e.g. view=("x", "-z")) are now implemented by inverting axes rather than changing the underlying data
  • minimum version of matplotlib is now 3.9 (was 3.6)
  • the plotly backend is not part of a minimal install anymore (still installed with navis[all])
  • the Vispy backend is now marked as deprecated and will be removed in a future release (unless anybody complains that is)

Now to the fun bits:

Octarine

This PR adds Octarine as the default backend for 3d plotting from terminal (it also works in Jupyter). With it comes a smart(er) backend selection where we check which backends are available and choose the nominally "best". For non-Jupyter the order is: Octarine > Vispy > plotly. For Jupyter the order is currently plotly > Octarine > k3d. The latter may still change since this PR also makes the internal navis.Viewer Jupyter-capable.

pip install navis[all] will now install all supported backends. The minimal install pip install navis installs only matplotlib.

Plot 2D style

The default style for plot2d() has been a bit of an eyesore and I noticed that in my own code I often heavily tinker with the defaults. With this PR we're changing the defaults so we get nice looking plots off the bat. Here are two examples:

import navis
nl = navis.example_neurons()
fig, ax = navis.plot2d(nl, method='2d', view=('x', '-z'))

plot2d_comp

import navis
nl = navis.example_neurons()
fig, ax = navis.plot2d(nl, method='3d', view=('x', '-z'))

plot2d_comp3d

These are the major changes:

  • radius=True by default (quietly ignored if skeleton has no radius)
  • axes visible by default
  • meshes not shaded by default (see new mesh_shade parameter)
  • the view parameter now also works with methods 3d and 3d_complex (previously it was quietly ignored)

To account for the new radius=True default, the color_by and shade_by parameters should now also work when plotting skeletons with radius=True (i.e. we internally map the skeleton node colors onto the derived mesh's vertices).

Parameter validation

Previously, both plot2d and plot3d accepted a vague number of keyword arguments, some of which were documented while others were not. Both plotting functions still accept keyword arguments but this PR introduces Python data classes to validate parameters. Here is a quick example of what this looks like:

@dataclass
class Matplotlib2dSettings(BasePlottingSettings):
    """Additional plotting parameters for Matplotlib 2d backend."""

    method: Literal["2d", "3d", "3d_complex"] = "2d"
    group_neurons: bool = False
    autoscale: bool = True
    orthogonal: bool = True
    ...

settings = Matplotlib2dSettings().update_settings(**kwargs)

Advantages:

  • they are fully type-hinted which should help development (@clbarnes will appreciate that)
  • a single location where the defaults are defined - no more kwargs.get(parameter, default=XXX) in multiple locations
  • trying to initialize the class with an invalid (i.e. non-existent) parameter will throw an exception with a hopefully helpful message for the user (this could still be improved)

It's still not perfect because some features are backend-specific but definitely better than before.

Misc

  • the legendgroup parameter (plot3d with plotly backend) now also sets the legend group's title (@Robbie1977)
  • flybrains and cloud-volume are now treated as "specialised" dependency, meaning they aren't automatically installed with navis[all] (note to self: this may be an issue with e.g. the neuprint interface but neuprint-python is also not installed by default)

TODOs

  • update tutorials
  • add optional axes to all backends (?)

- avoid using pandas indexing (=faster)
- new parameter `radius_scale_factor` to scale radii
- add `.vertex_map` property to resulting MeshNeuron
- plot2d: invert plot axes instead of data to match negative view ("-x")
- meshes: use PolyCollection instead of Polygon+PatchCollection
- update docstrings
- use dataclasses to validate plotting settings
- define depth colormap via global variable
- change defaults:
  - radius=True
  - alpha=1
  - figsize=None (i.e. use mpl default size)
- rename parameters:
  - synapse_layout -> cn_layout
- new parameters:
  - mesh_shade (default False)
  - non_view_axes3d (default = "hide")
- make `view` parameter work with methods "3d" and "3d_complex"
- shade/color_by now works with skeletons when radius=True
- loose a bunch of unnecessary ballast
- change style:
  - axis on by default
- bump matplotlib minimum version 3.6 -> 3.7
- add octarine as default backend
- use dataclasses to validate and pass around plotting parameters
- update docstrings
- change defaults:
  - radius=True (ignored if skeleton has no radius)
- actually respect `cn_size`
- `legendgroup` now also used as title for plotly backend
- navis.Viewer:
  - `show()` now works properly in Jupyter
  - new .`size` property, including setter
- a bunch of code formatting
- bump minimum matplotlib version 3.7 -> 3.9 (for add_collection3d)
- make plotly an extra dependency
- add octarine as extra dependency
- add k3d as extra dependency
- install jupyter_rfb alongside vispy (if requested)
@schlegelp
Copy link
Collaborator Author

schlegelp commented Aug 28, 2024

As expected, adding a new backend has played havoc with the test suite but all tests are passing now. Visualisation-related testing is still a bit of a hack and relies mostly on the tutorial notebooks. For example, not every functionality is tested with every backend. I don't think I want to address that in this PR.

A couple more test-related notes:

  1. This PR drops "no igraph" from the matrix since igraph is an implicit hard dependency through skeletor by now
  2. I ended up having to skip the test_lookupdistdotbuilder_builds tests because they started hanging for no good reason. We've had issues with those and the use of threads in the past. Need to investigate.

@schlegelp schlegelp marked this pull request as ready for review August 28, 2024 18:23
@schlegelp schlegelp merged commit 39df866 into master Aug 29, 2024
21 checks passed
@schlegelp schlegelp deleted the pygfx2 branch August 29, 2024 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant