@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.
- package/.oxlintrc.json +12 -2
- package/app/components/Basic/Slider.vue +7 -0
- package/app/components/Basic/Switch.vue +7 -0
- package/app/components/HybridRenderingView.vue +1 -1
- package/app/components/SearchBar.vue +1 -1
- package/app/components/Viewer/ContextMenu.vue +1 -3
- package/app/components/Viewer/ContextMenuItem.vue +2 -2
- package/app/components/Viewer/EdgedCurve/SpecificEdgesOptions.vue +5 -9
- package/app/components/Viewer/Generic/Mesh/CellsOptions.vue +7 -2
- package/app/components/Viewer/Generic/Mesh/EdgesOptions.vue +11 -9
- package/app/components/Viewer/Generic/Mesh/PointsOptions.vue +14 -9
- package/app/components/Viewer/Generic/Mesh/PolygonsOptions.vue +10 -2
- package/app/components/Viewer/Generic/Mesh/PolyhedraOptions.vue +10 -2
- package/app/components/Viewer/Generic/Model/PointsOptions.vue +2 -14
- package/app/components/Viewer/ObjectTree/Base/CommonTreeView.vue +189 -0
- package/app/components/Viewer/ObjectTree/Base/Controls.vue +124 -38
- package/app/components/Viewer/ObjectTree/Base/ItemLabel.vue +43 -18
- package/app/components/Viewer/ObjectTree/Base/StickyHeader.vue +46 -0
- package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +77 -0
- package/app/components/Viewer/ObjectTree/Box.vue +106 -15
- package/app/components/Viewer/ObjectTree/Layout.vue +14 -12
- package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +48 -33
- package/app/components/Viewer/ObjectTree/Views/ModelComponents.vue +102 -66
- package/app/components/Viewer/Options/Sliders/Size.vue +8 -0
- package/app/components/Viewer/Options/Sliders/Slider.vue +17 -0
- package/app/components/Viewer/Options/Sliders/Width.vue +8 -0
- package/app/components/Viewer/Options/VisibilitySwitch.vue +3 -1
- package/app/components/Viewer/PointSet/SpecificPointsOptions.vue +1 -5
- package/app/components/Viewer/Surface/Triangulated/TrianglesOptions.vue +11 -0
- package/app/composables/hover_highlight.js +85 -0
- package/app/composables/model_components.js +68 -0
- package/app/composables/{use_tree_filter.js → tree_filter.js} +49 -31
- package/app/composables/tree_keyboard_nav.js +81 -0
- package/app/composables/tree_scroll.js +91 -0
- package/app/composables/virtual_tree.js +164 -0
- package/app/stores/data.js +41 -1
- package/app/stores/hybrid_viewer.js +30 -38
- package/app/stores/menu.js +8 -14
- package/app/utils/hybrid_viewer.js +101 -0
- package/package.json +3 -3
- package/tests/integration/setup.js +2 -1
- package/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/mesh/index.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/mesh/points.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/model/blocks.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/model/corners.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/model/edges.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/model/index.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/model/lines.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/model/points.nuxt.test.js +2 -3
- package/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +2 -3
- package/tests/integration/stores/viewer.nuxt.test.js +2 -2
- package/app/components/Viewer/HybridSolid/EdgesOptions.vue +0 -12
- package/app/components/Viewer/HybridSolid/PointsOptions.vue +0 -12
- package/app/components/Viewer/HybridSolid/PolygonsOptions.vue +0 -12
- package/app/components/Viewer/HybridSolid/PolyhedraOptions.vue +0 -12
- package/app/components/Viewer/PolygonalSurface/EdgesOptions.vue +0 -12
- package/app/components/Viewer/PolygonalSurface/PointsOptions.vue +0 -12
- package/app/components/Viewer/TriangulatedSurface/TrianglesOptions.vue +0 -16
- package/app/composables/use_hover_highlight.js +0 -48
- package/tests/integration/microservices/back/requirements.txt +0 -7
- package/tests/integration/microservices/viewer/requirements.txt +0 -7
- /package/app/components/Viewer/{TriangulatedSurface → Surface}/EdgesOptions.vue +0 -0
- /package/app/components/Viewer/{TriangulatedSurface → Surface}/PointsOptions.vue +0 -0
- /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(
|
|
13
|
+
function sortAndFormatItems(itemList, sortType, options = {}) {
|
|
14
|
+
if (!itemList || !Array.isArray(itemList)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
11
17
|
const field = sortType === "name" ? "title" : "id";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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(
|
|
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
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
})
|
|
73
|
-
|
|
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
|
+
}
|
package/app/stores/data.js
CHANGED
|
@@ -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
|
-
|
|
2
|
-
|
|
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
|
|
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
|
});
|