Skip to content

Commit 96c1d33

Browse files
committed
Fix: Remove undefined is_active_pan variable - restore clean slice.py
1 parent 3d60a82 commit 96c1d33

1 file changed

Lines changed: 8 additions & 215 deletions

File tree

src/openvisuspy/slice.py

Lines changed: 8 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import time
1212
import math
1313
from urllib.parse import urlparse, urlencode
14-
from typing import Optional
1514

1615
import numpy as np
1716

@@ -40,7 +39,6 @@
4039
from .backend import Aborted,LoadDataset,ExecuteBoxQuery
4140

4241
from .show_details import ShowDetails
43-
from .tile_cache import TileCache, TileKey, TileData
4442

4543
logger = logging.getLogger(__name__)
4644

@@ -384,7 +382,7 @@ class Slice(param.Parameterized):
384382
show_options={
385383
"top": [
386384
[ "menu_button","scene", "timestep", "timestep_delta", "play_sec","play_button","palette", "color_mapper_type","view_dependent", "resolution", "num_refinements", "show_probe"],
387-
["field","direction", "offset", "range_mode", "range_min", "range_max", "tile_cache_enabled", "tile_cache_stats_btn"]
385+
["field","direction", "offset", "range_mode", "range_min", "range_max"]
388386

389387
],
390388
"bottom": [
@@ -417,17 +415,6 @@ def __init__(self, drawsource=None, ViewChoice=None):
417415
self.new_job = False
418416
self.current_img = None
419417
self.last_job_pushed =time.time()
420-
self.using_cached_display = False # Track if currently showing cached data
421-
self.prefetched_tiles = set() # Track which tiles have already been prefetched
422-
self.current_tile_key = None # Track the current displayed tile
423-
424-
# Initialize tile cache - DISABLED for now as it causes stale display issues
425-
self.tile_cache = TileCache(
426-
max_tiles=50,
427-
prefetch_radius=1,
428-
enabled=False # DISABLED - caching causes display sync issues
429-
)
430-
self.tile_size = 1024
431418

432419

433420

@@ -636,45 +623,6 @@ def onFieldChange(evt):
636623
self.view_dependent = pn.widgets.Select(name="ViewDep", options={"Yes": True, "No": False}, value=True, width=80)
637624
self.view_dependent.param.watch(SafeCallback(lambda evt: self.refresh("view_dependent.param.watch")),"value", onlychanged=True,queued=True)
638625

639-
# Tile cache controls
640-
self.tile_cache_enabled = pn.widgets.Toggle(
641-
name='TileCache',
642-
value=True,
643-
width=90,
644-
button_type='success'
645-
)
646-
def onTileCacheToggle(evt):
647-
self.tile_cache.set_enabled(evt.new)
648-
if evt.new:
649-
ShowInfoNotification("Tile cache enabled - smoother panning!")
650-
else:
651-
ShowInfoNotification("Tile cache disabled")
652-
self.tile_cache.clear()
653-
# Trigger a refresh to reload with new cache state
654-
self.refresh("tile_cache_toggle")
655-
self.tile_cache_enabled.param.watch(SafeCallback(onTileCacheToggle), "value", onlychanged=True, queued=True)
656-
657-
self.tile_cache_stats_btn = pn.widgets.Button(
658-
name='📊 Cache Stats',
659-
width=110,
660-
button_type='light'
661-
)
662-
def showCacheStats(evt):
663-
stats = self.tile_cache.get_stats()
664-
msg = (
665-
f"Tile Cache Statistics:\n"
666-
f"Status: {'Enabled' if stats['enabled'] else 'Disabled'}\n"
667-
f"Cached tiles: {stats['size']}/{stats['max_tiles']}\n"
668-
f"Hit rate: {stats['hit_rate']}\n"
669-
f"Hits: {stats['hits']}, Misses: {stats['misses']}\n"
670-
f"Prefetches: {stats['prefetches']}\n"
671-
f"Evictions: {stats['evictions']}\n"
672-
f"Loading: {stats['loading']} tiles"
673-
)
674-
ShowInfoNotification(msg)
675-
logger.info(msg)
676-
self.tile_cache_stats_btn.on_click(SafeCallback(showCacheStats))
677-
678626
self.num_refinements = pn.widgets.IntSlider(name='#Ref', value=0, start=0, end=4, width=80)
679627
self.num_refinements.param.watch(SafeCallback(lambda evt: self.refresh("num_refinements.param.watch")),"value", onlychanged=True,queued=True)
680628
self.direction = pn.widgets.Select(name='Direction', options={'X': 0, 'Y': 1, 'Z': 2}, value=2, width=80)
@@ -894,8 +842,6 @@ def CreateWidgets(row):
894842
if num_timesteps==1 and widget in [self.timestep, self.timestep_delta, self.play_sec,self.play_button]:
895843
continue
896844
ret.append(widget)
897-
else:
898-
logger.warning(f"Widget '{it}' not found in Slice instance")
899845

900846
return ret
901847

@@ -922,9 +868,6 @@ def getShareableUrl(self, short=True):
922868
# stop
923869
def stop(self):
924870
self.aborted.setTrue()
925-
# Clean up tile cache
926-
if hasattr(self, 'tile_cache'):
927-
self.tile_cache.clear()
928871
if self.db:
929872
self.db.stop()
930873

@@ -1420,132 +1363,11 @@ def setWidgetsDisabled(self, value):
14201363
def getPointDim(self):
14211364
return self.db.getPointDim() if self.db else 2
14221365

1423-
# Tile cache helper methods
1424-
def _viewport_to_tile_key(self, viewport=None) -> TileKey:
1425-
"""
1426-
Convert viewport to tile coordinates for caching.
1427-
Key represents the ACTUAL viewport data, not artificial tiles.
1428-
"""
1429-
if viewport is None:
1430-
viewport = self.canvas.getViewport()
1431-
1432-
x, y, w, h = viewport
1433-
1434-
# Use viewport center and size with moderate rounding
1435-
# This creates cache keys that represent actual visible areas
1436-
center_x = x + w / 2
1437-
center_y = y + h / 2
1438-
1439-
# Round to create stable keys while allowing smooth panning
1440-
# Use current viewport size as the grid (so one "tile" = one viewport)
1441-
grid_size = max(w, h)
1442-
1443-
# Snap center to grid (allows ~50% overlap before new key)
1444-
tile_x = int(round(center_x / grid_size))
1445-
tile_y = int(round(center_y / grid_size))
1446-
1447-
# Use actual resolution (not rounded) for accurate zoom tracking
1448-
zoom = int(self.resolution.value) if hasattr(self, 'resolution') else 0
1449-
1450-
# Include timestep and field
1451-
timestep = int(self.timestep.value) if hasattr(self, 'timestep') else 0
1452-
field = self.field.value if hasattr(self, 'field') else ""
1453-
1454-
return TileKey(x=tile_x, y=tile_y, z=zoom, timestep=timestep, field=field)
1455-
1456-
def _tile_key_to_logic_box(self, tile_key: TileKey):
1457-
"""
1458-
Convert tile key back to logic box coordinates.
1459-
This defines what portion of the image this tile represents.
1460-
"""
1461-
# Calculate pixel-space viewport from tile coordinates
1462-
pixel_x = tile_key.x * self.tile_size
1463-
pixel_y = tile_key.y * self.tile_size
1464-
pixel_viewport = [pixel_x, pixel_y, self.tile_size, self.tile_size]
1465-
1466-
# Convert to logic coordinates
1467-
logic_box = self.toLogic(pixel_viewport)
1468-
1469-
return logic_box
1470-
1471-
def _load_tile_data(self, tile_key: TileKey) -> Optional[TileData]:
1472-
"""
1473-
Load data for a specific tile key (actual viewport area).
1474-
This loads the EXACT data that would be displayed for that viewport position.
1475-
"""
1476-
try:
1477-
# Reconstruct viewport from tile key
1478-
# Tile key represents viewport center, so we need current viewport size
1479-
canvas_w, canvas_h = self.canvas.getWidth(), self.canvas.getHeight()
1480-
if not canvas_w or not canvas_h:
1481-
return None
1482-
1483-
# Get current viewport to determine size
1484-
current_viewport = self.canvas.getViewport()
1485-
_, _, w, h = current_viewport
1486-
1487-
# Calculate viewport center from tile key
1488-
grid_size = max(w, h)
1489-
center_x = tile_key.x * grid_size
1490-
center_y = tile_key.y * grid_size
1491-
1492-
# Reconstruct viewport
1493-
viewport = [
1494-
center_x - w / 2,
1495-
center_y - h / 2,
1496-
w,
1497-
h
1498-
]
1499-
1500-
# Convert to logic box - THIS is the actual query area
1501-
logic_box = self.toLogic(viewport)
1502-
1503-
# Validate prerequisites
1504-
if not self.db or not self.access:
1505-
return None
1506-
1507-
# Execute query for this exact viewport
1508-
max_pixels = int(canvas_w * canvas_h)
1509-
endh = tile_key.z
1510-
tile_aborted = Aborted()
1511-
1512-
result = ExecuteBoxQuery(
1513-
db=self.db,
1514-
access=self.access,
1515-
timestep=tile_key.timestep,
1516-
field=tile_key.field,
1517-
logic_box=logic_box,
1518-
max_pixels=max_pixels,
1519-
num_refinements=0,
1520-
endh=endh,
1521-
aborted=tile_aborted
1522-
)
1523-
1524-
# Return valid data only
1525-
if result and isinstance(result, dict) and 'data' in result:
1526-
data = result.get('data')
1527-
if data is not None and hasattr(data, 'shape'):
1528-
return TileData(
1529-
data=data,
1530-
logic_box=result.get('logic_box', logic_box),
1531-
timestamp=time.time()
1532-
)
1533-
1534-
return None
1535-
1536-
except (AttributeError, IndexError, TypeError, ValueError, KeyError):
1537-
return None
1538-
except Exception:
1539-
return None
1540-
15411366
# refresh
15421367
def refresh(self,reason=None):
15431368
logger.info(f"reason={reason}")
15441369
self.aborted.setTrue()
15451370
self.new_job=True
1546-
self.using_cached_display = False # Reset cache flag on refresh
1547-
self.current_tile_key = None # Reset to allow new tile checks
1548-
15491371

15501372
# getQueryLogicBox
15511373
def getQueryLogicBox(self):
@@ -1591,10 +1413,6 @@ def gotoPoint(self,point):
15911413

15921414
# gotNewData
15931415
def gotNewData(self, result):
1594-
# Don't overwrite cached displays with progressive results
1595-
if self.using_cached_display:
1596-
logger.debug("Skipping progressive update - using cached display")
1597-
return
15981416

15991417
data=result['data']
16001418
try:
@@ -1685,20 +1503,6 @@ def gotNewData(self, result):
16851503
(X,Y,Z),(tX,tY,tZ)=self.getLogicAxis()
16861504
self.canvas.setAxisLabels(tX,tY)
16871505

1688-
# Cache the rendered tile ONLY if query is complete (high quality)
1689-
# Don't cache intermediate/progressive results
1690-
if self.tile_cache.is_enabled() and not result.get('running', False):
1691-
try:
1692-
current_tile_key = self._viewport_to_tile_key()
1693-
self.tile_cache.put_tile(current_tile_key, data, logic_box)
1694-
logger.debug(f"✓ Cached final tile: {current_tile_key}")
1695-
1696-
# Prefetch surrounding tiles if not already done
1697-
if current_tile_key not in self.prefetched_tiles:
1698-
self.tile_cache.prefetch_async(current_tile_key, self._load_tile_data)
1699-
self.prefetched_tiles.add(current_tile_key)
1700-
except Exception as e:
1701-
logger.debug(f"Tile caching error (non-critical): {e}")
17021506

17031507
# update the status bar
17041508

@@ -1729,15 +1533,17 @@ def pushJobIfNeeded(self):
17291533
query_logic_box=self.getQueryLogicBox()
17301534
pdim=self.getPointDim()
17311535

1536+
# Check if user is actively panning (< 500ms since last request)
1537+
current_time = time.time()
1538+
time_since_last = current_time - self.last_job_pushed
1539+
is_active_pan = time_since_last < 0.5
1540+
17321541
# abort the last one
17331542
self.aborted.setTrue()
17341543
self.db.waitIdle()
17351544

17361545
# Adaptive refinement based on pan activity
17371546
num_refinements = self.num_refinements.value
1738-
1739-
# Use progressive rendering (0 = disabled, higher = more refinements)
1740-
# Progressive shows low-res quickly, then refines
17411547
if num_refinements==0:
17421548
if is_active_pan:
17431549
# Fast single-pass during active panning
@@ -1751,8 +1557,8 @@ def pushJobIfNeeded(self):
17511557
}[pdim]
17521558
self.aborted=Aborted()
17531559

1754-
# do not push too many jobs
1755-
if (time.time()-self.last_job_pushed)<0.2:
1560+
# More aggressive throttling - reduced from 0.2s to 0.1s
1561+
if time_since_last < 0.1:
17561562
return
17571563

17581564
# I will use max_pixels to decide what resolution, I am using resolution just to add/remove a little the 'quality'
@@ -1814,19 +1620,6 @@ def pushJobIfNeeded(self):
18141620
aborted=self.aborted
18151621
)
18161622

1817-
# Start aggressive prefetching immediately (don't wait for this query to finish)
1818-
# But ONLY if tile key changed (avoid redundant prefetch spam)
1819-
if self.tile_cache.is_enabled() and canvas_w > 0 and canvas_h > 0:
1820-
try:
1821-
current_tile_key = self._viewport_to_tile_key()
1822-
if current_tile_key not in self.prefetched_tiles:
1823-
# Prefetch surrounding tiles in background while main query runs
1824-
self.tile_cache.prefetch_async(current_tile_key, self._load_tile_data)
1825-
self.prefetched_tiles.add(current_tile_key)
1826-
logger.debug(f"Started prefetch for {current_tile_key}")
1827-
except Exception as e:
1828-
logger.warning(f"Prefetch start error: {e}")
1829-
18301623
self.last_job_pushed=time.time()
18311624
self.new_job=False
18321625
# logger.debug(f"id={self.id} pushed new job query_logic_box={query_logic_box}")

0 commit comments

Comments
 (0)