diff --git a/assets/functions.js b/assets/functions.js index 0b1a71c7..dc3523f8 100644 --- a/assets/functions.js +++ b/assets/functions.js @@ -9,15 +9,3 @@ function changeFilters(js_path, brightness, contrast) { -window.dash_clientside = Object.assign({}, window.dash_clientside, { - clientside: { - get_container_size: function(url) { - let W = window.innerWidth; - let H = window.innerHeight; - if(W == 0 || H == 0){ - return dash_clientside.no_update - } - return {'W': W, 'H':H} - }, - } -}); \ No newline at end of file diff --git a/callbacks/image_viewer.py b/callbacks/image_viewer.py index 42686e7f..52f5d88a 100644 --- a/callbacks/image_viewer.py +++ b/callbacks/image_viewer.py @@ -25,12 +25,7 @@ downscale_view, get_view_finder_max_min, resize_canvas, -) - -clientside_callback( - ClientsideFunction(namespace="clientside", function_name="get_container_size"), - Output("screen-size", "data"), - Input("url", "href"), + resize_canvas_with_zoom, ) @@ -41,21 +36,25 @@ Output("image-viewer-loading", "zIndex", allow_duplicate=True), Output("image-metadata", "data"), Input("image-selection-slider", "value"), + Input("window-resize", "width"), + Input("window-resize", "height"), + Input("reset-view", "n_clicks"), State({"type": "annotation-class-store", "index": ALL}, "data"), State("project-name-src", "value"), State("annotation-store", "data"), State("image-metadata", "data"), - State("screen-size", "data"), State("current-class-selection", "data"), prevent_initial_call=True, ) def render_image( image_idx, + screen_width, + screen_height, + reset_view, all_annotation_class_store, project_name, annotation_store, image_metadata, - screen_size, current_color, ): if image_idx: @@ -86,24 +85,22 @@ def render_image( fig["layout"]["shapes"] = all_annotations view = annotation_store["view"] - - if screen_size: - if view: - image_center_coor = annotation_store["image_center_coor"] + patched_annotation_store = Patch() + if screen_width and screen_height: + if view and ctx.triggered_id != "reset-view": # we have a zoom + window size to take into account if "xaxis_range_0" in view: - fig.update_layout( - xaxis=dict(range=[view["xaxis_range_0"], view["xaxis_range_1"]]), - yaxis=dict(range=[view["yaxis_range_0"], view["yaxis_range_1"]]), + fig, view = resize_canvas_with_zoom( + view, screen_height, screen_width, fig ) else: - # no zoom level to take into account, window size only - fig, image_center_coor = resize_canvas( - tf.shape[0], tf.shape[1], screen_size["H"], screen_size["W"], fig + # no zoom level to take into account, window size only, also used for reset view case + fig = resize_canvas( + tf.shape[0], tf.shape[1], screen_height, screen_width, fig ) + patched_annotation_store["view"] = {} + view = {} - patched_annotation_store = Patch() - patched_annotation_store["image_center_coor"] = image_center_coor patched_annotation_store["active_img_shape"] = list(tf.shape) fig_loading_overlay = -1 @@ -387,38 +384,3 @@ def update_selection_and_image( disable_next_image, f"Slice {new_slider_value}", ) - - -@callback( - Output("image-viewer", "figure", allow_duplicate=True), - Output("image-viewer", "relayoutData"), - Input("reset-view", "n_clicks"), - State("annotation-store", "data"), - prevent_initial_call=True, -) -def reset_figure_view(n_clicks, annotation_store): - """ - This callback will reset the view of the image to the center of the screen (no zoom, no pan). - RelayoutData is updated too, which then triggers callback that updates viewfinder box - """ - image_center_coor = annotation_store["image_center_coor"] - if image_center_coor is None: - raise PreventUpdate - - new_figure = Patch() - new_figure["layout"]["yaxis"]["range"] = [ - image_center_coor["y1"], - image_center_coor["y0"], - ] - new_figure["layout"]["xaxis"]["range"] = [ - image_center_coor["x0"], - image_center_coor["x1"], - ] - - relayout_data = { - "xaxis.range[0]": image_center_coor["x0"], - "yaxis.range[0]": image_center_coor["y0"], - "xaxis.range[1]": image_center_coor["x1"], - "yaxis.range[1]": image_center_coor["y1"], - } - return new_figure, relayout_data diff --git a/components/control_bar.py b/components/control_bar.py index 97ec6f83..ec9bf765 100644 --- a/components/control_bar.py +++ b/components/control_bar.py @@ -1,6 +1,7 @@ import dash_bootstrap_components as dbc import dash_mantine_components as dmc from dash import dcc, html +from dash_breakpoints import WindowBreakpoints from dash_extensions import EventListener from dash_iconify import DashIconify @@ -683,7 +684,6 @@ def drawer_section(children): "visible": True, "view": {}, "active_img_shape": [], - "image_center_coor": {}, }, ), create_reset_view_affix(), @@ -702,6 +702,9 @@ def drawer_section(children): ], id="keybind-event-listener", ), + WindowBreakpoints( + id="window-resize", + ), ] ) diff --git a/components/image_viewer.py b/components/image_viewer.py index 76065b30..2d3f20dc 100644 --- a/components/image_viewer.py +++ b/components/image_viewer.py @@ -20,7 +20,6 @@ def layout(): style=COMPONENT_STYLE, children=[ dcc.Store("image-metadata", data={"name": None}), - dcc.Store("screen-size"), dcc.Location("url"), dmc.LoadingOverlay( id="image-viewer-loading", diff --git a/requirements.txt b/requirements.txt index 813ed1e4..435cf3cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,4 +31,5 @@ svgpathtools matplotlib scipy dash-extensions==1.0.1 -dash-bootstrap-components==1.5.0 \ No newline at end of file +dash-bootstrap-components==1.5.0 +dash-breakpoints==0.1.0 \ No newline at end of file diff --git a/utils/plot_utils.py b/utils/plot_utils.py index eb215fb8..4d111947 100644 --- a/utils/plot_utils.py +++ b/utils/plot_utils.py @@ -158,8 +158,20 @@ def resize_canvas(h, w, H, W, figure): y1 = h y0 = 0 - figure.update_yaxes(range=[y1, y0]) - figure.update_xaxes(range=[x0, x1]) + figure.update_layout( + xaxis=dict(range=[x0, x1]), + yaxis=dict(range=[y1, y0]), + ) + + return figure + - image_center_coor = {"y1": y1, "y0": y0, "x0": x0, "x1": x1} - return figure, image_center_coor +def resize_canvas_with_zoom(view, H, W, fig): + x0 = view["xaxis_range_0"] + y0 = view["yaxis_range_0"] + x1 = view["xaxis_range_1"] + y1 = view["yaxis_range_1"] + y1 = y0 - H / W * (x1 - x0) + fig.update_layout(xaxis=dict(range=[x0, x1]), yaxis=dict(range=[y0, y1])) + view["yaxis_range_1"] = y1 + return fig, view