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

changed main file for sync view and changed show_details file for box… #26

Merged
merged 2 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 140 additions & 78 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,152 @@
import base64
import json
import panel as pn
from openvisuspy import SetupLogger, Slice, ProbeTool, cbool
import time

from openvisuspy import SetupLogger, Slice, ProbeTool,cbool
# Global synchronization lock and throttle variables
sync_in_progress = False
last_sync_time = 0
throttle_delay = 0.1 # Throttle delay in seconds

# ////////////////////////////////////////////////////////////////////////////
def SyncSlices(slice1, slice2, box1=None, box2=None):

if box1 is None:
box1=slice1.db.getPhysicBox()

if box2 is None:
box2=slice2.db.getPhysicBox()

# map box1 to box2
(a, b), (c, d)=box1
(A, B), (C, D)=box2
def throttle(func, *args, **kwargs):
"""Throttle function calls to limit frequency."""
global last_sync_time
current_time = time.time()
if current_time - last_sync_time >= throttle_delay:
last_sync_time = current_time
func(*args, **kwargs)

x, y, w, h = slice1.canvas.getViewport()
x1, x2 = [A + ((value - a) / (b - a)) * (B - A) for value in [x, x + w]]
y1, y2 = [C + ((value - c) / (d - c)) * (D - C) for value in [y, y + h]]
slice2.canvas.setViewport([x1, y1, x2 - x1, y2 - y1])
slice2.refresh("SyncSlices")
def SyncSlices(slice1, slice2, box1=None, box2=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

can you merge functions to avoid replication of code (syncSlices and reverse)?
also no to use global... you can use variables inside the class (i.e. if there are two couples of synced-images it will not work)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

syncSlices and reverse are merged. removed global variables and included them in the class.

"""Synchronize from slice1 (original) to slice2 (super-resolution)"""
global sync_in_progress
if sync_in_progress:
return # Prevent re-entrant synchronization

sync_in_progress = True
try:
if box1 is None:
box1 = slice1.db.getPhysicBox()
if box2 is None:
box2 = slice2.db.getPhysicBox()

# Map box1 to box2
(a, b), (c, d) = box1
(A, B), (C, D) = box2

x, y, w, h = slice1.canvas.getViewport()

x1, x2 = [A + ((value - a) / (b - a)) * (B - A) for value in [x, x + w]]
y1, y2 = [C + ((value - c) / (d - c)) * (D - C) for value in [y, y + h]]
slice2.canvas.setViewport([x1, y1, x2 - x1, y2 - y1])
slice2.refresh("SyncSlices")
finally:
sync_in_progress = False

def SyncSlicesReverse(slice2, slice1, box1=None, box2=None):
"""Synchronize from slice2 (super-resolution) to slice1 (original)"""
global sync_in_progress
if sync_in_progress:
return # Prevent re-entrant synchronization

sync_in_progress = True
try:
if box1 is None:
box1 = slice1.db.getPhysicBox()
if box2 is None:
box2 = slice2.db.getPhysicBox()

# Map box2 to box1
(A, B), (C, D) = box2
(a, b), (c, d) = box1

x, y, w, h = slice2.canvas.getViewport()

x1, x2 = [a + ((value - A) / (B - A)) * (b - a) for value in [x, x + w]]
y1, y2 = [c + ((value - C) / (D - C)) * (d - c) for value in [y, y + h]]
slice1.canvas.setViewport([x1, y1, x2 - x1, y2 - y1])
slice1.refresh("SyncSlicesReverse")
finally:
sync_in_progress = False

# ////////////////////////////////////////////////////////////////////////////
if __name__.startswith('bokeh'):

pn.extension(
"ipywidgets",
"floatpanel",
"codeeditor",
log_level="DEBUG",
notifications=True,
sizing_mode="stretch_width"
)

query_params = {k: v for k,v in pn.state.location.query_params.items()}

log_filename = os.environ.get("OPENVISUSPY_DASHBOARDS_LOG_FILENAME", "/tmp/openvisuspy-dashboards.log")
logger = SetupLogger(log_filename=log_filename, logging_level=logging.DEBUG)

# sync view
if len(sys.argv[1:])==2:

from openvisuspy.utils import SafeCallback
from panel import Column, Row

slice1 = Slice();slice1.load(sys.argv[1])
slice2 = Slice();slice2.load(sys.argv[2])

show_options = {
"top": [
[
"palette",
"color_mapper_type",
"resolution",
"num_refinements",
"field",
"range_mode",
"range_min",
"range_max"
],
],
# "bottom": [["request", "response"]],
}
slice1.setShowOptions(show_options)
slice2.setShowOptions(show_options)
slice1.scene_body.param.watch( SafeCallback(lambda evt: SyncSlices(slice1,slice2)), "value", onlychanged=True, queued=True)
main_layout = pn.Row(slice1.getMainLayout(), slice2.getMainLayout())
main_layout.servable()

else:

slice = Slice()
slice.load(sys.argv[1])

# load a whole scene
if "load" in query_params:
body = json.loads(base64.b64decode(query_params['load']).decode("utf-8"))
slice.setBody(body)

# select from list of choices
elif "dataset" in query_params:
scene_name = query_params["dataset"]
slice.scene.value = scene_name

main_layout = slice.getMainLayout()
main_layout.servable()

pn.extension(
"ipywidgets",
"floatpanel",
"codeeditor",
log_level="DEBUG",
notifications=True,
)

query_params = {k: v for k, v in pn.state.location.query_params.items()}

log_filename = os.environ.get("OPENVISUSPY_DASHBOARDS_LOG_FILENAME", "/tmp/openvisuspy-dashboards.log")
logger = SetupLogger(log_filename=log_filename, logging_level=logging.DEBUG)

# Sync view
if len(sys.argv[1:]) == 2:

slice1 = Slice()
slice1.load(sys.argv[1])
slice2 = Slice()
slice2.load(sys.argv[2])

show_options = {
"top": [
[
"palette",
"color_mapper_type",
"resolution",
"num_refinements",
"field",
"range_mode",
"range_min",
"range_max"
],
],
}
slice1.setShowOptions(show_options)
slice2.setShowOptions(show_options)

# Define update functions with throttling
def update_slice2(attr, old, new):
throttle(SyncSlices, slice1, slice2)

def update_slice1(attr, old, new):
throttle(SyncSlicesReverse, slice2, slice1)

# Watch for viewport changes in slice1 and slice2 using on_change
slice1.canvas.fig.x_range.on_change('start', update_slice2)
slice1.canvas.fig.x_range.on_change('end', update_slice2)
slice1.canvas.fig.y_range.on_change('start', update_slice2)
slice1.canvas.fig.y_range.on_change('end', update_slice2)

slice2.canvas.fig.x_range.on_change('start', update_slice1)
slice2.canvas.fig.x_range.on_change('end', update_slice1)
slice2.canvas.fig.y_range.on_change('start', update_slice1)
slice2.canvas.fig.y_range.on_change('end', update_slice1)

# Layout for both slices
main_layout = pn.Row(slice1.getMainLayout(), slice2.getMainLayout())
main_layout.servable()

else:
slice = Slice()
slice.load(sys.argv[1])

# Load a whole scene
if "load" in query_params:
body = json.loads(base64.b64decode(query_params['load']).decode("utf-8"))
slice.setBody(body)

# Select from list of choices
elif "dataset" in query_params:
scene_name = query_params["dataset"]
slice.scene.value = scene_name

main_layout = slice.getMainLayout()
main_layout.servable()
64 changes: 44 additions & 20 deletions src/openvisuspy/show_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
from bokeh.models import LinearColorMapper
import bokeh.models
import logging
from mpl_toolkits.axes_grid1 import make_axes_locatable
import matplotlib.pyplot as plt
from bokeh.models import ColumnDataSource, ColorBar, LinearColorMapper
from .utils import *

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -106,7 +110,7 @@ def ShowDetails(self,x,y,w,h):
pdim=self.getPointDim()

# todo for 2D dataset
assert(pdim==3)
# assert(pdim==3)

z=int(self.offset.value)
logic_box=self.toLogic([x,y,w,h])
Expand Down Expand Up @@ -148,29 +152,49 @@ def ShowDetails(self,x,y,w,h):
self.range_min.value = min(self.range_min.value, self.vmin)
self.range_max.value = max(self.range_max.value, self.vmax)
logger.info(f"Updating range with selected area vmin={self.vmin} vmax={self.vmax}")

p = figure(x_range=(self.selected_physic_box[0][0], self.selected_physic_box[0][1]), y_range=(self.selected_physic_box[1][0], self.selected_physic_box[1][1]))
fig, ax = plt.subplots()

p1 = figure(x_range=(0,100), y_range=(0,100))
palette_name = self.palette.value_name if self.palette.value_name.endswith("256") else "Turbo256"

mapper = LinearColorMapper(palette=palette_name, low=np.min(self.detailed_data), high=np.max(self.detailed_data))

# Flip data to match imshow orientation
data_flipped = data
source = bokeh.models.ColumnDataSource(data=dict(image=[data_flipped]))
data_flipped = data

print(type(self.selected_physic_box[0][1]))
dw = abs(self.selected_physic_box[0][1] -self.selected_physic_box[0][0])

dh = abs(self.selected_physic_box[1][1] - self.selected_physic_box[1][0])
p.image(image='image', x=self.selected_physic_box[0][0], y=self.selected_physic_box[1][0], dw=dw, dh=dh, color_mapper=mapper, source=source)
color_bar = bokeh.models.ColorBar(color_mapper=mapper, label_standoff=12, location=(0,0))
p.add_layout(color_bar, 'right')
p.xaxis.axis_label = "X"
p.yaxis.axis_label = "Y"

self.showDialog(
pn.Column(
self.file_name_input,
pn.Row(save_numpy_button,download_script_button),
pn.Row(apply_avg_min_colormap_button,apply_avg_max_colormap_button,add_range_button,apply_colormap_button),
pn.Row(pn.pane.Bokeh(p,sizing_mode="stretch_both")),
sizing_mode="stretch_both"
)
, width=900, height=800, name=f"Palette: {palette_name} Min: {self.vmin}, Max: {self.vmax}")
x_min, x_max = int(self.selected_physic_box[0][0]), int(self.selected_physic_box[0][1])
y_min, y_max = int(self.selected_physic_box[1][0]), int(self.selected_physic_box[1][1])

#fig, ax = plt.subplots(figsize=(14, 20))
fig, ax = plt.subplots(figsize=(4, 4))
im = ax.imshow(data_flipped, cmap='turbo',extent=[x_min, x_max, y_min, y_max], aspect='auto')
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.1)
cbar = plt.colorbar(im, cax=cax)

ax.set_xlim(self.selected_physic_box[0][0], self.selected_physic_box[0][1])
ax.set_ylim(self.selected_physic_box[1][1], self.selected_physic_box[1][0])

ax.set_xlabel("X")
ax.set_ylabel("Y")
plt.tight_layout()

dialog_layout = pn.Column(
self.file_name_input,
pn.Row(save_numpy_button, download_script_button),
pn.Row(pn.pane.Matplotlib(fig), pn.Column(
pn.pane.Markdown(f"#### Palette Used: {palette_name}"),
pn.pane.Markdown(f"#### New Min/Max Found.."),
pn.pane.Markdown(f"#### Min: {self.vmin}, Max: {self.vmax}"),
pn.Row(apply_avg_min_colormap_button, apply_avg_max_colormap_button),
add_range_button,
apply_colormap_button
)),
sizing_mode="stretch_both"
)

self.showDialog(dialog_layout, width=500, height=600, name="Details")
Loading