@geode/opengeodeweb-front 10.30.0-rc.2 → 10.30.0-rc.4
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/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/use_overlapping_picker.js +15 -2
- package/app/stores/app.js +20 -2
- 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/utils/clipping_planes.js +110 -0
- package/app/utils/import_workflow.js +8 -3
- package/internal/stores/hybrid_viewer.js +8 -63
- package/internal/stores/hybrid_viewer_brightness.js +62 -0
- package/package.json +1 -1
- package/tests/unit/composables/project_manager.nuxt.test.js +1 -0
- package/tests/unit/stores/data.nuxt.test.js +35 -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>
|
|
@@ -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">
|
|
@@ -179,46 +179,48 @@ function expandAll() {
|
|
|
179
179
|
</template>
|
|
180
180
|
|
|
181
181
|
<template #append="{ item }">
|
|
182
|
-
<v-
|
|
183
|
-
v-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
v-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
v-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
182
|
+
<template v-if="item.geode_object_type !== 'HorizonStack3D'">
|
|
183
|
+
<v-btn
|
|
184
|
+
v-if="item.viewer_type"
|
|
185
|
+
icon="mdi-target"
|
|
186
|
+
size="medium"
|
|
187
|
+
variant="text"
|
|
188
|
+
v-tooltip="'Focus camera on object'"
|
|
189
|
+
@click.stop="hybridViewerStore.focusCameraOnObject(item.id)"
|
|
190
|
+
/>
|
|
191
|
+
<v-btn
|
|
192
|
+
v-if="isModel(item)"
|
|
193
|
+
icon="mdi-magnify-expand"
|
|
194
|
+
size="medium"
|
|
195
|
+
class="ml-2"
|
|
196
|
+
variant="text"
|
|
197
|
+
v-tooltip="'Model\'s mesh components'"
|
|
198
|
+
@click.stop="
|
|
199
|
+
treeviewStore.displayAdditionalTree(
|
|
200
|
+
item.id,
|
|
201
|
+
item.title,
|
|
202
|
+
item.geode_object_type,
|
|
203
|
+
'model_components',
|
|
204
|
+
)
|
|
205
|
+
"
|
|
206
|
+
/>
|
|
207
|
+
<v-btn
|
|
208
|
+
v-if="isModel(item) && hasCollectionsMap[item.id]"
|
|
209
|
+
icon="mdi-format-list-group"
|
|
210
|
+
size="medium"
|
|
211
|
+
class="ml-2"
|
|
212
|
+
variant="text"
|
|
213
|
+
v-tooltip="'Model\'s collections'"
|
|
214
|
+
@click.stop="
|
|
215
|
+
treeviewStore.displayAdditionalTree(
|
|
216
|
+
item.id,
|
|
217
|
+
item.title,
|
|
218
|
+
item.geode_object_type,
|
|
219
|
+
'model_collections',
|
|
220
|
+
)
|
|
221
|
+
"
|
|
222
|
+
/>
|
|
223
|
+
</template>
|
|
222
224
|
</template>
|
|
223
225
|
</CommonTreeView>
|
|
224
226
|
</div>
|