@3cr/viewer-browser 0.0.220 → 0.0.246
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/components.d.ts +6 -2
- package/dist/Viewer3CR.js +32 -32
- package/dist/Viewer3CR.mjs +17808 -14315
- package/dist/Viewer3CR.umd.js +32 -32
- package/index.html +1 -1
- package/package.json +4 -3
- package/playground/index.html +4 -12
- package/src/App.vue +29 -14
- package/src/__tests__/main.spec.ts +4 -4
- package/src/assets/styles.scss +6 -2
- package/src/components/demo/DemoPatientModal.vue +12 -22
- package/src/components/demo/licence/DemoLicenceInfoModal.vue +11 -29
- package/src/components/demo/options.ts +42 -39
- package/src/components/demo/patient/DemoPatientInfoModal.vue +11 -29
- package/src/components/modal/CloseViewerModal.vue +1 -1
- package/src/components/modal/MftpWebGL3DRModal.vue +133 -120
- package/src/components/modal/ViewerActionRail.vue +0 -6
- package/src/components/modal/ViewerNavigationDrawerContent.vue +25 -16
- package/src/components/modal/ViewerNavigationDrawerFooter.vue +32 -9
- package/src/components/modal/ViewerNavigationDrawerHeader.vue +6 -1
- package/src/components/modal/WebGL3DR.vue +7 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +3 -2
- package/src/components/modal/buttons/AutoAnnotateBtn.vue +181 -13
- package/src/components/modal/menus/FileMenu.vue +2 -9
- package/src/components/modal/menus/SettingsMenu.vue +2 -7
- package/src/components/navigation/mcad/McadGlobalActions.vue +20 -0
- package/src/components/navigation/mcad/McadGlobalOpacitySlider.vue +103 -0
- package/src/components/navigation/mcad/McadGlobalScanViewBtn.vue +28 -0
- package/src/components/navigation/mcad/McadGlobalVisibilityBtn.vue +38 -0
- package/src/components/shared/LoadingSpinner.vue +27 -36
- package/src/components/views/AnnotationTreeView.vue +3 -1
- package/src/components/views/MarkupTreeView.vue +3 -1
- package/src/components/views/McadObjectTreeView.vue +46 -36
- package/src/components/views/modals/DataOverlayGeneralModal.vue +71 -0
- package/src/components/views/modals/DataOverlayMarkupModal.vue +60 -0
- package/src/components/views/modals/DataOverlayModal.vue +54 -55
- package/src/components/views/shared/Opacity.vue +0 -1
- package/src/composables/useDebounce.ts +11 -0
- package/src/composables/useIntroJs.ts +23 -6
- package/src/composables/useViewerOptions.ts +1 -0
- package/src/functions/guards/isDataOverlayAngle.ts +6 -0
- package/src/functions/guards/isDataOverlayLength.ts +6 -0
- package/src/functions/guards/isDataOverlayPolygon.ts +6 -0
- package/src/functions/modelHelper.ts +2 -0
- package/src/functions/notification.ts +63 -29
- package/src/main.ts +22 -13
- package/src/models/loadViewerOptions.ts +39 -0
- package/src/models/loadViewerPayload.ts +0 -7
- package/src/services/viewer-3cr.service.ts +13 -1
- package/src/tools/data-overlay.tool.ts +71 -0
- package/src/types/data-overlay-event.ts +5 -0
- package/src/types/data-overlay-info.ts +4 -0
- package/src/types/demo-type.ts +4 -0
- package/src/components/modal/actions/HideViewAction.vue +0 -38
- package/src/components/views/modals/DataOverlayModalManager.vue +0 -104
- package/src/components/views/modals/__tests__/DataOverlayModal.spec.ts +0 -33
- package/src/components/views/modals/__tests__/DataOverlayModalManager.spec.ts +0 -93
|
@@ -1,15 +1,44 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
v-if="opts.OnAutoAnnotate"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<v-menu :close-on-content-click="false" v-if="opts.OnAutoAnnotate">
|
|
4
|
+
<template #activator="{ props }">
|
|
5
|
+
<v-btn v-bind="props" data-testid="menu" class="mx-2" color="primary" prepend-icon="psychology" variant="flat">
|
|
6
|
+
AI
|
|
7
|
+
</v-btn>
|
|
8
|
+
</template>
|
|
9
|
+
<v-list>
|
|
10
|
+
<v-list-item
|
|
11
|
+
data-testid="auto_annotate"
|
|
12
|
+
v-if="opts.OnAutoAnnotate"
|
|
13
|
+
@click="autoAnnotate()"
|
|
14
|
+
:disabled="done"
|
|
15
|
+
prepend-icon="flash_auto"
|
|
16
|
+
:append-icon="done ? 'check' : undefined"
|
|
17
|
+
>
|
|
18
|
+
<v-list-item-title>Auto Annotate</v-list-item-title>
|
|
19
|
+
</v-list-item>
|
|
20
|
+
</v-list>
|
|
21
|
+
</v-menu>
|
|
22
|
+
<v-dialog v-model:model-value="loading">
|
|
23
|
+
<template #default>
|
|
24
|
+
<v-card :title="`Annotiva AI: ${type}`" max-width="450" class="mx-auto">
|
|
25
|
+
<div class="prompt">
|
|
26
|
+
<div style="font-size: 6px; top: 30px; left: 40px">
|
|
27
|
+
<div class="cog" style="--cog-colour: #5a93ed"></div>
|
|
28
|
+
<div
|
|
29
|
+
class="cog anti-clockwise"
|
|
30
|
+
style="--cog-colour: #2955da; margin-top: 2.25em; margin-left: -3.375em"
|
|
31
|
+
></div>
|
|
32
|
+
<div class="cog" style="--cog-colour: #bad3f8; margin-top: 5.625em; margin-left: -1.125em"></div>
|
|
33
|
+
</div>
|
|
34
|
+
<v-card-text class="ml-10" style="font-size: 16px">
|
|
35
|
+
{{ text }}
|
|
36
|
+
</v-card-text>
|
|
37
|
+
</div>
|
|
38
|
+
</v-card>
|
|
39
|
+
</template>
|
|
40
|
+
</v-dialog>
|
|
41
|
+
</div>
|
|
13
42
|
</template>
|
|
14
43
|
|
|
15
44
|
<script setup lang="ts">
|
|
@@ -19,11 +48,150 @@ import { ref } from 'vue';
|
|
|
19
48
|
const opts = useViewerOptions();
|
|
20
49
|
const loading = ref<boolean>(false);
|
|
21
50
|
const done = ref<boolean>(false);
|
|
51
|
+
const text = ref<string>('');
|
|
52
|
+
const type = ref<string>('');
|
|
22
53
|
|
|
23
54
|
async function autoAnnotate() {
|
|
55
|
+
type.value = 'Auto Annotate';
|
|
56
|
+
text.value = getRandomItemFromList(loaderTexts);
|
|
24
57
|
loading.value = true;
|
|
25
58
|
await opts.value!.OnAutoAnnotate!();
|
|
26
|
-
|
|
27
|
-
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
loading.value = false;
|
|
61
|
+
done.value = true;
|
|
62
|
+
}, 4000);
|
|
28
63
|
}
|
|
64
|
+
|
|
65
|
+
function getRandomItemFromList(list: Array<string>) {
|
|
66
|
+
const randomIndex = Math.floor(Math.random() * list.length);
|
|
67
|
+
return list[randomIndex];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const loaderTexts = [
|
|
71
|
+
"AI: Analyzing X-rays faster than you can say 'supercalifragilisticexpialidocious'!",
|
|
72
|
+
'Just a moment, our AI is putting on its lab coat.',
|
|
73
|
+
'Scanning pixels like Sherlock scans clues.',
|
|
74
|
+
'Our AI is channeling its inner doctor.',
|
|
75
|
+
'Hang tight, our AI is donning its stethoscope.',
|
|
76
|
+
"Analyzing with AI precision – because guessing isn't an option!",
|
|
77
|
+
'Our AI is checking the scan like a detective at a crime scene.',
|
|
78
|
+
'Crunching data like a pro! Results on the way.',
|
|
79
|
+
'AI is on it, decoding your scan like a mystery novel.',
|
|
80
|
+
'Our AI is in deep thought... about your health!',
|
|
81
|
+
'Letting our AI take a close look at those pixels.',
|
|
82
|
+
'Your scan is getting the AI treatment.',
|
|
83
|
+
'Analyzing like a champ! Our AI is almost there.',
|
|
84
|
+
'Hold tight! Our AI is putting on its thinking cap.',
|
|
85
|
+
'AI is hard at work, diagnosing like a pro!',
|
|
86
|
+
'Our AI is busy solving the puzzle of your scan.',
|
|
87
|
+
'AI is processing... and sipping virtual coffee.',
|
|
88
|
+
"Your scan is in good hands – AI's hands.",
|
|
89
|
+
'Just a sec, our AI is perfecting the diagnosis.',
|
|
90
|
+
'Scanning... AI style.',
|
|
91
|
+
'AI is reading your scan like an open book.',
|
|
92
|
+
'Give us a moment, our AI is analyzing every detail.',
|
|
93
|
+
'Our AI is double-checking for good measure.',
|
|
94
|
+
'AI: Making sense of pixels and patterns.',
|
|
95
|
+
'AI is decoding your scan, Sherlock style.',
|
|
96
|
+
'Processing with precision – AI is on it.',
|
|
97
|
+
'Our AI is diving deep into the data.',
|
|
98
|
+
'AI is dissecting the details for an accurate diagnosis.',
|
|
99
|
+
'Hold on, our AI is piecing together the puzzle.',
|
|
100
|
+
'AI is at the helm, navigating your scan results.',
|
|
101
|
+
'Analyzing with AI magic – results coming up!',
|
|
102
|
+
'Our AI is untangling the mysteries of your scan.',
|
|
103
|
+
"Processing... AI's putting its brain to work.",
|
|
104
|
+
'Your scan + our AI = accurate results.',
|
|
105
|
+
'AI is fine-tuning the analysis for perfection.',
|
|
106
|
+
'Letting our AI do the heavy lifting – almost done!',
|
|
107
|
+
'AI is reading the scan like a pro.',
|
|
108
|
+
'Our AI is on a mission – accurate results ahead.',
|
|
109
|
+
'Analyzing with AI expertise – hang tight!',
|
|
110
|
+
'AI is taking a closer look – results are on the way.'
|
|
111
|
+
];
|
|
29
112
|
</script>
|
|
113
|
+
<style>
|
|
114
|
+
.prompt {
|
|
115
|
+
margin-left: 20px;
|
|
116
|
+
margin-right: 20px;
|
|
117
|
+
padding: 20px;
|
|
118
|
+
text-align: center;
|
|
119
|
+
color: black;
|
|
120
|
+
display: flex;
|
|
121
|
+
justify-content: space-between;
|
|
122
|
+
align-items: center;
|
|
123
|
+
margin-bottom: 15px;
|
|
124
|
+
z-index: 100;
|
|
125
|
+
position: relative;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.prompt::before {
|
|
129
|
+
content: '';
|
|
130
|
+
position: absolute;
|
|
131
|
+
inset: 0;
|
|
132
|
+
border-radius: 16px;
|
|
133
|
+
padding: 3px;
|
|
134
|
+
background: linear-gradient(to right, #6fb3f2, #4e51f5);
|
|
135
|
+
|
|
136
|
+
-webkit-mask:
|
|
137
|
+
linear-gradient(#fff 0 0) content-box,
|
|
138
|
+
linear-gradient(#fff 0 0);
|
|
139
|
+
-webkit-mask-composite: xor;
|
|
140
|
+
mask-composite: exclude;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
div.cog {
|
|
144
|
+
nfont-size: 25px;
|
|
145
|
+
border-radius: 0.25em;
|
|
146
|
+
border: solid 0.55em var(--cog-colour, #eee);
|
|
147
|
+
display: inline-block;
|
|
148
|
+
padding: 1.125em;
|
|
149
|
+
position: absolute;
|
|
150
|
+
top: inherit;
|
|
151
|
+
left: inherit;
|
|
152
|
+
-webkit-animation: rotate var(--cog-speed, 3s) linear infinite;
|
|
153
|
+
-moz-animation: rotate var(--cog-speed, 3s) linear infinite;
|
|
154
|
+
animation: rotate var(--cog-speed, 3s) linear infinite;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
div.cog.anti-clockwise {
|
|
158
|
+
-webkit-animation-direction: reverse;
|
|
159
|
+
-moz-animation-direction: reverse;
|
|
160
|
+
animation-direction: reverse;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
div.cog::before {
|
|
164
|
+
border: solid 0.55em var(--cog-colour, #eee);
|
|
165
|
+
padding: 1.125em;
|
|
166
|
+
position: absolute;
|
|
167
|
+
content: '';
|
|
168
|
+
transform: translate(-1.675em, -1.675em) rotate(45deg);
|
|
169
|
+
border-radius: 0.25em;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
div.cog::after {
|
|
173
|
+
content: '';
|
|
174
|
+
position: absolute;
|
|
175
|
+
border: solid 1em var(--cog-colour, #eee);
|
|
176
|
+
padding: 0.375em;
|
|
177
|
+
border-radius: 1.8125em;
|
|
178
|
+
transform: translate(-1.375em, -1.375em);
|
|
179
|
+
filter: brightness(87.5%);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@-moz-keyframes rotate {
|
|
183
|
+
100% {
|
|
184
|
+
-moz-transform: rotate(360deg);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
@-webkit-keyframes rotate {
|
|
188
|
+
100% {
|
|
189
|
+
-webkit-transform: rotate(360deg);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
@keyframes rotate {
|
|
193
|
+
100% {
|
|
194
|
+
-webkit-transform: rotate(360deg);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
</style>
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-menu :close-on-content-click="false">
|
|
3
|
-
<template #activator="{ props
|
|
4
|
-
<v-btn
|
|
5
|
-
v-bind="props"
|
|
6
|
-
data-testid="menu"
|
|
7
|
-
class="mr-2"
|
|
8
|
-
:color="isActive ? 'secondary' : 'primary'"
|
|
9
|
-
prepend-icon="description"
|
|
10
|
-
variant="flat"
|
|
11
|
-
>
|
|
3
|
+
<template #activator="{ props }">
|
|
4
|
+
<v-btn v-bind="props" data-testid="menu" class="mr-2" color="primary" prepend-icon="description" variant="flat">
|
|
12
5
|
File
|
|
13
6
|
</v-btn>
|
|
14
7
|
</template>
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-menu v-model="menuState" :close-on-content-click="false">
|
|
3
|
-
<template #activator="{ props
|
|
4
|
-
<v-btn
|
|
5
|
-
v-bind="mergeProps(props, activatorProps)"
|
|
6
|
-
:color="isActive ? 'secondary' : 'primary'"
|
|
7
|
-
prepend-icon="settings"
|
|
8
|
-
variant="flat"
|
|
9
|
-
>
|
|
3
|
+
<template #activator="{ props }">
|
|
4
|
+
<v-btn v-bind="mergeProps(props, activatorProps)" color="primary" prepend-icon="settings" variant="flat">
|
|
10
5
|
Settings
|
|
11
6
|
</v-btn>
|
|
12
7
|
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card flat density="compact" color="transparent">
|
|
3
|
+
<v-card-title class="text-overline">Global Actions</v-card-title>
|
|
4
|
+
<v-card-text class="d-flex flex-column ga-1">
|
|
5
|
+
<mcad-global-scan-view-btn />
|
|
6
|
+
<!-- <mcad-global-visibility-btn :mcads="items" />-->
|
|
7
|
+
<!-- <mcad-global-opacity-slider :mcads="items" />-->
|
|
8
|
+
</v-card-text>
|
|
9
|
+
</v-card>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import { DataOverlayMcad } from '@/types/data-overlay-mcad';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
items: DataOverlayMcad[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
defineProps<Props>();
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row class="flex-column" no-gutters>
|
|
3
|
+
<v-row no-gutters>
|
|
4
|
+
<v-col>
|
|
5
|
+
<span class="text-caption">Global Opacity</span>
|
|
6
|
+
</v-col>
|
|
7
|
+
</v-row>
|
|
8
|
+
<v-row no-gutters>
|
|
9
|
+
<v-col class="my-auto" cols="9">
|
|
10
|
+
<v-slider
|
|
11
|
+
v-model="opacity"
|
|
12
|
+
class="pr-4"
|
|
13
|
+
hide-details
|
|
14
|
+
min="0"
|
|
15
|
+
max="100"
|
|
16
|
+
thumb-color="primary"
|
|
17
|
+
thumb-size="16"
|
|
18
|
+
step="1"
|
|
19
|
+
/>
|
|
20
|
+
</v-col>
|
|
21
|
+
<v-col class="my-auto" cols="3">
|
|
22
|
+
<v-text-field
|
|
23
|
+
v-model="opacity"
|
|
24
|
+
density="compact"
|
|
25
|
+
hide-details
|
|
26
|
+
variant="outlined"
|
|
27
|
+
type="number"
|
|
28
|
+
min="0"
|
|
29
|
+
max="100"
|
|
30
|
+
>
|
|
31
|
+
<template #append-inner>
|
|
32
|
+
<v-icon icon="percent" size="16" />
|
|
33
|
+
</template>
|
|
34
|
+
</v-text-field>
|
|
35
|
+
</v-col>
|
|
36
|
+
</v-row>
|
|
37
|
+
</v-row>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { DataOverlayMcad } from '@/types/data-overlay-mcad';
|
|
42
|
+
import { useViewer3cr } from '@/composables/useViewer3cr';
|
|
43
|
+
import { computed, ref } from 'vue';
|
|
44
|
+
import { ObjectColour } from '@3cr/types-ts';
|
|
45
|
+
import { clamp } from '@/functions/clamp';
|
|
46
|
+
import { useDebounce } from '@/composables/useDebounce';
|
|
47
|
+
|
|
48
|
+
interface Props {
|
|
49
|
+
mcads: DataOverlayMcad[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const props = defineProps<Props>();
|
|
53
|
+
|
|
54
|
+
const viewer3cr = useViewer3cr();
|
|
55
|
+
const setOpacityDebounce = useDebounce(setOpacity, 500);
|
|
56
|
+
|
|
57
|
+
const _opacity = ref<number>(100);
|
|
58
|
+
|
|
59
|
+
const opacity = computed({
|
|
60
|
+
get(): number {
|
|
61
|
+
return _opacity.value;
|
|
62
|
+
},
|
|
63
|
+
set(value: number): void {
|
|
64
|
+
const next = clamp(value, 0, 100);
|
|
65
|
+
_opacity.value = next;
|
|
66
|
+
setOpacityDebounce(next);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
async function setOpacity(value: number): Promise<void> {
|
|
71
|
+
const alpha = clamp(value, 0, 100) / 100;
|
|
72
|
+
for (const mcad of props.mcads) {
|
|
73
|
+
const message: ObjectColour = {
|
|
74
|
+
Version: '0.0.0',
|
|
75
|
+
Id: mcad.Id,
|
|
76
|
+
Colour: {
|
|
77
|
+
Version: '0.0.0',
|
|
78
|
+
R: mcad.Colour.R,
|
|
79
|
+
G: mcad.Colour.G,
|
|
80
|
+
B: mcad.Colour.B,
|
|
81
|
+
A: alpha
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
await viewer3cr.setMcadObjectColour(message);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<style scoped lang="scss">
|
|
90
|
+
:deep(.v-field) {
|
|
91
|
+
padding-right: 1px !important;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
:deep(input) {
|
|
95
|
+
font-size: 11px;
|
|
96
|
+
padding: 4px 0 4px 4px;
|
|
97
|
+
min-height: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
:deep(.v-field__append-inner) {
|
|
101
|
+
padding-top: 4px;
|
|
102
|
+
}
|
|
103
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-btn variant="text" size="small" @click="toggle">
|
|
3
|
+
<v-icon start :icon="icon" size="20" />
|
|
4
|
+
<span>{{ visibility ? 'Hide' : 'Show' }} 3D Volume</span>
|
|
5
|
+
</v-btn>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { computed } from 'vue';
|
|
10
|
+
import { useViewer3cr } from '@/composables/useViewer3cr';
|
|
11
|
+
import { ScanView, ViewToggleData } from '@3cr/types-ts';
|
|
12
|
+
import { scanState } from '@/models/scanState';
|
|
13
|
+
|
|
14
|
+
const viewer3cr = useViewer3cr();
|
|
15
|
+
|
|
16
|
+
const visibility = computed(() => {
|
|
17
|
+
return scanState.value.Orientations.Volume.Visibility;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const icon = computed(() => {
|
|
21
|
+
return visibility.value ? 'visibility' : 'visibility_off';
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
async function toggle(): Promise<void> {
|
|
25
|
+
const message: ViewToggleData = { Version: '0.0.0', View: ScanView.Volume, Visibility: !visibility.value };
|
|
26
|
+
await viewer3cr.viewSelectionToggleView(message);
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-btn variant="text" size="small" @click="toggle">
|
|
3
|
+
<v-icon start :icon="icon" size="20" />
|
|
4
|
+
<span>{{ visibility ? 'Hide' : 'Show' }} all MCADs</span>
|
|
5
|
+
</v-btn>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { computed } from 'vue';
|
|
10
|
+
import { DataOverlayMcad } from '@/types/data-overlay-mcad';
|
|
11
|
+
import { ObjectVisibility } from '@3cr/types-ts';
|
|
12
|
+
import { useViewer3cr } from '@/composables/useViewer3cr';
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
mcads: DataOverlayMcad[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = defineProps<Props>();
|
|
19
|
+
|
|
20
|
+
const viewer3cr = useViewer3cr();
|
|
21
|
+
|
|
22
|
+
const visibility = computed(() => {
|
|
23
|
+
return props.mcads.some((mcad) => mcad.Visibility);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const icon = computed(() => {
|
|
27
|
+
return visibility.value ? 'visibility' : 'visibility_off';
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
async function toggle(): Promise<void> {
|
|
31
|
+
for (const mcad of props.mcads) {
|
|
32
|
+
const message: ObjectVisibility = { Version: '0.0.0', Id: mcad.Id, Visibility: !visibility.value };
|
|
33
|
+
await viewer3cr.setMcadObjectVisibility(message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
defineOptions({ inheritAttrs: false });
|
|
38
|
+
</script>
|
|
@@ -4,12 +4,12 @@ export interface Props {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
const props = withDefaults(defineProps<Props>(), {
|
|
7
|
-
text:
|
|
7
|
+
text: 'Loading Online Viewer'
|
|
8
8
|
});
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<template>
|
|
12
|
-
<div
|
|
12
|
+
<div class="w-100 h-100">
|
|
13
13
|
<div id="spinner">
|
|
14
14
|
<div class="content">
|
|
15
15
|
<div class="circle"></div>
|
|
@@ -20,12 +20,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
20
20
|
</div>
|
|
21
21
|
<div
|
|
22
22
|
class="mx-auto text-center text-white text-h3"
|
|
23
|
-
style="
|
|
24
|
-
position: absolute;
|
|
25
|
-
bottom: 10%;
|
|
26
|
-
left: 50%;
|
|
27
|
-
transform: translateX(-50%);
|
|
28
|
-
"
|
|
23
|
+
style="position: absolute; bottom: 10%; left: 50%; transform: translateX(-50%)"
|
|
29
24
|
v-html="props.text"
|
|
30
25
|
></div>
|
|
31
26
|
</div>
|
|
@@ -62,20 +57,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
62
57
|
--in: 80%;
|
|
63
58
|
--ar: #8799a4;
|
|
64
59
|
--dt: #ffffff;
|
|
65
|
-
--shadow: drop-shadow(0vmin 0vmin 0.5vmin rgba(0, 0, 0, 0.35))
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
0deg,
|
|
69
|
-
#fff0 calc(50% - 2px),
|
|
70
|
-
#000 calc(50% - 1px) calc(50% + 1px),
|
|
71
|
-
#fff0 calc(50% + 2px)
|
|
72
|
-
),
|
|
73
|
-
linear-gradient(
|
|
74
|
-
90deg,
|
|
75
|
-
#fff0 calc(50% - 2px),
|
|
76
|
-
#000 calc(50% - 1px) calc(50% + 1px),
|
|
77
|
-
#fff0 calc(50% + 2px)
|
|
78
|
-
);
|
|
60
|
+
--shadow: drop-shadow(0vmin 0vmin 0.5vmin rgba(0, 0, 0, 0.35)) drop-shadow(0vmin 1vmin 0.5vmin rgba(0, 0, 0, 0.09));
|
|
61
|
+
--cross: linear-gradient(0deg, #fff0 calc(50% - 2px), #000 calc(50% - 1px) calc(50% + 1px), #fff0 calc(50% + 2px)),
|
|
62
|
+
linear-gradient(90deg, #fff0 calc(50% - 2px), #000 calc(50% - 1px) calc(50% + 1px), #fff0 calc(50% + 2px));
|
|
79
63
|
border: 6vmin solid var(--ar);
|
|
80
64
|
width: var(--in);
|
|
81
65
|
height: var(--in);
|
|
@@ -87,10 +71,12 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
87
71
|
top: 15vmin;
|
|
88
72
|
right: -10vmin;
|
|
89
73
|
animation: spin-bot var(--sp) ease 0s infinite;
|
|
90
|
-
background-image: var(--cross),
|
|
91
|
-
radial-gradient(var(--dt) 5.5vmin, #fff0 calc(5.5vmin + 1px));
|
|
74
|
+
background-image: var(--cross), radial-gradient(var(--dt) 5.5vmin, #fff0 calc(5.5vmin + 1px));
|
|
92
75
|
background-repeat: no-repeat;
|
|
93
|
-
background-size:
|
|
76
|
+
background-size:
|
|
77
|
+
3vmin 1vmin,
|
|
78
|
+
1vmin 3vmin,
|
|
79
|
+
100% 100%;
|
|
94
80
|
background-position: center center;
|
|
95
81
|
filter: var(--shadow);
|
|
96
82
|
}
|
|
@@ -100,11 +86,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
100
86
|
top: -2vmin;
|
|
101
87
|
animation: spin-top var(--sp) ease 0s infinite;
|
|
102
88
|
transform: rotate(-45deg);
|
|
103
|
-
background-image: var(--cross),
|
|
104
|
-
radial-gradient(var(--dt) 1.25vmin, #fff0 calc(1.25vmin + 1px));
|
|
89
|
+
background-image: var(--cross), radial-gradient(var(--dt) 1.25vmin, #fff0 calc(1.25vmin + 1px));
|
|
105
90
|
right: -4vmin;
|
|
106
91
|
filter: hue-rotate(10deg) var(--shadow);
|
|
107
|
-
background-size:
|
|
92
|
+
background-size:
|
|
93
|
+
1.4vmin 1vmin,
|
|
94
|
+
1vmin 1.4vmin,
|
|
95
|
+
100% 100%;
|
|
108
96
|
}
|
|
109
97
|
|
|
110
98
|
.circle:nth-child(3) {
|
|
@@ -113,10 +101,12 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
113
101
|
left: -13vmin;
|
|
114
102
|
transform: rotate(175deg);
|
|
115
103
|
animation: spin-left var(--sp) ease calc(var(--sp) / 4) infinite;
|
|
116
|
-
background-image: var(--cross),
|
|
117
|
-
radial-gradient(var(--dt) 9vmin, #fff0 calc(9vmin + 1px));
|
|
104
|
+
background-image: var(--cross), radial-gradient(var(--dt) 9vmin, #fff0 calc(9vmin + 1px));
|
|
118
105
|
filter: hue-rotate(20deg) var(--shadow);
|
|
119
|
-
background-size:
|
|
106
|
+
background-size:
|
|
107
|
+
5vmin 1vmin,
|
|
108
|
+
1vmin 5vmin,
|
|
109
|
+
100% 100%;
|
|
120
110
|
}
|
|
121
111
|
|
|
122
112
|
.circle:nth-child(4) {
|
|
@@ -124,12 +114,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
124
114
|
top: 35vmin;
|
|
125
115
|
left: -6vmin;
|
|
126
116
|
transform: rotate(-280deg);
|
|
127
|
-
animation: spin-last var(--sp) ease
|
|
128
|
-
|
|
129
|
-
background-image: var(--cross),
|
|
130
|
-
radial-gradient(var(--dt) 2.5vmin, #fff0 calc(2.5vmin + 1px));
|
|
117
|
+
animation: spin-last var(--sp) ease calc(calc(calc(var(--sp) / 4) + var(--sp)) * -1) infinite;
|
|
118
|
+
background-image: var(--cross), radial-gradient(var(--dt) 2.5vmin, #fff0 calc(2.5vmin + 1px));
|
|
131
119
|
filter: hue-rotate(30deg) var(--shadow);
|
|
132
|
-
background-size:
|
|
120
|
+
background-size:
|
|
121
|
+
2vmin 1vmin,
|
|
122
|
+
1vmin 2vmin,
|
|
123
|
+
100% 100%;
|
|
133
124
|
}
|
|
134
125
|
|
|
135
126
|
@keyframes spin-all {
|
|
@@ -89,7 +89,9 @@ const questionText = ref<string>('');
|
|
|
89
89
|
const title = ref<string>('');
|
|
90
90
|
|
|
91
91
|
function setTreeViewData(annotations: DataOverlayData<DataOverlayAnnotation>[]): void {
|
|
92
|
-
data.value = annotations
|
|
92
|
+
data.value = annotations
|
|
93
|
+
.sort((a, b) => a.Data.Title.localeCompare(b.Data.Title))
|
|
94
|
+
.map((annotation) => mapToTreeViewItem(annotation));
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
watch(
|
|
@@ -61,7 +61,9 @@ watch(
|
|
|
61
61
|
);
|
|
62
62
|
|
|
63
63
|
function setTreeViewData(markups: DataOverlayData<DataOverlayMarkup>[]): void {
|
|
64
|
-
data.value = markups
|
|
64
|
+
data.value = markups
|
|
65
|
+
.sort((a, b) => a.Data.Title.localeCompare(b.Data.Title))
|
|
66
|
+
.map((markup) => mapToTreeViewItem(markup));
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
function getMarkupColour(data: DataOverlayData<DataOverlayMarkup>): string {
|