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 mechanism for inheritance of affine, rotate, scale, shear, translate #6827

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

Czaki
Copy link
Collaborator

@Czaki Czaki commented Apr 11, 2024

References and relevant issues

extracted from #6780

Description

This PR infroduces:

  1. SampleLayer - simple layer to simplify and speedup testing that does not depend on given layer type. It is minimum class inheriting from Layer
  2. add parameters_with_default_values property to Layer and LayerList to hold information which properties have default values.
  3. inheritance for affine, rotate, scale, translate (I need to dive deeper in shear).
  4. Allow set item in TransformChain by name, not index (I may extract it to separate PR).

This PR adds a basic test. It will be improved If the whole shape looks good.

@Czaki Czaki added this to the 0.5.0 milestone Apr 11, 2024
@Czaki Czaki added the feature New feature or request label Apr 11, 2024
@github-actions github-actions bot added tests Something related to our tests qt Relates to qt labels Apr 11, 2024
@Czaki Czaki marked this pull request as draft April 11, 2024 12:46
@Czaki Czaki changed the title Add mechanism for inheritance of affine, rotate, scale', shear, translate` Add mechanism for inheritance of affine, rotate, scale, shear, translate Apr 11, 2024
@brisvag
Copy link
Contributor

brisvag commented Apr 11, 2024

Could you explain a bit the background of this feature, what problem it tries to solve?

@Czaki
Copy link
Collaborator Author

Czaki commented Apr 11, 2024

Could you explain a bit the background of this feature, what problem it tries to solve?

If one layer does not have some parameters set, then try to guess them to keep layers in same coordinate space.

For example:

Copy link

codecov bot commented Apr 16, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.49%. Comparing base (36bb0d3) to head (7c43158).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6827      +/-   ##
==========================================
+ Coverage   92.45%   92.49%   +0.04%     
==========================================
  Files         617      618       +1     
  Lines       55161    55484     +323     
==========================================
+ Hits        50998    51319     +321     
- Misses       4163     4165       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Czaki Czaki marked this pull request as ready for review May 2, 2024 18:44
@@ -44,7 +44,7 @@ def test_scaled_and_translated_images():
viewer = ViewerModel()
np.random.seed(0)
data = np.random.random((10, 10, 10))
viewer.add_image(data)
Copy link
Contributor

Choose a reason for hiding this comment

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

These changes worry me a bit; does this mean that existing layers will have their transforms changed by newly added layers? I feel like it should only work the other way around.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. Currently, it works both way. I try to get feedback about this for 3 weeks. I'm not sure which option is better.

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 once a layer's in the viewer, I'd be confused if it suddenly changed. The other way around is more likely to be the desired behaviour, even though mostly from pressing the new layer buttons in the gui. Ther are other cases (like the CSV example above) where this would be nice when coming from readers, but honestly I'd prefer the expplicit action from #6864.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In contradiction, when you have bidirectional inheritance data presentation do not depend on load order. And you could assume that neither non of layer has set given property or all layers has given property.

So user will not ask, why loading image before annotation is important for proper presentation.

Once there is the ability to copy property from gui it is not so important.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair point... Given this, I think even more I'd prefer the explicit copy.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But what with axis_labels and units (planned for #6780) it is impossible to do it without such sort of inheritance.

Or maybe I should limit inheritance only to scale and assume that the rest should be done via right click?

Copy link
Member

Choose a reason for hiding this comment

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

I try to get feedback about this for 3 weeks.

At the community meeting 3 weeks ago I was also quite apprehensive about modifying existing layers. As we discussed then, I do agree with the converse concern, that it's somewhat icky if layer-addition operations are not commutative. On the other, it's easily comprehensible (new layers inherit from existing layers), and it gives users an easy pathway to getting either result — all layers get the metadata, or only the layers with explicit metadata get metadata.

Given napari's GUI-only use cases, I can relax my general "let's not do magic" policy to "all magic is dark magic". Layer metadata inheritance is magic, so some ickyness is inevitable. I think my and Lorenzo's instincts suggest that the non-commutative ickyness is preferable to the existing-layer-overwriting ickyness.

It's also a bit of a red flag when many existing tests need to be modified to work correctly. By removing the backwards effect, we can reduce the number of changes we need to make in this PR.

I also might suggest that we should only do magic in the GUI and not in the API. But I'm less sure about this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I also might suggest that we should only do magic in the GUI and not in the API. But I'm less sure about this.

I do not know how to distinguish it, especially in context of plugins.

Given napari's GUI-only use cases, I can relax my general "let's not do magic" policy to "all magic is dark magic". Layer metadata inheritance is magic, so some ickyness is inevitable. I think my and Lorenzo's instincts suggest that the non-commutative ickyness is preferable to the existing-layer-overwriting ickyness.

What with units and axes labels? There need to be bidirectional inheritance?

As I think now, I think that scale, units and axis labels should be bidirectional. Translate, shear single directional and affine only using manual coping. What did you think?

Copy link
Member

Choose a reason for hiding this comment

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

I do not know how to distinguish it, especially in context of plugins.

Treat viewer.open and viewer._add_from_layer_data_tuple (I forget what it's called) differently from viewer.add_*? Maybe better is to distinguish Actions from API calls? Maybe just Qt events (drop, file -> open, etc)?

Those are just vague ideas — I did say I was less sure. 😅 Happy to skip this.

What with units and axes labels? There need to be bidirectional inheritance?

Does there? Once we start setting the viewer/canvas axes from the union of the layer data, maybe. But again, maybe not? If we provide a way for the user to get the result (in fact two: add the layers in a different order, or copy the metadata manually), then they can fix their issue. The converse is not true in the case where we incorrectly inherit.

As I think now, I think that scale, units and axis labels should be bidirectional. Translate, shear single directional and affine only using manual coping. What did you think?

It sounds messy and complicated compared to unidirectional for everything. I'm getting a headache re logic flow just thinking about it. 😂 And I don't like the inconsistency between attributes, especially between scale and translate.

Even manual copy for everything is starting to sound nice, just from the perspective of code maintenance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Treat viewer.open and viewer._add_from_layer_data_tuple (I forget what it's called) differently from viewer.add_*? Maybe better is to distinguish Actions from API calls? Maybe just Qt events (drop, file -> open, etc)?

viewer.open and viewer._add_from_layer_data_tuple are using viewer.add_* internally.

Limiting to Qt events is impossible, because of functional plugins.

Does there? Once we start setting the viewer/canvas axes from the union of the layer data, maybe. But again, maybe not? If we provide a way for the user to get the result (in fact two: add the layers in a different order, or copy the metadata manually), then they can fix their issue. The converse is not true in the case where we incorrectly inherit.

So what do if one layer have units, and the second do not have, but they have the same axes names? By axes names they should be presented in the same coordinate space, but it is impossible for non-consistent units.

Even manual copy for everything is starting to sound nice, just from the perspective of code maintenance.

And from the perspective of user, instead of just load layer, or execute plugin, then there is a need to all time use right click button until all plugin authors update their plugin.

@andy-sweet
Copy link
Member

andy-sweet commented May 13, 2024

4. Allow set item in TransformChain by name, not index (I may extract it to separate PR).

I haven't taken a proper look at this PR, but as a minor bonus, this fixes the almost 3-year old #3058

In the discussion there, we suggested removing the lookup by name feature entirely, but none of us felt too strongly. Given that supporting setting by name is easy, I think the addition here is also fine. Though I'd maybe expect the functionality to be in TransformChain rather than EventedList especially as psygnal's EventedList does not support this functionality. None of this is blocking, but hopefully is useful information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request qt Relates to qt tests Something related to our tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants