Repository: napari/napari

Issue:
Adding points on heavily-scaled data slices them on the wrong plane
## 🐛 Bug

```python
import napari
from skimage import data

image = data.binary_blobs(128, n_dim=3)[:60]

with napari.gui_qt():
    v = napari.view_image(image, scale=[0.29, 0.26, 0.26])
    pts = v.add_points(name='interactive', size=1, ndim=3)
    pts.mode = 'add'
    v.dims.set_point(0, 30 * 0.29)  # middle plane
```

Clicking on points adds them on the right plane, but they are not shown. One must slice several planes lower to see them. When you switch to 3D mode, they appear in the middle plane correctly.



Discussion:
That doesn't sound good. I'll check this out later, but curious if the following would behave better

```python
from skimage import data

image = data.binary_blobs(128, n_dim=3)[:60]

with napari.gui_qt():
    v = napari.view_image(image, scale=[0.29, 0.26, 0.26])
    pts = v.add_points(name='interactive', size=1, ndim=3, scale=[0.29, 0.26, 0.26])
    pts.mode = 'add'
    v.dims.set_point(0, 30 * 0.29)  # middle plane
```
I have a similar poblem with non-rescaled data, but I never got around to test or make an issue. If you have a 3D points layer only and add points to some 2D slice, sometimes after adding 2 or 3 points they stop appearing. However, switching to 3D reveals that they were added correctly. I could not tell when or why this happened.
I think I found the issue: the discrepancy happens when we compressing, in this particular example (0.29, 0.26. 0.26), the data is sliced at 9 while indices are sliced at 8 for plane 30, and data is sliced at 8 for plane 26, 27, 28, and 29, hence the mismatch.

code related: https://github.com/napari/napari/blob/1d784cf8373495d9591594b6dd6ac479d5566ed1/napari/layers/base/base.py#L628-L629

I am looking into 
one potential partial solution: https://github.com/napari/napari/blob/1d784cf8373495d9591594b6dd6ac479d5566ed1/napari/layers/points/points.py#L1523-L1525

Change the assignment to also use np round: `data = np.round(self.data[:, not_disp]).astype('int')`
This would make sure that the points are displayed when they should be displayed, however, this does not address the issue where points are displayed when they are not supposed to (coordinates compressed to match multiple planes)

Resolving patch:
diff --git a/napari/layers/base/base.py b/napari/layers/base/base.py
--- a/napari/layers/base/base.py
+++ b/napari/layers/base/base.py
@@ -625,8 +625,9 @@ def _slice_indices(self):
 
         world_pts = [self._dims_point[ax] for ax in self._dims_not_displayed]
         data_pts = slice_inv_transform(world_pts)
-        # A round is taken to convert these values to slicing integers
-        data_pts = np.round(data_pts).astype(int)
+        if not hasattr(self, "_round_index") or self._round_index:
+            # A round is taken to convert these values to slicing integers
+            data_pts = np.round(data_pts).astype(int)
 
         indices = [slice(None)] * self.ndim
         for i, ax in enumerate(self._dims_not_displayed):
diff --git a/napari/layers/points/points.py b/napari/layers/points/points.py
--- a/napari/layers/points/points.py
+++ b/napari/layers/points/points.py
@@ -379,6 +379,7 @@ def __init__(
         self._drag_box_stored = None
         self._is_selecting = False
         self._clipboard = {}
+        self._round_index = False
 
         with self.block_update_properties():
             self._edge_color_property = ''
@@ -1520,7 +1521,7 @@ def _slice_data(
                 slice_indices = np.where(matches)[0].astype(int)
                 return slice_indices, scale
             else:
-                data = self.data[:, not_disp].astype('int')
+                data = self.data[:, not_disp]
                 matches = np.all(data == indices[not_disp], axis=1)
                 slice_indices = np.where(matches)[0].astype(int)
                 return slice_indices, 1


Test patch:
diff --git a/napari/_qt/_tests/test_qt_viewer.py b/napari/_qt/_tests/test_qt_viewer.py
--- a/napari/_qt/_tests/test_qt_viewer.py
+++ b/napari/_qt/_tests/test_qt_viewer.py
@@ -289,3 +289,15 @@ def test_qt_viewer_data_integrity(make_test_viewer, dtype):
     viewer.dims.ndisplay = 2
     datamean = viewer.layers[0].data.mean()
     assert datamean == imean
+
+
+def test_points_layer_display_correct_slice_on_scale(make_test_viewer):
+    viewer = make_test_viewer()
+    data = np.zeros((60, 60, 60))
+    viewer.add_image(data, scale=[0.29, 0.26, 0.26])
+    pts = viewer.add_points(name='test', size=1, ndim=3)
+    pts.add((8.7, 0, 0))
+    viewer.dims.set_point(0, 30 * 0.29)  # middle plane
+    layer = viewer.layers[1]
+    indices, scale = layer._slice_data(layer._slice_indices)
+    np.testing.assert_equal(indices, [0])