@geode/opengeodeweb-front 10.36.0-rc.2 → 10.36.0-rc.3
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.
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { DEBOUNCE_DELAY, DEFAULT_NORMALS } from "@ogw_front/utils/clipping_planes";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_SLICE_AXIS,
|
|
5
|
+
SLICE_AXES,
|
|
6
|
+
type SliceAxis,
|
|
7
|
+
areAllGrids,
|
|
8
|
+
} from "@ogw_front/utils/slice";
|
|
9
|
+
import type { ClippingPlane } from "@ogw_front/composables/clipping_planes_widget_entries";
|
|
3
10
|
import ClippingPlaneCard from "@ogw_front/components/ClippingPlaneCard.vue";
|
|
11
|
+
import SliceCard from "@ogw_front/components/SliceCard.vue";
|
|
4
12
|
import ToolPanel from "@ogw_front/components/ToolPanel.vue";
|
|
5
13
|
import { useClippingPlanesWidget } from "@ogw_front/composables/clipping_planes_widget";
|
|
6
14
|
import { useDataStore } from "@ogw_front/stores/data";
|
|
@@ -18,10 +26,23 @@ const dataStore = useDataStore();
|
|
|
18
26
|
const hybridViewerStore = useHybridViewerStore();
|
|
19
27
|
const targetAllVisible = ref<boolean>(true);
|
|
20
28
|
const selectedDatasetIds = ref<string[]>([]);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
let lastItemId = 0;
|
|
30
|
+
function newItemId(): number {
|
|
31
|
+
lastItemId += 1;
|
|
32
|
+
return lastItemId;
|
|
33
|
+
}
|
|
34
|
+
const planes = ref<ClippingPlane[]>([{ id: newItemId(), origin: undefined, normal: [1, 0, 0] }]);
|
|
24
35
|
const allItems = dataStore.refAllItems();
|
|
36
|
+
const sliceEnabled = ref<boolean>(false);
|
|
37
|
+
const slices = ref<{ id: number; axis: SliceAxis; index: number }[]>([
|
|
38
|
+
{ id: newItemId(), axis: DEFAULT_SLICE_AXIS, index: 0 },
|
|
39
|
+
]);
|
|
40
|
+
const sliceMaxIndices = ref<[number, number, number]>([0, 0, 0]);
|
|
41
|
+
const targetIds = computed<string[]>(() =>
|
|
42
|
+
targetAllVisible.value ? allItems.value.map((item) => item.id) : selectedDatasetIds.value,
|
|
43
|
+
);
|
|
44
|
+
const isAllGrid = computed<boolean>(() => areAllGrids(allItems.value, targetIds.value));
|
|
45
|
+
const isSliceActive = computed<boolean>(() => isAllGrid.value && sliceEnabled.value);
|
|
25
46
|
const availableDatasets = computed<{ title: string; value: string }[]>(() =>
|
|
26
47
|
allItems.value.map((item) => ({
|
|
27
48
|
title: item.name || item.id,
|
|
@@ -30,6 +51,8 @@ const availableDatasets = computed<{ title: string; value: string }[]>(() =>
|
|
|
30
51
|
);
|
|
31
52
|
const widgetContainer = useTemplateRef("widgetContainer");
|
|
32
53
|
let debouncedApply: ((...args: unknown[]) => void) | undefined = undefined;
|
|
54
|
+
let areSlicesApplied = false;
|
|
55
|
+
let pendingSlices: Promise<void> = Promise.resolve();
|
|
33
56
|
|
|
34
57
|
const {
|
|
35
58
|
getSceneCenter,
|
|
@@ -49,41 +72,103 @@ const {
|
|
|
49
72
|
debouncedApply: (...args: unknown[]) => debouncedApply?.(...args),
|
|
50
73
|
});
|
|
51
74
|
|
|
75
|
+
function getUntargetedIds(): string[] {
|
|
76
|
+
return allItems.value.map((item) => item.id).filter((id) => !targetIds.value.includes(id));
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
async function applyClippingPlanes(): Promise<void> {
|
|
53
|
-
|
|
54
|
-
if (allIds.length === 0) {
|
|
80
|
+
if (allItems.value.length === 0) {
|
|
55
81
|
return;
|
|
56
82
|
}
|
|
57
83
|
const center = getSceneCenter();
|
|
58
|
-
const
|
|
59
|
-
const untargetedIds = allIds.filter((id) => !targetIds.includes(id));
|
|
84
|
+
const untargetedIds = getUntargetedIds();
|
|
60
85
|
const planesData = planes.value.map((plane) => ({
|
|
61
86
|
origin: (plane.origin || center).map(Number),
|
|
62
87
|
normal: plane.normal.map(Number),
|
|
63
88
|
}));
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
if (targetIds.value.length > 0) {
|
|
90
|
+
await hybridViewerStore.setClippingPlanes(
|
|
91
|
+
targetIds.value,
|
|
92
|
+
isSliceActive.value ? [] : planesData,
|
|
93
|
+
);
|
|
67
94
|
}
|
|
68
95
|
if (untargetedIds.length > 0) {
|
|
69
96
|
await hybridViewerStore.setClippingPlanes(untargetedIds, []);
|
|
70
97
|
}
|
|
71
98
|
}
|
|
72
99
|
|
|
100
|
+
async function sendSlices(): Promise<void> {
|
|
101
|
+
if (!isAllGrid.value && !areSlicesApplied) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const isActive = isSliceActive.value;
|
|
105
|
+
const untargetedIds = getUntargetedIds();
|
|
106
|
+
if (targetIds.value.length > 0) {
|
|
107
|
+
const maxIndices = await hybridViewerStore.setSlice(
|
|
108
|
+
targetIds.value,
|
|
109
|
+
isActive ? slices.value.map(({ axis, index }) => ({ axis, index })) : [],
|
|
110
|
+
);
|
|
111
|
+
if (isAllGrid.value) {
|
|
112
|
+
sliceMaxIndices.value = maxIndices;
|
|
113
|
+
}
|
|
114
|
+
if (isActive) {
|
|
115
|
+
for (const slice of slices.value) {
|
|
116
|
+
slice.index = Math.min(slice.index, maxIndices[slice.axis]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (areSlicesApplied && untargetedIds.length > 0) {
|
|
121
|
+
await hybridViewerStore.setSlice(untargetedIds, []);
|
|
122
|
+
}
|
|
123
|
+
areSlicesApplied = isActive;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function queueSliceTask(task: () => Promise<void>): Promise<void> {
|
|
127
|
+
const previous = pendingSlices;
|
|
128
|
+
pendingSlices = (async (): Promise<void> => {
|
|
129
|
+
await Promise.allSettled([previous]);
|
|
130
|
+
await task();
|
|
131
|
+
})();
|
|
132
|
+
return pendingSlices;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function applySlices(): Promise<void> {
|
|
136
|
+
return queueSliceTask(sendSlices);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function applyAll(): Promise<void> {
|
|
140
|
+
await applyClippingPlanes();
|
|
141
|
+
await applySlices();
|
|
142
|
+
}
|
|
143
|
+
|
|
73
144
|
debouncedApply = useDebounceFn(() => applyClippingPlanes(), DEBOUNCE_DELAY);
|
|
145
|
+
const debouncedApplySlices = useDebounceFn(() => applySlices(), DEBOUNCE_DELAY);
|
|
74
146
|
|
|
75
147
|
function addPlane(): void {
|
|
76
148
|
// Index is always in-bounds (modulo the fixed-size list); the fallbacks only
|
|
77
149
|
// Satisfy noUncheckedIndexedAccess and are never hit at runtime.
|
|
78
150
|
const normal = DEFAULT_NORMALS[planes.value.length % DEFAULT_NORMALS.length] ??
|
|
79
151
|
DEFAULT_NORMALS[0] ?? [1, 0, 0];
|
|
80
|
-
planes.value.push({ origin: getSceneCenter(), normal });
|
|
152
|
+
planes.value.push({ id: newItemId(), origin: getSceneCenter(), normal });
|
|
81
153
|
}
|
|
82
154
|
|
|
83
155
|
function removePlane(index: number): void {
|
|
84
156
|
planes.value.splice(index, 1);
|
|
85
157
|
}
|
|
86
158
|
|
|
159
|
+
function addSlice(): void {
|
|
160
|
+
const lastSlice = slices.value.at(-1);
|
|
161
|
+
slices.value.push({
|
|
162
|
+
id: newItemId(),
|
|
163
|
+
axis: lastSlice ? SLICE_AXES[lastSlice.axis].next : DEFAULT_SLICE_AXIS,
|
|
164
|
+
index: 0,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function removeSlice(index: number): void {
|
|
169
|
+
slices.value.splice(index, 1);
|
|
170
|
+
}
|
|
171
|
+
|
|
87
172
|
function flipNormal(plane: { normal: number[] }): void {
|
|
88
173
|
plane.normal = plane.normal.map((component) => -component);
|
|
89
174
|
syncWidgets();
|
|
@@ -92,15 +177,23 @@ function flipNormal(plane: { normal: number[] }): void {
|
|
|
92
177
|
|
|
93
178
|
async function resetClippingPlanes(): Promise<void> {
|
|
94
179
|
setFromWidget(true);
|
|
95
|
-
planes.value = [{ origin: undefined, normal: [1, 0, 0] }];
|
|
180
|
+
planes.value = [{ id: newItemId(), origin: undefined, normal: [1, 0, 0] }];
|
|
181
|
+
sliceEnabled.value = false;
|
|
182
|
+
slices.value = [{ id: newItemId(), axis: DEFAULT_SLICE_AXIS, index: 0 }];
|
|
96
183
|
updateWidgetPlacement({ isReset: true });
|
|
97
184
|
setFromWidget(false);
|
|
98
|
-
await
|
|
185
|
+
await applyAll();
|
|
99
186
|
}
|
|
100
187
|
|
|
101
188
|
async function removeClippingPlanes(): Promise<void> {
|
|
102
189
|
const allIds = allItems.value.map((item) => item.id);
|
|
103
190
|
await hybridViewerStore.setClippingPlanes(allIds, []);
|
|
191
|
+
await queueSliceTask(async () => {
|
|
192
|
+
if (areSlicesApplied) {
|
|
193
|
+
await hybridViewerStore.setSlice(allIds, []);
|
|
194
|
+
areSlicesApplied = false;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
104
197
|
}
|
|
105
198
|
|
|
106
199
|
watch(widgetContainer, (container) => {
|
|
@@ -124,7 +217,7 @@ watch(
|
|
|
124
217
|
watch(show, (visible) => {
|
|
125
218
|
if (visible) {
|
|
126
219
|
updateWidgetPlacement({ isReset: true });
|
|
127
|
-
|
|
220
|
+
applyAll();
|
|
128
221
|
}
|
|
129
222
|
});
|
|
130
223
|
|
|
@@ -133,7 +226,23 @@ watch(
|
|
|
133
226
|
() => {
|
|
134
227
|
if (show.value) {
|
|
135
228
|
updateWidgetPlacement({ isReset: true });
|
|
136
|
-
|
|
229
|
+
applyAll();
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
{ deep: true },
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
watch(sliceEnabled, () => {
|
|
236
|
+
if (show.value) {
|
|
237
|
+
applyAll();
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
watch(
|
|
242
|
+
slices,
|
|
243
|
+
() => {
|
|
244
|
+
if (show.value) {
|
|
245
|
+
debouncedApplySlices();
|
|
137
246
|
}
|
|
138
247
|
},
|
|
139
248
|
{ deep: true },
|
|
@@ -142,7 +251,7 @@ watch(
|
|
|
142
251
|
watch(allItems, () => {
|
|
143
252
|
if (show.value) {
|
|
144
253
|
updateWidgetPlacement({ isReset: true });
|
|
145
|
-
|
|
254
|
+
applyAll();
|
|
146
255
|
}
|
|
147
256
|
});
|
|
148
257
|
|
|
@@ -151,7 +260,7 @@ watch(
|
|
|
151
260
|
(actorCount) => {
|
|
152
261
|
if (show.value && actorCount > 0) {
|
|
153
262
|
updateWidgetPlacement({ isReset: true });
|
|
154
|
-
|
|
263
|
+
applyAll();
|
|
155
264
|
}
|
|
156
265
|
},
|
|
157
266
|
);
|
|
@@ -171,6 +280,7 @@ onBeforeUnmount(cleanupLocalWidget);
|
|
|
171
280
|
>
|
|
172
281
|
<v-card-text class="pa-3 max-panel-height overflow-y-auto">
|
|
173
282
|
<v-sheet
|
|
283
|
+
v-show="!isSliceActive"
|
|
174
284
|
ref="widgetContainer"
|
|
175
285
|
height="180"
|
|
176
286
|
color="transparent"
|
|
@@ -203,28 +313,66 @@ onBeforeUnmount(cleanupLocalWidget);
|
|
|
203
313
|
|
|
204
314
|
<v-divider class="my-2" />
|
|
205
315
|
|
|
206
|
-
<v-
|
|
207
|
-
<v-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
316
|
+
<template v-if="isAllGrid">
|
|
317
|
+
<v-switch
|
|
318
|
+
v-model="sliceEnabled"
|
|
319
|
+
data-testid="sliceSwitch"
|
|
320
|
+
label="Slice"
|
|
321
|
+
color="primary"
|
|
322
|
+
density="compact"
|
|
323
|
+
hide-details
|
|
324
|
+
class="mb-2 text-caption"
|
|
325
|
+
/>
|
|
326
|
+
<template v-if="sliceEnabled">
|
|
327
|
+
<v-row align="center" justify="space-between" no-gutters class="mb-2">
|
|
328
|
+
<v-col class="text-caption font-weight-bold">Slices ({{ slices.length }})</v-col>
|
|
329
|
+
<v-col cols="auto">
|
|
330
|
+
<v-btn
|
|
331
|
+
data-testid="addSliceButton"
|
|
332
|
+
size="x-small"
|
|
333
|
+
variant="tonal"
|
|
334
|
+
color="primary"
|
|
335
|
+
icon="mdi-plus"
|
|
336
|
+
@click="addSlice"
|
|
337
|
+
/>
|
|
338
|
+
</v-col>
|
|
339
|
+
</v-row>
|
|
340
|
+
<SliceCard
|
|
341
|
+
v-for="(slice, idx) in slices"
|
|
342
|
+
:key="slice.id"
|
|
343
|
+
:slice="slice"
|
|
344
|
+
:index="idx"
|
|
345
|
+
:max-index="sliceMaxIndices[slice.axis]"
|
|
346
|
+
@remove="removeSlice(idx)"
|
|
216
347
|
/>
|
|
217
|
-
</
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
348
|
+
</template>
|
|
349
|
+
<v-divider class="my-2" />
|
|
350
|
+
</template>
|
|
351
|
+
|
|
352
|
+
<template v-if="!isSliceActive">
|
|
353
|
+
<v-row align="center" justify="space-between" no-gutters class="mb-2">
|
|
354
|
+
<v-col class="text-caption font-weight-bold">Planes ({{ planes.length }})</v-col>
|
|
355
|
+
<v-col cols="auto">
|
|
356
|
+
<v-btn
|
|
357
|
+
data-testid="addPlaneButton"
|
|
358
|
+
size="x-small"
|
|
359
|
+
variant="tonal"
|
|
360
|
+
color="primary"
|
|
361
|
+
icon="mdi-plus"
|
|
362
|
+
@click="addPlane"
|
|
363
|
+
/>
|
|
364
|
+
</v-col>
|
|
365
|
+
</v-row>
|
|
366
|
+
|
|
367
|
+
<ClippingPlaneCard
|
|
368
|
+
v-for="(plane, idx) in planes"
|
|
369
|
+
:key="plane.id"
|
|
370
|
+
:plane="plane"
|
|
371
|
+
:index="idx"
|
|
372
|
+
@remove="removePlane(idx)"
|
|
373
|
+
@flip-normal="flipNormal(plane)"
|
|
374
|
+
/>
|
|
375
|
+
</template>
|
|
228
376
|
</v-card-text>
|
|
229
377
|
|
|
230
378
|
<template #actions>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { SLICE_AXES, type SliceAxis } from "@ogw_front/utils/slice";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
slice: { axis: SliceAxis; index: number };
|
|
6
|
+
index: number;
|
|
7
|
+
maxIndex: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { slice, index, maxIndex } = defineProps<Props>();
|
|
11
|
+
|
|
12
|
+
interface Emits {
|
|
13
|
+
remove: [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const emit = defineEmits<Emits>();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<v-card data-testid="sliceCard" variant="outlined" class="pa-2 mb-3 rounded-lg border-opacity-50">
|
|
21
|
+
<v-row align="center" no-gutters class="mb-1">
|
|
22
|
+
<v-col cols="auto">
|
|
23
|
+
<v-chip size="x-small" variant="flat" color="primary" class="font-weight-bold">
|
|
24
|
+
Slice #{{ index + 1 }}
|
|
25
|
+
</v-chip>
|
|
26
|
+
</v-col>
|
|
27
|
+
<v-col class="px-2">
|
|
28
|
+
<v-slider
|
|
29
|
+
v-model="slice.index"
|
|
30
|
+
data-testid="sliceIndexSlider"
|
|
31
|
+
:min="0"
|
|
32
|
+
:max="maxIndex"
|
|
33
|
+
:step="1"
|
|
34
|
+
color="primary"
|
|
35
|
+
track-color="grey-lighten-2"
|
|
36
|
+
density="compact"
|
|
37
|
+
hide-details
|
|
38
|
+
/>
|
|
39
|
+
</v-col>
|
|
40
|
+
<v-col cols="auto">
|
|
41
|
+
<v-btn
|
|
42
|
+
data-testid="removeSliceButton"
|
|
43
|
+
icon="mdi-trash-can-outline"
|
|
44
|
+
size="x-small"
|
|
45
|
+
variant="text"
|
|
46
|
+
color="error"
|
|
47
|
+
@click="emit('remove')"
|
|
48
|
+
/>
|
|
49
|
+
</v-col>
|
|
50
|
+
</v-row>
|
|
51
|
+
|
|
52
|
+
<v-row align="center" justify="space-between" no-gutters>
|
|
53
|
+
<v-col cols="auto">
|
|
54
|
+
<v-btn-toggle
|
|
55
|
+
v-model="slice.axis"
|
|
56
|
+
data-testid="sliceAxisToggle"
|
|
57
|
+
mandatory
|
|
58
|
+
density="compact"
|
|
59
|
+
color="primary"
|
|
60
|
+
variant="outlined"
|
|
61
|
+
divided
|
|
62
|
+
>
|
|
63
|
+
<v-btn
|
|
64
|
+
v-for="axis in SLICE_AXES"
|
|
65
|
+
:key="axis.value"
|
|
66
|
+
:value="axis.value"
|
|
67
|
+
size="small"
|
|
68
|
+
class="text-caption"
|
|
69
|
+
>
|
|
70
|
+
{{ axis.title }}
|
|
71
|
+
</v-btn>
|
|
72
|
+
</v-btn-toggle>
|
|
73
|
+
</v-col>
|
|
74
|
+
<v-col cols="auto" class="text-caption text-primary font-weight-bold">
|
|
75
|
+
{{ slice.index }} / {{ maxIndex }}
|
|
76
|
+
</v-col>
|
|
77
|
+
</v-row>
|
|
78
|
+
</v-card>
|
|
79
|
+
</template>
|
|
@@ -11,6 +11,7 @@ import type vtkAbstractWidgetFactory from "@kitware/vtk.js/Widgets/Core/Abstract
|
|
|
11
11
|
import { newInstance as vtkImplicitPlaneWidget } from "@kitware/vtk.js/Widgets/Widgets3D/ImplicitPlaneWidget";
|
|
12
12
|
|
|
13
13
|
interface ClippingPlane {
|
|
14
|
+
id: number;
|
|
14
15
|
origin?: number[];
|
|
15
16
|
normal: number[];
|
|
16
17
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const GRID_TYPES = new Set([
|
|
2
|
+
"RegularGrid2D",
|
|
3
|
+
"LightRegularGrid2D",
|
|
4
|
+
"RegularGrid3D",
|
|
5
|
+
"LightRegularGrid3D",
|
|
6
|
+
]);
|
|
7
|
+
type SliceAxis = 0 | 1 | 2;
|
|
8
|
+
const SLICE_AXES = [
|
|
9
|
+
{ title: "YZ", value: 0, next: 1 },
|
|
10
|
+
{ title: "XZ", value: 1, next: 2 },
|
|
11
|
+
{ title: "XY", value: 2, next: 0 },
|
|
12
|
+
] as const satisfies readonly { title: string; value: SliceAxis; next: SliceAxis }[];
|
|
13
|
+
const DEFAULT_SLICE_AXIS: SliceAxis = 2;
|
|
14
|
+
|
|
15
|
+
function areAllGrids(items: { id: string; geode_object_type: string }[], ids: string[]): boolean {
|
|
16
|
+
const targetedItems = items.filter((item) => ids.includes(item.id));
|
|
17
|
+
return (
|
|
18
|
+
targetedItems.length > 0 &&
|
|
19
|
+
targetedItems.every((item) => GRID_TYPES.has(item.geode_object_type))
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { SLICE_AXES, DEFAULT_SLICE_AXIS, areAllGrids };
|
|
24
|
+
export type { SliceAxis };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SliceAxis } from "@ogw_front/utils/slice";
|
|
1
2
|
import { useHybridViewerCore } from "./core";
|
|
2
3
|
import { useViewerStore } from "@ogw_front/stores/viewer";
|
|
3
4
|
import viewer_schemas from "@geode/opengeodeweb-viewer/opengeodeweb_viewer_schemas.json";
|
|
@@ -30,6 +31,25 @@ async function setShrink(ids: string[], shrink_factor: number): Promise<void> {
|
|
|
30
31
|
});
|
|
31
32
|
await remoteRender();
|
|
32
33
|
}
|
|
34
|
+
async function setSlice(
|
|
35
|
+
ids: string[],
|
|
36
|
+
slices: { axis: SliceAxis; index: number }[],
|
|
37
|
+
): Promise<[number, number, number]> {
|
|
38
|
+
const viewerStore = useViewerStore();
|
|
39
|
+
const { remoteRender } = useHybridViewerCore();
|
|
40
|
+
const schema = viewer_schemas.opengeodeweb_viewer.viewer.slice;
|
|
41
|
+
const params = {
|
|
42
|
+
ids,
|
|
43
|
+
slices,
|
|
44
|
+
};
|
|
45
|
+
const response = await viewerStore.request({
|
|
46
|
+
schema,
|
|
47
|
+
params,
|
|
48
|
+
});
|
|
49
|
+
await remoteRender();
|
|
50
|
+
// oxlint-disable-next-line no-unsafe-type-assertion -- response shape is defined by the slice schema.
|
|
51
|
+
return (response as { max_indices: [number, number, number] }).max_indices;
|
|
52
|
+
}
|
|
33
53
|
interface ThresholdAttribute {
|
|
34
54
|
name: string;
|
|
35
55
|
location: "point" | "cell";
|
|
@@ -54,11 +74,16 @@ async function setThreshold(ids: string[], attribute?: ThresholdAttribute): Prom
|
|
|
54
74
|
function useHybridViewerFilters(): {
|
|
55
75
|
setClippingPlanes: (ids: string[], planes: unknown) => Promise<void>;
|
|
56
76
|
setShrink: (ids: string[], shrink_factor: number) => Promise<void>;
|
|
77
|
+
setSlice: (
|
|
78
|
+
ids: string[],
|
|
79
|
+
slices: { axis: SliceAxis; index: number }[],
|
|
80
|
+
) => Promise<[number, number, number]>;
|
|
57
81
|
setThreshold: (ids: string[], attribute?: ThresholdAttribute) => Promise<void>;
|
|
58
82
|
} {
|
|
59
83
|
return {
|
|
60
84
|
setClippingPlanes,
|
|
61
85
|
setShrink,
|
|
86
|
+
setSlice,
|
|
62
87
|
setThreshold,
|
|
63
88
|
};
|
|
64
89
|
}
|
package/package.json
CHANGED