napari / napari

napari: a fast, interactive, multi-dimensional image viewer for python
https://napari.org
BSD 3-Clause "New" or "Revised" License
2.07k stars 410 forks source link

When using 4D data for a mesh an IndexError happens when the selected Shape layer doesn't have any points at the current frame. #6870

Closed odinsbane closed 1 day ago

odinsbane commented 3 weeks ago

🐛 Bug Report

I created two meshes that have 4 dimensions, [t, z, y, x]. The time slider can be used to change mesh vertexes that are shown. If the mesh in the layers selection pane doesn't have any points in the current time frame then an IndexError is thrown.

Everything still appears ok, but the error is annoying.

File ~/linux-desktop/napari/napari-venv/lib/python3.10/site-packages/napari/layers/surface/surface.py:677, in Surface._get_value_3d(self=<Surface layer 'Test'>, start_point=<class 'numpy.ndarray'> (4,) float64, end_point=<class 'numpy.ndarray'> (4,) float64, dims_displayed=[1, 2, 3])
    670 start_position, ray_direction = nd_line_segment_to_displayed_data_ray(
    671     start_point=start_point,
    672     end_point=end_point,
    673     dims_displayed=dims_displayed,
    674 )
    676 # get the mesh triangles
--> 677 mesh_triangles = self._data_view[self._view_faces]
        self._data_view = <class 'numpy.ndarray'> (4, 3) int64
        self._view_faces = <class 'numpy.ndarray'> (0, 3) float64
        self = <Surface layer 'Test' at 0x7f5c0452f5b0>
    679 # get the triangles intersection
    680 intersection_index, intersection = find_nearest_triangle_intersection(
    681     ray_position=start_position,
    682     ray_direction=ray_direction,
    683     triangles=mesh_triangles,
    684 )

IndexError: arrays used as indices must be of integer (or boolean) type

As can be seen, self._view_faces is a float64. This only occurs if the select layer has no points in the current frame.

💡 Steps to Reproduce

Here is an example. I was updating the nD_surface.py example

import numpy as np

import napari

# create the viewer and window
viewer = napari.Viewer(ndisplay=3)

def triangles( t ):
    o = t*4;

    data = [[t, 0, 0, 0], [t, 0, 20, 10], [t, 10, 0, -10], [t, 10, 10, -10]]
    faces = [[0 + o, 1 + o, 2 + o], [1 + o, 2 + o, 3 + o]]
    return data, faces

data = []
faces = []
for t in range(10):
    di, fi = triangles( t )
    data += di
    faces += fi
data = np.array(data)
faces = np.array(faces)
values = np.linspace(0, 1, len(data))

# add the surface
layer = viewer.add_surface((data, faces, values))

d2, f2 = triangles(0)
l2 = viewer.add_surface(
    (np.array(d2) + 1, np.array(f2), np.linspace(0, 1, len(d2))) )
l2.name = "Test"

if __name__ == '__main__':
    napari.run()

That will show errors until either, the slider is moved to timepoint 1 and "Test" has points in the frame, or the other layer is selected.

💡 Expected Behavior

I do not expect any errors to be thrown.

🌎 Environment

napari version 0.4.19.post1 WSL2 Ubuntu on Windows 10

Installed through python3 -m venv napari-env then ./naprai-env/bin/pip install napari[all]

I can provide more details.

I also clones the main branch, and installed it into the same environment using pip install -e napari-clone/ and I have the same results.

💡 Additional Context

I can see the issue in the current main when there are no faces being shown self._view_faces = np.zeros((0, 3)) causes self._view_faces to become a float64. This can be fixed by changing it to self._view_faces = np.zeros((0, 3), dtype=int) although maybe the real issue is why it only happens if the layer is selected in the layers panel.

jni commented 1 day ago

Fixed by #6874