arrayview 0.31.0__py3-none-any.whl → 0.31.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
arrayview/_analysis.py CHANGED
@@ -34,7 +34,15 @@ def _build_metadata(session) -> dict:
34
34
  "is_rgb": session.rgb_axis is not None,
35
35
  "has_source_file": bool(getattr(session, "filepath", None)),
36
36
  }
37
- default_dims = _session_mod._startup_dims_for_data(session.data, target_shape)
37
+ collection_spatial_ndim = getattr(session, "collection_spatial_ndim", None)
38
+ default_shape = target_shape
39
+ if collection_spatial_ndim is not None:
40
+ spatial_ndim = int(collection_spatial_ndim)
41
+ if 2 <= spatial_ndim <= len(target_shape):
42
+ # Collection axes are appended after the image dimensions. They
43
+ # select a case; they must not influence the startup plane.
44
+ default_shape = target_shape[:spatial_ndim]
45
+ default_dims = _session_mod._startup_dims_for_data(session.data, default_shape)
38
46
  if default_dims is not None:
39
47
  meta["default_dims"] = [int(default_dims[0]), int(default_dims[1])]
40
48
  if getattr(session, "spatial_meta", None) is not None:
@@ -49,7 +57,6 @@ def _build_metadata(session) -> dict:
49
57
  )
50
58
  if getattr(session, "array_keys", None):
51
59
  meta["array_keys"] = session.array_keys
52
- collection_spatial_ndim = getattr(session, "collection_spatial_ndim", None)
53
60
  if collection_spatial_ndim is not None:
54
61
  meta["collection_spatial_ndim"] = int(collection_spatial_ndim)
55
62
  if getattr(session.data, "_av_ragged", False):
@@ -115,11 +115,17 @@ def _windows_process_start(pid: int) -> str | None:
115
115
  handle = kernel32.OpenProcess(0x1000, False, pid)
116
116
  if not handle:
117
117
  return None
118
+ still_active = 259
119
+ exit_code = ctypes.c_ulong()
118
120
  creation = ctypes.c_ulonglong()
119
121
  exit_time = ctypes.c_ulonglong()
120
122
  kernel = ctypes.c_ulonglong()
121
123
  user = ctypes.c_ulonglong()
122
124
  try:
125
+ if not kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code)):
126
+ return None
127
+ if exit_code.value != still_active:
128
+ return None
123
129
  ok = kernel32.GetProcessTimes(handle, ctypes.byref(creation),
124
130
  ctypes.byref(exit_time), ctypes.byref(kernel),
125
131
  ctypes.byref(user))
arrayview/_overlays.py CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import numpy as np
6
6
 
7
7
  from arrayview._render import (
8
+ _build_mosaic_grid,
8
9
  _composite_overlay_mask,
9
10
  _extract_overlay_mask,
10
11
  _overlay_is_label_map,
@@ -36,6 +37,7 @@ def _composite_overlays(
36
37
  dim_y: int,
37
38
  idx_tuple: tuple[int, ...],
38
39
  shape_hw: tuple[int, int],
40
+ base_shape: tuple[int, ...] | None = None,
39
41
  ) -> np.ndarray:
40
42
  """Composite one or more overlay masks onto RGBA pixels."""
41
43
  if not overlay_sid_str:
@@ -62,6 +64,7 @@ def _composite_overlays(
62
64
  dim_y,
63
65
  idx_tuple,
64
66
  expected_shape=shape_hw,
67
+ base_shape=base_shape,
65
68
  )
66
69
  rgba = _composite_overlay_mask(
67
70
  rgba,
@@ -72,3 +75,76 @@ def _composite_overlays(
72
75
  outline_only=overlay_outline,
73
76
  )
74
77
  return rgba
78
+
79
+
80
+ def _composite_mosaic_overlays(
81
+ rgba: np.ndarray,
82
+ overlay_sid_str: str | None,
83
+ overlay_colors_str: str | None,
84
+ overlay_alpha: float,
85
+ overlay_alphas_str: str | None,
86
+ overlay_outline: bool,
87
+ dim_x: int,
88
+ dim_y: int,
89
+ dim_z: int,
90
+ idx_tuple: tuple[int, ...],
91
+ base_shape: tuple[int, ...],
92
+ mosaic_cols: int | None = None,
93
+ ) -> np.ndarray:
94
+ """Composite each overlay as a mosaic using the base mosaic's frame indices.
95
+
96
+ Missing overlay axes are resolved per frame by ``_extract_overlay_mask``.
97
+ That lets a mask omit, for example, an echo axis without materialising a
98
+ repeated full-volume copy.
99
+ """
100
+ if not overlay_sid_str:
101
+ return rgba
102
+ sids = [sid.strip() for sid in overlay_sid_str.split(",") if sid.strip()]
103
+ colors_raw = (
104
+ [color.strip() for color in overlay_colors_str.split(",")]
105
+ if overlay_colors_str
106
+ else []
107
+ )
108
+ alphas_raw = (
109
+ [alpha.strip() for alpha in overlay_alphas_str.split(",")]
110
+ if overlay_alphas_str
111
+ else []
112
+ )
113
+ tile_shape = (int(base_shape[dim_y]), int(base_shape[dim_x]))
114
+ n_frames = int(base_shape[dim_z])
115
+
116
+ for i, sid in enumerate(sids):
117
+ color = _parse_hex_color(colors_raw[i]) if i < len(colors_raw) else None
118
+ try:
119
+ alpha = float(alphas_raw[i]) if i < len(alphas_raw) else overlay_alpha
120
+ except ValueError:
121
+ alpha = overlay_alpha
122
+ alpha = max(0.0, min(1.0, alpha))
123
+
124
+ frames = []
125
+ for frame in range(n_frames):
126
+ frame_idx = list(idx_tuple)
127
+ frame_idx[dim_z] = frame
128
+ ov_raw = _extract_overlay_mask(
129
+ sid,
130
+ dim_x,
131
+ dim_y,
132
+ tuple(frame_idx),
133
+ expected_shape=tile_shape,
134
+ base_shape=base_shape,
135
+ )
136
+ frames.append(
137
+ ov_raw if ov_raw is not None else np.zeros(tile_shape, dtype=np.float32)
138
+ )
139
+ grid, _, _ = _build_mosaic_grid(frames, n_frames, cols=mosaic_cols)
140
+ # Gaps are transparent, not NaN label values or heatmap samples.
141
+ grid = np.nan_to_num(grid, nan=0.0)
142
+ rgba = _composite_overlay_mask(
143
+ rgba,
144
+ grid,
145
+ alpha=alpha,
146
+ is_label=_overlay_is_label_map(sid, grid),
147
+ override_color=color,
148
+ outline_only=overlay_outline,
149
+ )
150
+ return rgba
arrayview/_render.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Rendering pipeline: colormaps, LUTs, slice extraction, RGBA, mosaic, RGB, preload."""
2
2
 
3
+ import itertools
3
4
  import threading
4
5
  import time
5
6
 
@@ -527,6 +528,7 @@ def _extract_overlay_mask(
527
528
  dim_y: int,
528
529
  idx_tuple: tuple[int, ...],
529
530
  expected_shape: tuple[int, int],
531
+ base_shape: tuple[int, ...] | None = None,
530
532
  ) -> np.ndarray | None:
531
533
  """Return the raw overlay slice (float32 H×W) for compositing, or None.
532
534
 
@@ -538,27 +540,43 @@ def _extract_overlay_mask(
538
540
  if ov_session is None:
539
541
  return None
540
542
 
541
- ov_ndim = ov_session.data.ndim
542
- if dim_x >= ov_ndim or dim_y >= ov_ndim:
543
+ ov_shape = tuple(int(size) for size in ov_session.shape)
544
+ main_shape = tuple(int(size) for size in (base_shape or ov_shape))
545
+ if len(ov_shape) > len(main_shape):
543
546
  return None
544
547
 
545
- idx_candidates: list[tuple[int, ...]] = [
546
- tuple(int(idx_tuple[i]) if i < len(idx_tuple) else 0 for i in range(ov_ndim))
547
- ]
548
- if len(idx_tuple) >= ov_ndim:
549
- trailing = tuple(int(v) for v in idx_tuple[-ov_ndim:])
550
- if trailing != idx_candidates[0]:
551
- idx_candidates.append(trailing)
552
-
553
- for ov_idx in idx_candidates:
554
- try:
555
- ov_raw = extract_slice(ov_session, dim_x, dim_y, list(ov_idx))
556
- except Exception:
557
- continue
558
- if ov_raw.shape != expected_shape:
559
- continue
560
- if np.any(np.isfinite(ov_raw) & (ov_raw != 0)):
561
- return ov_raw.astype(np.float32)
548
+ # An overlay may omit one or more non-spatial dimensions from the base.
549
+ # Match its axes to equal-sized base axes, then index only those axes. This
550
+ # broadcasts the overlay over every omitted axis without allocating a tiled
551
+ # copy (important for masks next to multi-echo or multi-channel data).
552
+ map_key = (main_shape, dim_x, dim_y)
553
+ axis_maps = getattr(ov_session, "_overlay_axis_maps", None)
554
+ if axis_maps is None:
555
+ axis_maps = {}
556
+ ov_session._overlay_axis_maps = axis_maps
557
+ if map_key not in axis_maps:
558
+ axis_maps[map_key] = next(
559
+ (
560
+ base_axes
561
+ for base_axes in itertools.combinations(range(len(main_shape)), len(ov_shape))
562
+ if dim_x in base_axes
563
+ and dim_y in base_axes
564
+ and tuple(main_shape[axis] for axis in base_axes) == ov_shape
565
+ ),
566
+ None,
567
+ )
568
+ base_axes = axis_maps[map_key]
569
+ if base_axes is None:
570
+ return None
571
+ ov_dim_x = base_axes.index(dim_x)
572
+ ov_dim_y = base_axes.index(dim_y)
573
+ ov_idx = [int(idx_tuple[axis]) for axis in base_axes]
574
+ try:
575
+ ov_raw = extract_slice(ov_session, ov_dim_x, ov_dim_y, ov_idx)
576
+ except Exception:
577
+ return None
578
+ if ov_raw.shape == expected_shape:
579
+ return ov_raw.astype(np.float32)
562
580
  return None
563
581
 
564
582
 
@@ -6,7 +6,7 @@ from fastapi import Depends, Response
6
6
  from fastapi.responses import JSONResponse
7
7
 
8
8
  from arrayview._diff import _compute_diff, _diff_histogram, _render_diff_rgba
9
- from arrayview._overlays import _composite_overlays
9
+ from arrayview._overlays import _composite_mosaic_overlays, _composite_overlays
10
10
  from arrayview._render import (
11
11
  LUTS,
12
12
  _build_mosaic_grid,
@@ -178,6 +178,11 @@ def register_rendering_routes(app, *, get_session_or_404) -> None:
178
178
  vmin_override=vmin_override,
179
179
  vmax_override=vmax_override,
180
180
  )
181
+ rgba = _composite_mosaic_overlays(
182
+ rgba, overlay_sid, overlay_colors, overlay_alpha, overlay_alphas,
183
+ overlay_outline, dim_x, dim_y, dim_z, idx_tuple, session.shape,
184
+ mosaic_cols,
185
+ )
181
186
  if vmin_override is not None and vmax_override is not None:
182
187
  vmin, vmax = float(vmin_override), float(vmax_override)
183
188
  else:
@@ -251,6 +256,7 @@ def register_rendering_routes(app, *, get_session_or_404) -> None:
251
256
  dim_y,
252
257
  idx_tuple,
253
258
  rgba.shape[:2],
259
+ session.shape,
254
260
  )
255
261
  _, vmin, vmax = _prepare_display(
256
262
  session,
@@ -263,6 +269,12 @@ def register_rendering_routes(app, *, get_session_or_404) -> None:
263
269
  )
264
270
  if qmri_role:
265
271
  vmin, vmax = _qmri_adjust_vmin_vmax(vmin, vmax, qmri_role)
272
+ if dim_z >= 0 and qmri_role:
273
+ rgba = _composite_mosaic_overlays(
274
+ rgba, overlay_sid, overlay_colors, overlay_alpha, overlay_alphas,
275
+ overlay_outline, dim_x, dim_y, dim_z, idx_tuple, session.shape,
276
+ mosaic_cols,
277
+ )
266
278
  render_ms = (time.perf_counter() - render_t0) * 1000.0
267
279
  encode_t0 = time.perf_counter()
268
280
  img = _pil_image().fromarray(rgba[:, :, :3], mode="RGB")
@@ -8,7 +8,7 @@ from starlette.websockets import WebSocketDisconnect
8
8
  import arrayview._session as _session_mod
9
9
  from arrayview._analysis import _build_metadata
10
10
  from arrayview._lifecycle import release_session
11
- from arrayview._overlays import _composite_overlays
11
+ from arrayview._overlays import _composite_mosaic_overlays, _composite_overlays
12
12
  from arrayview._render import (
13
13
  LUTS,
14
14
  _prepare_display,
@@ -25,6 +25,7 @@ from arrayview._session import (
25
25
  SESSIONS,
26
26
  SHELL_SOCKETS,
27
27
  _render,
28
+ _schedule_overlay_prefetch,
28
29
  _schedule_prefetch,
29
30
  _vprint,
30
31
  wait_for_session_ready,
@@ -231,6 +232,12 @@ def register_websocket_routes(app) -> None:
231
232
  vmax_override=vmax_override,
232
233
  ),
233
234
  )
235
+ rgba = _composite_mosaic_overlays(
236
+ rgba, msg.get("overlay_sid"), msg.get("overlay_colors"),
237
+ float(msg.get("overlay_alpha", 0.45)), msg.get("overlay_alphas"),
238
+ bool(msg.get("overlay_outline", False)), dim_x, dim_y, dim_z,
239
+ idx_tuple, session.shape, mosaic_cols,
240
+ )
234
241
  h, w = rgba.shape[:2]
235
242
  raw = extract_slice(session, dim_x, dim_y, list(idx_tuple))
236
243
  _, vmin, vmax = _prepare_display(
@@ -340,6 +347,15 @@ def register_websocket_routes(app) -> None:
340
347
  dim_y,
341
348
  idx_tuple,
342
349
  (h, w),
350
+ session.shape,
351
+ )
352
+
353
+ if dim_z >= 0 and qmri_role:
354
+ rgba = _composite_mosaic_overlays(
355
+ rgba, msg.get("overlay_sid"), msg.get("overlay_colors"),
356
+ float(msg.get("overlay_alpha", 0.45)), msg.get("overlay_alphas"),
357
+ bool(msg.get("overlay_outline", False)), dim_x, dim_y, dim_z,
358
+ idx_tuple, session.shape, mosaic_cols,
343
359
  )
344
360
 
345
361
  render_ms = (time.perf_counter() - render_t0) * 1000.0
@@ -414,6 +430,16 @@ def register_websocket_routes(app) -> None:
414
430
  collection_axis,
415
431
  collection_direction,
416
432
  )
433
+ _schedule_overlay_prefetch(
434
+ session,
435
+ msg.get("overlay_sid"),
436
+ msg.get("overlay_alphas"),
437
+ dim_x,
438
+ dim_y,
439
+ list(idx_tuple),
440
+ collection_axis,
441
+ collection_direction,
442
+ )
417
443
  previous_indices = idx_tuple
418
444
  except Exception as _ws_exc:
419
445
  import traceback
arrayview/_session.py CHANGED
@@ -101,6 +101,7 @@ def _ensure_render_thread() -> None:
101
101
  # Neighbor prefetch thread pool (Phase 3)
102
102
  # ---------------------------------------------------------------------------
103
103
  _PREFETCH_POOL = None
104
+ _OVERLAY_PREFETCH_POOL = None
104
105
  _PREFETCH_LOCK = threading.Lock()
105
106
 
106
107
 
@@ -116,6 +117,19 @@ def _get_prefetch_pool():
116
117
  return _PREFETCH_POOL
117
118
 
118
119
 
120
+ def _get_overlay_prefetch_pool():
121
+ """Return the small pool reserved for collection overlay lookahead."""
122
+ global _OVERLAY_PREFETCH_POOL
123
+ with _PREFETCH_LOCK:
124
+ if _OVERLAY_PREFETCH_POOL is None or _OVERLAY_PREFETCH_POOL._shutdown:
125
+ import concurrent.futures
126
+
127
+ _OVERLAY_PREFETCH_POOL = concurrent.futures.ThreadPoolExecutor(
128
+ max_workers=2, thread_name_prefix="arrayview-overlay-prefetch"
129
+ )
130
+ return _OVERLAY_PREFETCH_POOL
131
+
132
+
119
133
  def _schedule_prefetch(session, dim_x, dim_y, idx_list, slice_dim, direction):
120
134
  """Warm raw_cache for the next PREFETCH_NEIGHBORS slices in *direction*.
121
135
 
@@ -170,6 +184,85 @@ def _schedule_prefetch(session, dim_x, dim_y, idx_list, slice_dim, direction):
170
184
  pass # pool shutting down
171
185
 
172
186
 
187
+ def _schedule_overlay_prefetch(
188
+ session,
189
+ overlay_sid_str,
190
+ overlay_alphas_str,
191
+ dim_x,
192
+ dim_y,
193
+ idx_list,
194
+ collection_axis,
195
+ direction,
196
+ ):
197
+ """Warm the next collection volume for visible overlays.
198
+
199
+ Overlay work uses its own bounded pool so a long mask decompression cannot
200
+ hold up the main image's single-worker prefetch lane. New navigation
201
+ supersedes overlay jobs that have not started loading a volume yet.
202
+ """
203
+ if not overlay_sid_str:
204
+ return
205
+
206
+ sids = [sid.strip() for sid in overlay_sid_str.split(",") if sid.strip()]
207
+ alphas = [a.strip() for a in (overlay_alphas_str or "").split(",")]
208
+ visible_sids = []
209
+ for i, sid in enumerate(sids):
210
+ try:
211
+ alpha = float(alphas[i]) if i < len(alphas) and alphas[i] else 1.0
212
+ except ValueError:
213
+ alpha = 1.0
214
+ if alpha > 0.0:
215
+ visible_sids.append(sid)
216
+ if not visible_sids:
217
+ return
218
+
219
+ target = idx_list[collection_axis] + direction
220
+ if not 0 <= target < session.shape[collection_axis]:
221
+ return
222
+
223
+ request_key = (collection_axis, target, tuple(visible_sids))
224
+ if request_key == getattr(session, "_overlay_prefetch_request", None):
225
+ return
226
+ session._overlay_prefetch_request = request_key
227
+ generation = getattr(session, "_overlay_prefetch_generation", 0) + 1
228
+ session._overlay_prefetch_generation = generation
229
+
230
+ def _warm(overlay_sid):
231
+ if generation != getattr(session, "_overlay_prefetch_generation", 0):
232
+ return
233
+ overlay = SESSIONS.get(overlay_sid)
234
+ if overlay is None or collection_axis >= len(overlay.shape):
235
+ return
236
+ overlay_stack_axes = tuple(getattr(overlay.data, "_stack_axes", ()))
237
+ if collection_axis not in overlay_stack_axes or target >= overlay.shape[collection_axis]:
238
+ return
239
+ overlay_idx = list(idx_list[: len(overlay.shape)])
240
+ overlay_idx[collection_axis] = target
241
+ # Sparse --overlay-dir collections represent a missing patient with a
242
+ # None filepath. Avoid allocating a fresh full-size zero volume merely
243
+ # to prefetch an overlay that will be shown empty.
244
+ file_matrix = getattr(overlay.data, "_file_matrix", None)
245
+ if file_matrix is not None:
246
+ modality = 0
247
+ if len(overlay_stack_axes) > 1:
248
+ modality = overlay_idx[overlay_stack_axes[1]]
249
+ if not file_matrix[target][modality]:
250
+ return
251
+ try:
252
+ from arrayview._render import extract_slice
253
+
254
+ extract_slice(overlay, dim_x, dim_y, overlay_idx)
255
+ except Exception:
256
+ pass
257
+
258
+ try:
259
+ pool = _get_overlay_prefetch_pool()
260
+ for overlay_sid in visible_sids:
261
+ pool.submit(_warm, overlay_sid)
262
+ except RuntimeError:
263
+ pass # pool shutting down
264
+
265
+
173
266
  async def _render(loop: asyncio.AbstractEventLoop, func) -> object:
174
267
  """Await *func()* in the render thread without using concurrent.futures."""
175
268
  _ensure_render_thread()
@@ -476,9 +569,12 @@ __all__ = [
476
569
  "_render",
477
570
  # Prefetch
478
571
  "_PREFETCH_POOL",
572
+ "_OVERLAY_PREFETCH_POOL",
479
573
  "_PREFETCH_LOCK",
480
574
  "_get_prefetch_pool",
575
+ "_get_overlay_prefetch_pool",
481
576
  "_schedule_prefetch",
577
+ "_schedule_overlay_prefetch",
482
578
  # Session
483
579
  "Session",
484
580
  "SESSIONS",
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import json
7
+ import platform
7
8
  import subprocess
8
9
  import sys
9
10
  import threading
@@ -219,7 +220,7 @@ def _open_browser(
219
220
  port_hint = int(url.split(":")[2].split("/")[0].split("?")[0])
220
221
  except Exception:
221
222
  port_hint = parsed_port
222
- hostname = os.uname().nodename or "<this-host>"
223
+ hostname = platform.node() or "<this-host>"
223
224
  print(
224
225
  f"[ArrayView] Plain SSH session detected.\n"
225
226
  f"\n"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arrayview
3
- Version: 0.31.0
3
+ Version: 0.31.2
4
4
  Summary: Fast multi-dimensional array viewer
5
5
  Project-URL: Home, https://github.com/oscarvanderheide/arrayview
6
6
  Project-URL: Source, https://github.com/oscarvanderheide/arrayview
@@ -1,35 +1,35 @@
1
1
  arrayview/ARCHITECTURE.md,sha256=3uM548p70zJ_Fj8eYWN1cN8fEsCA2C7eSRvUx-3HhtU,16515
2
2
  arrayview/__init__.py,sha256=CBRg7Aqj4Ft2bSNvKb9pfkLrbVxL7bzKsx6ZridmR-E,775
3
3
  arrayview/__main__.py,sha256=Fj5RszShrCcAS7_nIyBndtkpMI_PLBxM5GG9huzqhVs,101
4
- arrayview/_analysis.py,sha256=BAEUmeBgp4gZaQZXkULoxYd_OyJOtfZnigUsKcEm3tc,10209
4
+ arrayview/_analysis.py,sha256=JCbMT6NcUDDIZ24ABCdOXT1NAyGAX96rebq34DloElI,10594
5
5
  arrayview/_app.py,sha256=2lA5P4YD8zEJ-HnDiGdZUtO4uOSxRoKrs110Iq5Xl0k,5336
6
6
  arrayview/_codex_open.py,sha256=keegkA8G5BIm6g0IgvRMqXCpOg4nKtdTOAsRDlvAB5A,3541
7
7
  arrayview/_config.py,sha256=64rtArzgJGF0_ukrWlaNZWQHVRk8wiIinUQQBXoB27g,4356
8
8
  arrayview/_diff.py,sha256=mSIo1ArNxkrB0v2iUMy3dezJOlpos0xNu1V_umNUMhU,5677
9
9
  arrayview/_icon.png,sha256=a8RTAbvweLhh3hzD5eGg76Peq-KSDcS6YrCphRb_HEs,2748
10
10
  arrayview/_imaging.py,sha256=MQX1uy4m1lofOeoLDhCp6cHO5p_V5fXm4qlSu9Ykn-c,534
11
- arrayview/_instance_registry.py,sha256=E6__kmj5guNSlk5y_cdey7OKjBKwlsu4TTPAhKPR-9c,8638
11
+ arrayview/_instance_registry.py,sha256=N-6uWAmYsO6bkr2v55GDbWX-xRktErP-kFT8FfJ7Y7U,8863
12
12
  arrayview/_io.py,sha256=PSSjD7MWQlYQPrY0ELxNtbrEdLYBtdNJsOs-F6JHSyM,50596
13
13
  arrayview/_launch_plan.py,sha256=wNWnnrDNzEaDbH3VKK8P_dhQVzv-bvKCGn6nNLXo8P0,16072
14
14
  arrayview/_launcher.py,sha256=b91U8XaPGiCVThn-b9p1QEQFMrC0XsM6dTYhHuSscCE,163017
15
15
  arrayview/_lifecycle.py,sha256=WE-vXHioGU2H_zBK_sRivmqRfoO9RLDSAJtTq3Oe-WY,1693
16
- arrayview/_overlays.py,sha256=Qfis82NiIBl4IlilhFj0y1kuu-mUaKqIXKU4Hxuq4F4,2080
16
+ arrayview/_overlays.py,sha256=TsMnXrP_BNSzCj4k8hbWNM-M5lcrro0xA_wusvAdLLw,4622
17
17
  arrayview/_platform.py,sha256=U1XPqEjPeqy3l_xJh9Rmn_3d_6Fh_clJUcA0uR6QBGA,19043
18
- arrayview/_render.py,sha256=Er6EMZCM1LkPC7-Nym1IQBNqlm_eSjyyRyaVGE-VazI,29463
18
+ arrayview/_render.py,sha256=wVvwxQtZ7ldWYZTzlg2CS-esnPHlpSXMW_z22eh-rU0,30296
19
19
  arrayview/_routes_analysis.py,sha256=QTTdZrq6bXBAxqrAPf4-3_ANZPv0b8VXyPK8mW_Xe1s,31629
20
20
  arrayview/_routes_export.py,sha256=Ha8mMO2E9TlRL5x25-ADV8_8xUiC-jRz9N1oWwgKOsg,3898
21
21
  arrayview/_routes_loading.py,sha256=pqhiOaH-hXdCR8P9X26JHm_psR5gurT1N4vh8BIuwcw,21864
22
22
  arrayview/_routes_persistence.py,sha256=T03csHb7ppGT-8yMDf227XkqhnhjgSLYj0RW2wmg59g,17176
23
23
  arrayview/_routes_preload.py,sha256=KOsankNguxdIWmFW9iZcY9p2SvUN1mOKY7KkW1qMGcc,1729
24
24
  arrayview/_routes_query.py,sha256=vEwkNWKVkwTOBswGU079gf_C0fWbO4Bgvg0U7jusBCU,5227
25
- arrayview/_routes_rendering.py,sha256=qraJoMxCnHDx2Kcvfcrt1N_a6O_KOZj_Hy2GPZC7MDs,21040
25
+ arrayview/_routes_rendering.py,sha256=OiYvdYE2PnTXVo9fUdWN1JvZ-VvUTUgGA7LYGrmMDSo,21688
26
26
  arrayview/_routes_segmentation.py,sha256=x2bBjQffZryeTMmOH38YdBWethw1o3KpA8V0chJcZHI,23599
27
27
  arrayview/_routes_state.py,sha256=TmxC__NPPsvg59jM2D_FRc_oPv1wWbRmUr6t6ukVj64,11173
28
28
  arrayview/_routes_vectorfield.py,sha256=P8avWU8jDH173YQ4Kfpzs8SZz5gIL4ki0-Gc6pGA9w4,7383
29
- arrayview/_routes_websocket.py,sha256=nBK2wHlxSIiA1G3rMYoYGJUCD3fZNP6Vj_slGqnpUsQ,17367
29
+ arrayview/_routes_websocket.py,sha256=6WCK665MGAbCpOhkxQdEnJN7o1CYB4e2_VB4ilgmr-I,18722
30
30
  arrayview/_segmentation.py,sha256=aENWHmefrKDScXhcZL1X3KgYXE784dpMkmFFo83qnrw,7377
31
31
  arrayview/_server.py,sha256=XqrC1jbQ_wke4_YgxZ-1xACmUFPYxq2hEU--namvtqA,10407
32
- arrayview/_session.py,sha256=0Q1iVkoyqrqsIqFNcc6eqiDmLMxs8fKpnJ8a3fgoxVE,18192
32
+ arrayview/_session.py,sha256=NXhx1-XbMNqqsF-A57wmmW2rE9jOUbWtXtjgQRl0b7s,21683
33
33
  arrayview/_session_spec.py,sha256=AEqt_5eXIYdlWkJeeVBmG_MqSUgmVDZCTNaMkdRTZ5Y,6969
34
34
  arrayview/_shell.html,sha256=xzHIB-kRX12okubZoqdrgsWDY8CCLteg5jtXxDQru8U,11624
35
35
  arrayview/_synthetic_mri.py,sha256=CNL-2WC835i6WCqkvmWBCO6WQ5w7HE5trNb8OyEfe64,7365
@@ -37,13 +37,13 @@ arrayview/_torch.py,sha256=13sqIMCUu_-b9SQ9KfDCExDz2UMStL87EDVjnbJxAEo,7536
37
37
  arrayview/_vectorfield.py,sha256=eDqMCMEGjO8Jdt-6Auh01cz3WD9dbF7xYUWD9xHAf1E,8115
38
38
  arrayview/_viewer.html,sha256=42OHpTAuXsFLB5AGLJhCRs1qw9oIqjEXJjq3i9EKm9c,1789431
39
39
  arrayview/_vscode.py,sha256=uY3SYJYjgzuYCbdycmm53ZaPNJIPPtGiIQZKBUobMz4,871
40
- arrayview/_vscode_browser.py,sha256=BvDyPjBKBUbalPqN0fb5kC5O8ZjucfU9va6kPjY9tlM,11773
40
+ arrayview/_vscode_browser.py,sha256=7WjnJwN--bppJmSv8TFjJWG0_IpGwGqHkNI6ubjxgE8,11785
41
41
  arrayview/_vscode_extension.py,sha256=UZqjMPJASvz8U6moa-cX7xVJhbvwpVSmCHXd-wc_W4Y,13423
42
42
  arrayview/_vscode_signal.py,sha256=9WZKG3CqZKA9yG6GvSVmkunQs-0DwmRNy5wML4dD0p0,45107
43
43
  arrayview/arrayview-opener.vsix,sha256=QeeLuH0h0sFN_tBXIRmFOdm4owSrZ4_aLSQPe7KbOR0,21352
44
44
  arrayview/gsap.min.js,sha256=VGihm4idI0E1uqrWezWsiScB9F_jMosn7qs6pPSkeac,72304
45
- arrayview-0.31.0.dist-info/METADATA,sha256=UFnJ1Mnn10C8pw3Wqp8WnSq_-1ZcYrlcBavk7b_6KlQ,2054
46
- arrayview-0.31.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
47
- arrayview-0.31.0.dist-info/entry_points.txt,sha256=FQSz3M11B-zaVLDyXofKUDvMhHASz7OjmoLgI4nn7ko,105
48
- arrayview-0.31.0.dist-info/licenses/LICENSE,sha256=34OivjM9faB2QaPjZpMRTIiSx31V3U9cEyQ6F2k-bH0,1062
49
- arrayview-0.31.0.dist-info/RECORD,,
45
+ arrayview-0.31.2.dist-info/METADATA,sha256=ID8dLoV42W9GRgXawTdg4nupId1kmrlnxMcPNYspMcc,2054
46
+ arrayview-0.31.2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
47
+ arrayview-0.31.2.dist-info/entry_points.txt,sha256=FQSz3M11B-zaVLDyXofKUDvMhHASz7OjmoLgI4nn7ko,105
48
+ arrayview-0.31.2.dist-info/licenses/LICENSE,sha256=34OivjM9faB2QaPjZpMRTIiSx31V3U9cEyQ6F2k-bH0,1062
49
+ arrayview-0.31.2.dist-info/RECORD,,