@geode/opengeodeweb-front 10.29.1 → 10.30.0-rc.10
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/app/components/ClippingPlaneCard.vue +112 -0
- package/app/components/ClippingPlanes.vue +238 -0
- package/app/components/Launcher.vue +1 -1
- package/app/components/ObjectSelector.vue +26 -76
- package/app/components/ToolPanel.vue +3 -2
- package/app/components/ViewToolbar.vue +12 -0
- package/app/components/Viewer/ObjectTree/Base/TreeRow.vue +58 -20
- package/app/components/Viewer/ObjectTree/Views/GlobalObjects.vue +42 -40
- package/app/composables/clipping_planes_widget.js +262 -0
- package/app/composables/hover_highlight.js +6 -0
- package/app/composables/project_manager.js +14 -11
- package/app/composables/use_overlapping_picker.js +15 -2
- package/app/stores/app.js +56 -11
- package/app/stores/back.js +10 -27
- package/app/stores/cloud.js +7 -1
- package/app/stores/data.js +32 -1
- package/app/stores/data_style.js +11 -0
- package/app/stores/hybrid_viewer.js +13 -5
- package/app/stores/infra.js +2 -7
- package/app/stores/viewer.js +9 -27
- package/app/utils/clipping_planes.js +110 -0
- package/app/utils/extension.js +30 -3
- package/app/utils/import_workflow.js +9 -12
- package/app/utils/log.js +18 -0
- package/app/utils/stores.js +32 -0
- package/internal/stores/hybrid_viewer.js +8 -63
- package/internal/stores/hybrid_viewer_brightness.js +62 -0
- package/internal/utils/api_fetch.js +27 -53
- package/internal/utils/upload_file.js +36 -29
- package/internal/utils/viewer_call.js +7 -2
- package/nuxt.config.js +2 -1
- package/package.json +4 -3
- package/server/api/local/{project_folder_path.post.js → app/project_folder_path.post.js} +1 -1
- package/server/api/local/{run_back.post.js → app/run_back.post.js} +1 -1
- package/server/api/local/{run_viewer.post.js → app/run_viewer.post.js} +1 -1
- package/server/api/{microservice → local}/extensions/upload.put.js +16 -37
- package/server/api/microservice/app/set_app_base_url.post.js +25 -0
- package/server/api/microservice/app/set_back_base_url.post.js +22 -0
- package/server/api/microservice/app/set_viewer_base_url.post.js +22 -0
- package/server/api/microservice/extensions/download.post.js +32 -0
- package/server/api/microservice/extensions/kill.post.js +3 -3
- package/server/api/microservice/extensions/run.post.js +5 -4
- package/server/api/serverless/{run_cloud.js → run_cloud.post.js} +1 -1
- package/{app/utils/config.js → server/utils/app_config.js} +28 -3
- package/{app/utils/local → server/utils}/path.js +1 -1
- package/{app/utils/local → server/utils}/scripts.js +5 -2
- package/server/utils/server_config.js +32 -0
- package/shared/scripts.js +56 -0
- package/shared/utils/fetch_raw.js +61 -0
- package/shared/utils/fetch_schema.js +46 -0
- package/shared/utils/file.js +16 -0
- package/shared/utils/response_handlers/load.js +74 -0
- package/tests/integration/setup.js +3 -3
- package/tests/integration/stores/data_style/mesh/cells.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/edges.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/index.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/points.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/polygons.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/mesh/polyhedra.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/blocks.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/corners.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/edges.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/index.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/lines.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/points.nuxt.test.js +1 -1
- package/tests/integration/stores/data_style/model/surfaces.nuxt.test.js +1 -1
- package/tests/integration/stores/viewer.nuxt.test.js +1 -1
- package/tests/unit/composables/project_manager.nuxt.test.js +14 -2
- package/tests/unit/composables/upload_file.nuxt.test.js +2 -2
- package/tests/unit/stores/back.nuxt.test.js +1 -1
- package/tests/unit/stores/cloud.nuxt.test.js +22 -20
- package/tests/unit/stores/data.nuxt.test.js +35 -0
- package/tests/unit/stores/infra.nuxt.test.js +13 -6
- package/tests/unit/stores/viewer.nuxt.test.js +1 -1
- package/tests/unit/utils/validate_schema.nuxt.test.js +1 -1
- package/tests/vitest.config.js +7 -5
- /package/server/api/local/{kill.post.js → app/kill.post.js} +0 -0
- /package/{app/utils/local → server/utils}/cleanup.js +0 -0
- /package/{app/utils/local → server/utils}/microservices.js +0 -0
- /package/{app → server}/utils/server.js +0 -0
- /package/{app/utils/local → shared}/app_mode.js +0 -0
- /package/{app → shared}/utils/validate_schema.js +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { getPlaneCssColor } from "@ogw_front/utils/clipping_planes";
|
|
3
|
+
|
|
4
|
+
const { plane, index } = defineProps({
|
|
5
|
+
plane: {
|
|
6
|
+
type: Object,
|
|
7
|
+
required: true,
|
|
8
|
+
},
|
|
9
|
+
index: {
|
|
10
|
+
type: Number,
|
|
11
|
+
required: true,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const emit = defineEmits(["remove", "flipNormal"]);
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<v-card
|
|
20
|
+
data-testid="planeCard"
|
|
21
|
+
variant="outlined"
|
|
22
|
+
class="pa-2 mb-3 rounded-lg border-opacity-50"
|
|
23
|
+
:style="{ borderColor: getPlaneCssColor(index) }"
|
|
24
|
+
>
|
|
25
|
+
<v-row align="center" justify="space-between" no-gutters class="mb-1">
|
|
26
|
+
<v-col cols="auto" class="d-flex align-center">
|
|
27
|
+
<v-chip
|
|
28
|
+
size="x-small"
|
|
29
|
+
variant="flat"
|
|
30
|
+
:style="{
|
|
31
|
+
backgroundColor: getPlaneCssColor(index),
|
|
32
|
+
color: 'white',
|
|
33
|
+
}"
|
|
34
|
+
class="font-weight-bold"
|
|
35
|
+
>
|
|
36
|
+
Plane #{{ index + 1 }}
|
|
37
|
+
</v-chip>
|
|
38
|
+
</v-col>
|
|
39
|
+
<v-col cols="auto">
|
|
40
|
+
<v-btn
|
|
41
|
+
data-testid="removePlaneButton"
|
|
42
|
+
icon="mdi-trash-can-outline"
|
|
43
|
+
size="x-small"
|
|
44
|
+
variant="text"
|
|
45
|
+
color="error"
|
|
46
|
+
@click="emit('remove')"
|
|
47
|
+
/>
|
|
48
|
+
</v-col>
|
|
49
|
+
</v-row>
|
|
50
|
+
|
|
51
|
+
<v-row no-gutters class="mb-1">
|
|
52
|
+
<v-col class="text-caption text-medium-emphasis">Origin [X, Y, Z]</v-col>
|
|
53
|
+
</v-row>
|
|
54
|
+
<v-row v-if="plane.origin" dense class="mb-2">
|
|
55
|
+
<v-col v-for="axis in 3" :key="'orig-' + axis" cols="4">
|
|
56
|
+
<v-text-field
|
|
57
|
+
v-model.number="plane.origin[axis - 1]"
|
|
58
|
+
data-testid="planeOriginInput"
|
|
59
|
+
type="number"
|
|
60
|
+
variant="outlined"
|
|
61
|
+
density="compact"
|
|
62
|
+
hide-details
|
|
63
|
+
step="any"
|
|
64
|
+
class="text-caption"
|
|
65
|
+
/>
|
|
66
|
+
</v-col>
|
|
67
|
+
</v-row>
|
|
68
|
+
<v-row v-else dense class="mb-2">
|
|
69
|
+
<v-col v-for="axis in 3" :key="'orig-placeholder-' + axis" cols="4">
|
|
70
|
+
<v-text-field
|
|
71
|
+
placeholder="—"
|
|
72
|
+
variant="outlined"
|
|
73
|
+
density="compact"
|
|
74
|
+
hide-details
|
|
75
|
+
disabled
|
|
76
|
+
class="text-caption"
|
|
77
|
+
/>
|
|
78
|
+
</v-col>
|
|
79
|
+
</v-row>
|
|
80
|
+
|
|
81
|
+
<v-row align="center" justify="space-between" no-gutters class="mb-1">
|
|
82
|
+
<v-col class="text-caption text-medium-emphasis">Normal [X, Y, Z]</v-col>
|
|
83
|
+
<v-col cols="auto">
|
|
84
|
+
<v-btn
|
|
85
|
+
data-testid="invertNormalButton"
|
|
86
|
+
size="x-small"
|
|
87
|
+
variant="text"
|
|
88
|
+
color="primary"
|
|
89
|
+
prepend-icon="mdi-swap-horizontal"
|
|
90
|
+
class="text-none text-caption px-1"
|
|
91
|
+
@click="emit('flipNormal')"
|
|
92
|
+
>
|
|
93
|
+
Invert Normal
|
|
94
|
+
</v-btn>
|
|
95
|
+
</v-col>
|
|
96
|
+
</v-row>
|
|
97
|
+
<v-row dense>
|
|
98
|
+
<v-col v-for="axis in 3" :key="'norm-' + axis" cols="4">
|
|
99
|
+
<v-text-field
|
|
100
|
+
v-model.number="plane.normal[axis - 1]"
|
|
101
|
+
data-testid="planeNormalInput"
|
|
102
|
+
type="number"
|
|
103
|
+
variant="outlined"
|
|
104
|
+
density="compact"
|
|
105
|
+
hide-details
|
|
106
|
+
step="any"
|
|
107
|
+
class="text-caption"
|
|
108
|
+
/>
|
|
109
|
+
</v-col>
|
|
110
|
+
</v-row>
|
|
111
|
+
</v-card>
|
|
112
|
+
</template>
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { DEBOUNCE_DELAY, DEFAULT_NORMALS } from "@ogw_front/utils/clipping_planes";
|
|
3
|
+
import ClippingPlaneCard from "@ogw_front/components/ClippingPlaneCard";
|
|
4
|
+
import ToolPanel from "@ogw_front/components/ToolPanel";
|
|
5
|
+
import { useClippingPlanesWidget } from "@ogw_front/composables/clipping_planes_widget";
|
|
6
|
+
import { useDataStore } from "@ogw_front/stores/data";
|
|
7
|
+
import { useDebounceFn } from "@vueuse/core";
|
|
8
|
+
import { useHybridViewerStore } from "@ogw_front/stores/hybrid_viewer";
|
|
9
|
+
|
|
10
|
+
const show = defineModel("show", { type: Boolean, default: false });
|
|
11
|
+
const dataStore = useDataStore();
|
|
12
|
+
const hybridViewerStore = useHybridViewerStore();
|
|
13
|
+
const targetAllVisible = ref(true);
|
|
14
|
+
const selectedDatasetIds = ref([]);
|
|
15
|
+
const planes = ref([{ origin: undefined, normal: [1, 0, 0] }]);
|
|
16
|
+
const allItems = dataStore.refAllItems();
|
|
17
|
+
const availableDatasets = computed(() =>
|
|
18
|
+
allItems.value.map((item) => ({ title: item.name || item.id, value: item.id })),
|
|
19
|
+
);
|
|
20
|
+
const widgetContainer = useTemplateRef("widgetContainer");
|
|
21
|
+
const debouncedApply = useDebounceFn(() => applyClippingPlanes(), DEBOUNCE_DELAY);
|
|
22
|
+
const {
|
|
23
|
+
getSceneCenter,
|
|
24
|
+
syncWidgets,
|
|
25
|
+
syncLocalCamera,
|
|
26
|
+
cleanupLocalWidget,
|
|
27
|
+
initLocalWidget,
|
|
28
|
+
updateWidgetPlacement,
|
|
29
|
+
isFromWidget,
|
|
30
|
+
setFromWidget,
|
|
31
|
+
} = useClippingPlanesWidget({
|
|
32
|
+
planes,
|
|
33
|
+
targetAllVisible,
|
|
34
|
+
selectedDatasetIds,
|
|
35
|
+
allItems,
|
|
36
|
+
hybridViewerStore,
|
|
37
|
+
debouncedApply,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function addPlane() {
|
|
41
|
+
const normal = DEFAULT_NORMALS[planes.value.length % DEFAULT_NORMALS.length];
|
|
42
|
+
planes.value.push({ origin: getSceneCenter(), normal });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function removePlane(index) {
|
|
46
|
+
planes.value.splice(index, 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function flipNormal(plane) {
|
|
50
|
+
plane.normal = plane.normal.map((component) => -component);
|
|
51
|
+
syncWidgets();
|
|
52
|
+
applyClippingPlanes();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function applyClippingPlanes() {
|
|
56
|
+
const allIds = allItems.value.map((item) => item.id);
|
|
57
|
+
if (allIds.length === 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const center = getSceneCenter();
|
|
61
|
+
const targetIds = targetAllVisible.value ? allIds : selectedDatasetIds.value;
|
|
62
|
+
const untargetedIds = allIds.filter((id) => !targetIds.includes(id));
|
|
63
|
+
const planesData = planes.value.map((plane) => ({
|
|
64
|
+
origin: (plane.origin || center).map(Number),
|
|
65
|
+
normal: plane.normal.map(Number),
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
if (targetIds.length > 0) {
|
|
69
|
+
await hybridViewerStore.setClippingPlanes(targetIds, planesData);
|
|
70
|
+
}
|
|
71
|
+
if (untargetedIds.length > 0) {
|
|
72
|
+
await hybridViewerStore.setClippingPlanes(untargetedIds, []);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function resetClippingPlanes() {
|
|
77
|
+
setFromWidget(true);
|
|
78
|
+
planes.value = [{ origin: undefined, normal: [1, 0, 0] }];
|
|
79
|
+
updateWidgetPlacement({ isReset: true });
|
|
80
|
+
setFromWidget(false);
|
|
81
|
+
await applyClippingPlanes();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function removeClippingPlanes() {
|
|
85
|
+
const allIds = allItems.value.map((item) => item.id);
|
|
86
|
+
await hybridViewerStore.setClippingPlanes(allIds, []);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
watch(widgetContainer, (container) => {
|
|
90
|
+
if (container) {
|
|
91
|
+
initLocalWidget(container.$el || container);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
watch(
|
|
96
|
+
planes,
|
|
97
|
+
() => {
|
|
98
|
+
if (isFromWidget()) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
syncWidgets();
|
|
102
|
+
debouncedApply();
|
|
103
|
+
},
|
|
104
|
+
{ deep: true },
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
watch(show, (visible) => {
|
|
108
|
+
if (visible) {
|
|
109
|
+
updateWidgetPlacement({ isReset: true });
|
|
110
|
+
applyClippingPlanes();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
watch(
|
|
115
|
+
[targetAllVisible, selectedDatasetIds],
|
|
116
|
+
() => {
|
|
117
|
+
if (show.value) {
|
|
118
|
+
updateWidgetPlacement({ isReset: true });
|
|
119
|
+
applyClippingPlanes();
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{ deep: true },
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
watch(allItems, () => {
|
|
126
|
+
if (show.value) {
|
|
127
|
+
updateWidgetPlacement({ isReset: true });
|
|
128
|
+
applyClippingPlanes();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
watch(
|
|
133
|
+
() => Object.values(hybridViewerStore.hybridDb).filter((entry) => entry && entry.actor).length,
|
|
134
|
+
(actorCount) => {
|
|
135
|
+
if (show.value && actorCount > 0) {
|
|
136
|
+
updateWidgetPlacement({ isReset: true });
|
|
137
|
+
applyClippingPlanes();
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
watch(() => hybridViewerStore.camera_options, syncLocalCamera, { deep: true });
|
|
143
|
+
|
|
144
|
+
onBeforeUnmount(cleanupLocalWidget);
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<template>
|
|
148
|
+
<ToolPanel v-model="show" title="Clipping Planes" :width="360" :click-outside="false">
|
|
149
|
+
<v-card-text class="pa-3 max-panel-height overflow-y-auto">
|
|
150
|
+
<v-sheet
|
|
151
|
+
ref="widgetContainer"
|
|
152
|
+
height="180"
|
|
153
|
+
color="transparent"
|
|
154
|
+
class="rounded-lg mb-3 overflow-hidden"
|
|
155
|
+
/>
|
|
156
|
+
<v-switch
|
|
157
|
+
v-model="targetAllVisible"
|
|
158
|
+
data-testid="targetAllVisibleSwitch"
|
|
159
|
+
label="Apply to all visible datasets"
|
|
160
|
+
color="primary"
|
|
161
|
+
density="compact"
|
|
162
|
+
hide-details
|
|
163
|
+
class="mb-2 text-caption"
|
|
164
|
+
/>
|
|
165
|
+
|
|
166
|
+
<v-select
|
|
167
|
+
v-if="!targetAllVisible"
|
|
168
|
+
v-model="selectedDatasetIds"
|
|
169
|
+
data-testid="selectedDatasetsSelect"
|
|
170
|
+
:items="availableDatasets"
|
|
171
|
+
label="Select datasets"
|
|
172
|
+
multiple
|
|
173
|
+
chips
|
|
174
|
+
closable-chips
|
|
175
|
+
variant="outlined"
|
|
176
|
+
density="compact"
|
|
177
|
+
hide-details
|
|
178
|
+
class="mb-3 text-caption"
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
<v-divider class="my-2" />
|
|
182
|
+
|
|
183
|
+
<v-row align="center" justify="space-between" no-gutters class="mb-2">
|
|
184
|
+
<v-col class="text-caption font-weight-bold">Planes ({{ planes.length }})</v-col>
|
|
185
|
+
<v-col cols="auto">
|
|
186
|
+
<v-btn
|
|
187
|
+
data-testid="addPlaneButton"
|
|
188
|
+
size="x-small"
|
|
189
|
+
variant="tonal"
|
|
190
|
+
color="primary"
|
|
191
|
+
icon="mdi-plus"
|
|
192
|
+
@click="addPlane"
|
|
193
|
+
/>
|
|
194
|
+
</v-col>
|
|
195
|
+
</v-row>
|
|
196
|
+
|
|
197
|
+
<ClippingPlaneCard
|
|
198
|
+
v-for="(plane, idx) in planes"
|
|
199
|
+
:key="idx"
|
|
200
|
+
:plane="plane"
|
|
201
|
+
:index="idx"
|
|
202
|
+
@remove="removePlane(idx)"
|
|
203
|
+
@flip-normal="flipNormal(plane)"
|
|
204
|
+
/>
|
|
205
|
+
</v-card-text>
|
|
206
|
+
|
|
207
|
+
<template #actions>
|
|
208
|
+
<v-card-actions class="justify-space-between px-3 pb-3 pt-0">
|
|
209
|
+
<v-btn
|
|
210
|
+
data-testid="removeClippingPlanesButton"
|
|
211
|
+
variant="text"
|
|
212
|
+
size="small"
|
|
213
|
+
color="error"
|
|
214
|
+
class="text-caption text-none"
|
|
215
|
+
@click="removeClippingPlanes"
|
|
216
|
+
>
|
|
217
|
+
Remove Clipping
|
|
218
|
+
</v-btn>
|
|
219
|
+
<v-btn
|
|
220
|
+
data-testid="resetClippingPlanesButton"
|
|
221
|
+
variant="tonal"
|
|
222
|
+
size="small"
|
|
223
|
+
color="secondary"
|
|
224
|
+
class="text-caption text-none"
|
|
225
|
+
@click="resetClippingPlanes"
|
|
226
|
+
>
|
|
227
|
+
Reset
|
|
228
|
+
</v-btn>
|
|
229
|
+
</v-card-actions>
|
|
230
|
+
</template>
|
|
231
|
+
</ToolPanel>
|
|
232
|
+
</template>
|
|
233
|
+
|
|
234
|
+
<style scoped>
|
|
235
|
+
.max-panel-height {
|
|
236
|
+
max-height: 520px;
|
|
237
|
+
}
|
|
238
|
+
</style>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import Loading from "@ogw_front/components/Loading";
|
|
3
3
|
import Recaptcha from "@ogw_front/components/Recaptcha";
|
|
4
4
|
import { Status } from "@ogw_front/utils/status";
|
|
5
|
-
import { appMode } from "@
|
|
5
|
+
import { appMode } from "@ogw_shared/app_mode";
|
|
6
6
|
import { useInfraStore } from "@ogw_front/stores/infra";
|
|
7
7
|
|
|
8
8
|
const { appName, email, isUserAuthenticated, logo } = defineProps({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import FetchingData from "@ogw_front/components/FetchingData.vue";
|
|
3
3
|
import { geode_objects } from "@ogw_front/assets/geode_objects";
|
|
4
|
+
import { resolveAllowedObjects } from "@ogw_shared/utils/response_handlers/load.js";
|
|
4
5
|
import schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json";
|
|
5
6
|
import { useBackStore } from "@ogw_front/stores/back";
|
|
6
7
|
|
|
@@ -15,100 +16,49 @@ const { filenames } = defineProps({
|
|
|
15
16
|
const backStore = useBackStore();
|
|
16
17
|
|
|
17
18
|
const loading = ref(false);
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
19
|
+
const allowedGeodeObjects = ref({});
|
|
20
|
+
const toggleLoading = useToggle(loading);
|
|
21
|
+
const multipleFilesNoCommon = ref(false);
|
|
21
22
|
|
|
22
|
-
function
|
|
23
|
-
const
|
|
24
|
-
if (object_keys.length === 0) {
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
if (object_keys.length === 1 && object_map[object_keys[0]].is_loadable > 0) {
|
|
28
|
-
return object_keys[0];
|
|
29
|
-
}
|
|
30
|
-
const highest_load_score = Math.max(...object_keys.map((key) => object_map[key].is_loadable));
|
|
31
|
-
if (highest_load_score <= 0) {
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
const best_score_objects = object_keys.filter(
|
|
35
|
-
(key) => object_map[key].is_loadable === highest_load_score,
|
|
36
|
-
);
|
|
37
|
-
if (best_score_objects.length === 1) {
|
|
38
|
-
return best_score_objects[0];
|
|
39
|
-
}
|
|
40
|
-
const highest_priority = Math.max(
|
|
41
|
-
...best_score_objects.map((key) => object_map[key].object_priority ?? -Infinity),
|
|
42
|
-
);
|
|
43
|
-
const best_priority_objects = best_score_objects.filter(
|
|
44
|
-
(key) => object_map[key].object_priority === highest_priority,
|
|
45
|
-
);
|
|
46
|
-
if (highest_priority !== -Infinity && best_priority_objects.length === 1) {
|
|
47
|
-
return best_priority_objects[0];
|
|
48
|
-
}
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function isCsvFile(filename) {
|
|
53
|
-
return filename.toLowerCase().endsWith(".csv") || filename.toLowerCase().endsWith(".csv.json");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function get_allowed_objects() {
|
|
57
|
-
toggle_loading();
|
|
58
|
-
allowed_objects.value = {};
|
|
59
|
-
multiple_files_no_common.value = false;
|
|
60
|
-
|
|
61
|
-
const promise_array = filenames.map((filename) => {
|
|
23
|
+
async function fetchAllowedObjectsList() {
|
|
24
|
+
const promiseArray = filenames.map((filename) => {
|
|
62
25
|
const params = { filename };
|
|
63
26
|
return backStore.request({ schema, params });
|
|
64
27
|
});
|
|
65
|
-
const responses = await Promise.all(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const common_keys = all_keys.filter((key) => allowed_objects_list.every((obj) => key in obj));
|
|
28
|
+
const responses = await Promise.all(promiseArray);
|
|
29
|
+
return responses.map((response) => response.allowed_objects);
|
|
30
|
+
}
|
|
69
31
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
32
|
+
async function getAllowedGeodeObjects() {
|
|
33
|
+
toggleLoading();
|
|
34
|
+
allowedGeodeObjects.value = {};
|
|
35
|
+
multipleFilesNoCommon.value = false;
|
|
73
36
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
const load_scores = allowed_objects_list.map((obj) => obj[key].is_loadable);
|
|
77
|
-
const priorities = allowed_objects_list
|
|
78
|
-
.map((obj) => obj[key].object_priority)
|
|
79
|
-
.filter((priority) => priority !== undefined);
|
|
80
|
-
final_object[key] = { is_loadable: Math.min(...load_scores) };
|
|
81
|
-
if (priorities.length > 0) {
|
|
82
|
-
final_object[key].object_priority = Math.max(...priorities);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
const isCsv = filenames.some((filename) => isCsvFile(filename));
|
|
86
|
-
if (isCsv && !final_object["PointSet3D"]) {
|
|
87
|
-
final_object["PointSet3D"] = { is_loadable: 1, object_priority: 100 };
|
|
88
|
-
}
|
|
37
|
+
const allowedGeodeObjectsList = await fetchAllowedObjectsList();
|
|
38
|
+
const resolved = resolveAllowedObjects(filenames, allowedGeodeObjectsList);
|
|
89
39
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
40
|
+
allowedGeodeObjects.value = resolved.mergedAllowedObjects;
|
|
41
|
+
multipleFilesNoCommon.value = resolved.multipleFilesNoCommon;
|
|
42
|
+
if (resolved.selectedGeodeObject) {
|
|
43
|
+
setGeodeObject(resolved.selectedGeodeObject);
|
|
94
44
|
}
|
|
95
|
-
|
|
45
|
+
toggleLoading();
|
|
96
46
|
}
|
|
97
47
|
|
|
98
|
-
function
|
|
48
|
+
function setGeodeObject(geode_object_type) {
|
|
99
49
|
if (geode_object_type) {
|
|
100
50
|
emit("update_values", { geode_object_type });
|
|
101
51
|
emit("increment_step");
|
|
102
52
|
}
|
|
103
53
|
}
|
|
104
54
|
// oxlint-disable-next-line no-top-level-await
|
|
105
|
-
await
|
|
55
|
+
await getAllowedGeodeObjects();
|
|
106
56
|
</script>
|
|
107
57
|
|
|
108
58
|
<template>
|
|
109
59
|
<FetchingData v-if="loading" />
|
|
110
|
-
<v-row v-else-if="Object.keys(
|
|
111
|
-
<v-col v-for="(value, key) in
|
|
60
|
+
<v-row v-else-if="Object.keys(allowedGeodeObjects).length" class="justify-left">
|
|
61
|
+
<v-col v-for="(value, key) in allowedGeodeObjects" :key="key" cols="3" md="4">
|
|
112
62
|
<v-tooltip
|
|
113
63
|
:text="
|
|
114
64
|
value['is_loadable']
|
|
@@ -124,7 +74,7 @@ await get_allowed_objects();
|
|
|
124
74
|
class="card ma-2"
|
|
125
75
|
hover
|
|
126
76
|
rounded
|
|
127
|
-
@click="
|
|
77
|
+
@click="setGeodeObject(key)"
|
|
128
78
|
:disabled="!value['is_loadable']"
|
|
129
79
|
:elevation="value['is_loadable'] ? 5 : 3"
|
|
130
80
|
>
|
|
@@ -139,7 +89,7 @@ await get_allowed_objects();
|
|
|
139
89
|
</v-tooltip>
|
|
140
90
|
</v-col>
|
|
141
91
|
</v-row>
|
|
142
|
-
<v-row v-else-if="
|
|
92
|
+
<v-row v-else-if="multipleFilesNoCommon" class="pa-5">
|
|
143
93
|
<v-card class="card" variant="tonal" rounded>
|
|
144
94
|
<v-card-text>
|
|
145
95
|
These files cannot be loaded together because they don't share a common data type.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import GlassCard from "@ogw_front/components/GlassCard";
|
|
3
3
|
|
|
4
|
-
const { title, width, closeLabel, actionLabel } = defineProps({
|
|
4
|
+
const { title, width, closeLabel, actionLabel, clickOutside } = defineProps({
|
|
5
5
|
title: { type: String, default: "" },
|
|
6
6
|
width: { type: Number, default: 260 },
|
|
7
7
|
closeLabel: { type: String, default: "Close" },
|
|
8
8
|
actionLabel: { type: String, default: undefined },
|
|
9
|
+
clickOutside: { type: Boolean, default: true },
|
|
9
10
|
});
|
|
10
11
|
|
|
11
12
|
const model = defineModel({ type: Boolean, default: false });
|
|
@@ -19,7 +20,7 @@ function close() {
|
|
|
19
20
|
<template>
|
|
20
21
|
<GlassCard
|
|
21
22
|
v-if="model"
|
|
22
|
-
v-click-outside="close"
|
|
23
|
+
v-click-outside="{ handler: close, closeConditional: () => clickOutside }"
|
|
23
24
|
:title="title"
|
|
24
25
|
:width="width"
|
|
25
26
|
:ripple="false"
|
|
@@ -3,6 +3,7 @@ import ActionButton from "@ogw_front/components/ActionButton";
|
|
|
3
3
|
import CameraBookmarkIcon from "@ogw_front/assets/viewer_svgs/camera-bookmark.svg";
|
|
4
4
|
import CameraManager from "@ogw_front/components/CameraManager";
|
|
5
5
|
import CameraOrientation from "@ogw_front/components/CameraOrientation";
|
|
6
|
+
import ClippingPlanes from "@ogw_front/components/ClippingPlanes";
|
|
6
7
|
import Screenshot from "@ogw_front/components/Screenshot";
|
|
7
8
|
import ZScaling from "@ogw_front/components/ZScaling";
|
|
8
9
|
import schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
|
|
@@ -15,6 +16,7 @@ const take_screenshot = ref(false);
|
|
|
15
16
|
const show_camera_manager = ref(false);
|
|
16
17
|
const showCameraOrientation = ref(false);
|
|
17
18
|
const showZScaling = ref(false);
|
|
19
|
+
const showClippingPlanes = ref(false);
|
|
18
20
|
const grid_scale = ref(false);
|
|
19
21
|
const zScale = ref(hybridViewerStore.zScale);
|
|
20
22
|
|
|
@@ -151,6 +153,15 @@ const camera_options = computed(() => [
|
|
|
151
153
|
showZScaling.value = !showZScaling.value;
|
|
152
154
|
},
|
|
153
155
|
},
|
|
156
|
+
{
|
|
157
|
+
testId: "clippingPlanesButton",
|
|
158
|
+
tooltip: "Clipping Planes",
|
|
159
|
+
icon: "mdi-content-cut",
|
|
160
|
+
color: showClippingPlanes.value ? "primary" : undefined,
|
|
161
|
+
action: () => {
|
|
162
|
+
showClippingPlanes.value = !showClippingPlanes.value;
|
|
163
|
+
},
|
|
164
|
+
},
|
|
154
165
|
]);
|
|
155
166
|
</script>
|
|
156
167
|
|
|
@@ -223,6 +234,7 @@ const camera_options = computed(() => [
|
|
|
223
234
|
:width="260"
|
|
224
235
|
@apply="handleZScalingClose"
|
|
225
236
|
/>
|
|
237
|
+
<ClippingPlanes v-model:show="showClippingPlanes" />
|
|
226
238
|
</template>
|
|
227
239
|
|
|
228
240
|
<style module>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { useDataStore } from "@ogw_front/stores/data";
|
|
3
|
+
|
|
4
|
+
const dataStore = useDataStore();
|
|
5
|
+
|
|
2
6
|
const { item, itemProps, selection, isSelected, getIndeterminate } = defineProps({
|
|
3
7
|
item: { type: Object, required: true },
|
|
4
8
|
itemProps: { type: Object, required: true },
|
|
@@ -7,13 +11,34 @@ const { item, itemProps, selection, isSelected, getIndeterminate } = defineProps
|
|
|
7
11
|
getIndeterminate: { type: Function, required: true },
|
|
8
12
|
});
|
|
9
13
|
|
|
10
|
-
defineEmits(["toggle-open", "toggle-select", "hover-eye-enter", "hover-eye-leave"]);
|
|
14
|
+
const emit = defineEmits(["toggle-open", "toggle-select", "hover-eye-enter", "hover-eye-leave"]);
|
|
11
15
|
|
|
12
16
|
const INDENT_STEP = 10;
|
|
17
|
+
|
|
18
|
+
function triggerHorizonStackModal(rawItem) {
|
|
19
|
+
globalThis.dispatchEvent(new CustomEvent("open-horizon-stack-modal", { detail: rawItem }));
|
|
20
|
+
}
|
|
21
|
+
const isHorizonStack = computed(() => item.raw.geode_object_type === "HorizonStack3D");
|
|
22
|
+
const isViewable = computed(() => dataStore.isItemViewable(item.raw));
|
|
23
|
+
const showEyeButton = computed(
|
|
24
|
+
() => !isHorizonStack.value && item.raw.title !== "HorizonStack3D" && isViewable.value,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
function handleRowClick(event) {
|
|
28
|
+
if (isHorizonStack.value) {
|
|
29
|
+
if (!item.isLeaf) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
event.stopPropagation();
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
triggerHorizonStackModal(item.raw);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
13
38
|
</script>
|
|
14
39
|
|
|
15
40
|
<template>
|
|
16
|
-
<div class="tree-row-content d-flex align-center px-2 ps-2 w-100">
|
|
41
|
+
<div class="tree-row-content d-flex align-center px-2 ps-2 w-100" @click="handleRowClick">
|
|
17
42
|
<div
|
|
18
43
|
v-if="item.depth > 0"
|
|
19
44
|
class="flex-shrink-0"
|
|
@@ -30,24 +55,37 @@ const INDENT_STEP = 10;
|
|
|
30
55
|
/>
|
|
31
56
|
<div v-else class="icon-placeholder" />
|
|
32
57
|
|
|
33
|
-
<v-
|
|
34
|
-
v-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
<template v-if="selection.selectable">
|
|
59
|
+
<v-btn
|
|
60
|
+
v-if="isHorizonStack && item.isLeaf"
|
|
61
|
+
icon="mdi-layers-triple"
|
|
62
|
+
variant="text"
|
|
63
|
+
density="compact"
|
|
64
|
+
color="black"
|
|
65
|
+
class="flex-shrink-0"
|
|
66
|
+
style="z-index: 4"
|
|
67
|
+
@click.stop="triggerHorizonStackModal(item.raw)"
|
|
68
|
+
@mousedown.stop
|
|
69
|
+
/>
|
|
70
|
+
<v-btn
|
|
71
|
+
v-else-if="showEyeButton"
|
|
72
|
+
:icon="
|
|
73
|
+
getIndeterminate(item.raw)
|
|
74
|
+
? 'mdi-eye-minus-outline'
|
|
75
|
+
: isSelected(item.raw)
|
|
76
|
+
? 'mdi-eye'
|
|
77
|
+
: 'mdi-eye-off-outline'
|
|
78
|
+
"
|
|
79
|
+
variant="text"
|
|
80
|
+
density="compact"
|
|
81
|
+
color="black"
|
|
82
|
+
class="flex-shrink-0"
|
|
83
|
+
@click.stop="$emit('toggle-select', item.raw)"
|
|
84
|
+
@mousedown.stop
|
|
85
|
+
@mouseenter="$emit('hover-eye-enter', item.raw)"
|
|
86
|
+
@mouseleave="$emit('hover-eye-leave', item.raw)"
|
|
87
|
+
/>
|
|
88
|
+
</template>
|
|
51
89
|
</div>
|
|
52
90
|
|
|
53
91
|
<div class="tree-title flex-grow-1 overflow-hidden d-flex align-center ms-1 pt-1">
|