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

Add NAP for multicanvas #249

Merged
merged 9 commits into from
Jan 31, 2024
Merged

Add NAP for multicanvas #249

merged 9 commits into from
Jan 31, 2024

Conversation

aganders3
Copy link
Contributor

Particular thanks to @brisvag and @melonora for previous work and discussions on this, and for agreeing to be co-authors. I am happy to absorb any criticism and direct praise their way 😄.

Thanks also to @andy-sweet and @Czaki, both of whom provided helpful discussion while I worked on this NAP.

Of course I expect many changes to this and look forward to feedback.

@github-actions github-actions bot added the documentation Improvements or additions to documentation label Oct 13, 2023
Copy link
Member

@andy-sweet andy-sweet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a first pass through this and left a few comments. Overall, I'm very excited to see something like this get approval and get implemented!

* Specific UI implementations will be explored as part of this work, but I expect UX and UI will be formalized later (possibly in a separate NAP).
* Supporting alternative frontend (Qt) and backend (Vispy) frameworks. While this work should not make such tasks more difficult in the future, explicit consideration is out-of-scope until further progress is made in these areas.
* [Non-goals also in NAP-3](https://napari.org/dev/naps/3-spaces.html#non-goals) are related but also considered out-of-scope here
* Separation of rendering information from the Layer(Data) model
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this benefit the work here or do you think of it as mostly orthogonal? Would a simultaneous effort help or hinder progress here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think layergroups here could potentially provide a cleaner API, but overall the API is similar to current grid mode at least for multichannel view, thus could be an orthogonal effort.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's entirely orthogonal, but perhaps mostly. I think it depends on the scope of features we want. For example just below I propose for all canvases to share a common layer list, with independent slicing, camera, dimensionality, and layer visibility. I think this would be more relevant if supporting (for example) different colormaps for the same data in two canvases.

* `ViewerModel` will own a *list* of `CanvasModel` objects
* `QtViewer` will own a *list* of `VispyCanvas` objects
* `VispyLayer` subclasses will hold references to their `Layer` (for rendering information) as well as a `LayerSlice` (for data)
* The LayerSlicer will need to update the sync and async callbacks[^async-only] to pass a canvas parameter where the resulting sliced data will be stored, rather than storing it on the layer itself. Slice task cancellation logic will need to be revisited accordingly.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One alternative here that came to mind is that each canvas (or maybe vispy canvas) could own its own layer slicer - that way you don't need to pass in the extra canvas parameter and you can do cancellation per canvas (which I think is desirable).

With lots of canvas, you might introduce too much concurrency, but in most cases, maybe that won't be too bad. And we may be able to serialize it with a shared resource/controller if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - that's an interesting idea I hadn't considered. I was initially thinking the current LayerSlicer may expand to use a pool instead of a single thread.

I think you're right passing the full canvas is probably more than necessary, and trying to pass a canvas ID or something feels a bit hacky to me. One benefit of just passing the canvas object is that it would bring with it the camera, which might be useful for finding corner_pixels without waiting for the draw.


> Note: in both diagrams, the `on_draw()` method on the `VispyCanvas` breaks MVC convention (view layer talks directly to the model layer). This is a separate/known issue and I believe is mostly only true for multiscale image layers right now. Changing this is considered out of scope for this project at this time.

Slice state for each layer is currently stored on the Layer model. Again, see [NAP-4 for previous discussion](https://napari.org/stable/naps/4-async-slicing.html#existing-slice-state). This NAP proposes to move this state off the Layer instance, into a specific Layer Slice instance. This is what will allow multiple slices of a single layer to be visualized simultaneously. *Table 1* lists the attributes related to slice state that will be moved in this work from each Layer class into corresponding Layer Slice classes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I misread the out-of-scope section, but my impression was that moving slice state off the layer was a non-goal - is that wrong?

Or maybe this is the proposed approach that incidentally achieves that non-goal?

Anyway, I'm generally in favor of that move, but just wanted to clarify the proposal and intent here.

Copy link
Contributor Author

@aganders3 aganders3 Oct 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean this non-goal?

Normalizing slice data for different layer types, though this may benefit in the course of this work.

That's not really what I meant there, but also I think it's ambiguous enough warrant changing or removing. I was trying to say it is a non-goal to create a uniform/generic concept (or Protocol) of a "slice" - it's okay for the slices to be quite specific for each layer type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I meant this one.

Separation of rendering information from the Layer(Data) model

But maybe you meant that only with respect to NAP-3.

Anyway, I also agree that a generic slice class/protocol should be a non-goal. Though we might consider during/after.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see - I will try to make this more clear. The goal here is indeed to move the slice state off the layer. The non-goal was meant to indicate that the remaining attributes of the layer will not be separated. So for example this work does not necessarily make it easier to share data between two layers with different colormaps.


## Implementation

1. Introduce minimally disruptive `_canvases` attribute on the ViewerModel
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fully in support of this ordering where implementation of architecture changes happen first to support new functionality later.

One mistake on the async slicing project is that I didn't do enough to prioritize things that way, which meant that things didn't end up quite as I'd hoped.

docs/naps/8-multiple-canvases.md Outdated Show resolved Hide resolved
docs/naps/8-multiple-canvases.md Outdated Show resolved Hide resolved
> [name=Ashley A]

### UI Design and Architecture
Specific UI design and architecture remains to be determined. This will be explored as part of step 4 in the [Implementation Plan](#Implementation). UI design needs additional refinement and exploration, and this is expected to continue after basic/core implementation propsed in this NAP is complete. UI changes may also be described in a separate NAP along with a discussion of convenience functions and affordances for common operations. Some placeholder or experimental code will be used in the meantime as a prototype implementation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any prototype code available to look at and play with? And/or some videos showing some of the proposals here at least kind-of in action?

Both could help to ground some of the proposals and discussion here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My prototype/experimentation code is pretty early, but is here:
aganders3/napari#13

> Note: some layers include "seleciton" information (Points, Shapes, and Tracks). How selection is handled in multicanvas view is TBD at this point.

***Table 1** - Layer attributes that hold slice data. These attributes will be moved from the Layer onto individual Layer Slice objects (one Layer Slice per Layer per Canvas).*
> TODO: make this a list or figure out a way to center it [name=Ashley A]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I regretted adding something like this to the async slicing NAP, because it's just too much detail, so another option might be to remove it from the main NAP document and maybe link to it or shove it into an appendix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found it helpful to write this out, but if it's too detailed I'm happy to move or remove it. It might just fit better in a PR with actual code changes.

Co-authored-by: Andy Sweet <[email protected]>
@aganders3
Copy link
Contributor Author

I took a first pass through this and left a few comments. Overall, I'm very excited to see something like this get approval and get implemented!

Thanks! I appreciate the support and feedback.

Czaki
Czaki previously requested changes Oct 14, 2023
Copy link
Contributor

@Czaki Czaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The number 8 is for telemetry (#226), also please add a new NAP to the table of contents.

@brisvag brisvag requested a review from Czaki October 19, 2023 11:47
Copy link
Contributor

@brisvag brisvag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, great work on the NAP! Other than comments and clarifications here and there, I have one main disagreement, and it seems it comes from a misunderstanding when we discussed before, because I thing this is quite important. You say:

* All canvases shall share a common layer list and (unsliced) layer data.

I think we need to ensure from the get-go that canvases can hold different layer lists, but can also share some layers. This is key for example for applications like highly multiplexed data (@melonora can add some more specifics here if needed). I fear that leaving this out will make it harder to support it afterwards.

Anyways, approving as draft as per normal NAP procedure :)

docs/naps/9-multiple-canvases.md Show resolved Hide resolved
docs/naps/9-multiple-canvases.md Outdated Show resolved Hide resolved
docs/naps/9-multiple-canvases.md Outdated Show resolved Hide resolved
docs/naps/9-multiple-canvases.md Outdated Show resolved Hide resolved
* The application shall natively display multiple canvases simultaneously.
* There shall be a minimum of one canvas (current status) per viewer.
* There shall be a maximum of 8 canvases (tentative) per viewer.
* All canvases shall share a common layer list and (unsliced) layer data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my only disagreement with the current form of the NAP, I wrote more details in the general review comment.

docs/naps/9-multiple-canvases.md Show resolved Hide resolved
docs/naps/9-multiple-canvases.md Outdated Show resolved Hide resolved
Comment on lines +169 to +171
* What kind of cross-reference displays or tools should there be?
* through-plane slice indicators
* three-point slice definition
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to do unless we assume a shared space between canvases, which I'm not sure we should do. Initially, I think we should leave this to the user.

Creating an orthoview example as part of this would would be a great way to test these ideas.

docs/naps/9-multiple-canvases.md Outdated Show resolved Hide resolved
docs/naps/9-multiple-canvases.md Outdated Show resolved Hide resolved
@aganders3
Copy link
Contributor Author

Thanks for the review and great comments @brisvag! I will try to respond to each and update accordingly, but generally your comments cleared up a few things for me. We're not too far apart on anything here :).

@psobolewskiPhD psobolewskiPhD added this to the 0.4.19 milestone Oct 26, 2023
@aganders3
Copy link
Contributor Author

I updated a bit but not ready for your re-review @brisvag. I should be able to finish addressing your comments next week, but you can ignore GitHub notifications for now :)

@aganders3
Copy link
Contributor Author

I have one main disagreement, and it seems it comes from a misunderstanding when we discussed before, because I thing this is quite important. You say:

  • All canvases shall share a common layer list and (unsliced) layer data.

I think we need to ensure from the get-go that canvases can hold different layer lists, but can also share some layers. This is key for example for applications like highly multiplexed data (@melonora can add some more specifics here if needed). I fear that leaving this out will make it harder to support it afterwards.

My way of addressing this use case was specifying layers would gain a per-canvas visibility setting. I think this is also where misalignment on #249 (comment) comes from; using a common layer list would also inherently share space between canvases. If you want a truly independent space, I don't see that much benefit over opening a separate viewer window.

Changing to fully independent layer lists would definitely offer more flexibility, and may actually make this implementation simpler in some ways. My concern is that it will make for a more difficult UI design, and may make it more difficult to implement certain common use cases later (for example, where you do want a common space between canvases). That said, I am definitely still open to this approach. You (and @melonora) may have a better understanding of the use-cases that would benefit.

@Czaki Czaki merged commit 7a204c0 into napari:main Jan 31, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants