@geode/opengeodeweb-front 10.14.1 → 10.14.2-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.oxlintrc.json +12 -2
  2. package/app/components/Basic/Slider.vue +7 -0
  3. package/app/components/Basic/Switch.vue +7 -0
  4. package/app/components/HybridRenderingView.vue +1 -1
  5. package/app/components/SearchBar.vue +1 -1
  6. package/app/components/Viewer/ContextMenu.vue +1 -3
  7. package/app/components/Viewer/ContextMenuItem.vue +2 -2
  8. package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +5 -9
  9. package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +7 -2
  10. package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +11 -9
  11. package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +14 -9
  12. package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +10 -2
  13. package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +10 -2
  14. package/app/components/Viewer/Generic/Model/PointsOptions.vue +2 -14
  15. package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +189 -0
  16. package/app/components/Viewer/ObjectTree/Base/Controls.vue +124 -38
  17. package/app/components/Viewer/ObjectTree/Base/ItemLabel.vue +43 -18
  18. package/app/components/Viewer/ObjectTree/Base/StickyHeader.vue +46 -0
  19. package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +77 -0
  20. package/app/components/Viewer/ObjectTree/Box.vue +106 -15
  21. package/app/components/Viewer/ObjectTree/Layout.vue +14 -12
  22. package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +48 -33
  23. package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +102 -66
  24. package/app/components/Viewer/Options/Sliders/Size.vue +8 -0
  25. package/app/components/Viewer/Options/Sliders/Slider.vue +17 -0
  26. package/app/components/Viewer/Options/Sliders/Width.vue +8 -0
  27. package/app/components/Viewer/Options/VisibilitySwitch.vue +3 -1
  28. package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +1 -5
  29. package/app/components/Viewer/Surface/Triangulated/TrianglesOptions.vue +11 -0
  30. package/app/composables/hover_highlight.js +85 -0
  31. package/app/composables/model_components.js +68 -0
  32. package/app/composables/{use_tree_filter.js → tree_filter.js} +49 -31
  33. package/app/composables/tree_keyboard_nav.js +81 -0
  34. package/app/composables/tree_scroll.js +91 -0
  35. package/app/composables/virtual_tree.js +164 -0
  36. package/app/stores/data.js +41 -1
  37. package/app/stores/hybrid_viewer.js +30 -38
  38. package/app/stores/menu.js +8 -14
  39. package/app/utils/hybrid_viewer.js +101 -0
  40. package/package.json +3 -3
  41. package/tests/integration/setup.js +2 -1
  42. package/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +2 -3
  43. package/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +2 -3
  44. package/tests/integration/stores/data_style/mesh/index.nuxt.test.js +2 -3
  45. package/tests/integration/stores/data_style/mesh/points.nuxt.test.js +2 -3
  46. package/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +2 -3
  47. package/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +2 -3
  48. package/tests/integration/stores/data_style/model/blocks.nuxt.test.js +2 -3
  49. package/tests/integration/stores/data_style/model/corners.nuxt.test.js +2 -3
  50. package/tests/integration/stores/data_style/model/edges.nuxt.test.js +2 -3
  51. package/tests/integration/stores/data_style/model/index.nuxt.test.js +2 -3
  52. package/tests/integration/stores/data_style/model/lines.nuxt.test.js +2 -3
  53. package/tests/integration/stores/data_style/model/points.nuxt.test.js +2 -3
  54. package/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +2 -3
  55. package/tests/integration/stores/viewer.nuxt.test.js +2 -2
  56. package/app/components/Viewer/HybridSolid/EdgesOptions.vue +0 -12
  57. package/app/components/Viewer/HybridSolid/PointsOptions.vue +0 -12
  58. package/app/components/Viewer/HybridSolid/PolygonsOptions.vue +0 -12
  59. package/app/components/Viewer/HybridSolid/PolyhedraOptions.vue +0 -12
  60. package/app/components/Viewer/PolygonalSurface/EdgesOptions.vue +0 -12
  61. package/app/components/Viewer/PolygonalSurface/PointsOptions.vue +0 -12
  62. package/app/components/Viewer/TriangulatedSurface/TrianglesOptions.vue +0 -16
  63. package/app/composables/use_hover_highlight.js +0 -48
  64. package/tests/integration/microservices/back/requirements.txt +0 -7
  65. package/tests/integration/microservices/viewer/requirements.txt +0 -7
  66. /package/app/components/Viewer/{TriangulatedSurface → Surface}/EdgesOptions.vue +0 -0
  67. /package/app/components/Viewer/{TriangulatedSurface → Surface}/PointsOptions.vue +0 -0
  68. /package/app/components/Viewer/{PolygonalSurface/SpecificPolygonsOptions.vue → Surface/PolygonsOptions.vue} +0 -0
@@ -2,30 +2,45 @@ function customFilter(value, searchQuery, item) {
2
2
  if (!searchQuery) {
3
3
  return true;
4
4
  }
5
+ if (!item || !item.raw) {
6
+ return false;
7
+ }
5
8
  const query = searchQuery.toLowerCase();
6
9
  const { title = "", id = value } = item.raw || {};
7
10
  return [title, id].some((field) => String(field).toLowerCase().includes(query));
8
11
  }
9
12
 
10
- function sortAndFormatItems(items, sortType) {
13
+ function sortAndFormatItems(itemList, sortType, options = {}) {
14
+ if (!itemList || !Array.isArray(itemList)) {
15
+ return [];
16
+ }
11
17
  const field = sortType === "name" ? "title" : "id";
12
- return items.map((category) => {
13
- const children = (category.children || []).toSorted((itemA, itemB) => {
14
- const valueA = itemA[field] || "";
15
- const valueB = itemB[field] || "";
16
- return valueA.localeCompare(valueB, undefined, {
17
- numeric: true,
18
- sensitivity: "base",
19
- });
18
+ const localeOptions = { numeric: true, sensitivity: "base" };
19
+
20
+ const sorted = itemList
21
+ .filter((item) => item !== null && item !== undefined)
22
+ .toSorted((itemA, itemB) => {
23
+ const fieldA = String(itemA[field] || itemA.id || "");
24
+ const fieldB = String(itemB[field] || itemB.id || "");
25
+ return fieldA.localeCompare(fieldB, undefined, localeOptions);
20
26
  });
21
- return {
22
- ...category,
23
- children,
24
- };
25
- });
27
+
28
+ if (options.recursiveSort) {
29
+ return sorted.map((item) => {
30
+ if (item.children && item.children.length > 0) {
31
+ return {
32
+ ...item,
33
+ children: sortAndFormatItems(item.children, sortType, options),
34
+ };
35
+ }
36
+ return item;
37
+ });
38
+ }
39
+ return sorted;
26
40
  }
27
41
 
28
- function useTreeFilter(rawItems, options = {}) {
42
+ function useTreeFilter(itemsIn, options = {}) {
43
+ const rawItems = typeof itemsIn === "function" ? computed(itemsIn) : toRef(itemsIn);
29
44
  const search = ref("");
30
45
  const sortType = ref(options.defaultSort || "name");
31
46
  const filterOptions = ref(options.defaultFilters || {});
@@ -53,24 +68,27 @@ function useTreeFilter(rawItems, options = {}) {
53
68
  if (!rawItems.value) {
54
69
  return [];
55
70
  }
56
- const sorted = sortAndFormatItems(
57
- rawItems.value.filter((category) => {
58
- const key = category.title || category.id;
59
- return filterOptions.value[key] !== false;
60
- }),
61
- sortType.value,
62
- );
71
+ const filteredByCategory = rawItems.value.filter((category) => {
72
+ const key = category.title || category.id;
73
+ return filterOptions.value[key] !== false;
74
+ });
75
+
76
+ const sorted = sortAndFormatItems(filteredByCategory, sortType.value, options);
77
+
63
78
  if (!search.value) {
64
79
  return sorted;
65
80
  }
66
- return sorted
67
- .map((category) => {
68
- category.children = (category.children || []).filter((child) =>
69
- customFilter(child.id, search.value, { raw: child }),
70
- );
71
- return category;
72
- })
73
- .filter((category) => category.children.length > 0);
81
+
82
+ const result = [];
83
+ for (const category of sorted) {
84
+ const children = (category.children || []).filter((child) =>
85
+ customFilter(child.id, search.value, { raw: child }),
86
+ );
87
+ if (children.length > 0 || customFilter(category.id, search.value, { raw: category })) {
88
+ result.push({ ...category, children });
89
+ }
90
+ }
91
+ return result;
74
92
  });
75
93
 
76
94
  function toggleSort() {
@@ -117,4 +135,4 @@ function useTreeFilter(rawItems, options = {}) {
117
135
  };
118
136
  }
119
137
 
120
- export { customFilter, useTreeFilter };
138
+ export { customFilter, useTreeFilter, sortAndFormatItems };
@@ -0,0 +1,81 @@
1
+ export function useTreeKeyboardNav(displayItems, emit, scrollToIndex, toggleOpen, handleItemClick) {
2
+ const focusedIndex = ref(-1);
3
+
4
+ function findParentIndex(item, currentIndex) {
5
+ for (let index = currentIndex - 1; index >= 0; index -= 1) {
6
+ if (displayItems.value[index].depth < item.depth) {
7
+ return index;
8
+ }
9
+ }
10
+ return -1;
11
+ }
12
+
13
+ function getNextIndex(key, item) {
14
+ const lastIndex = displayItems.value.length - 1;
15
+ const currentIndex = focusedIndex.value;
16
+
17
+ if (key === "ArrowDown") {
18
+ return Math.min(currentIndex + 1, lastIndex);
19
+ }
20
+ if (key === "ArrowUp") {
21
+ return Math.max(currentIndex - 1, 0);
22
+ }
23
+ if (key === "Home") {
24
+ return 0;
25
+ }
26
+ if (key === "End") {
27
+ return lastIndex;
28
+ }
29
+
30
+ if (key === "ArrowRight") {
31
+ if (!item.isLeaf && !item.isOpen) {
32
+ toggleOpen(item.raw);
33
+ return currentIndex;
34
+ }
35
+ return Math.min(currentIndex + 1, lastIndex);
36
+ }
37
+
38
+ if (key === "ArrowLeft") {
39
+ if (!item.isLeaf && item.isOpen) {
40
+ toggleOpen(item.raw);
41
+ return currentIndex;
42
+ }
43
+ const parentIndex = findParentIndex(item, currentIndex);
44
+ return parentIndex === -1 ? currentIndex : parentIndex;
45
+ }
46
+
47
+ return currentIndex;
48
+ }
49
+
50
+ function handleKeyDown(event) {
51
+ if (displayItems.value.length === 0) {
52
+ return;
53
+ }
54
+
55
+ const prevIndex = focusedIndex.value;
56
+ const item = displayItems.value[focusedIndex.value];
57
+
58
+ if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) {
59
+ event.preventDefault();
60
+ focusedIndex.value = getNextIndex(event.key, item);
61
+ } else if (event.key === "Enter" || event.key === " ") {
62
+ event.preventDefault();
63
+ if (focusedIndex.value !== -1) {
64
+ handleItemClick(item);
65
+ }
66
+ }
67
+
68
+ if (focusedIndex.value !== prevIndex) {
69
+ if (prevIndex !== -1) {
70
+ emit("hover:leave", { item: displayItems.value[prevIndex] });
71
+ }
72
+ emit("hover:enter", {
73
+ item: displayItems.value[focusedIndex.value],
74
+ immediate: true,
75
+ });
76
+ scrollToIndex(focusedIndex.value);
77
+ }
78
+ }
79
+
80
+ return { focusedIndex, handleKeyDown };
81
+ }
@@ -0,0 +1,91 @@
1
+ export function useTreeScroll(propsIn, emit, displayItems, actualItemProps) {
2
+ const SCROLL_STICKY_THRESHOLD = 10;
3
+ const DEFAULT_ITEM_HEIGHT = 44;
4
+
5
+ const props = toRef(propsIn);
6
+ const internalScrollTop = ref(props.value.scrollTop || 0);
7
+ const virtualScrollRef = ref(undefined);
8
+
9
+ function handleScroll(event) {
10
+ internalScrollTop.value = event.target.scrollTop;
11
+ emit("update:scrollTop", event.target.scrollTop);
12
+ }
13
+
14
+ watch(
15
+ () => props.value.scrollTop,
16
+ (newVal) => {
17
+ if (Math.abs(newVal - internalScrollTop.value) > 1) {
18
+ internalScrollTop.value = newVal;
19
+ if (virtualScrollRef.value && virtualScrollRef.value.$el) {
20
+ virtualScrollRef.value.$el.scrollTop = newVal;
21
+ }
22
+ }
23
+ },
24
+ );
25
+
26
+ const stickyHeader = computed(() => {
27
+ if (internalScrollTop.value <= SCROLL_STICKY_THRESHOLD) {
28
+ return undefined;
29
+ }
30
+
31
+ const itemHeight = actualItemProps.value.height || DEFAULT_ITEM_HEIGHT;
32
+ const firstVisibleIndex = Math.floor(internalScrollTop.value / itemHeight);
33
+
34
+ if (firstVisibleIndex < 0 || firstVisibleIndex >= displayItems.value.length) {
35
+ return undefined;
36
+ }
37
+
38
+ const firstVisibleItem = displayItems.value[firstVisibleIndex];
39
+ if (!firstVisibleItem) {
40
+ return undefined;
41
+ }
42
+
43
+ let current = firstVisibleIndex;
44
+ const firstVisibleDepth = firstVisibleItem.depth;
45
+
46
+ while (current >= 0) {
47
+ const item = displayItems.value[current];
48
+ if (item && !item.isLeaf && item.depth < firstVisibleDepth) {
49
+ return item;
50
+ }
51
+ if (item && item.depth === 0 && !item.isLeaf && current < firstVisibleIndex) {
52
+ return item;
53
+ }
54
+ current -= 1;
55
+ }
56
+ return undefined;
57
+ });
58
+
59
+ function scrollToIndex(index) {
60
+ if (index === -1 || !virtualScrollRef.value) {
61
+ return;
62
+ }
63
+
64
+ const container = virtualScrollRef.value.$el;
65
+ if (!container) {
66
+ return;
67
+ }
68
+
69
+ const itemHeight = actualItemProps.value.height;
70
+ const itemTop = index * itemHeight;
71
+ const itemBottom = itemTop + itemHeight;
72
+
73
+ const currentScrollTop = container.scrollTop;
74
+ const containerHeight = container.clientHeight;
75
+ const scrollBottom = currentScrollTop + containerHeight;
76
+
77
+ if (itemTop < currentScrollTop) {
78
+ container.scrollTop = itemTop;
79
+ } else if (itemBottom > scrollBottom) {
80
+ container.scrollTop = itemBottom - containerHeight;
81
+ }
82
+ }
83
+
84
+ return {
85
+ internalScrollTop,
86
+ virtualScrollRef,
87
+ stickyHeader,
88
+ handleScroll,
89
+ scrollToIndex,
90
+ };
91
+ }
@@ -0,0 +1,164 @@
1
+ export function useVirtualTree(propsIn, emit) {
2
+ const props = toRef(propsIn);
3
+
4
+ const actualItemProps = computed(() => ({
5
+ value: "id",
6
+ title: "title",
7
+ children: "children",
8
+ height: 44,
9
+ ...props.value.itemProps,
10
+ }));
11
+
12
+ const actualSelection = computed(() => ({
13
+ selectable: false,
14
+ strategy: "classic",
15
+ ...props.value.selection,
16
+ }));
17
+
18
+ const openedSet = computed(() => new Set(props.value.opened));
19
+ const selectedSet = computed(() => new Set(props.value.selected));
20
+
21
+ function toggleOpen(item) {
22
+ const id = item[actualItemProps.value.value];
23
+ const { opened: openedArray = [] } = props.value;
24
+ const newOpened = new Set(openedArray);
25
+ if (newOpened.has(id)) {
26
+ newOpened.delete(id);
27
+ } else {
28
+ newOpened.add(id);
29
+ }
30
+ emit("update:opened", [...newOpened]);
31
+ }
32
+
33
+ function getAllChildrenIds(item, ids = []) {
34
+ const children = item[actualItemProps.value.children];
35
+ if (children) {
36
+ for (const child of children) {
37
+ ids.push(child[actualItemProps.value.value]);
38
+ getAllChildrenIds(child, ids);
39
+ }
40
+ }
41
+ return ids;
42
+ }
43
+
44
+ function isSelected(item) {
45
+ const id = item[actualItemProps.value.value];
46
+ if (selectedSet.value.has(id)) {
47
+ return true;
48
+ }
49
+ if (actualSelection.value.strategy === "classic") {
50
+ const childrenIds = getAllChildrenIds(item);
51
+ return (
52
+ childrenIds.length > 0 && childrenIds.every((childId) => selectedSet.value.has(childId))
53
+ );
54
+ }
55
+ return false;
56
+ }
57
+
58
+ function getIndeterminate(item) {
59
+ if (actualSelection.value.strategy !== "classic") {
60
+ return false;
61
+ }
62
+ const childrenIds = getAllChildrenIds(item);
63
+ if (childrenIds.length === 0) {
64
+ return false;
65
+ }
66
+
67
+ const selectedChildren = childrenIds.filter((childId) => selectedSet.value.has(childId));
68
+ return selectedChildren.length > 0 && selectedChildren.length < childrenIds.length;
69
+ }
70
+
71
+ function toggleSelect(item) {
72
+ const id = item[actualItemProps.value.value];
73
+ const { selected: selectedArray = [] } = props.value;
74
+ const newSelected = new Set(selectedArray);
75
+ const isCurrentlySelected = newSelected.has(id) || isSelected(item);
76
+
77
+ if (actualSelection.value.strategy === "classic") {
78
+ const childrenIds = getAllChildrenIds(item);
79
+ if (isCurrentlySelected) {
80
+ newSelected.delete(id);
81
+ for (const childId of childrenIds) {
82
+ newSelected.delete(childId);
83
+ }
84
+ } else {
85
+ newSelected.add(id);
86
+ for (const childId of childrenIds) {
87
+ newSelected.add(childId);
88
+ }
89
+ }
90
+ } else if (isCurrentlySelected) {
91
+ newSelected.delete(id);
92
+ } else {
93
+ newSelected.add(id);
94
+ }
95
+ emit("update:selected", [...newSelected]);
96
+ }
97
+
98
+ function flattenTree(itemsList, depth = 0, result = []) {
99
+ const { search, customFilter } = props.value;
100
+ const lowerSearch = search ? search.toLowerCase() : "";
101
+
102
+ for (const item of itemsList) {
103
+ const id = item[actualItemProps.value.value];
104
+ const children = item[actualItemProps.value.children];
105
+ const hasChildren = children && children.length > 0;
106
+
107
+ const isOpen = openedSet.value.has(id);
108
+
109
+ if (lowerSearch) {
110
+ const matches = customFilter
111
+ ? customFilter(id, search, { raw: item })
112
+ : (item[actualItemProps.value.title] || "").toLowerCase().includes(lowerSearch) ||
113
+ String(id).toLowerCase().includes(lowerSearch);
114
+
115
+ if (!hasChildren && !matches) {
116
+ continue;
117
+ }
118
+
119
+ if (hasChildren) {
120
+ const subtree = [];
121
+ flattenTree(children, depth + 1, subtree);
122
+ if (subtree.length === 0 && !matches) {
123
+ continue;
124
+ }
125
+
126
+ result.push({
127
+ raw: item,
128
+ id,
129
+ depth,
130
+ isOpen: true,
131
+ isLeaf: false,
132
+ });
133
+ result.push(...subtree);
134
+ continue;
135
+ }
136
+ }
137
+
138
+ result.push({
139
+ raw: item,
140
+ id,
141
+ depth,
142
+ isOpen,
143
+ isLeaf: !hasChildren,
144
+ });
145
+
146
+ if (isOpen && hasChildren) {
147
+ flattenTree(children, depth + 1, result);
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+
153
+ const displayItems = computed(() => flattenTree(props.value.items || []));
154
+
155
+ return {
156
+ actualItemProps,
157
+ actualSelection,
158
+ displayItems,
159
+ toggleOpen,
160
+ toggleSelect,
161
+ isSelected,
162
+ getIndeterminate,
163
+ };
164
+ }
@@ -63,12 +63,49 @@ export const useDataStore = defineStore("data", () => {
63
63
  id: meshComponent.geode_id,
64
64
  title: meshComponent.name,
65
65
  category: meshComponent.type,
66
- viewer_id: meshComponent.viewer_id,
66
+ viewer_id: Number(meshComponent.viewer_id),
67
67
  is_active: meshComponent.is_active,
68
68
  })),
69
69
  }));
70
70
  }
71
71
 
72
+ async function getMeshComponentsByType(modelId, type) {
73
+ const components = await database.model_components
74
+ .where("[id+type]")
75
+ .equals([modelId, type])
76
+ .toArray();
77
+ return components.map((meshComponent) => ({
78
+ id: meshComponent.geode_id,
79
+ title: meshComponent.name,
80
+ category: meshComponent.type,
81
+ viewer_id: Number(meshComponent.viewer_id),
82
+ is_active: meshComponent.is_active,
83
+ }));
84
+ }
85
+
86
+ async function getAllMeshComponents(modelId) {
87
+ const items = await database.model_components.where("id").equals(modelId).toArray();
88
+ return items.map((meshComponent) => ({
89
+ id: meshComponent.geode_id,
90
+ title: meshComponent.name,
91
+ category: meshComponent.type,
92
+ viewer_id: Number(meshComponent.viewer_id),
93
+ is_active: meshComponent.is_active,
94
+ }));
95
+ }
96
+
97
+ async function fetchAllMeshComponents(modelId) {
98
+ const components = await getAllMeshComponents(modelId);
99
+ const byType = {};
100
+ for (const component of components) {
101
+ if (!byType[component.category]) {
102
+ byType[component.category] = [];
103
+ }
104
+ byType[component.category].push(component);
105
+ }
106
+ return byType;
107
+ }
108
+
72
109
  function refFormatedMeshComponents(modelId) {
73
110
  return useObservable(
74
111
  liveQuery(() => formatedMeshComponents(modelId)),
@@ -240,6 +277,8 @@ export const useDataStore = defineStore("data", () => {
240
277
  meshComponentType,
241
278
  formatedMeshComponents,
242
279
  refFormatedMeshComponents,
280
+ getMeshComponentsByType,
281
+ getAllMeshComponents,
243
282
  registerObject,
244
283
  deregisterObject,
245
284
  addItem,
@@ -259,5 +298,6 @@ export const useDataStore = defineStore("data", () => {
259
298
  exportStores,
260
299
  importStores,
261
300
  clear,
301
+ fetchAllMeshComponents,
262
302
  };
263
303
  });
@@ -1,13 +1,17 @@
1
- // oxlint-disable-next-line import/no-unassigned-import
2
- import "@kitware/vtk.js/Rendering/Profiles/Geometry";
1
+ import {
2
+ applyCameraOptions,
3
+ computeAverageBrightness,
4
+ getCameraOptions,
5
+ } from "@ogw_front/utils/hybrid_viewer";
3
6
  import { newInstance as vtkActor } from "@kitware/vtk.js/Rendering/Core/Actor";
4
7
  import { newInstance as vtkGenericRenderWindow } from "@kitware/vtk.js/Rendering/Misc/GenericRenderWindow";
5
8
  import { newInstance as vtkMapper } from "@kitware/vtk.js/Rendering/Core/Mapper";
6
9
  import { newInstance as vtkXMLPolyDataReader } from "@kitware/vtk.js/IO/XML/XMLPolyDataReader";
7
10
 
8
- import { Status } from "@ogw_front/utils/status";
9
11
  import { useDataStore } from "@ogw_front/stores/data";
10
12
  import { useViewerStore } from "@ogw_front/stores/viewer";
13
+
14
+ import { Status } from "@ogw_front/utils/status";
11
15
  import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
12
16
 
13
17
  const RGB_MAX = 255;
@@ -37,6 +41,13 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
37
41
  let viewStream = undefined;
38
42
  const gridActor = undefined;
39
43
 
44
+ const latestImage = ref(undefined);
45
+ const offscreenCanvas =
46
+ typeof document === "undefined" ? undefined : document.createElement("canvas");
47
+ const offscreenCtx = offscreenCanvas
48
+ ? offscreenCanvas.getContext("2d", { willReadFrequently: true })
49
+ : undefined;
50
+
40
51
  async function initHybridViewer() {
41
52
  if (status.value !== Status.NOT_CREATED) {
42
53
  return;
@@ -58,6 +69,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
58
69
  if (is_moving.value) {
59
70
  return;
60
71
  }
72
+ latestImage.value = event.image;
61
73
  webGLRenderWindow.setBackgroundImage(event.image);
62
74
  imageStyle.opacity = 1;
63
75
  });
@@ -136,18 +148,10 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
136
148
  }
137
149
 
138
150
  function syncRemoteCamera() {
139
- console.log("syncRemoteCamera");
140
151
  const renderer = genericRenderWindow.value.getRenderer();
141
152
  const camera = renderer.getActiveCamera();
142
153
  const params = {
143
- camera_options: {
144
- focal_point: [...camera.getFocalPoint()],
145
- view_up: [...camera.getViewUp()],
146
- position: [...camera.getPosition()],
147
- view_angle: camera.getViewAngle(),
148
- clipping_range: [...camera.getClippingRange()],
149
- distance: camera.getDistance(),
150
- },
154
+ camera_options: getCameraOptions(camera),
151
155
  };
152
156
  viewerStore.request(viewer_schemas.opengeodeweb_viewer.viewer.update_camera, params, {
153
157
  response_function: () => {
@@ -173,12 +177,10 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
173
177
  imageStyle.transition = "opacity 0.1s ease-in";
174
178
  imageStyle.zIndex = 1;
175
179
  resize(container.value.$el.offsetWidth, container.value.$el.offsetHeight);
176
- console.log("setContainer", container.value.$el);
177
180
 
178
181
  useMousePressed({
179
182
  target: container,
180
183
  onPressed: (event) => {
181
- console.log("onPressed");
182
184
  if (event.button === 0) {
183
185
  is_moving.value = true;
184
186
  event.stopPropagation();
@@ -190,7 +192,6 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
190
192
  return;
191
193
  }
192
194
  is_moving.value = false;
193
- console.log("onReleased");
194
195
  syncRemoteCamera();
195
196
  },
196
197
  });
@@ -223,25 +224,24 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
223
224
  remoteRender();
224
225
  }
225
226
 
227
+ function getAverageBrightness(rect) {
228
+ return computeAverageBrightness(rect, {
229
+ latestImage: latestImage.value,
230
+ offscreenCtx,
231
+ offscreenCanvas,
232
+ genericRenderWindow: genericRenderWindow.value,
233
+ });
234
+ }
235
+
226
236
  function exportStores() {
227
237
  const renderer = genericRenderWindow.value.getRenderer();
228
238
  const camera = renderer.getActiveCamera();
229
- const cameraSnapshot = camera
230
- ? {
231
- focal_point: [...camera.getFocalPoint()],
232
- view_up: [...camera.getViewUp()],
233
- position: [...camera.getPosition()],
234
- view_angle: camera.getViewAngle(),
235
- clipping_range: [...camera.getClippingRange()],
236
- distance: camera.getDistance(),
237
- }
238
- : camera_options;
239
+ const cameraSnapshot = getCameraOptions(camera) || camera_options;
239
240
  return { zScale: zScale.value, camera_options: cameraSnapshot };
240
241
  }
241
242
 
242
243
  async function importStores(snapshot) {
243
244
  if (!snapshot) {
244
- console.warn("importStores called with undefined snapshot");
245
245
  return;
246
246
  }
247
247
  const z_scale = snapshot.zScale;
@@ -255,22 +255,12 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
255
255
  const renderer = genericRenderWindow.value.getRenderer();
256
256
  const camera = renderer.getActiveCamera();
257
257
 
258
- camera.setFocalPoint(...snapshot_camera_options.focal_point);
259
- camera.setViewUp(...snapshot_camera_options.view_up);
260
- camera.setPosition(...snapshot_camera_options.position);
261
- camera.setViewAngle(snapshot_camera_options.view_angle);
262
- camera.setClippingRange(...snapshot_camera_options.clipping_range);
258
+ applyCameraOptions(camera, snapshot_camera_options);
263
259
 
264
260
  genericRenderWindow.value.getRenderWindow().render();
265
261
 
266
262
  const payload = {
267
- camera_options: {
268
- focal_point: [...snapshot_camera_options.focal_point],
269
- view_up: [...snapshot_camera_options.view_up],
270
- position: [...snapshot_camera_options.position],
271
- view_angle: snapshot_camera_options.view_angle,
272
- clipping_range: [...snapshot_camera_options.clipping_range],
273
- },
263
+ camera_options: getCameraOptions(snapshot_camera_options),
274
264
  };
275
265
  return viewerStore.request(viewer_schemas.opengeodeweb_viewer.viewer.update_camera, payload, {
276
266
  response_function: () => {
@@ -316,5 +306,7 @@ export const useHybridViewerStore = defineStore("hybridViewer", () => {
316
306
  clear,
317
307
  exportStores,
318
308
  importStores,
309
+ latestImage,
310
+ getAverageBrightness,
319
311
  };
320
312
  });