From d249f8f20fa867d7c73497960f8b4de469162a13 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 29 Apr 2026 14:58:40 +0200 Subject: [PATCH 01/10] temp logic --- app/components/VeaseViewToolbar.vue | 8 +++++ app/stores/hybrid_viewer.js | 46 ++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/app/components/VeaseViewToolbar.vue b/app/components/VeaseViewToolbar.vue index 195094e2..3330f5a2 100644 --- a/app/components/VeaseViewToolbar.vue +++ b/app/components/VeaseViewToolbar.vue @@ -65,6 +65,14 @@ const camera_options = [ showZScaling.value = !showZScaling.value; }, }, + { + tooltip: "Center on click", + icon: "mdi-crosshairs-question", + color: hybridViewerStore.is_picking ? "primary" : undefined, + action: () => { + hybridViewerStore.is_picking = !hybridViewerStore.is_picking; + }, + }, ]; diff --git a/app/stores/hybrid_viewer.js b/app/stores/hybrid_viewer.js index f8af0893..b6819404 100644 --- a/app/stores/hybrid_viewer.js +++ b/app/stores/hybrid_viewer.js @@ -37,6 +37,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { const camera_options = reactive({}); const genericRenderWindow = reactive({}); const is_moving = ref(false); + const is_picking = ref(false); const zScale = ref(1); let viewStream = undefined; const gridActor = undefined; @@ -181,11 +182,47 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { useMousePressed({ target: container, onPressed: (event) => { - if (event.button === 0) { - is_moving.value = true; - event.stopPropagation(); - imageStyle.opacity = 0; + if (event.button !== 0) { + return; + } + if (is_picking.value) { + const rect = container.value.$el.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = rect.height - (event.clientY - rect.top); + console.log("Picking RPC at:", x, y); + viewerStore.request( + viewer_schemas.opengeodeweb_viewer.viewer.get_point_position, + { x, y }, + { + response_function: (response) => { + console.log("RPC Response:", response); + const pickedPos = response.world_position; + if (pickedPos && pickedPos.some((v) => v !== 0)) { + console.log("Centering camera on:", pickedPos); + const renderer = genericRenderWindow.value.getRenderer(); + const camera = renderer.getActiveCamera(); + const focalPoint = camera.getFocalPoint(); + const position = camera.getPosition(); + camera.setFocalPoint(...pickedPos); + camera.setPosition( + position[0] + pickedPos[0] - focalPoint[0], + position[1] + pickedPos[1] - focalPoint[1], + position[2] + pickedPos[2] - focalPoint[2], + ); + genericRenderWindow.value.getRenderWindow().render(); + syncRemoteCamera(); + } else { + console.warn("Invalid pickedPos:", pickedPos); + } + }, + }, + ); + is_picking.value = false; + return; } + is_moving.value = true; + event.stopPropagation(); + imageStyle.opacity = 0; }, onReleased: () => { if (!is_moving.value) { @@ -303,6 +340,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { resetCamera, setContainer, zScale, + is_picking, clear, exportStores, importStores, From 4796cb270d6721c8733af4afd69d5ad45976c899 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 29 Apr 2026 15:45:04 +0200 Subject: [PATCH 02/10] logs --- app/stores/hybrid_viewer.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/stores/hybrid_viewer.js b/app/stores/hybrid_viewer.js index b6819404..a5769b96 100644 --- a/app/stores/hybrid_viewer.js +++ b/app/stores/hybrid_viewer.js @@ -187,17 +187,17 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { } if (is_picking.value) { const rect = container.value.$el.getBoundingClientRect(); - const x = event.clientX - rect.left; - const y = rect.height - (event.clientY - rect.top); - console.log("Picking RPC at:", x, y); + const display_x = Math.round(event.clientX - rect.left); + const display_y = Math.round(rect.height - (event.clientY - rect.top)); + console.log("Picking RPC at:", display_x, display_y); viewerStore.request( viewer_schemas.opengeodeweb_viewer.viewer.get_point_position, - { x, y }, + { x: display_x, y: display_y }, { response_function: (response) => { console.log("RPC Response:", response); - const pickedPos = response.world_position; - if (pickedPos && pickedPos.some((v) => v !== 0)) { + const pickedPos = [response.x, response.y, response.z]; + if (pickedPos.some((value) => value !== 0)) { console.log("Centering camera on:", pickedPos); const renderer = genericRenderWindow.value.getRenderer(); const camera = renderer.getActiveCamera(); @@ -212,7 +212,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { genericRenderWindow.value.getRenderWindow().render(); syncRemoteCamera(); } else { - console.warn("Invalid pickedPos:", pickedPos); + console.warn("Invalid pickedPos (all zeros):", pickedPos); } }, }, From a0dd6c53a039caa3b4f6a825e63ccbb9ba2539ad Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 29 Apr 2026 15:59:22 +0200 Subject: [PATCH 03/10] yes --- app/stores/hybrid_viewer.js | 15 ++------------- app/utils/hybrid_viewer.js | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/app/stores/hybrid_viewer.js b/app/stores/hybrid_viewer.js index a5769b96..e8c4c326 100644 --- a/app/stores/hybrid_viewer.js +++ b/app/stores/hybrid_viewer.js @@ -1,5 +1,6 @@ import { applyCameraOptions, + centerCameraOnPosition, computeAverageBrightness, getCameraOptions, } from "@ogw_front/utils/hybrid_viewer"; @@ -189,30 +190,18 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { const rect = container.value.$el.getBoundingClientRect(); const display_x = Math.round(event.clientX - rect.left); const display_y = Math.round(rect.height - (event.clientY - rect.top)); - console.log("Picking RPC at:", display_x, display_y); viewerStore.request( viewer_schemas.opengeodeweb_viewer.viewer.get_point_position, { x: display_x, y: display_y }, { response_function: (response) => { - console.log("RPC Response:", response); const pickedPos = [response.x, response.y, response.z]; if (pickedPos.some((value) => value !== 0)) { - console.log("Centering camera on:", pickedPos); const renderer = genericRenderWindow.value.getRenderer(); const camera = renderer.getActiveCamera(); - const focalPoint = camera.getFocalPoint(); - const position = camera.getPosition(); - camera.setFocalPoint(...pickedPos); - camera.setPosition( - position[0] + pickedPos[0] - focalPoint[0], - position[1] + pickedPos[1] - focalPoint[1], - position[2] + pickedPos[2] - focalPoint[2], - ); + centerCameraOnPosition(camera, pickedPos); genericRenderWindow.value.getRenderWindow().render(); syncRemoteCamera(); - } else { - console.warn("Invalid pickedPos (all zeros):", pickedPos); } }, }, diff --git a/app/utils/hybrid_viewer.js b/app/utils/hybrid_viewer.js index db68324d..94e0fe82 100644 --- a/app/utils/hybrid_viewer.js +++ b/app/utils/hybrid_viewer.js @@ -98,4 +98,18 @@ function computeAverageBrightness(rect, options) { } } -export { applyCameraOptions, computeAverageBrightness, getCameraOptions }; +function centerCameraOnPosition(camera, pickedPosition) { + if (!camera || !pickedPosition) { + return; + } + const focalPoint = camera.getFocalPoint(); + const position = camera.getPosition(); + camera.setFocalPoint(...pickedPosition); + camera.setPosition( + position[0] + pickedPosition[0] - focalPoint[0], + position[1] + pickedPosition[1] - focalPoint[1], + position[2] + pickedPosition[2] - focalPoint[2], + ); +} + +export { applyCameraOptions, centerCameraOnPosition, computeAverageBrightness, getCameraOptions }; From 3c680e4c76b637b9c044fd95e3a8bd39b37c1f11 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 29 Apr 2026 16:52:58 +0200 Subject: [PATCH 04/10] refacto --- app/stores/hybrid_viewer.js | 72 +++++++++---------------------------- app/utils/hybrid_viewer.js | 72 ++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 56 deletions(-) diff --git a/app/stores/hybrid_viewer.js b/app/stores/hybrid_viewer.js index fb04ee45..7264e140 100644 --- a/app/stores/hybrid_viewer.js +++ b/app/stores/hybrid_viewer.js @@ -1,8 +1,8 @@ import { - applyCameraOptions, - centerCameraOnPosition, + applySnapshot, computeAverageBrightness, getCameraOptions, + performClickPicking, } from "@ogw_front/utils/hybrid_viewer"; import { newInstance as vtkActor } from "@kitware/vtk.js/Rendering/Core/Actor"; import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow"; @@ -190,25 +190,13 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { return; } if (is_picking.value) { - const rect = container.value.$el.getBoundingClientRect(); - const display_x = Math.round(event.clientX - rect.left); - const display_y = Math.round(rect.height - (event.clientY - rect.top)); - viewerStore.request( - viewer_schemas.opengeodeweb_viewer.viewer.get_point_position, - { x: display_x, y: display_y }, - { - response_function: (response) => { - const pickedPos = [response.x, response.y, response.z]; - if (pickedPos.some((value) => value !== 0)) { - const renderer = genericRenderWindow.value.getRenderer(); - const camera = renderer.getActiveCamera(); - centerCameraOnPosition(camera, pickedPos); - genericRenderWindow.value.getRenderWindow().render(); - syncRemoteCamera(); - } - }, - }, - ); + performClickPicking(event, { + container: container.value.$el, + viewerStore, + viewer_schemas, + genericRenderWindow: genericRenderWindow.value, + syncRemoteCamera, + }); is_picking.value = false; return; } @@ -270,40 +258,14 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { } async function importStores(snapshot) { - if (!snapshot) { - return; - } - const z_scale = snapshot.zScale; - - function applyCamera() { - const { camera_options: snapshot_camera_options } = snapshot; - if (!snapshot_camera_options) { - return; - } - - const renderer = genericRenderWindow.value.getRenderer(); - const camera = renderer.getActiveCamera(); - - applyCameraOptions(camera, snapshot_camera_options); - - genericRenderWindow.value.getRenderWindow().render(); - - const payload = { - camera_options: getCameraOptions(snapshot_camera_options), - }; - return viewerStore.request(viewer_schemas.opengeodeweb_viewer.viewer.update_camera, payload, { - response_function: () => { - remoteRender(); - Object.assign(camera_options, payload.camera_options); - }, - }); - } - - if (typeof z_scale === "number") { - await setZScaling(z_scale); - return await applyCamera(); - } - return await applyCamera(); + return await applySnapshot(snapshot, { + genericRenderWindow: genericRenderWindow.value, + viewerStore, + viewer_schemas, + camera_options, + setZScaling, + remoteRender, + }); } function clear() { diff --git a/app/utils/hybrid_viewer.js b/app/utils/hybrid_viewer.js index 94e0fe82..2960c81d 100644 --- a/app/utils/hybrid_viewer.js +++ b/app/utils/hybrid_viewer.js @@ -112,4 +112,74 @@ function centerCameraOnPosition(camera, pickedPosition) { ); } -export { applyCameraOptions, centerCameraOnPosition, computeAverageBrightness, getCameraOptions }; +function performClickPicking(event, options) { + const { container, viewerStore, viewer_schemas, genericRenderWindow, syncRemoteCamera } = options; + const rect = container.getBoundingClientRect(); + const display_x = Math.round(event.clientX - rect.left); + const display_y = Math.round(rect.height - (event.clientY - rect.top)); + viewerStore.request( + viewer_schemas.opengeodeweb_viewer.viewer.get_point_position, + { x: display_x, y: display_y }, + { + response_function: (response) => { + const pickedPos = [response.x, response.y, response.z]; + if (pickedPos.some((value) => value !== 0)) { + const camera = genericRenderWindow.getRenderer().getActiveCamera(); + centerCameraOnPosition(camera, pickedPos); + genericRenderWindow.getRenderWindow().render(); + syncRemoteCamera(); + } + }, + }, + ); +} + +async function applySnapshot(snapshot, options) { + const { + genericRenderWindow, + viewerStore, + viewer_schemas, + camera_options, + setZScaling, + remoteRender, + } = options; + if (!snapshot) { + return; + } + const z_scale = snapshot.zScale; + + function applyCamera() { + const { camera_options: snapshot_camera_options } = snapshot; + if (!snapshot_camera_options) { + return; + } + const renderer = genericRenderWindow.getRenderer(); + const camera = renderer.getActiveCamera(); + applyCameraOptions(camera, snapshot_camera_options); + genericRenderWindow.getRenderWindow().render(); + + const payload = { + camera_options: getCameraOptions(snapshot_camera_options), + }; + return viewerStore.request(viewer_schemas.opengeodeweb_viewer.viewer.update_camera, payload, { + response_function: () => { + remoteRender(); + Object.assign(camera_options, payload.camera_options); + }, + }); + } + + if (typeof z_scale === "number") { + await setZScaling(z_scale); + } + return await applyCamera(); +} + +export { + applyCameraOptions, + applySnapshot, + centerCameraOnPosition, + computeAverageBrightness, + getCameraOptions, + performClickPicking, +}; From 8b9e83fdf534b6db8b78d79002f10963b5ee25b1 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 29 Apr 2026 17:11:04 +0200 Subject: [PATCH 05/10] refacto applySnapshot for importStores and add crosshair --- app/stores/hybrid_viewer.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/stores/hybrid_viewer.js b/app/stores/hybrid_viewer.js index 7264e140..89173c4c 100644 --- a/app/stores/hybrid_viewer.js +++ b/app/stores/hybrid_viewer.js @@ -43,6 +43,15 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => { let viewStream = undefined; const gridActor = undefined; + watch(is_picking, (value) => { + const element = genericRenderWindow.value + ?.getApiSpecificRenderWindow() + ?.getCanvas()?.parentElement; + if (element) { + element.style.cursor = value ? "crosshair" : "default"; + } + }); + const latestImage = ref(undefined); const offscreenCanvas = typeof document === "undefined" ? undefined : document.createElement("canvas"); From 9aa9008788f0af24644cff45d0f8a8fdc84d536b Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 30 Apr 2026 09:21:50 +0200 Subject: [PATCH 06/10] rm constants.js --- app/components/ViewToolbar.vue | 7 +++++++ app/utils/vtk/constants.js | 31 ------------------------------- 2 files changed, 7 insertions(+), 31 deletions(-) delete mode 100644 app/utils/vtk/constants.js diff --git a/app/components/ViewToolbar.vue b/app/components/ViewToolbar.vue index 96007773..25e3f7d7 100644 --- a/app/components/ViewToolbar.vue +++ b/app/components/ViewToolbar.vue @@ -37,6 +37,13 @@ const camera_options = [ hybridViewerStore.resetCamera(); }, }, + { + tooltip: "Center on click", + icon: "mdi-crosshairs-question", + action: () => { + hybridViewerStore.is_picking = !hybridViewerStore.is_picking; + }, + }, { tooltip: "Camera orientation", icon: "mdi-rotate-3d", diff --git a/app/utils/vtk/constants.js b/app/utils/vtk/constants.js deleted file mode 100644 index 02d89ae5..00000000 --- a/app/utils/vtk/constants.js +++ /dev/null @@ -1,31 +0,0 @@ -const RGB_MAX = 255; -const BACKGROUND_GREY_VALUE = 180; -const ACTOR_DARK_VALUE = 20; - -const BACKGROUND_COLOR = [ - BACKGROUND_GREY_VALUE / RGB_MAX, - BACKGROUND_GREY_VALUE / RGB_MAX, - BACKGROUND_GREY_VALUE / RGB_MAX, -]; -const ACTOR_COLOR = [ - ACTOR_DARK_VALUE / RGB_MAX, - ACTOR_DARK_VALUE / RGB_MAX, - ACTOR_DARK_VALUE / RGB_MAX, -]; -const WHEEL_TIME_OUT_MS = 600; -const BUMP_MULTIPLIER = 0.2; -const ALIGNMENT_THRESHOLD = 0.9; -const LONG_ANIMATION_DURATION = 1000; -const SHORT_ANIMATION_DURATION = 500; -const EASE_EXPONENT = 1.1; - -export { - BACKGROUND_COLOR, - ACTOR_COLOR, - WHEEL_TIME_OUT_MS, - BUMP_MULTIPLIER, - ALIGNMENT_THRESHOLD, - LONG_ANIMATION_DURATION, - SHORT_ANIMATION_DURATION, - EASE_EXPONENT, -}; From 43cf256ff4d9c9974bf4df2b2c6c204070ea6790 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 30 Apr 2026 10:00:49 +0200 Subject: [PATCH 07/10] refacto --- app/utils/hybrid_viewer.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/utils/hybrid_viewer.js b/app/utils/hybrid_viewer.js index fef7de44..04d72b14 100644 --- a/app/utils/hybrid_viewer.js +++ b/app/utils/hybrid_viewer.js @@ -132,17 +132,17 @@ function centerCameraOnPosition(camera, pickedPosition) { function performClickPicking(event, options) { const { container, viewerStore, viewer_schemas, genericRenderWindow, syncRemoteCamera } = options; const rect = container.getBoundingClientRect(); - const display_x = Math.round(event.clientX - rect.left); - const display_y = Math.round(rect.height - (event.clientY - rect.top)); viewerStore.request( viewer_schemas.opengeodeweb_viewer.viewer.get_point_position, - { x: display_x, y: display_y }, { - response_function: (response) => { - const pickedPos = [response.x, response.y, response.z]; - if (pickedPos.some((value) => value !== 0)) { - const camera = genericRenderWindow.getRenderer().getActiveCamera(); - centerCameraOnPosition(camera, pickedPos); + x: Math.round(event.clientX - rect.left), + y: Math.round(rect.height - (event.clientY - rect.top)), + }, + { + response_function: ({ x, y, z }) => { + const pickedPos = [x, y, z]; + if (pickedPos.some((val) => val !== 0)) { + centerCameraOnPosition(genericRenderWindow.getRenderer().getActiveCamera(), pickedPos); genericRenderWindow.getRenderWindow().render(); syncRemoteCamera(); } From 5bcd39ca7d43513afc0c83babcc584e4994590d3 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 30 Apr 2026 14:14:19 +0200 Subject: [PATCH 08/10] move utils/hybrid_viewer into internal/stores/ --- app/stores/hybrid_viewer.js | 2 +- {app/utils => internal/stores}/hybrid_viewer.js | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {app/utils => internal/stores}/hybrid_viewer.js (100%) diff --git a/app/stores/hybrid_viewer.js b/app/stores/hybrid_viewer.js index 09b3eab5..dbd05268 100644 --- a/app/stores/hybrid_viewer.js +++ b/app/stores/hybrid_viewer.js @@ -12,7 +12,7 @@ import { getCameraOptions, performCameraOrientation, performClickPicking, -} from "@ogw_front/utils/hybrid_viewer"; +} from "@ogw_internal/stores/hybrid_viewer"; import { newInstance as vtkActor } from "@kitware/vtk.js/Rendering/Core/Actor"; import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow"; import { newInstance as vtkMapper } from "@kitware/vtk.js/Rendering/Core/Mapper"; diff --git a/app/utils/hybrid_viewer.js b/internal/stores/hybrid_viewer.js similarity index 100% rename from app/utils/hybrid_viewer.js rename to internal/stores/hybrid_viewer.js From 743cd9480a5549e130f9ccffdf213815e7baf799 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 30 Apr 2026 16:44:33 +0200 Subject: [PATCH 09/10] applyCameraOptions import --- app/components/CameraOrientation.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/CameraOrientation.vue b/app/components/CameraOrientation.vue index 6a0d47c8..146435f6 100644 --- a/app/components/CameraOrientation.vue +++ b/app/components/CameraOrientation.vue @@ -1,6 +1,6 @@