@3cr/viewer-browser 0.0.162 → 0.0.194
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/.circleci/config.yml +8 -6
- package/__tests__/index.spec.ts +31 -24
- package/components.d.ts +32 -0
- package/dist/Viewer3CR.js +44 -27
- package/dist/Viewer3CR.mjs +22731 -17742
- package/dist/Viewer3CR.umd.js +44 -27
- package/index.html +5 -8
- package/index.ts +7 -2
- package/package.json +4 -2
- package/playground/index.html +6 -4
- package/src/App.vue +12 -4
- package/src/__tests__/app.spec.ts +2 -13
- package/src/assets/magic_wand.svg +24 -0
- package/src/components/WebGL3DR.vue +53 -92
- package/src/components/__tests__/webgl3dr.spec.ts +29 -48
- package/src/{demo → components/demo}/DemoModal.vue +1 -1
- package/src/{demo → components/demo}/DemoPatientModal.vue +4 -1
- package/src/components/demo/__tests__/DemoModal.spec.ts +25 -0
- package/src/components/demo/__tests__/DemoPatientModal.spec.ts +37 -0
- package/src/components/demo/__tests__/options.spec.ts +25 -0
- package/src/components/demo/licence/DemoLicenceEnableCloudStorageModal.vue +64 -0
- package/src/{demo → components/demo}/licence/DemoLicenceInfoModal.vue +5 -4
- package/src/{demo → components/demo}/licence/DemoLicenceSendToPartyModal.vue +6 -4
- package/src/{demo → components/demo}/licence/DemoLicenceShareToMobileModal.vue +8 -6
- package/src/components/demo/licence/__tests__/DemoLicenceEnableCloudStorageModal.spec.ts +37 -0
- package/src/components/demo/licence/__tests__/DemoLicenceInfoModal.spec.ts +37 -0
- package/src/components/demo/licence/__tests__/DemoLicenceSendToPartyModal.spec.ts +36 -0
- package/src/components/demo/licence/__tests__/DemoLicenceShareToMobileModal.spec.ts +51 -0
- package/src/{demo → components/demo}/options.ts +18 -20
- package/src/components/demo/patient/DemoPatientEnableCloudStorageModal.vue +64 -0
- package/src/{demo → components/demo}/patient/DemoPatientInfoModal.vue +5 -4
- package/src/{demo → components/demo}/patient/DemoPatientSendToPartyModal.vue +4 -3
- package/src/{demo → components/demo}/patient/DemoPatientShareToMobileModal.vue +8 -6
- package/src/components/demo/patient/__tests__/DemoPatientEnableCloudStorageModal.spec.ts +37 -0
- package/src/components/demo/patient/__tests__/DemoPatientInfoModal.spec.ts +37 -0
- package/src/components/demo/patient/__tests__/DemoPatientSendToPartyModal.spec.ts +36 -0
- package/src/components/demo/patient/__tests__/DemoPatientShareToMobileModal.spec.ts +51 -0
- package/src/components/loading/LoadingSpinner.vue +1 -1
- package/src/components/modal/ActionRail.vue +96 -0
- package/src/components/modal/AskAI.vue +250 -0
- package/src/components/modal/CloseViewerModal.vue +104 -0
- package/src/components/modal/MftpWebGL3DRModal.vue +415 -834
- package/src/components/modal/ViewerActionRail.vue +123 -0
- package/src/components/modal/ViewerAnnotationModal.vue +115 -0
- package/src/components/modal/ViewerAnnotations.vue +283 -0
- package/src/components/modal/ViewerDisplaySettings.vue +102 -0
- package/src/components/modal/ViewerNavigationDrawer.vue +90 -0
- package/src/components/modal/ViewerNavigationDrawerContent.vue +126 -0
- package/src/components/modal/ViewerNavigationDrawerFooter.vue +111 -0
- package/src/components/modal/ViewerNavigationDrawerHeader.vue +63 -0
- package/src/components/modal/__tests__/CloseViewerModal.spec.ts +60 -0
- package/src/components/modal/__tests__/{mftp-webgl-3dr-modal.spec.ts → MftpWebGL3DRModal.spec.ts} +47 -298
- package/src/components/modal/__tests__/ViewerAnnotationModal.spec.ts +79 -0
- package/src/components/modal/__tests__/ViewerDisplaySettings.spec.ts +61 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawer.spec.ts +32 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawerContent.spec.ts +29 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawerFooter.spec.ts +43 -0
- package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +37 -0
- package/src/components/modal/actions/Action.vue +40 -0
- package/src/components/modal/actions/Flip3dAction.vue +79 -0
- package/src/components/modal/actions/FlipHorizontalAction.vue +36 -0
- package/src/components/modal/actions/FlipVerticalAction.vue +36 -0
- package/src/components/modal/actions/FullscreenAction.vue +47 -0
- package/src/components/modal/actions/NavigationCubeAction.vue +33 -0
- package/src/components/modal/actions/PanAction.vue +78 -0
- package/src/components/modal/actions/ResetViewAction.vue +29 -0
- package/src/components/modal/actions/Rotate2dAction.vue +78 -0
- package/src/components/modal/actions/Slice3dAction.vue +71 -0
- package/src/components/modal/actions/ZoomAction.vue +70 -0
- package/src/components/modal/actions/__tests__/Action.spec.ts +29 -0
- package/src/components/modal/actions/__tests__/Flip3dAction.spec.ts +48 -0
- package/src/components/modal/actions/__tests__/FlipHorizontalAction.spec.ts +17 -0
- package/src/components/modal/actions/__tests__/FlipVerticalAction.spec.ts +17 -0
- package/src/components/modal/actions/__tests__/FullscreenAction.spec.ts +28 -0
- package/src/components/modal/actions/__tests__/NavigationCubeAction.spec.ts +25 -0
- package/src/components/modal/actions/__tests__/PanAction.spec.ts +46 -0
- package/src/components/modal/actions/__tests__/ResetViewAction.spec.ts +17 -0
- package/src/components/modal/actions/__tests__/Rotate2dAction.spec.ts +23 -0
- package/src/components/modal/actions/__tests__/Slice3dAction.spec.ts +14 -0
- package/src/components/modal/actions/__tests__/ZoomAction.spec.ts +34 -0
- package/src/components/modal/composables/__tests__/useNavigationCubeObserver.spec.ts +56 -0
- package/src/components/modal/composables/useEventListener.ts +22 -0
- package/src/components/modal/composables/useNavigationCubeObserver.ts +104 -0
- package/src/components/selectors/ValueSelector.vue +30 -33
- package/src/components/selectors/__tests__/value-selector.spec.ts +1 -1
- package/src/components/sliders/DoubleSliderSelector.vue +79 -71
- package/src/components/sliders/VerticalSliderSelector.vue +12 -17
- package/src/components/sliders/__tests__/double-slider-selector.spec.ts +1 -1
- package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +1 -1
- package/src/dataLayer/__tests__/clamp.spec.ts +16 -0
- package/src/dataLayer/__tests__/eventHandlers.spec.ts +38 -0
- package/src/dataLayer/__tests__/getIconForPreset.spec.ts +40 -0
- package/src/dataLayer/__tests__/patchDataOverlay.spec.ts +88 -0
- package/src/dataLayer/__tests__/scanState.spec.ts +93 -0
- package/src/dataLayer/__tests__/useViewer3cr.spec.ts +10 -0
- package/src/dataLayer/__tests__/viewer3cr.spec.ts +331 -0
- package/src/dataLayer/clamp.ts +9 -0
- package/src/dataLayer/eventHandlers.ts +26 -0
- package/src/dataLayer/patchDataOverlay.ts +101 -0
- package/src/dataLayer/scanState.ts +105 -26
- package/src/dataLayer/useViewer3cr.ts +7 -0
- package/src/dataLayer/viewer3cr.ts +410 -0
- package/src/helpers/__tests__/layout-overlay-style.spec.ts +24 -22
- package/src/helpers/__tests__/model-helper.spec.ts +44 -13
- package/src/helpers/layoutOverlayStyle.ts +16 -27
- package/src/helpers/modelHelper.ts +62 -10
- package/src/models/Callbacks.ts +2 -2
- package/src/models/LoadViewerOptions.ts +2 -0
- package/src/models/LoadViewerPayload.ts +1 -0
- package/src/notifications/notification.ts +3 -4
- package/src/plugins/vuetify.ts +5 -0
- package/src/services/gpt/__tests__/gpt.service.spec.ts +27 -0
- package/src/services/gpt/gpt.service.ts +27 -0
- package/static/3cr-types-browser/index.ts +74 -0
- package/static/3cr-types-browser/types/Action.ts +6 -0
- package/static/3cr-types-browser/types/ActionData.ts +4 -0
- package/static/3cr-types-browser/types/AlphaKeys.ts +5 -0
- package/static/3cr-types-browser/types/AnchorPoint.ts +12 -0
- package/static/3cr-types-browser/types/CallToAction.ts +5 -0
- package/static/3cr-types-browser/types/ColourData.ts +7 -0
- package/static/3cr-types-browser/types/ColourPresetData.ts +9 -0
- package/static/3cr-types-browser/types/CurrentDataOverlayState.ts +6 -0
- package/static/3cr-types-browser/types/CurrentScanState.ts +22 -0
- package/static/3cr-types-browser/types/DataOverlay.ts +22 -0
- package/static/3cr-types-browser/types/DataOverlayActions.ts +14 -0
- package/static/3cr-types-browser/types/DataOverlayData.ts +8 -0
- package/static/3cr-types-browser/types/DataOverlayEvent.ts +8 -0
- package/static/3cr-types-browser/types/DecryptionKey.ts +4 -0
- package/static/3cr-types-browser/types/DisplaySettings.ts +10 -0
- package/static/3cr-types-browser/types/EmptyPayload.ts +3 -0
- package/static/3cr-types-browser/types/EnumPayload.ts +4 -0
- package/static/3cr-types-browser/types/FileManagementActions.ts +11 -0
- package/static/3cr-types-browser/types/FlipValue.ts +7 -0
- package/static/3cr-types-browser/types/FrontEndInterfaces.ts +14 -0
- package/static/3cr-types-browser/types/GradientKeys.ts +7 -0
- package/static/3cr-types-browser/types/GreyscalePresetData.ts +6 -0
- package/static/3cr-types-browser/types/InitialDataOverlayState.ts +6 -0
- package/static/3cr-types-browser/types/InitialScanState.ts +19 -0
- package/static/3cr-types-browser/types/InteractionType.ts +8 -0
- package/static/3cr-types-browser/types/InteractivityActions.ts +6 -0
- package/static/3cr-types-browser/types/InteractivityState.ts +4 -0
- package/static/3cr-types-browser/types/InvertTransformData.ts +6 -0
- package/static/3cr-types-browser/types/LayoutActions.ts +6 -0
- package/static/3cr-types-browser/types/LayoutData.ts +7 -0
- package/static/3cr-types-browser/types/LoadDataSet.ts +6 -0
- package/static/3cr-types-browser/types/LoadSessionState.ts +4 -0
- package/static/3cr-types-browser/types/LocalLoadDataset.ts +3 -0
- package/static/3cr-types-browser/types/MovementData.ts +7 -0
- package/static/3cr-types-browser/types/NavigationCubeActions.ts +8 -0
- package/static/3cr-types-browser/types/NavigationCubeData.ts +12 -0
- package/static/3cr-types-browser/types/NavigationCubeTransform.ts +9 -0
- package/static/3cr-types-browser/types/NotificationPayload.ts +7 -0
- package/static/3cr-types-browser/types/NotificationsActions.ts +6 -0
- package/static/3cr-types-browser/types/Object.ts +1 -0
- package/static/3cr-types-browser/types/ObjectColour.ts +7 -0
- package/static/3cr-types-browser/types/ObjectIcon.ts +5 -0
- package/static/3cr-types-browser/types/ObjectInvert.ts +7 -0
- package/static/3cr-types-browser/types/ObjectSize.ts +7 -0
- package/static/3cr-types-browser/types/ObjectSize2D.ts +7 -0
- package/static/3cr-types-browser/types/ObjectVisible.ts +5 -0
- package/static/3cr-types-browser/types/PositionData.ts +14 -0
- package/static/3cr-types-browser/types/PresetsActions.ts +4 -0
- package/static/3cr-types-browser/types/RotationValue.ts +7 -0
- package/static/3cr-types-browser/types/ScanMovementActions.ts +27 -0
- package/static/3cr-types-browser/types/ScanMovementData.ts +3 -0
- package/static/3cr-types-browser/types/ScanOrientationActions.ts +6 -0
- package/static/3cr-types-browser/types/ScanStateActions.ts +4 -0
- package/static/3cr-types-browser/types/ScanView.ts +6 -0
- package/static/3cr-types-browser/types/SettingsData.ts +12 -0
- package/static/3cr-types-browser/types/SlicerData.ts +9 -0
- package/static/3cr-types-browser/types/SliderValue.ts +4 -0
- package/static/3cr-types-browser/types/SlidersActions.ts +18 -0
- package/static/3cr-types-browser/types/Vector2Data.ts +5 -0
- package/static/3cr-types-browser/types/Vector3Data.ts +6 -0
- package/static/3cr-types-browser/types/VectorMovementData.ts +8 -0
- package/static/3cr-types-browser/types/ViewInteractiveMode.ts +5 -0
- package/static/3cr-types-browser/types/ViewOrientation.ts +8 -0
- package/static/3cr-types-browser/types/ViewOrientations.ts +10 -0
- package/static/3cr-types-browser/types/ViewSelectionActions.ts +9 -0
- package/static/3cr-types-browser/types/ViewToggleData.ts +7 -0
- package/static/3cr-types-browser/types/VolumeOrientation.ts +7 -0
- package/test/helper.ts +10 -1
- package/test/setup.ts +13 -0
- package/tsconfig.json +1 -0
- package/vite.config.mts +1 -0
- package/src/dataLayer/__tests__/payload-handler.spec.ts +0 -214
- package/src/dataLayer/payloadHandler.ts +0 -138
- /package/src/dataLayer/{iconData.ts → getIconForPreset.ts} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {describe, it, expect, vi} from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import { demoOptions } from "@/components/demo/options";
|
|
4
|
+
import { isSessionLoaded } from "@/dataLayer/scanState";
|
|
5
|
+
import ViewerNavigationDrawerContent from "../ViewerNavigationDrawerContent.vue";
|
|
6
|
+
import {flushPromises} from "@vue/test-utils";
|
|
7
|
+
|
|
8
|
+
describe('ViewerNavigationDrawerContent tests', () => {
|
|
9
|
+
it('should mount', () => {
|
|
10
|
+
const props = { drawer: false, options: demoOptions.value };
|
|
11
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerContent, props);
|
|
12
|
+
expect(wrapper).toBeTruthy();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should expand', async () => {
|
|
16
|
+
vi.spyOn(isSessionLoaded, 'value', 'get').mockReturnValue(true);
|
|
17
|
+
const props = { drawer: true, options: demoOptions.value };
|
|
18
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerContent, props);
|
|
19
|
+
const panel0 = wrapper.findComponent('[data-testid="panel-0"]');
|
|
20
|
+
await panel0.trigger('click');
|
|
21
|
+
await flushPromises();
|
|
22
|
+
expect(panel0.isVisible()).toBeTruthy();
|
|
23
|
+
|
|
24
|
+
const panel1 = wrapper.findComponent('[data-testid="panel-1"]');
|
|
25
|
+
await panel1.trigger('click')
|
|
26
|
+
await flushPromises();
|
|
27
|
+
expect(panel1.isVisible()).toBeTruthy();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mountVuetify } from '~/helper';
|
|
3
|
+
import { demoLicenceOptions, demoPatientOptions } from "@/components/demo/options";
|
|
4
|
+
import { VueWrapper } from "@vue/test-utils";
|
|
5
|
+
import ViewerNavigationDrawerFooter from '@/components/modal/ViewerNavigationDrawerFooter.vue';
|
|
6
|
+
|
|
7
|
+
async function testFooterItems(wrapper: VueWrapper): Promise<void> {
|
|
8
|
+
const resetScan = wrapper.findComponent('[data-testid="footer-0"]');
|
|
9
|
+
await resetScan.trigger('click');
|
|
10
|
+
expect(resetScan).toBeTruthy();
|
|
11
|
+
|
|
12
|
+
const enableCloudStorage = wrapper.findComponent('[data-testid="footer-1"]');
|
|
13
|
+
await enableCloudStorage.trigger('click');
|
|
14
|
+
expect(enableCloudStorage).toBeTruthy();
|
|
15
|
+
|
|
16
|
+
const sendTo3rdParty = wrapper.findComponent('[data-testid="footer-2"]');
|
|
17
|
+
await sendTo3rdParty.trigger('click');
|
|
18
|
+
expect(sendTo3rdParty).toBeTruthy();
|
|
19
|
+
|
|
20
|
+
const shareToMobile = wrapper.findComponent('[data-testid="footer-3"]');
|
|
21
|
+
await shareToMobile.trigger('click');
|
|
22
|
+
expect(shareToMobile).toBeTruthy();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('ViewerNavigationDrawerFooter tests', () => {
|
|
26
|
+
it('should mount', () => {
|
|
27
|
+
const props = { options: {}, drawer: false };
|
|
28
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerFooter, props);
|
|
29
|
+
expect(wrapper).toBeTruthy();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should open patient modals', async () => {
|
|
33
|
+
const props = { options: demoPatientOptions, drawer: true };
|
|
34
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerFooter, props);
|
|
35
|
+
await testFooterItems(wrapper);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should open licence modals', async () => {
|
|
39
|
+
const props = { options: demoLicenceOptions, drawer: true };
|
|
40
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerFooter, props);
|
|
41
|
+
await testFooterItems(wrapper);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, vi, expect } from "vitest";
|
|
2
|
+
import { mountVuetify } from "~/helper";
|
|
3
|
+
import { demoType } from "@/components/demo/options";
|
|
4
|
+
import ViewerNavigationDrawerHeader from "@/components/modal/ViewerNavigationDrawerHeader.vue";
|
|
5
|
+
|
|
6
|
+
describe('ViewerNavigationDrawerHeader tests', () => {
|
|
7
|
+
it('should mount', () => {
|
|
8
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerHeader);
|
|
9
|
+
expect(wrapper).toBeTruthy();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should show collapsed licence logo', () => {
|
|
13
|
+
const props = { drawer: false };
|
|
14
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerHeader, props);
|
|
15
|
+
expect(wrapper.find('img').exists()).toBeTruthy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should show expanded licence logo', () => {
|
|
19
|
+
const props = { drawer: true };
|
|
20
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerHeader, props);
|
|
21
|
+
expect(wrapper.find('img').exists()).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should show collapsed patient logo', async () => {
|
|
25
|
+
vi.spyOn(demoType, 'value', 'get').mockReturnValue('patient');
|
|
26
|
+
const props = { drawer: false };
|
|
27
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerHeader, props);
|
|
28
|
+
expect(wrapper.find('img').exists()).toBeTruthy();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should show expanded patient logo', async () => {
|
|
32
|
+
vi.spyOn(demoType, 'value', 'get').mockReturnValue('patient');
|
|
33
|
+
const props = { drawer: true };
|
|
34
|
+
const wrapper = mountVuetify(ViewerNavigationDrawerHeader, props);
|
|
35
|
+
expect(wrapper.find('img').exists()).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template v-if="variant === 'button'">
|
|
3
|
+
<v-tooltip location="top">
|
|
4
|
+
<template #activator="{ props: tooltip }">
|
|
5
|
+
<v-btn
|
|
6
|
+
v-bind="mergeProps($attrs, tooltip)"
|
|
7
|
+
data-testid="button"
|
|
8
|
+
variant="text"
|
|
9
|
+
:color="color"
|
|
10
|
+
:icon="icon" />
|
|
11
|
+
</template>
|
|
12
|
+
<span>{{ text }}</span>
|
|
13
|
+
</v-tooltip>
|
|
14
|
+
</template>
|
|
15
|
+
<template v-if="variant == 'menu-item'">
|
|
16
|
+
<v-list-item v-bind="$attrs" data-testid="menu-item" :prepend-icon="icon">
|
|
17
|
+
<v-list-item-title>{{ text }}</v-list-item-title>
|
|
18
|
+
</v-list-item>
|
|
19
|
+
</template>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import {mergeProps} from "vue";
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
text: string;
|
|
27
|
+
icon: string;
|
|
28
|
+
color?: string;
|
|
29
|
+
variant?: 'button' | 'menu-item';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
withDefaults(defineProps<Props>(), {
|
|
33
|
+
color: 'white',
|
|
34
|
+
variant: 'button'
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
defineOptions({
|
|
38
|
+
inheritAttrs: false
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-menu v-model="menuState" data-testid="menu" :close-on-content-click="false">
|
|
3
|
+
<template #activator="{ props: menu }">
|
|
4
|
+
<v-tooltip location="top">
|
|
5
|
+
<template #activator="{ props: tooltip }">
|
|
6
|
+
<v-btn v-bind="mergeProps(menu, tooltip)"
|
|
7
|
+
data-testid="activator"
|
|
8
|
+
color="white"
|
|
9
|
+
variant="text"
|
|
10
|
+
icon="flip"/>
|
|
11
|
+
</template>
|
|
12
|
+
<span>Flip</span>
|
|
13
|
+
</v-tooltip>
|
|
14
|
+
</template>
|
|
15
|
+
<v-list>
|
|
16
|
+
<v-list-item data-testid="sagittal" @click="flipSagittal">
|
|
17
|
+
<v-list-item-title>Flip Sagittal</v-list-item-title>
|
|
18
|
+
</v-list-item>
|
|
19
|
+
<v-list-item data-testid="coronal" @click="flipCoronal">
|
|
20
|
+
<v-list-item-title>Flip Coronal</v-list-item-title>
|
|
21
|
+
</v-list-item>
|
|
22
|
+
<v-list-item data-testid="transverse" @click="flipTransverse">
|
|
23
|
+
<v-list-item-title>Flip Transverse</v-list-item-title>
|
|
24
|
+
</v-list-item>
|
|
25
|
+
</v-list>
|
|
26
|
+
</v-menu>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script setup lang="ts">
|
|
30
|
+
import {computed, mergeProps, ref} from "vue";
|
|
31
|
+
import {ScanView} from "@3cr/types-ts";
|
|
32
|
+
import {useViewer3cr} from "@/dataLayer/useViewer3cr";
|
|
33
|
+
|
|
34
|
+
interface Props {
|
|
35
|
+
view: ScanView;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type Emits = {
|
|
39
|
+
'update:modal': [value: boolean];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
defineProps<Props>();
|
|
43
|
+
|
|
44
|
+
const emit = defineEmits<Emits>();
|
|
45
|
+
|
|
46
|
+
const viewer3cr = useViewer3cr();
|
|
47
|
+
const menu = ref<boolean>(false);
|
|
48
|
+
const sagittal = ref<boolean>(false);
|
|
49
|
+
const coronal = ref<boolean>(false);
|
|
50
|
+
const transverse = ref<boolean>(false);
|
|
51
|
+
|
|
52
|
+
const menuState = computed({
|
|
53
|
+
get(): boolean {
|
|
54
|
+
return menu.value;
|
|
55
|
+
},
|
|
56
|
+
set(value: boolean): void {
|
|
57
|
+
menu.value = value;
|
|
58
|
+
emit('update:modal', value);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
async function flipSagittal(): Promise<void> {
|
|
63
|
+
const value = !sagittal.value;
|
|
64
|
+
await viewer3cr.invertTransform(value, coronal.value, transverse.value);
|
|
65
|
+
sagittal.value = value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function flipCoronal(): Promise<void> {
|
|
69
|
+
const value = !coronal.value;
|
|
70
|
+
await viewer3cr.invertTransform(sagittal.value, value, transverse.value);
|
|
71
|
+
coronal.value = value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function flipTransverse(): Promise<void> {
|
|
75
|
+
const value = !transverse.value;
|
|
76
|
+
await viewer3cr.invertTransform(sagittal.value, coronal.value, value);
|
|
77
|
+
transverse.value = value;
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Action
|
|
3
|
+
text="Flip Horizontally"
|
|
4
|
+
icon="swap_horiz"
|
|
5
|
+
:variant="variant"
|
|
6
|
+
@click="flipHorizontal"
|
|
7
|
+
/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { computed } from "vue";
|
|
12
|
+
import { ScanView } from "@3cr/types-ts";
|
|
13
|
+
import { isHorizontalFlip } from "@/dataLayer/scanState";
|
|
14
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
view: ScanView;
|
|
18
|
+
variant?: 'button' | 'menu-item';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
22
|
+
variant: 'button'
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const viewer3cr = useViewer3cr();
|
|
26
|
+
|
|
27
|
+
const horizontal = computed(() => {
|
|
28
|
+
return isHorizontalFlip(props.view);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function flipHorizontal(): Promise<void> {
|
|
32
|
+
const view2d = props.view as ScanView.Sagittal | ScanView.Coronal | ScanView.Transverse;
|
|
33
|
+
const value = !horizontal.value;
|
|
34
|
+
await viewer3cr.flipHorizontally(view2d, value);
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Action
|
|
3
|
+
text="Flip Vertically"
|
|
4
|
+
icon="swap_vert"
|
|
5
|
+
:variant="variant"
|
|
6
|
+
@click="flipVertical"
|
|
7
|
+
/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { computed } from "vue";
|
|
12
|
+
import { ScanView } from "@3cr/types-ts";
|
|
13
|
+
import { isVerticalFlip } from "@/dataLayer/scanState";
|
|
14
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
view: ScanView;
|
|
18
|
+
variant?: 'button' | 'menu-item';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
22
|
+
variant: 'button'
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const viewer3cr = useViewer3cr();
|
|
26
|
+
|
|
27
|
+
const vertical = computed(() => {
|
|
28
|
+
return isVerticalFlip(props.view);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function flipVertical(): Promise<void> {
|
|
32
|
+
const view2d = props.view as ScanView.Sagittal | ScanView.Coronal | ScanView.Transverse;
|
|
33
|
+
const value = !vertical.value;
|
|
34
|
+
await viewer3cr.flipVertically(view2d, value);
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Action
|
|
3
|
+
:text="text"
|
|
4
|
+
:icon="icon"
|
|
5
|
+
:variant="variant"
|
|
6
|
+
@click="action"
|
|
7
|
+
/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { getViewName, isFullscreen, previousLayout } from "@/dataLayer/scanState";
|
|
12
|
+
import { LayoutActions, ScanView, ViewSelectionActions } from "@3cr/types-ts";
|
|
13
|
+
import { computed } from "vue";
|
|
14
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
view: ScanView;
|
|
18
|
+
variant?: 'button' | 'menu-item';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const viewer3cr = useViewer3cr();
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
+
variant: 'button'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const text = computed(() => {
|
|
28
|
+
return isFullscreen.value ? 'Exit Fullscreen' : `Make ${getViewName(props.view)} Fullscreen`;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const icon = computed(() => {
|
|
32
|
+
return isFullscreen.value ? 'fullscreen_exit' : 'fullscreen';
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const action = computed(() => {
|
|
36
|
+
return isFullscreen.value ? exitFullscreen : fullscreen
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
async function fullscreen(): Promise<void> {
|
|
40
|
+
await viewer3cr.layouts(LayoutActions.lo01);
|
|
41
|
+
await viewer3cr.viewSelection(`vs_0${props.view + 1}` as ViewSelectionActions);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function exitFullscreen(): Promise<void> {
|
|
45
|
+
await viewer3cr.layouts(previousLayout.value);
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row no-gutters align="center">
|
|
3
|
+
<v-col>
|
|
4
|
+
<span class="text-caption">3D Navigation Cube</span>
|
|
5
|
+
</v-col>
|
|
6
|
+
<v-col cols="4" class="d-flex justify-center">
|
|
7
|
+
<v-switch
|
|
8
|
+
:model-value="isEnabled"
|
|
9
|
+
data-testid="switch"
|
|
10
|
+
color="primary"
|
|
11
|
+
density="compact"
|
|
12
|
+
hide-details
|
|
13
|
+
@click="setEnabled(!isEnabled)"
|
|
14
|
+
/>
|
|
15
|
+
</v-col>
|
|
16
|
+
</v-row>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import { computed } from "vue";
|
|
21
|
+
import { scanState } from "@/dataLayer/scanState";
|
|
22
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
23
|
+
|
|
24
|
+
const viewer3cr = useViewer3cr();
|
|
25
|
+
|
|
26
|
+
const isEnabled = computed(() => {
|
|
27
|
+
return scanState.value.NavigationCube.Interactivity.Value
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
async function setEnabled(value: boolean): Promise<void> {
|
|
31
|
+
await viewer3cr.setNavCubeInteractivity(value);
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Action
|
|
3
|
+
text="Pan"
|
|
4
|
+
icon="pan_tool"
|
|
5
|
+
:color="enabled ? 'blue-lighten-1' : 'white'"
|
|
6
|
+
:variant="variant"
|
|
7
|
+
@click="togglePan"
|
|
8
|
+
/>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import { ref } from "vue";
|
|
13
|
+
import { ScanView } from "@3cr/types-ts";
|
|
14
|
+
import { useEventListener } from "@/components/modal/composables/useEventListener";
|
|
15
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
view: ScanView;
|
|
19
|
+
element: HTMLElement;
|
|
20
|
+
variant?: 'button' | 'menu-item';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
+
variant: 'button'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
useEventListener(props.element, 'mousedown', onMouseDown);
|
|
28
|
+
useEventListener(props.element, 'mouseenter', onMouseEnter);
|
|
29
|
+
useEventListener(props.element, 'mouseleave', onMouseLeave);
|
|
30
|
+
useEventListener(window, 'mousemove', onMouseMove);
|
|
31
|
+
useEventListener(window, 'mouseup', onMouseUp);
|
|
32
|
+
|
|
33
|
+
const viewer3cr = useViewer3cr();
|
|
34
|
+
const enabled = ref<boolean>(false);
|
|
35
|
+
const mouseDown = ref<boolean>(false);
|
|
36
|
+
const prev = ref({ x: 0, y: 0 });
|
|
37
|
+
|
|
38
|
+
async function togglePan(): Promise<void> {
|
|
39
|
+
const value = !enabled.value;
|
|
40
|
+
await viewer3cr.hoverOverCanvas(!value);
|
|
41
|
+
enabled.value = value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function onMouseDown(event: Event): void {
|
|
45
|
+
if (enabled.value) {
|
|
46
|
+
const { x, y } = event as MouseEvent;
|
|
47
|
+
prev.value = { x, y };
|
|
48
|
+
mouseDown.value = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function onMouseMove(event: Event): Promise<void> {
|
|
53
|
+
if (enabled.value && mouseDown.value) {
|
|
54
|
+
const { x, y } = event as MouseEvent;
|
|
55
|
+
const xDelta = x - prev.value.x;
|
|
56
|
+
const yDelta = prev.value.y - y; // swapped because in js down is positive
|
|
57
|
+
await viewer3cr.panRight(props.view, xDelta);
|
|
58
|
+
await viewer3cr.panUp(props.view, yDelta);
|
|
59
|
+
prev.value = { x, y };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function onMouseEnter(_: Event): Promise<void> {
|
|
64
|
+
if (enabled.value && !mouseDown.value) {
|
|
65
|
+
await viewer3cr.hoverOverCanvas(false);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function onMouseLeave(_: Event): Promise<void> {
|
|
70
|
+
if (enabled.value && !mouseDown.value) {
|
|
71
|
+
await viewer3cr.hoverOverCanvas(true);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function onMouseUp(_: Event): void {
|
|
76
|
+
mouseDown.value = false;
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Action
|
|
3
|
+
text="Reset Views to Default"
|
|
4
|
+
icon="home"
|
|
5
|
+
:variant="variant"
|
|
6
|
+
@click="reset"
|
|
7
|
+
/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { ScanView, ViewSelectionActions } from "@3cr/types-ts";
|
|
12
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
view: ScanView;
|
|
16
|
+
variant?: 'button' | 'menu-item';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
withDefaults(defineProps<Props>(), {
|
|
20
|
+
variant: 'button'
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const viewer3cr = useViewer3cr();
|
|
24
|
+
|
|
25
|
+
async function reset(): Promise<void> {
|
|
26
|
+
await viewer3cr.viewSelection(ViewSelectionActions.vs06);
|
|
27
|
+
await viewer3cr.viewSelection(ViewSelectionActions.vs05);
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-menu
|
|
3
|
+
data-testid="menu"
|
|
4
|
+
v-model="menuState"
|
|
5
|
+
location="top"
|
|
6
|
+
:close-on-content-click="false"
|
|
7
|
+
>
|
|
8
|
+
<template #activator="{ props: menu }">
|
|
9
|
+
<Action
|
|
10
|
+
v-bind="menu"
|
|
11
|
+
:text="`Rotate ${getViewName(view)} View`"
|
|
12
|
+
icon="360"
|
|
13
|
+
:variant="variant"
|
|
14
|
+
/>
|
|
15
|
+
</template>
|
|
16
|
+
<v-card>
|
|
17
|
+
<v-card-title class="text-body-1">
|
|
18
|
+
<span>Rotate {{ getViewName(view) }} View</span>
|
|
19
|
+
</v-card-title>
|
|
20
|
+
<v-card-text>
|
|
21
|
+
<v-text-field
|
|
22
|
+
data-testid="rotation"
|
|
23
|
+
v-model="rotationDeg"
|
|
24
|
+
type="number"
|
|
25
|
+
variant="outlined"
|
|
26
|
+
suffix="deg"
|
|
27
|
+
hide-details
|
|
28
|
+
/>
|
|
29
|
+
</v-card-text>
|
|
30
|
+
<v-card-actions>
|
|
31
|
+
<v-spacer />
|
|
32
|
+
<v-btn data-testid="rotate" color="primary" @click="rotate">
|
|
33
|
+
Rotate
|
|
34
|
+
</v-btn>
|
|
35
|
+
</v-card-actions>
|
|
36
|
+
</v-card>
|
|
37
|
+
</v-menu>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { getViewName } from "@/dataLayer/scanState";
|
|
42
|
+
import { computed, ref } from "vue";
|
|
43
|
+
import { ScanView } from "@3cr/types-ts";
|
|
44
|
+
import { useViewer3cr } from "@/dataLayer/useViewer3cr";
|
|
45
|
+
|
|
46
|
+
interface Props {
|
|
47
|
+
view: ScanView;
|
|
48
|
+
variant?: 'button' | 'menu-item';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type Emits = {
|
|
52
|
+
'update:modal': [value: boolean];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
56
|
+
variant: 'button'
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const emit = defineEmits<Emits>();
|
|
60
|
+
|
|
61
|
+
const viewer3cr = useViewer3cr();
|
|
62
|
+
const menu = ref<boolean>(false);
|
|
63
|
+
const rotationDeg = ref<number>(0);
|
|
64
|
+
|
|
65
|
+
const menuState = computed({
|
|
66
|
+
get(): boolean {
|
|
67
|
+
return menu.value;
|
|
68
|
+
},
|
|
69
|
+
set(value: boolean): void {
|
|
70
|
+
menu.value = value;
|
|
71
|
+
emit('update:modal', value);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
async function rotate(): Promise<void> {
|
|
76
|
+
await viewer3cr.rotateByDeg(props.view, rotationDeg.value);
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-menu
|
|
3
|
+
v-model="menuState"
|
|
4
|
+
data-testid="menu"
|
|
5
|
+
:close-on-content-click="false"
|
|
6
|
+
location="end"
|
|
7
|
+
>
|
|
8
|
+
<template #activator="{ props }">
|
|
9
|
+
<Action
|
|
10
|
+
v-bind="props"
|
|
11
|
+
text="Slice the 3D Volume"
|
|
12
|
+
icon="cut"
|
|
13
|
+
:variant="variant"
|
|
14
|
+
/>
|
|
15
|
+
</template>
|
|
16
|
+
<v-card min-width="400" class="pb-2">
|
|
17
|
+
<v-card-title>Slice into the 3D Volume</v-card-title>
|
|
18
|
+
<v-card-text>
|
|
19
|
+
<DoubleSliderSelector
|
|
20
|
+
v-model:value="tSlider"
|
|
21
|
+
label="Transverse"
|
|
22
|
+
v-bind="tMinMax"
|
|
23
|
+
/>
|
|
24
|
+
<DoubleSliderSelector
|
|
25
|
+
v-model:value="sSlider"
|
|
26
|
+
label="Sagittal"
|
|
27
|
+
v-bind="sMinMax"
|
|
28
|
+
/>
|
|
29
|
+
<DoubleSliderSelector
|
|
30
|
+
v-model:value="cSlider"
|
|
31
|
+
label="Coronal"
|
|
32
|
+
v-bind="cMinMax"
|
|
33
|
+
/>
|
|
34
|
+
</v-card-text>
|
|
35
|
+
</v-card>
|
|
36
|
+
</v-menu>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import {computed, ref} from "vue";
|
|
41
|
+
import {cMinMax, cSlider, sMinMax, sSlider, tMinMax, tSlider} from "@/dataLayer/scanState";
|
|
42
|
+
import DoubleSliderSelector from "@/components/sliders/DoubleSliderSelector.vue";
|
|
43
|
+
import {ScanView} from "@3cr/types-ts";
|
|
44
|
+
|
|
45
|
+
interface Props {
|
|
46
|
+
view: ScanView;
|
|
47
|
+
variant?: 'button' | 'menu-item';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type Emits = {
|
|
51
|
+
'update:modal': [value: boolean];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
withDefaults(defineProps<Props>(), {
|
|
55
|
+
variant: 'button'
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const emit = defineEmits<Emits>();
|
|
59
|
+
|
|
60
|
+
const menu = ref<boolean>(false);
|
|
61
|
+
|
|
62
|
+
const menuState = computed({
|
|
63
|
+
get(): boolean {
|
|
64
|
+
return menu.value;
|
|
65
|
+
},
|
|
66
|
+
set(value: boolean): void {
|
|
67
|
+
menu.value = value;
|
|
68
|
+
emit('update:modal', value);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
</script>
|