@3cr/viewer-browser 0.0.1
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/.browserslistrc +4 -0
- package/.editorconfig +5 -0
- package/.idea/git_toolbox_prj.xml +15 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/workspace.xml +183 -0
- package/.vite/deps/_metadata.json +8 -0
- package/.vite/deps/package.json +3 -0
- package/README.md +81 -0
- package/components.d.ts +19 -0
- package/dist/3cr-viewer-browser.mjs +18644 -0
- package/dist/3cr-viewer-browser.umd.js +18 -0
- package/dist/style.css +5 -0
- package/index.html +24 -0
- package/index.ts +37 -0
- package/package.json +41 -0
- package/src/App.vue +40 -0
- package/src/assets/images/MainBackdrop.svg +48 -0
- package/src/assets/images/dark/3DICOM.png +0 -0
- package/src/assets/images/dark/3dicom-logo.svg +1 -0
- package/src/assets/images/dark/Singular-Health-Disc-Mono.svg +9 -0
- package/src/assets/images/dark/Singular-Health-Trademark-mono.svg +23 -0
- package/src/assets/images/light/3DICOM.png +0 -0
- package/src/assets/images/light/3dicom-logo.svg +1 -0
- package/src/assets/images/light/Singular-Health-Disc-Mono.svg +9 -0
- package/src/assets/images/light/Singular-Health-Trademark-mono.svg +23 -0
- package/src/assets/logo.png +0 -0
- package/src/assets/logo.svg +6 -0
- package/src/components/DoubleSliderSelector.vue +112 -0
- package/src/components/ExpansionHeaderMiniMenu.vue +19 -0
- package/src/components/HelloWorld.vue +157 -0
- package/src/components/LoadingSpinner.vue +157 -0
- package/src/components/MftpWebGL3DRModal.vue +995 -0
- package/src/components/README.md +35 -0
- package/src/components/SliderSelector.vue +101 -0
- package/src/components/VerticalSliderSelector.vue +83 -0
- package/src/components/WebGL3DR.vue +107 -0
- package/src/helpers/layoutOverlayStyle.ts +86 -0
- package/src/helpers/modelHelper.ts +109 -0
- package/src/helpers/models.ts +69 -0
- package/src/helpers/utils.ts +16 -0
- package/src/main.ts +23 -0
- package/src/plugins/README.md +3 -0
- package/src/plugins/index.ts +15 -0
- package/src/plugins/vuetify.ts +27 -0
- package/src/types/window.shim.ts +24 -0
- package/src/vite-env.d.ts +7 -0
- package/tsconfig.json +34 -0
- package/tsconfig.node.json +9 -0
- package/vite.config.mts +74 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
Vue template files in this folder are automatically imported.
|
|
4
|
+
|
|
5
|
+
## 🚀 Usage
|
|
6
|
+
|
|
7
|
+
Importing is handled by [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components). This plugin automatically imports `.vue` files created in the `src/components` directory, and registers them as global components. This means that you can use any component in your application without having to manually import it.
|
|
8
|
+
|
|
9
|
+
The following example assumes a component located at `src/components/MyComponent.vue`:
|
|
10
|
+
|
|
11
|
+
```vue
|
|
12
|
+
<template>
|
|
13
|
+
<div>
|
|
14
|
+
<MyComponent />
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script lang="ts" setup>
|
|
19
|
+
//
|
|
20
|
+
</script>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
When your template is rendered, the component's import will automatically be inlined, which renders to this:
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<template>
|
|
27
|
+
<div>
|
|
28
|
+
<MyComponent />
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script lang="ts" setup>
|
|
33
|
+
import MyComponent from '@/components/MyComponent.vue'
|
|
34
|
+
</script>
|
|
35
|
+
```
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {computed, defineEmits} from 'vue';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export interface Props {
|
|
7
|
+
value: number;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
label: string;
|
|
10
|
+
min: number;
|
|
11
|
+
max: number;
|
|
12
|
+
step: number,
|
|
13
|
+
prepend: string,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
17
|
+
value: 0,
|
|
18
|
+
loading: false,
|
|
19
|
+
label: "Test",
|
|
20
|
+
min: -99999,
|
|
21
|
+
max: 99999,
|
|
22
|
+
step: 1,
|
|
23
|
+
prepend: '',
|
|
24
|
+
});
|
|
25
|
+
const emit = defineEmits<{
|
|
26
|
+
input: [number];
|
|
27
|
+
}>();
|
|
28
|
+
const sliderValue = computed({
|
|
29
|
+
get() {
|
|
30
|
+
return props.value
|
|
31
|
+
},
|
|
32
|
+
set(value: number) {
|
|
33
|
+
emit('input', value);
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div class="single-slider-selector">
|
|
40
|
+
<!-- <v-card-subtitle class="pb-0 pt-1 text-caption">{{ label }}</v-card-subtitle>-->
|
|
41
|
+
<v-card-actions class="px-2 pb-0">
|
|
42
|
+
<span class="text-caption">
|
|
43
|
+
{{ label }}
|
|
44
|
+
</span>
|
|
45
|
+
<v-spacer />
|
|
46
|
+
<v-text-field
|
|
47
|
+
v-model="sliderValue"
|
|
48
|
+
outlined
|
|
49
|
+
dense
|
|
50
|
+
class="mt-0 pt-0"
|
|
51
|
+
hide-details
|
|
52
|
+
type="number"
|
|
53
|
+
:append-icon="prepend"
|
|
54
|
+
style="width: 44px"
|
|
55
|
+
></v-text-field>
|
|
56
|
+
</v-card-actions>
|
|
57
|
+
<!-- <v-slider-->
|
|
58
|
+
<!-- v-model="sliderValue"-->
|
|
59
|
+
<!-- :loading="loading"-->
|
|
60
|
+
<!-- thumb-label-->
|
|
61
|
+
<!-- :max="max"-->
|
|
62
|
+
<!-- :min="min"-->
|
|
63
|
+
<!-- :step="step"-->
|
|
64
|
+
<!-- hide-details-->
|
|
65
|
+
<!-- class="align-center px-2 black--text"-->
|
|
66
|
+
<!-- >-->
|
|
67
|
+
<!-- <!– <template v-slot:append>–>-->
|
|
68
|
+
<!-- <!– <v-text-field–>-->
|
|
69
|
+
<!-- <!– v-model="sliderValue"–>-->
|
|
70
|
+
<!-- <!– outlined–>-->
|
|
71
|
+
<!-- <!– hide-details–>-->
|
|
72
|
+
<!-- <!– dense–>-->
|
|
73
|
+
<!-- <!– class="mt-0 pt-0 ml-2"–>-->
|
|
74
|
+
<!-- <!– type="number"–>-->
|
|
75
|
+
<!-- <!– style="width: 68px"–>-->
|
|
76
|
+
<!-- <!– ></v-text-field>–>-->
|
|
77
|
+
<!-- <!– </template>–>-->
|
|
78
|
+
<!-- </v-slider>-->
|
|
79
|
+
</div>
|
|
80
|
+
</template>
|
|
81
|
+
|
|
82
|
+
<style>
|
|
83
|
+
.single-slider-selector
|
|
84
|
+
.v-text-field.v-text-field--enclosed:not(.v-text-field--rounded)
|
|
85
|
+
> .v-input__control
|
|
86
|
+
> .v-input__slot,
|
|
87
|
+
.single-slider-selector .v-text-field.v-text-field--enclosed .v-text-field__details {
|
|
88
|
+
padding: 0px 8px !important;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.single-slider-selector
|
|
92
|
+
.v-text-field--outlined.v-input--dense.v-text-field--outlined
|
|
93
|
+
> .v-input__control
|
|
94
|
+
> .v-input__slot {
|
|
95
|
+
min-height: 32px !important;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.single-slider-selector .v-slider__thumb-label {
|
|
99
|
+
color: black;
|
|
100
|
+
}
|
|
101
|
+
</style>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {computed, defineEmits, ref, unref, watch} from 'vue';
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
value: number;
|
|
6
|
+
loading: boolean;
|
|
7
|
+
label: string;
|
|
8
|
+
lower: number;
|
|
9
|
+
upper: number;
|
|
10
|
+
min: number;
|
|
11
|
+
max: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
+
value: 0,
|
|
16
|
+
loading: false,
|
|
17
|
+
label: "Test",
|
|
18
|
+
lower: 0,
|
|
19
|
+
upper: 100,
|
|
20
|
+
min: -99999,
|
|
21
|
+
max: 99999,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const emit = defineEmits<{
|
|
25
|
+
input: [number];
|
|
26
|
+
}>();
|
|
27
|
+
|
|
28
|
+
const prevValue = ref<number>(0)
|
|
29
|
+
const showThumb = ref<"always" | boolean>(true)
|
|
30
|
+
const showThumbTimeout = ref<number>(0)
|
|
31
|
+
const sliderValue = computed({
|
|
32
|
+
get() {
|
|
33
|
+
return props.value
|
|
34
|
+
},
|
|
35
|
+
set(value: number) {
|
|
36
|
+
emit('input', value);
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
watch(sliderValue, async (currentValue: number) => {
|
|
41
|
+
if (JSON.stringify(unref(prevValue)) !== JSON.stringify(currentValue)) {
|
|
42
|
+
clearTimeout(unref(showThumbTimeout));
|
|
43
|
+
showThumb.value = 'always';
|
|
44
|
+
showThumbTimeout.value = setTimeout(() => {
|
|
45
|
+
showThumb.value = true;
|
|
46
|
+
}, 1000);
|
|
47
|
+
}
|
|
48
|
+
prevValue.value = currentValue;
|
|
49
|
+
}, {deep: true})
|
|
50
|
+
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<v-slider
|
|
55
|
+
style="position: absolute; right: 16px; top: 0; height: 100%"
|
|
56
|
+
v-model="sliderValue"
|
|
57
|
+
:thumb-label="showThumb"
|
|
58
|
+
:min="min"
|
|
59
|
+
:max="max"
|
|
60
|
+
height="100%"
|
|
61
|
+
vertical
|
|
62
|
+
hide-details
|
|
63
|
+
class="vertical-slider-selector"
|
|
64
|
+
>
|
|
65
|
+
<template #append>
|
|
66
|
+
<div class="white--text py-1 my-0">{{ max }}</div>
|
|
67
|
+
</template>
|
|
68
|
+
<template #prepend>
|
|
69
|
+
<div class="white--text py-1 my-0">{{ min }}</div>
|
|
70
|
+
</template>
|
|
71
|
+
</v-slider>
|
|
72
|
+
</template>
|
|
73
|
+
|
|
74
|
+
<style lang="scss">
|
|
75
|
+
.vertical-slider-selector .v-slider__thumb-label {
|
|
76
|
+
width: 40px !important;
|
|
77
|
+
height: 40px !important;
|
|
78
|
+
transform: translateY(50%) translateX(-19px) translateX(-100%) rotate(-45deg) !important;
|
|
79
|
+
& div {
|
|
80
|
+
transform: rotate(45deg);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div id="screenshotWindow" class="" @mouseover="emit('hover', true)" @mouseout="emit('hover', false)">
|
|
3
|
+
<canvas
|
|
4
|
+
id="unity-canvas"
|
|
5
|
+
width="100%"
|
|
6
|
+
height="calc(100vh - 48px)"
|
|
7
|
+
tabindex="-1"
|
|
8
|
+
style="
|
|
9
|
+
width: 100%;
|
|
10
|
+
height: calc(100vh - 50px);
|
|
11
|
+
background: url('~@/assets/images/MainBackdrop.svg') !important;
|
|
12
|
+
background-size: cover !important;
|
|
13
|
+
"
|
|
14
|
+
>
|
|
15
|
+
</canvas>
|
|
16
|
+
<div id="parent-canvas" ref="parent_canvas">
|
|
17
|
+
<slot></slot>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
import {defineEmits, nextTick, onMounted, ref} from 'vue';
|
|
23
|
+
|
|
24
|
+
import { createInstance, executePayload, registerOnPayloadHandler } from '@3cr/sdk-browser';
|
|
25
|
+
import {FrontEndInterfaces, FrontEndPayload} from '@3cr/sdk-browser/types/payload';
|
|
26
|
+
|
|
27
|
+
const emit = defineEmits<{
|
|
28
|
+
instance_loaded: [void];
|
|
29
|
+
on_payload: [FrontEndInterfaces, string, string];
|
|
30
|
+
hover: [boolean];
|
|
31
|
+
}>();
|
|
32
|
+
|
|
33
|
+
defineExpose({
|
|
34
|
+
sendPayload,
|
|
35
|
+
snap,
|
|
36
|
+
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
onMounted(async () => {
|
|
40
|
+
await nextTick()
|
|
41
|
+
const canvas = document.querySelector('#unity-canvas') as HTMLCanvasElement;
|
|
42
|
+
|
|
43
|
+
await registerOnPayloadHandler(receiveMessageFromUnity);
|
|
44
|
+
|
|
45
|
+
if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
|
|
46
|
+
// Mobile device style: fill the whole browser client area with the game canvas:
|
|
47
|
+
const meta = document.createElement('meta');
|
|
48
|
+
meta.name = 'viewport';
|
|
49
|
+
meta.content = 'width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes';
|
|
50
|
+
document.getElementsByTagName('head')[0].appendChild(meta);
|
|
51
|
+
|
|
52
|
+
canvas.style.width = '100%';
|
|
53
|
+
canvas.style.height = '80vh';
|
|
54
|
+
canvas.style.position = 'fixed';
|
|
55
|
+
|
|
56
|
+
document.body.style.textAlign = 'left';
|
|
57
|
+
}
|
|
58
|
+
await nextTick()
|
|
59
|
+
unityInstance.value = await createInstance(canvas);
|
|
60
|
+
|
|
61
|
+
await nextTick()
|
|
62
|
+
|
|
63
|
+
if (navigator.storage && navigator.storage.estimate) {
|
|
64
|
+
const quota = await navigator.storage.estimate();
|
|
65
|
+
if (quota) {
|
|
66
|
+
const percentageUsed = (quota.usage! / quota.quota!) * 100;
|
|
67
|
+
console.log(`You've used ${percentageUsed}% of the available storage.`);
|
|
68
|
+
const remaining = quota.quota! - quota.usage!;
|
|
69
|
+
console.log(`You can write up to ${remaining} more bytes.`);
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
emit('instance_loaded')
|
|
74
|
+
// Overlay scroll events to the container instance
|
|
75
|
+
const fixedDiv = document.getElementById('parent-canvas');
|
|
76
|
+
fixedDiv?.addEventListener('wheel', function (e: WheelEvent) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
const evt = new WheelEvent('wheel', {
|
|
79
|
+
deltaY: e.deltaY,
|
|
80
|
+
});
|
|
81
|
+
canvas.dispatchEvent(evt);
|
|
82
|
+
});
|
|
83
|
+
fixedDiv?.addEventListener(
|
|
84
|
+
'contextmenu',
|
|
85
|
+
function (ev) {
|
|
86
|
+
ev.preventDefault();
|
|
87
|
+
const evt = new MouseEvent('contextmenu', {});
|
|
88
|
+
canvas.dispatchEvent(evt);
|
|
89
|
+
},
|
|
90
|
+
false,
|
|
91
|
+
);
|
|
92
|
+
})
|
|
93
|
+
async function sendPayload(payload: string) {
|
|
94
|
+
await executePayload(JSON.parse(payload));
|
|
95
|
+
}
|
|
96
|
+
function receiveMessageFromUnity(json: FrontEndPayload) {
|
|
97
|
+
emit('on_payload', json.Interface, json.Action, json.Message);
|
|
98
|
+
}
|
|
99
|
+
function snap() {
|
|
100
|
+
}
|
|
101
|
+
// function snapCanvas(canvas: HTMLCanvasElement) {
|
|
102
|
+
// // this.$emit('screenshot', canvas);
|
|
103
|
+
// }
|
|
104
|
+
|
|
105
|
+
const unityInstance = ref<any>(null);
|
|
106
|
+
|
|
107
|
+
</script>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AnchorPoint, PositionData } from '@3cr/types-ts';
|
|
2
|
+
|
|
3
|
+
import { toNumber } from '@/helpers/utils';
|
|
4
|
+
|
|
5
|
+
export function generateDivStyleForLayout(layout: PositionData) {
|
|
6
|
+
const padding = '0px';
|
|
7
|
+
const canvas = document.getElementById('unity-canvas');
|
|
8
|
+
|
|
9
|
+
const props = {} as any;
|
|
10
|
+
|
|
11
|
+
if (layout.Anchor === AnchorPoint.TOP) {
|
|
12
|
+
props['left'] = '50%';
|
|
13
|
+
props['transform'] = 'translateX(-50%)';
|
|
14
|
+
}
|
|
15
|
+
if (layout.Anchor === AnchorPoint.BOTTOM) {
|
|
16
|
+
props['left'] = '50%';
|
|
17
|
+
props['transform'] = 'translateX(-50%)';
|
|
18
|
+
}
|
|
19
|
+
if (layout.Anchor === AnchorPoint.LEFT) {
|
|
20
|
+
props['top'] = '50%';
|
|
21
|
+
props['transform'] = 'translateY(-50%)';
|
|
22
|
+
}
|
|
23
|
+
if (layout.Anchor === AnchorPoint.RIGHT) {
|
|
24
|
+
props['top'] = '50%';
|
|
25
|
+
props['transform'] = 'translateY(-50%)';
|
|
26
|
+
}
|
|
27
|
+
if (
|
|
28
|
+
layout.Anchor === AnchorPoint.TOP_LEFT ||
|
|
29
|
+
layout.Anchor === AnchorPoint.TOP ||
|
|
30
|
+
layout.Anchor === AnchorPoint.TOP_RIGHT ||
|
|
31
|
+
layout.Anchor === AnchorPoint.CENTER
|
|
32
|
+
) {
|
|
33
|
+
props['top'] = padding;
|
|
34
|
+
}
|
|
35
|
+
if (
|
|
36
|
+
layout.Anchor === AnchorPoint.LEFT ||
|
|
37
|
+
layout.Anchor === AnchorPoint.BOTTOM_LEFT ||
|
|
38
|
+
layout.Anchor === AnchorPoint.TOP_LEFT ||
|
|
39
|
+
layout.Anchor === AnchorPoint.CENTER
|
|
40
|
+
) {
|
|
41
|
+
props['left'] = padding;
|
|
42
|
+
}
|
|
43
|
+
if (
|
|
44
|
+
layout.Anchor === AnchorPoint.RIGHT ||
|
|
45
|
+
layout.Anchor === AnchorPoint.TOP_RIGHT ||
|
|
46
|
+
layout.Anchor === AnchorPoint.BOTTOM_RIGHT
|
|
47
|
+
) {
|
|
48
|
+
props['right'] = padding;
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
layout.Anchor === AnchorPoint.BOTTOM ||
|
|
52
|
+
layout.Anchor === AnchorPoint.BOTTOM_LEFT ||
|
|
53
|
+
layout.Anchor === AnchorPoint.BOTTOM_RIGHT
|
|
54
|
+
) {
|
|
55
|
+
props['bottom'] = '4px';
|
|
56
|
+
}
|
|
57
|
+
if (layout.Anchor === AnchorPoint.TOP_LEFT || layout.Anchor === AnchorPoint.CENTER) {
|
|
58
|
+
props['border-top-left-radius'] = padding;
|
|
59
|
+
}
|
|
60
|
+
if (layout.Anchor === AnchorPoint.TOP_RIGHT || layout.Anchor === AnchorPoint.CENTER) {
|
|
61
|
+
props['border-top-right-radius'] = padding;
|
|
62
|
+
}
|
|
63
|
+
if (layout.Anchor === AnchorPoint.BOTTOM_LEFT || layout.Anchor === AnchorPoint.CENTER) {
|
|
64
|
+
props['border-bottom-left-radius'] = padding;
|
|
65
|
+
}
|
|
66
|
+
if (layout.Anchor === AnchorPoint.BOTTOM_RIGHT || layout.Anchor === AnchorPoint.CENTER) {
|
|
67
|
+
props['border-bottom-right-radius'] = padding;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
props['width'] = `calc(${layout.MaxSize.X * 100}% - ${padding})`;
|
|
71
|
+
props['height'] = `calc(${layout.MaxSize.Y * 100}% - ${'2px'})`;
|
|
72
|
+
if (layout.Anchor === AnchorPoint.CENTER) {
|
|
73
|
+
props['width'] = `calc(${layout.MaxSize.X * 100}% - ${padding} - ${padding})`;
|
|
74
|
+
props['height'] = `calc(${layout.MaxSize.Y * 100}% - ${'2px'} - ${'2px'})`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (layout.AspectRatio === 1) {
|
|
78
|
+
if (layout.MaxSize.X === 1) {
|
|
79
|
+
props['width'] = `calc(${layout.MaxSize.Y * toNumber(canvas?.offsetHeight)}px)`;
|
|
80
|
+
}
|
|
81
|
+
if (layout.MaxSize.Y === 1) {
|
|
82
|
+
props['height'] = `calc(${layout.MaxSize.X * toNumber(canvas?.offsetWidth)}px)`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return props;
|
|
86
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {AnchorPoint, CurrentScanState, InitialScanState, ScanView, ViewInteractiveMode,} from '@3cr/types-ts';
|
|
2
|
+
|
|
3
|
+
export function inflateScanState(): CurrentScanState {
|
|
4
|
+
return {
|
|
5
|
+
Version: '1.0.0',
|
|
6
|
+
Display: {
|
|
7
|
+
Version: '1.0.0',
|
|
8
|
+
Brightness: 50,
|
|
9
|
+
Contrast: 50,
|
|
10
|
+
Opacity: 50,
|
|
11
|
+
WindowLower: 0,
|
|
12
|
+
WindowUpper: 100,
|
|
13
|
+
ThresholdLower: 0,
|
|
14
|
+
ThresholdUpper: 100,
|
|
15
|
+
},
|
|
16
|
+
CurrentView: ScanView.Volume,
|
|
17
|
+
Slice: {
|
|
18
|
+
Version: '1.0.0',
|
|
19
|
+
TransverseLower: 0,
|
|
20
|
+
TransverseUpper: 0,
|
|
21
|
+
SagittalLower: 0,
|
|
22
|
+
SagittalUpper: 0,
|
|
23
|
+
CoronalLower: 0,
|
|
24
|
+
CoronalUpper: 0,
|
|
25
|
+
},
|
|
26
|
+
InteractionSettings: {
|
|
27
|
+
Version: '1.0.0',
|
|
28
|
+
PanSensivitity: 0,
|
|
29
|
+
ZoomSensitivity: 0,
|
|
30
|
+
RotateSensitivity: 0,
|
|
31
|
+
CameraRotateSensitivity: 0,
|
|
32
|
+
KeyboardEnabled: true,
|
|
33
|
+
MouseEnabled: true,
|
|
34
|
+
InteractionMode: ViewInteractiveMode.STATIC,
|
|
35
|
+
},
|
|
36
|
+
Orientations: {
|
|
37
|
+
Version: '1.0.0',
|
|
38
|
+
Transverse: {
|
|
39
|
+
Version: '1.0.0',
|
|
40
|
+
View: ScanView.Transverse,
|
|
41
|
+
VFlip: false,
|
|
42
|
+
HFlip: false,
|
|
43
|
+
Rotation: 0,
|
|
44
|
+
Slice: 0,
|
|
45
|
+
},
|
|
46
|
+
Sagittal: {
|
|
47
|
+
Version: '1.0.0',
|
|
48
|
+
View: ScanView.Transverse,
|
|
49
|
+
VFlip: false,
|
|
50
|
+
HFlip: false,
|
|
51
|
+
Rotation: 0,
|
|
52
|
+
Slice: 0,
|
|
53
|
+
},
|
|
54
|
+
Coronal: {
|
|
55
|
+
Version: '1.0.0',
|
|
56
|
+
View: ScanView.Transverse,
|
|
57
|
+
VFlip: false,
|
|
58
|
+
HFlip: false,
|
|
59
|
+
Rotation: 0,
|
|
60
|
+
Slice: 0,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
Layout: {
|
|
64
|
+
Version: '1.0.0',
|
|
65
|
+
SwitchOnViewChange: false,
|
|
66
|
+
PositionData: [],
|
|
67
|
+
},
|
|
68
|
+
NavigationCube: {
|
|
69
|
+
Version: '1.0.0',
|
|
70
|
+
Transform: {
|
|
71
|
+
Version: '1.0.0',
|
|
72
|
+
AnchorPoint: AnchorPoint.DEFAULT,
|
|
73
|
+
Position: {
|
|
74
|
+
Version: '1.0.0',
|
|
75
|
+
X: 0,
|
|
76
|
+
Y: 0,
|
|
77
|
+
},
|
|
78
|
+
Size: {
|
|
79
|
+
Version: '1.0.0',
|
|
80
|
+
X: 0,
|
|
81
|
+
Y: 0,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
Visibility: {
|
|
85
|
+
Version: '1.0.0',
|
|
86
|
+
Value: false,
|
|
87
|
+
},
|
|
88
|
+
Interactivity: {
|
|
89
|
+
Version: '1.0.0',
|
|
90
|
+
Value: false,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function inflateInitialScanState(): InitialScanState {
|
|
97
|
+
return {
|
|
98
|
+
Version: '1.0.0',
|
|
99
|
+
XSlices: 0,
|
|
100
|
+
YSlices: 0,
|
|
101
|
+
ZSlices: 0,
|
|
102
|
+
Modality: 'CT',
|
|
103
|
+
HuUpper: 0,
|
|
104
|
+
HuLower: 0,
|
|
105
|
+
DefaultDisplaySettings: inflateScanState(),
|
|
106
|
+
GreyscalePresets: [],
|
|
107
|
+
ColourPresets: [],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface FrontEndPayload {
|
|
2
|
+
Interface: FrontEndInterfaces;
|
|
3
|
+
Action: FrontEndInterfaces;
|
|
4
|
+
Message: string;
|
|
5
|
+
}
|
|
6
|
+
export enum FrontEndInterfaces {
|
|
7
|
+
scan_loading = 'scan_loading',
|
|
8
|
+
file_management = 'file_management',
|
|
9
|
+
view_selection = 'view_selection',
|
|
10
|
+
layout = 'layout',
|
|
11
|
+
presets = 'presets',
|
|
12
|
+
sliders = 'sliders',
|
|
13
|
+
scan_orientation = 'scan_orientation',
|
|
14
|
+
scan_movement = 'scan_movement',
|
|
15
|
+
interactivity = 'interactivity',
|
|
16
|
+
}
|
|
17
|
+
export enum FrontEndActions {
|
|
18
|
+
pong = 'pong',
|
|
19
|
+
announcement = 'announcement',
|
|
20
|
+
fm02 = 'fm_02',
|
|
21
|
+
}
|
|
22
|
+
export enum FileManagementActions {
|
|
23
|
+
fm01 = 'fm_01',
|
|
24
|
+
fm02 = 'fm_02',
|
|
25
|
+
}
|
|
26
|
+
export enum NotificationActions {
|
|
27
|
+
no01 = 'no_01',
|
|
28
|
+
no02 = 'no_02',
|
|
29
|
+
no03 = 'no_03',
|
|
30
|
+
}
|
|
31
|
+
export enum ScanOrientationActions {
|
|
32
|
+
so01 = 'so_01',
|
|
33
|
+
}
|
|
34
|
+
export enum PresetActions {
|
|
35
|
+
pr01 = 'pr_01',
|
|
36
|
+
pr02 = 'pr_02',
|
|
37
|
+
}
|
|
38
|
+
export enum ScanMovementActions {
|
|
39
|
+
sm01 = 'sm_01',
|
|
40
|
+
sm02 = 'sm_02',
|
|
41
|
+
sm03 = 'sm_03',
|
|
42
|
+
sm04 = 'sm_04',
|
|
43
|
+
sm05 = 'sm_05',
|
|
44
|
+
sm06 = 'sm_06',
|
|
45
|
+
sm07 = 'sm_07',
|
|
46
|
+
sm08 = 'sm_08',
|
|
47
|
+
sm09 = 'sm_09',
|
|
48
|
+
sm10 = 'sm_10',
|
|
49
|
+
sm11 = 'sm_11',
|
|
50
|
+
sm12 = 'sm_12',
|
|
51
|
+
}
|
|
52
|
+
export enum SliderActions {
|
|
53
|
+
sl01 = 'sl_01',
|
|
54
|
+
sl02 = 'sl_02',
|
|
55
|
+
sl03 = 'sl_03',
|
|
56
|
+
sl04 = 'sl_04',
|
|
57
|
+
sl05 = 'sl_05',
|
|
58
|
+
sl06 = 'sl_06',
|
|
59
|
+
sl07 = 'sl_07',
|
|
60
|
+
sl08 = 'sl_08',
|
|
61
|
+
sl09 = 'sl_09',
|
|
62
|
+
sl10 = 'sl_10',
|
|
63
|
+
sl11 = 'sl_11',
|
|
64
|
+
sl12 = 'sl_12',
|
|
65
|
+
sl13 = 'sl_13',
|
|
66
|
+
sl14 = 'sl_14',
|
|
67
|
+
sl15 = 'sl_15',
|
|
68
|
+
sl16 = 'sl_16',
|
|
69
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
export function toNumber(value: any): number {
|
|
3
|
+
return parseInt((value || 0) + '');
|
|
4
|
+
}
|
|
5
|
+
export function MockRunOnNewThread(callback: () => void) {
|
|
6
|
+
setTimeout(callback, 1);
|
|
7
|
+
}
|
|
8
|
+
export function registerEvents(
|
|
9
|
+
domElement: HTMLElement,
|
|
10
|
+
eventList: Array<string>,
|
|
11
|
+
listener: EventListenerOrEventListenerObject,
|
|
12
|
+
) {
|
|
13
|
+
for (const event of eventList) {
|
|
14
|
+
domElement.addEventListener(event, listener);
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* main.ts
|
|
3
|
+
*
|
|
4
|
+
* Bootstraps Vuetify and other plugins then mounts the App`
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Plugins
|
|
8
|
+
// import { registerPlugins } from '@/plugins'
|
|
9
|
+
import 'material-design-icons-iconfont/dist/material-design-icons.css' // Ensure your project is capable of handling css files
|
|
10
|
+
|
|
11
|
+
// Components
|
|
12
|
+
import App from './App.vue'
|
|
13
|
+
import './types/window.shim';
|
|
14
|
+
|
|
15
|
+
// Composables
|
|
16
|
+
// import { createApp } from 'vue'
|
|
17
|
+
//
|
|
18
|
+
// export const app = createApp(App)
|
|
19
|
+
//
|
|
20
|
+
// registerPlugins(app)
|
|
21
|
+
export { App }
|
|
22
|
+
|
|
23
|
+
// app.mount('#app')
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/index.ts
|
|
3
|
+
*
|
|
4
|
+
* Automatically included in `./src/main.ts`
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Plugins
|
|
8
|
+
import vuetify from './vuetify'
|
|
9
|
+
|
|
10
|
+
// Types
|
|
11
|
+
import type { App } from 'vue'
|
|
12
|
+
|
|
13
|
+
export function registerPlugins (app: App) {
|
|
14
|
+
app.use(vuetify)
|
|
15
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/vuetify.ts
|
|
3
|
+
*
|
|
4
|
+
* Framework documentation: https://vuetifyjs.com`
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Styles
|
|
8
|
+
import '@mdi/font/css/materialdesignicons.css'
|
|
9
|
+
import 'vuetify/styles'
|
|
10
|
+
|
|
11
|
+
// Composables
|
|
12
|
+
import { createVuetify } from 'vuetify'
|
|
13
|
+
import { aliases, md } from 'vuetify/iconsets/md'
|
|
14
|
+
|
|
15
|
+
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
|
16
|
+
export default createVuetify({
|
|
17
|
+
theme: {
|
|
18
|
+
defaultTheme: 'dark',
|
|
19
|
+
},
|
|
20
|
+
icons: {
|
|
21
|
+
defaultSet: 'md',
|
|
22
|
+
aliases,
|
|
23
|
+
sets: {
|
|
24
|
+
md,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
})
|