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 all commits
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
216 changes: 134 additions & 82 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,142 @@
import base64
import json
import panel as pn
from openvisuspy import SetupLogger, Slice, ProbeTool, cbool
import time

class SliceSynchronizer:
def __init__(self, slice1, slice2, throttle_delay=0.05):
self.slice1 = slice1
self.slice2 = slice2
self.sync_in_progress = False
self.last_sync_time = 0
self.throttle_delay = throttle_delay

def throttle(self, func, *args, **kwargs):
"""Throttle function calls to limit frequency."""
current_time = time.time()
if current_time - self.last_sync_time >= self.throttle_delay:
self.last_sync_time = current_time
func(*args, **kwargs)

def sync_slices(self, box1=None, box2=None, is_reverse=False):
"""Synchronize viewports between two slices, with an option to reverse the direction."""
if self.sync_in_progress:
return
self.sync_in_progress = True
try:
# Set default boxes if not provided
if box1 is None:
box1 = self.slice1.db.getPhysicBox()
if box2 is None:
box2 = self.slice2.db.getPhysicBox()

# Determine source and target based on is_reverse flag
if is_reverse:
(src_box, tgt_box) = (box2, box1)
(src_slice, tgt_slice) = (self.slice2, self.slice1)
else:
(src_box, tgt_box) = (box1, box2)
(src_slice, tgt_slice) = (self.slice1, self.slice2)

# Unpack source and target boxes
(a, b), (c, d) = src_box
(A, B), (C, D) = tgt_box

# Get viewport from the source slice
x, y, w, h = src_slice.canvas.getViewport()

# Map coordinates from source to target
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]]

# Set the target slice's viewport and refresh
tgt_slice.canvas.setViewport([x1, y1, x2 - x1, y2 - y1])
tgt_slice.refresh("SyncSlices")
finally:
self.sync_in_progress = False

def update_slice2(self, attr, old, new):
"""Throttle and synchronize slice1 -> slice2."""
self.throttle(self.sync_slices, is_reverse=False)

def update_slice1(self, attr, old, new):
"""Throttle and synchronize slice2 -> slice1 (reverse direction)."""
self.throttle(self.sync_slices, is_reverse=True)

from openvisuspy import SetupLogger, Slice, ProbeTool,cbool

# ////////////////////////////////////////////////////////////////////////////
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

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")

# ////////////////////////////////////////////////////////////////////////////
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)

# Initialize the synchronizer
synchronizer = SliceSynchronizer(slice1, slice2, throttle_delay = 0.05)

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

slice2.canvas.fig.x_range.on_change('start', synchronizer.update_slice1)
slice2.canvas.fig.x_range.on_change('end', synchronizer.update_slice1)
slice2.canvas.fig.y_range.on_change('start', synchronizer.update_slice1)
slice2.canvas.fig.y_range.on_change('end', synchronizer.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