diff --git a/app/components/FeedBack/ErrorBanner.vue b/app/components/FeedBack/ErrorBanner.vue
index e78a9ddb7..8b04b4844 100644
--- a/app/components/FeedBack/ErrorBanner.vue
+++ b/app/components/FeedBack/ErrorBanner.vue
@@ -1,5 +1,6 @@
+
+
+
+
+
+
+ Name:
+
+ {{
+ hybridViewerStore.hoverData.component?.name ||
+ hybridViewerStore.hoverData.component?.id ||
+ hybridViewerStore.hoverData.blockName ||
+ hybridViewerStore.hoverData.modelName ||
+ `${capitalize(hybridViewerStore.hoverData.fieldType.toLowerCase())} #${hybridViewerStore.hoverData.pickedId}`
+ }}
+
+
+
+ Id:
+
+ {{ hybridViewerStore.hoverData.component?.id || hybridViewerStore.hoverData.modelId }}
+
+
+
+ Index:
+
+ {{ originalIndex }}
+
+
+
+ Type:
+
+ {{ hybridViewerStore.hoverData.component.type }}
+
+
+
+
+
+
+
+ Position:
+
+ [ {{ Number(hybridViewerStore.hoverData.attributes.coordinates[0]).toFixed(3) }},
+ {{ Number(hybridViewerStore.hoverData.attributes.coordinates[1]).toFixed(3) }},
+ {{ Number(hybridViewerStore.hoverData.attributes.coordinates[2]).toFixed(3) }} ]
+
+
+
+
+ {{ capitalize(name) }}:
+
+ {{ formatAttributeValue(val) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/InfraConnected.vue b/app/components/InfraConnected.vue
index 3c7623a8a..dcfb36350 100644
--- a/app/components/InfraConnected.vue
+++ b/app/components/InfraConnected.vue
@@ -1,5 +1,6 @@
diff --git a/app/components/Inspector/InspectionButton.vue b/app/components/Inspector/InspectionButton.vue
index 625c7894a..c9d9e7529 100644
--- a/app/components/Inspector/InspectionButton.vue
+++ b/app/components/Inspector/InspectionButton.vue
@@ -1,6 +1,7 @@
diff --git a/app/components/Viewer/Options/Sliders/Width.vue b/app/components/Viewer/Options/Sliders/Width.vue
index c699b9d61..c0b81e991 100644
--- a/app/components/Viewer/Options/Sliders/Width.vue
+++ b/app/components/Viewer/Options/Sliders/Width.vue
@@ -1,5 +1,6 @@
diff --git a/app/composables/run_function_when_microservices_connected.js b/app/composables/run_function_when_microservices_connected.js
index 0248f2208..e12371e56 100644
--- a/app/composables/run_function_when_microservices_connected.js
+++ b/app/composables/run_function_when_microservices_connected.js
@@ -1,4 +1,5 @@
import { useInfraStore } from "@ogw_front/stores/infra";
+
export function run_function_when_microservices_connected(function_to_run) {
const infraStore = useInfraStore();
const { microservices_connected } = storeToRefs(infraStore);
diff --git a/app/stores/hybrid_viewer.js b/app/stores/hybrid_viewer.js
index bce5237fd..a85936844 100644
--- a/app/stores/hybrid_viewer.js
+++ b/app/stores/hybrid_viewer.js
@@ -1,14 +1,15 @@
import {
ACTOR_COLOR,
BACKGROUND_COLOR,
- HOVER_THROTTLE_MS,
WHEEL_TIME_OUT_MS,
computeAverageBrightness,
performAddItem,
+ performClear,
performClearHoverHighlight,
performClickPicking,
- performHoverHighlight,
+ performRemoveItem,
performSetContainer,
+ performSetVisibility,
performSetZScaling,
} from "@ogw_internal/stores/hybrid_viewer";
import {
@@ -19,6 +20,10 @@ import {
performSetCamera,
performSyncRemoteCamera,
} from "@ogw_internal/stores/hybrid_viewer_camera";
+import {
+ createClearHoverData,
+ createHoverHighlight,
+} from "@ogw_internal/stores/hybrid_viewer_highlight";
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";
@@ -30,6 +35,7 @@ import { useViewerStore } from "@ogw_front/stores/viewer";
import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
+// oxlint-disable max-lines-per-function max-statements
export const useHybridViewerStore = defineStore("hybridViewer", () => {
const dataStore = useDataStore();
const hybridDb = reactive({});
@@ -41,6 +47,8 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
const is_picking = ref(false);
const is_hover_highlight = ref(false);
const hover_highlight_field_type = ref("CELL");
+ const hoverData = ref(undefined);
+ const hoverPosition = ref({ x: 0, y: 0 });
const zScale = ref(1);
let imageStyle = undefined;
let viewStream = undefined;
@@ -106,20 +114,14 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
}
function removeItem(id) {
- if (!hybridDb[id]) {
- return;
- }
- genericRenderWindow.value.getRenderer().removeActor(hybridDb[id].actor);
- genericRenderWindow.value.getRenderWindow().render();
- delete hybridDb[id];
+ performRemoveItem(id, { genericRenderWindow: genericRenderWindow.value, hybridDb });
}
function setVisibility(id, visibility) {
- if (!hybridDb[id]) {
- return;
- }
- hybridDb[id].actor.setVisibility(visibility);
- genericRenderWindow.value.getRenderWindow().render();
+ performSetVisibility(id, visibility, {
+ genericRenderWindow: genericRenderWindow.value,
+ hybridDb,
+ });
}
async function setZScaling(z_scale) {
@@ -184,20 +186,27 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
return viewerStore.request(viewer_schemas.opengeodeweb_viewer.viewer.render);
}
- const throttledHoverHighlight = useThrottleFn(
- (event) =>
- performHoverHighlight(event, {
- is_hover_highlight,
- genericRenderWindow: genericRenderWindow.value,
- viewerStore,
- viewer_schemas,
- hover_highlight_field_type,
- hybridDb,
- }),
- HOVER_THROTTLE_MS,
- );
+ const hoverTimeoutRef = ref(undefined);
+ const currentHoverId = ref(undefined);
+
+ const clearHoverData = createClearHoverData(hoverTimeoutRef, hoverData, currentHoverId);
+
+ const hoverHighlight = createHoverHighlight({
+ genericRenderWindow,
+ is_hover_highlight,
+ viewerStore,
+ viewer_schemas,
+ hover_highlight_field_type,
+ hybridDb,
+ hoverData,
+ hoverPosition,
+ currentHoverId,
+ hoverTimeoutRef,
+ clearHoverData,
+ });
function clearHoverHighlight() {
+ clearHoverData();
performClearHoverHighlight({
viewerStore,
viewer_schemas,
@@ -220,7 +229,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
viewerStore,
viewer_schemas,
syncRemoteCamera,
- throttledHoverHighlight,
+ hoverHighlight,
wheelTimeoutMs: WHEEL_TIME_OUT_MS,
wheelEventEndTimeout,
wheelTimeoutSetter: (timeout) => (wheelEventEndTimeout = timeout),
@@ -266,14 +275,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
}
function clear() {
- const renderer = genericRenderWindow.value.getRenderer();
- for (const actor of renderer.getActors()) {
- renderer.removeActor(actor);
- }
- genericRenderWindow.value.getRenderWindow().render();
- for (const id of Object.keys(hybridDb)) {
- delete hybridDb[id];
- }
+ performClear({ genericRenderWindow: genericRenderWindow.value, hybridDb });
}
return {
@@ -297,6 +299,8 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
is_hover_highlight,
hover_highlight_field_type,
clearHoverHighlight,
+ hoverData,
+ hoverPosition,
clear,
exportStores,
importStores,
diff --git a/app/stores/treeview.js b/app/stores/treeview.js
index e3db57de0..2dc6443fc 100644
--- a/app/stores/treeview.js
+++ b/app/stores/treeview.js
@@ -3,6 +3,7 @@ import { defineStore } from "pinia";
import { ref, toRaw, watch } from "vue";
import { compareSelections } from "@ogw_front/utils/treeview";
import { database } from "@ogw_internal/database/database";
+
const PANEL_WIDTH = 300;
export const useTreeviewStore = defineStore("treeview", () => {
diff --git a/commitlint.config.js b/commitlint.config.js
index 0055b515b..0c07c2645 100644
--- a/commitlint.config.js
+++ b/commitlint.config.js
@@ -14,6 +14,7 @@ const Configuration = {
"type-empty": [0],
"type-enum": [2, "always", ["feat", "fix", "perf"]],
},
+ defaultIgnores: false,
};
export default Configuration;
diff --git a/internal/stores/hybrid_viewer.js b/internal/stores/hybrid_viewer.js
index ff539e708..c31dfc2f7 100644
--- a/internal/stores/hybrid_viewer.js
+++ b/internal/stores/hybrid_viewer.js
@@ -16,6 +16,7 @@ const ACTOR_COLOR = [
];
const WHEEL_TIME_OUT_MS = 600;
const HOVER_THROTTLE_MS = 50;
+const HOVER_TIMEOUT_MS = 500;
const SAMPLE_SIZE = 10;
const TOTAL_CHANNELS = 400;
@@ -112,7 +113,7 @@ function performHoverHighlight(event, options) {
}
const rect = container.getBoundingClientRect();
viewerStore.request(
- viewer_schemas.opengeodeweb_viewer.viewer.hover_highlight,
+ viewer_schemas.opengeodeweb_viewer.viewer.highlight,
{
x: Math.round(event.clientX - rect.left),
y: Math.round(rect.height - (event.clientY - rect.top)),
@@ -127,7 +128,7 @@ function performHoverHighlight(event, options) {
function performClearHoverHighlight(options) {
const { viewerStore, viewer_schemas, hover_highlight_field_type, hybridDb } = options;
- viewerStore.request(viewer_schemas.opengeodeweb_viewer.viewer.hover_highlight, {
+ viewerStore.request(viewer_schemas.opengeodeweb_viewer.viewer.highlight, {
x: -1,
y: -1,
field_type: hover_highlight_field_type.value,
@@ -200,7 +201,7 @@ function performSetContainer(options) {
viewerStore,
viewer_schemas,
syncRemoteCamera,
- throttledHoverHighlight,
+ hoverHighlight,
wheelTimeoutMs,
wheelEventEndTimeout,
wheelTimeoutSetter,
@@ -251,7 +252,7 @@ function performSetContainer(options) {
},
});
- useEventListener(container, "mousemove", throttledHoverHighlight);
+ useEventListener(container, "mousemove", hoverHighlight);
useEventListener(container, "wheel", () => {
is_moving.value = true;
if (imageStyle) {
@@ -268,11 +269,43 @@ function performSetContainer(options) {
});
}
+function performRemoveItem(id, options) {
+ const { genericRenderWindow, hybridDb } = options;
+ if (!hybridDb[id]) {
+ return;
+ }
+ genericRenderWindow.getRenderer().removeActor(hybridDb[id].actor);
+ genericRenderWindow.getRenderWindow().render();
+ delete hybridDb[id];
+}
+
+function performSetVisibility(id, visibility, options) {
+ const { genericRenderWindow, hybridDb } = options;
+ if (!hybridDb[id]) {
+ return;
+ }
+ hybridDb[id].actor.setVisibility(visibility);
+ genericRenderWindow.getRenderWindow().render();
+}
+
+function performClear(options) {
+ const { genericRenderWindow, hybridDb } = options;
+ const renderer = genericRenderWindow.getRenderer();
+ for (const actor of renderer.getActors()) {
+ renderer.removeActor(actor);
+ }
+ genericRenderWindow.getRenderWindow().render();
+ for (const id of Object.keys(hybridDb)) {
+ delete hybridDb[id];
+ }
+}
+
export {
BACKGROUND_COLOR,
ACTOR_COLOR,
WHEEL_TIME_OUT_MS,
HOVER_THROTTLE_MS,
+ HOVER_TIMEOUT_MS,
computeAverageBrightness,
performAddItem,
performClearHoverHighlight,
@@ -280,4 +313,7 @@ export {
performHoverHighlight,
performSetContainer,
performSetZScaling,
+ performRemoveItem,
+ performSetVisibility,
+ performClear,
};
diff --git a/internal/stores/hybrid_viewer_highlight.js b/internal/stores/hybrid_viewer_highlight.js
new file mode 100644
index 000000000..c4949abdc
--- /dev/null
+++ b/internal/stores/hybrid_viewer_highlight.js
@@ -0,0 +1,113 @@
+import { HOVER_THROTTLE_MS, HOVER_TIMEOUT_MS, performHoverHighlight } from "./hybrid_viewer";
+import { database } from "@ogw_internal/database/database.js";
+
+function createClearHoverData(hoverTimeoutRef, hoverData, currentHoverId) {
+ return function clearHoverData() {
+ if (hoverTimeoutRef.value) {
+ clearTimeout(hoverTimeoutRef.value);
+ hoverTimeoutRef.value = undefined;
+ }
+ hoverData.value = undefined;
+ currentHoverId.value = undefined;
+ };
+}
+
+function createHoverHighlight(options) {
+ const {
+ genericRenderWindow,
+ is_hover_highlight,
+ viewerStore,
+ viewer_schemas,
+ hover_highlight_field_type,
+ hybridDb,
+ hoverData,
+ hoverPosition,
+ currentHoverId,
+ hoverTimeoutRef,
+ clearHoverData,
+ } = options;
+
+ return useThrottleFn((event) => {
+ const containerElement = genericRenderWindow.value?.getContainer();
+ let relativeMousePosition = { x: 0, y: 0 };
+ if (containerElement) {
+ const containerRect = containerElement.getBoundingClientRect();
+ relativeMousePosition = {
+ x: event.clientX - containerRect.left,
+ y: event.clientY - containerRect.top,
+ };
+ } else {
+ relativeMousePosition = { x: event.clientX, y: event.clientY };
+ }
+
+ performHoverHighlight(event, {
+ is_hover_highlight,
+ genericRenderWindow: genericRenderWindow.value,
+ viewerStore,
+ viewer_schemas,
+ hover_highlight_field_type,
+ hybridDb,
+ onResponse: async (response) => {
+ const isResponseValid =
+ response && response.id && response.picked_id !== undefined && response.picked_id !== -1;
+ if (!is_hover_highlight.value || !isResponseValid) {
+ clearHoverData();
+ return;
+ }
+
+ const hoverKey = `${response.id}_${response.field_type}_${response.picked_id}`;
+ if (currentHoverId.value === hoverKey) {
+ return;
+ }
+
+ if (hoverTimeoutRef.value) {
+ clearTimeout(hoverTimeoutRef.value);
+ hoverTimeoutRef.value = undefined;
+ }
+
+ hoverData.value = undefined;
+ currentHoverId.value = hoverKey;
+
+ let componentInfo = undefined;
+ let modelName = undefined;
+
+ const modelRecord = await database.data.get(response.id);
+ if (modelRecord) {
+ modelName = modelRecord.name;
+ }
+
+ if (response.geode_id) {
+ const component = await database.model_components
+ .where("[id+geode_id]")
+ .equals([response.id, response.geode_id])
+ .first();
+ if (component) {
+ componentInfo = {
+ name: component.name,
+ id: component.geode_id,
+ type: component.type,
+ };
+ }
+ }
+
+ const newHoverData = {
+ modelId: response.id,
+ modelName,
+ blockName: response.geode_id,
+ pickedId: response.picked_id,
+ fieldType: response.field_type,
+ component: componentInfo,
+ attributes: response.attributes || {},
+ };
+
+ hoverTimeoutRef.value = setTimeout(() => {
+ hoverPosition.value = relativeMousePosition;
+ hoverData.value = newHoverData;
+ hoverTimeoutRef.value = undefined;
+ }, HOVER_TIMEOUT_MS);
+ },
+ });
+ }, HOVER_THROTTLE_MS);
+}
+
+export { createClearHoverData, createHoverHighlight };