@3cr/viewer-browser 0.0.62 → 0.0.92
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 +0 -2
- package/coverage/3cr-viewer-browser/index.html +116 -0
- package/coverage/3cr-viewer-browser/index.ts.html +199 -0
- package/coverage/3cr-viewer-browser/src/App.vue.html +316 -0
- package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +442 -0
- package/coverage/3cr-viewer-browser/src/components/icons/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +148 -0
- package/coverage/3cr-viewer-browser/src/components/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +622 -0
- package/coverage/3cr-viewer-browser/src/components/loading/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +3118 -0
- package/coverage/3cr-viewer-browser/src/components/modal/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +358 -0
- package/coverage/3cr-viewer-browser/src/components/selectors/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +487 -0
- package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +358 -0
- package/coverage/3cr-viewer-browser/src/components/sliders/index.html +131 -0
- package/coverage/3cr-viewer-browser/src/dataLayer/iconData.ts.html +118 -0
- package/coverage/3cr-viewer-browser/src/dataLayer/index.html +146 -0
- package/coverage/3cr-viewer-browser/src/dataLayer/payloadHandler.ts.html +463 -0
- package/coverage/3cr-viewer-browser/src/dataLayer/scanState.ts.html +598 -0
- package/coverage/3cr-viewer-browser/src/helpers/index.html +146 -0
- package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +406 -0
- package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +412 -0
- package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +133 -0
- package/coverage/3cr-viewer-browser/src/index.html +131 -0
- package/coverage/3cr-viewer-browser/src/main.ts.html +124 -0
- package/coverage/3cr-viewer-browser/src/models/LoadViewerOptions.ts.html +166 -0
- package/coverage/3cr-viewer-browser/src/models/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/notifications/index.html +116 -0
- package/coverage/3cr-viewer-browser/src/notifications/notification.ts.html +238 -0
- package/coverage/3cr-viewer-browser/src/plugins/index.html +131 -0
- package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +136 -0
- package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +220 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +296 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/dist/Viewer3CR.js +12 -12
- package/dist/Viewer3CR.mjs +10142 -10179
- package/dist/Viewer3CR.umd.js +12 -12
- package/index.ts +7 -42
- package/package.json +1 -1
- package/src/App.vue +6 -2
- package/src/components/modal/MftpWebGL3DRModal.vue +149 -552
- package/src/components/modal/__tests__/mftp-webgl-3dr-modal.spec.ts +146 -199
- package/src/components/selectors/ValueSelector.vue +4 -0
- package/src/components/selectors/__tests__/value-selector.spec.ts +18 -0
- package/src/components/sliders/DoubleSliderSelector.vue +10 -4
- package/src/components/sliders/__tests__/double-slider-selector.spec.ts +32 -0
- package/src/dataLayer/__tests__/payload-handler.spec.ts +214 -0
- package/src/dataLayer/iconData.ts +11 -0
- package/src/dataLayer/payloadHandler.ts +126 -0
- package/src/dataLayer/scanState.ts +171 -0
- package/src/demo/options.ts +93 -0
- package/src/models/Callbacks.ts +2 -0
- package/src/models/LoadViewerOptions.ts +27 -0
- package/src/models/LoadViewerPayload.ts +8 -0
- package/src/models/__tests__/load-viewer-options.spec.ts +22 -0
- package/src/notifications/__tests__/notification.spec.ts +120 -0
- package/src/notifications/notification.ts +51 -0
- package/vitest.config.mts +1 -0
- package/coverage/.tmp/coverage-100.json +0 -1
- package/coverage/.tmp/coverage-101.json +0 -1
- package/coverage/.tmp/coverage-102.json +0 -1
- package/coverage/.tmp/coverage-103.json +0 -1
- package/coverage/.tmp/coverage-104.json +0 -1
- package/coverage/.tmp/coverage-105.json +0 -1
- package/coverage/.tmp/coverage-106.json +0 -1
- package/coverage/.tmp/coverage-107.json +0 -1
- package/coverage/.tmp/coverage-108.json +0 -1
- package/coverage/.tmp/coverage-109.json +0 -1
- package/coverage/.tmp/coverage-110.json +0 -1
- package/coverage/.tmp/coverage-98.json +0 -1
- package/coverage/.tmp/coverage-99.json +0 -1
- /package/src/{components/modal → demo}/DemoModal.vue +0 -0
- /package/src/{components/modal → demo}/DemoPatientModal.vue +0 -0
|
@@ -7,11 +7,15 @@ import { flushPromises } from "@vue/test-utils";
|
|
|
7
7
|
describe("DoubleSliderSelector.vue", () => {
|
|
8
8
|
let wrapper = mountVuetify(DoubleSliderSelector, {
|
|
9
9
|
value: [0, 0],
|
|
10
|
+
min: 0,
|
|
11
|
+
max: 100,
|
|
10
12
|
});
|
|
11
13
|
|
|
12
14
|
beforeEach(() => {
|
|
13
15
|
wrapper = mountVuetify(DoubleSliderSelector, {
|
|
14
16
|
value: [0, 0],
|
|
17
|
+
min: 0,
|
|
18
|
+
max: 100,
|
|
15
19
|
});
|
|
16
20
|
vi.useFakeTimers();
|
|
17
21
|
});
|
|
@@ -59,6 +63,34 @@ describe("DoubleSliderSelector.vue", () => {
|
|
|
59
63
|
await slider.setValue([1, 2]);
|
|
60
64
|
});
|
|
61
65
|
|
|
66
|
+
it("should set min text lower than min limits", async () => {
|
|
67
|
+
wrapper.vm.sliderValue = [-10, 0];
|
|
68
|
+
await flushPromises();
|
|
69
|
+
|
|
70
|
+
expect(wrapper.emitted()["update:value"]).toStrictEqual([[[0, 0]]]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should set max text lower than min limits", async () => {
|
|
74
|
+
wrapper.vm.sliderValue = [-100, -10];
|
|
75
|
+
await flushPromises();
|
|
76
|
+
|
|
77
|
+
expect(wrapper.emitted()["update:value"]).toStrictEqual([[[0, 0]]]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should set min text higher than max limits", async () => {
|
|
81
|
+
wrapper.vm.sliderValue = [9000, 9001];
|
|
82
|
+
await flushPromises();
|
|
83
|
+
|
|
84
|
+
expect(wrapper.emitted()["update:value"]).toStrictEqual([[[100, 100]]]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should set max text higher than max limits", async () => {
|
|
88
|
+
wrapper.vm.sliderValue = [50, 9000];
|
|
89
|
+
await flushPromises();
|
|
90
|
+
|
|
91
|
+
expect(wrapper.emitted()["update:value"]).toStrictEqual([[[50, 100]]]);
|
|
92
|
+
});
|
|
93
|
+
|
|
62
94
|
it("should set min text", async () => {
|
|
63
95
|
const slider = wrapper.findAllComponents(".v-text-field");
|
|
64
96
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { emptyPayload, PayloadHandler } from "@/dataLayer/payloadHandler";
|
|
3
|
+
import { Ref, unref, UnwrapRef } from "vue";
|
|
4
|
+
import WebGL3DR from "@/components/WebGL3DR.vue";
|
|
5
|
+
import { spyOn } from "@vitest/spy";
|
|
6
|
+
import {
|
|
7
|
+
ColourPresetData,
|
|
8
|
+
FrontEndInterfaces,
|
|
9
|
+
LayoutActions,
|
|
10
|
+
PresetsActions,
|
|
11
|
+
ScanMovementActions,
|
|
12
|
+
ScanOrientationActions,
|
|
13
|
+
ScanView,
|
|
14
|
+
SlidersActions,
|
|
15
|
+
} from "@3cr/types-ts";
|
|
16
|
+
import { toNumber } from "@/helpers/utils";
|
|
17
|
+
import {
|
|
18
|
+
currentColourPreset,
|
|
19
|
+
previousLayout,
|
|
20
|
+
transactionStarted,
|
|
21
|
+
} from "@/dataLayer/scanState";
|
|
22
|
+
|
|
23
|
+
const sendPayloadFunction = vi.fn();
|
|
24
|
+
const mockWebglInstance = {
|
|
25
|
+
value: {
|
|
26
|
+
sendPayload: sendPayloadFunction,
|
|
27
|
+
},
|
|
28
|
+
} as unknown as Ref<UnwrapRef<typeof WebGL3DR | null>>;
|
|
29
|
+
|
|
30
|
+
describe("payloadHandler.ts", () => {
|
|
31
|
+
it("should create instance", () => {
|
|
32
|
+
expect(new PayloadHandler(mockWebglInstance)).toBeTruthy();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("payload functions", () => {
|
|
36
|
+
let instance: PayloadHandler;
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
transactionStarted.value = false;
|
|
39
|
+
previousLayout.value = LayoutActions.lo01;
|
|
40
|
+
currentColourPreset.value = {} as ColourPresetData;
|
|
41
|
+
vi.resetAllMocks();
|
|
42
|
+
instance = new PayloadHandler(mockWebglInstance);
|
|
43
|
+
console.log(instance);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should sendPayload", async () => {
|
|
47
|
+
expect(sendPayloadFunction).not.toHaveBeenCalled();
|
|
48
|
+
|
|
49
|
+
await instance.sendPayload("123", "123", {});
|
|
50
|
+
|
|
51
|
+
expect(sendPayloadFunction).toHaveBeenCalledWith(
|
|
52
|
+
JSON.stringify({
|
|
53
|
+
Version: "0.0.1",
|
|
54
|
+
Interface: "123",
|
|
55
|
+
Action: "123",
|
|
56
|
+
Message: JSON.stringify({}),
|
|
57
|
+
})
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should rotateByDeg", async () => {
|
|
62
|
+
const view = ScanView.Coronal;
|
|
63
|
+
const deg = 100;
|
|
64
|
+
const spy = spyOn(instance, "sendPayload");
|
|
65
|
+
expect(spy).not.toHaveBeenCalled();
|
|
66
|
+
|
|
67
|
+
await instance.rotateByDeg(view, deg);
|
|
68
|
+
|
|
69
|
+
expect(spy).toHaveBeenCalledWith(
|
|
70
|
+
FrontEndInterfaces.scan_orientation,
|
|
71
|
+
ScanOrientationActions.so01,
|
|
72
|
+
{
|
|
73
|
+
Version: "0.0.1",
|
|
74
|
+
View: view,
|
|
75
|
+
Angle: deg,
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should sliderHandler", async () => {
|
|
81
|
+
const action = SlidersActions.sl08;
|
|
82
|
+
const value = 100;
|
|
83
|
+
const spy = spyOn(instance, "sendPayload");
|
|
84
|
+
expect(spy).not.toHaveBeenCalled();
|
|
85
|
+
|
|
86
|
+
await instance.sliderHandler(action, value);
|
|
87
|
+
|
|
88
|
+
expect(spy).toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, {
|
|
89
|
+
Version: "0.0.1",
|
|
90
|
+
Value: toNumber(value),
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should not sliderHandler transactionStarted", async () => {
|
|
95
|
+
transactionStarted.value = true;
|
|
96
|
+
const action = SlidersActions.sl08;
|
|
97
|
+
const value = 100;
|
|
98
|
+
const spy = spyOn(instance, "sendPayload");
|
|
99
|
+
expect(spy).not.toHaveBeenCalled();
|
|
100
|
+
|
|
101
|
+
await instance.sliderHandler(action, value);
|
|
102
|
+
|
|
103
|
+
expect(spy).not.toHaveBeenCalledWith(FrontEndInterfaces.sliders, action, {
|
|
104
|
+
Version: "0.0.1",
|
|
105
|
+
Value: toNumber(value),
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should scanMovementHandler", async () => {
|
|
110
|
+
const action = ScanMovementActions.sm05;
|
|
111
|
+
const value = 100;
|
|
112
|
+
const spy = spyOn(instance, "sendPayload");
|
|
113
|
+
expect(spy).not.toHaveBeenCalled();
|
|
114
|
+
|
|
115
|
+
await instance.scanMovementHandler(action, value);
|
|
116
|
+
|
|
117
|
+
expect(spy).toHaveBeenCalledWith(
|
|
118
|
+
FrontEndInterfaces.scan_movement,
|
|
119
|
+
action,
|
|
120
|
+
{
|
|
121
|
+
Version: "0.0.1",
|
|
122
|
+
Value: toNumber(value),
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should not scanMovementHandler transactionStarted", async () => {
|
|
128
|
+
transactionStarted.value = true;
|
|
129
|
+
const action = ScanMovementActions.sm05;
|
|
130
|
+
const value = 100;
|
|
131
|
+
const spy = spyOn(instance, "sendPayload");
|
|
132
|
+
expect(spy).not.toHaveBeenCalled();
|
|
133
|
+
|
|
134
|
+
await instance.scanMovementHandler(action, value);
|
|
135
|
+
|
|
136
|
+
expect(spy).not.toHaveBeenCalledWith(
|
|
137
|
+
FrontEndInterfaces.scan_movement,
|
|
138
|
+
action,
|
|
139
|
+
{
|
|
140
|
+
Version: "0.0.1",
|
|
141
|
+
Value: toNumber(value),
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should layouts", async () => {
|
|
147
|
+
const action = LayoutActions.lo01;
|
|
148
|
+
const spy = spyOn(instance, "sendPayload");
|
|
149
|
+
expect(spy).not.toHaveBeenCalled();
|
|
150
|
+
|
|
151
|
+
await instance.layouts(action);
|
|
152
|
+
|
|
153
|
+
expect(spy).toHaveBeenCalledWith(
|
|
154
|
+
FrontEndInterfaces.layout,
|
|
155
|
+
action,
|
|
156
|
+
emptyPayload
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should set prevLayout", async () => {
|
|
161
|
+
const action = LayoutActions.lo02;
|
|
162
|
+
const spy = spyOn(instance, "sendPayload");
|
|
163
|
+
expect(spy).not.toHaveBeenCalled();
|
|
164
|
+
expect(unref(previousLayout)).not.toBe(LayoutActions.lo02);
|
|
165
|
+
|
|
166
|
+
await instance.layouts(action);
|
|
167
|
+
|
|
168
|
+
expect(unref(previousLayout)).toBe(LayoutActions.lo02);
|
|
169
|
+
expect(spy).toHaveBeenCalledWith(
|
|
170
|
+
FrontEndInterfaces.layout,
|
|
171
|
+
action,
|
|
172
|
+
emptyPayload
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should set setPreset", async () => {
|
|
177
|
+
const action = PresetsActions.pr01;
|
|
178
|
+
const data = {
|
|
179
|
+
asd: "123",
|
|
180
|
+
};
|
|
181
|
+
const spy = spyOn(instance, "sendPayload");
|
|
182
|
+
expect(spy).not.toHaveBeenCalled();
|
|
183
|
+
expect(unref(currentColourPreset)).not.toBe(data);
|
|
184
|
+
|
|
185
|
+
await instance.setPreset(action, data);
|
|
186
|
+
|
|
187
|
+
expect(unref(currentColourPreset)).not.toBe(data);
|
|
188
|
+
expect(spy).toHaveBeenCalledWith(
|
|
189
|
+
FrontEndInterfaces.presets,
|
|
190
|
+
action,
|
|
191
|
+
data
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should set setPreset change colour", async () => {
|
|
196
|
+
const action = PresetsActions.pr02;
|
|
197
|
+
const data = {
|
|
198
|
+
asd: "123",
|
|
199
|
+
};
|
|
200
|
+
const spy = spyOn(instance, "sendPayload");
|
|
201
|
+
expect(spy).not.toHaveBeenCalled();
|
|
202
|
+
expect(unref(currentColourPreset)).not.toBe(data);
|
|
203
|
+
|
|
204
|
+
await instance.setPreset(action, data);
|
|
205
|
+
|
|
206
|
+
expect(unref(currentColourPreset)).toStrictEqual(data);
|
|
207
|
+
expect(spy).toHaveBeenCalledWith(
|
|
208
|
+
FrontEndInterfaces.presets,
|
|
209
|
+
action,
|
|
210
|
+
data
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function getIconForPreset(presetName: string): string | undefined {
|
|
2
|
+
if (presetName === "Bone") return "fa:fas fa-bone";
|
|
3
|
+
if (presetName === "Brain") return "fa:fas fa-brain";
|
|
4
|
+
if (presetName === "Liver") return "custom:liver_icon";
|
|
5
|
+
if (presetName === "Lungs") return "fa:fas fa-lungs";
|
|
6
|
+
if (presetName === "Muscle") return "custom:muscle_icon";
|
|
7
|
+
if (presetName === "Temporal Bones") return "custom:temporal_bones_icon";
|
|
8
|
+
if (presetName === "Soft Tissue") return "custom:torso_icon";
|
|
9
|
+
if (presetName === "Skin") return "custom:skin_icon";
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Ref, unref, UnwrapRef } from "vue";
|
|
2
|
+
import WebGL3DR from "@/components/WebGL3DR.vue";
|
|
3
|
+
import {
|
|
4
|
+
FrontEndInterfaces,
|
|
5
|
+
InteractivityActions,
|
|
6
|
+
LayoutActions,
|
|
7
|
+
PresetsActions,
|
|
8
|
+
ScanMovementActions,
|
|
9
|
+
ScanOrientationActions,
|
|
10
|
+
ScanView,
|
|
11
|
+
SlidersActions,
|
|
12
|
+
ViewSelectionActions,
|
|
13
|
+
} from "@3cr/types-ts";
|
|
14
|
+
import {
|
|
15
|
+
currentColourPreset,
|
|
16
|
+
previousLayout,
|
|
17
|
+
transactionStarted,
|
|
18
|
+
} from "@/dataLayer/scanState";
|
|
19
|
+
import { toNumber } from "@/helpers/utils";
|
|
20
|
+
|
|
21
|
+
export const emptyPayload = { Version: "1.1.0" };
|
|
22
|
+
|
|
23
|
+
export class PayloadHandler {
|
|
24
|
+
private readonly instance: Ref<UnwrapRef<typeof WebGL3DR | null>>;
|
|
25
|
+
constructor(protected webglInstance: Ref<UnwrapRef<typeof WebGL3DR | null>>) {
|
|
26
|
+
this.instance = webglInstance;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async sendPayload(interfaceType: string, actionType: string, message: any) {
|
|
30
|
+
await this.instance.value?.sendPayload(
|
|
31
|
+
JSON.stringify({
|
|
32
|
+
Version: "0.0.1",
|
|
33
|
+
Interface: interfaceType,
|
|
34
|
+
Action: actionType,
|
|
35
|
+
Message: JSON.stringify(message),
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
async rotateByDeg(view: ScanView, deg: number) {
|
|
40
|
+
await this.sendPayload(
|
|
41
|
+
FrontEndInterfaces.scan_orientation,
|
|
42
|
+
ScanOrientationActions.so01,
|
|
43
|
+
{
|
|
44
|
+
Version: "0.0.1",
|
|
45
|
+
View: view,
|
|
46
|
+
Angle: deg,
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
async viewSelection(action: ViewSelectionActions) {
|
|
51
|
+
await this.sendPayload(
|
|
52
|
+
FrontEndInterfaces.view_selection,
|
|
53
|
+
action,
|
|
54
|
+
emptyPayload
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async sliderHandler(action: SlidersActions, value: number) {
|
|
59
|
+
if (unref(transactionStarted)) return;
|
|
60
|
+
await this.slider(action, value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async scanMovementHandler(action: ScanMovementActions, value: number) {
|
|
64
|
+
if (unref(transactionStarted)) return;
|
|
65
|
+
await this.scanMovement(action, value);
|
|
66
|
+
}
|
|
67
|
+
async layouts(action: LayoutActions) {
|
|
68
|
+
if (action !== LayoutActions.lo01) previousLayout.value = action;
|
|
69
|
+
await this.sendPayload(FrontEndInterfaces.layout, action, emptyPayload);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async setPreset(action: PresetsActions, preset: any) {
|
|
73
|
+
await this.sendPayload(FrontEndInterfaces.presets, action, preset);
|
|
74
|
+
if (action === PresetsActions.pr02) {
|
|
75
|
+
currentColourPreset.value = preset;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async hoverOverCanvas(isHovering: boolean) {
|
|
80
|
+
await this.sendPayload(
|
|
81
|
+
FrontEndInterfaces.interactivity,
|
|
82
|
+
InteractivityActions.in01,
|
|
83
|
+
{
|
|
84
|
+
Version: "0.0.1",
|
|
85
|
+
Value: isHovering,
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
await this.sendPayload(
|
|
89
|
+
FrontEndInterfaces.interactivity,
|
|
90
|
+
InteractivityActions.in02,
|
|
91
|
+
{
|
|
92
|
+
Version: "0.0.1",
|
|
93
|
+
Value: isHovering,
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async scanMovement(action: ScanMovementActions, value: number) {
|
|
99
|
+
await this.sendPayload(FrontEndInterfaces.scan_movement, action, {
|
|
100
|
+
Version: "0.0.1",
|
|
101
|
+
Value: toNumber(value),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
private async slider(action: SlidersActions, value: number) {
|
|
105
|
+
await this.sendPayload(FrontEndInterfaces.sliders, action, {
|
|
106
|
+
Version: "0.0.1",
|
|
107
|
+
Value: toNumber(value),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function sendPayload(
|
|
113
|
+
instance: Ref<UnwrapRef<typeof WebGL3DR | null>>,
|
|
114
|
+
interfaceType: string,
|
|
115
|
+
actionType: string,
|
|
116
|
+
message: any
|
|
117
|
+
) {
|
|
118
|
+
await unref(instance)?.sendPayload(
|
|
119
|
+
JSON.stringify({
|
|
120
|
+
Version: "0.0.1",
|
|
121
|
+
Interface: interfaceType,
|
|
122
|
+
Action: actionType,
|
|
123
|
+
Message: JSON.stringify(message),
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { computed, nextTick, ref, unref } from "vue";
|
|
2
|
+
import {
|
|
3
|
+
AnchorPoint,
|
|
4
|
+
ColourPresetData,
|
|
5
|
+
CurrentScanState,
|
|
6
|
+
InitialScanState,
|
|
7
|
+
LayoutActions,
|
|
8
|
+
} from "@3cr/types-ts";
|
|
9
|
+
import {
|
|
10
|
+
inflateInitialScanState,
|
|
11
|
+
inflateScanState,
|
|
12
|
+
} from "@/helpers/modelHelper";
|
|
13
|
+
|
|
14
|
+
export const currentColourPreset = ref<ColourPresetData | undefined>(undefined);
|
|
15
|
+
|
|
16
|
+
export const transactionStarted = ref<boolean>(false);
|
|
17
|
+
export const scanStateIncoming = ref<string>("");
|
|
18
|
+
export const scanState = ref<CurrentScanState>(inflateScanState());
|
|
19
|
+
export const initialScanState = ref<InitialScanState>(
|
|
20
|
+
inflateInitialScanState()
|
|
21
|
+
);
|
|
22
|
+
export const previousLayout = ref<LayoutActions>(LayoutActions.lo02);
|
|
23
|
+
|
|
24
|
+
export const huMinMax = ref({
|
|
25
|
+
min: -999999,
|
|
26
|
+
max: 999999,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const tMinMax = ref({
|
|
30
|
+
min: 0,
|
|
31
|
+
max: 999999,
|
|
32
|
+
});
|
|
33
|
+
export const sMinMax = ref({
|
|
34
|
+
min: 0,
|
|
35
|
+
max: 999999,
|
|
36
|
+
});
|
|
37
|
+
export const cMinMax = ref({
|
|
38
|
+
min: 0,
|
|
39
|
+
max: 999999,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const windowSlider = computed({
|
|
43
|
+
get() {
|
|
44
|
+
return [
|
|
45
|
+
unref(scanState).Display.WindowLower,
|
|
46
|
+
unref(scanState).Display.WindowUpper,
|
|
47
|
+
];
|
|
48
|
+
},
|
|
49
|
+
set(value: Array<number>) {
|
|
50
|
+
scanState.value.Display.WindowLower = value[0];
|
|
51
|
+
scanState.value.Display.WindowUpper = value[1];
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
export const thresholdSlider = computed({
|
|
55
|
+
get() {
|
|
56
|
+
return [
|
|
57
|
+
Math.trunc(unref(scanState).Display.ThresholdLower),
|
|
58
|
+
Math.trunc(unref(scanState).Display.ThresholdUpper),
|
|
59
|
+
];
|
|
60
|
+
},
|
|
61
|
+
set(value: Array<number>) {
|
|
62
|
+
scanState.value.Display.ThresholdLower = value[0];
|
|
63
|
+
scanState.value.Display.ThresholdUpper = value[1];
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
export const tSlider = computed({
|
|
67
|
+
get() {
|
|
68
|
+
return [
|
|
69
|
+
Math.trunc(unref(scanState).Slice.TransverseLower),
|
|
70
|
+
Math.trunc(unref(scanState).Slice.TransverseUpper),
|
|
71
|
+
];
|
|
72
|
+
},
|
|
73
|
+
set(value: Array<number>) {
|
|
74
|
+
scanState.value.Slice.TransverseLower = value[0];
|
|
75
|
+
scanState.value.Slice.TransverseLower = value[1];
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
export const sSlider = computed({
|
|
79
|
+
get() {
|
|
80
|
+
return [
|
|
81
|
+
Math.trunc(unref(scanState).Slice.SagittalLower),
|
|
82
|
+
Math.trunc(unref(scanState).Slice.SagittalUpper),
|
|
83
|
+
];
|
|
84
|
+
},
|
|
85
|
+
set(value: Array<number>) {
|
|
86
|
+
scanState.value.Slice.SagittalLower = value[0];
|
|
87
|
+
scanState.value.Slice.SagittalUpper = value[1];
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export const cSlider = computed({
|
|
92
|
+
get() {
|
|
93
|
+
return [
|
|
94
|
+
Math.trunc(unref(scanState).Slice.CoronalLower),
|
|
95
|
+
Math.trunc(unref(scanState).Slice.CoronalUpper),
|
|
96
|
+
];
|
|
97
|
+
},
|
|
98
|
+
set(value: Array<number>) {
|
|
99
|
+
scanState.value.Slice.CoronalLower = value[0];
|
|
100
|
+
scanState.value.Slice.CoronalUpper = value[1];
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export const isLayout2x2 = computed(() => {
|
|
105
|
+
return (
|
|
106
|
+
unref(scanState).Layout.PositionData.length > 1 &&
|
|
107
|
+
unref(scanState).Layout.PositionData[0].Anchor === AnchorPoint.TOP_LEFT &&
|
|
108
|
+
unref(scanState).Layout.PositionData[1].Anchor === AnchorPoint.TOP_RIGHT &&
|
|
109
|
+
unref(scanState).Layout.PositionData[2].Anchor ===
|
|
110
|
+
AnchorPoint.BOTTOM_LEFT &&
|
|
111
|
+
unref(scanState).Layout.PositionData[3].Anchor === AnchorPoint.BOTTOM_RIGHT
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
export const isLayout1x3 = computed(() => {
|
|
115
|
+
return (
|
|
116
|
+
unref(scanState).Layout.PositionData.length > 1 &&
|
|
117
|
+
unref(scanState).Layout.PositionData[0].Anchor === AnchorPoint.CENTER &&
|
|
118
|
+
unref(scanState).Layout.PositionData[1].Anchor === AnchorPoint.TOP_RIGHT &&
|
|
119
|
+
unref(scanState).Layout.PositionData[2].Anchor === AnchorPoint.RIGHT &&
|
|
120
|
+
unref(scanState).Layout.PositionData[3].Anchor === AnchorPoint.BOTTOM_RIGHT
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export const currentGreyscalePreset = computed(() => {
|
|
125
|
+
for (const preset of unref(initialScanState).GreyscalePresets) {
|
|
126
|
+
if (
|
|
127
|
+
(unref(scanState).Display.WindowLower === preset.Lower ||
|
|
128
|
+
(preset.Lower < unref(initialScanState).HuLower &&
|
|
129
|
+
unref(scanState).Display.WindowLower ===
|
|
130
|
+
unref(initialScanState).HuLower)) &&
|
|
131
|
+
(unref(scanState).Display.WindowUpper === preset.Upper ||
|
|
132
|
+
(preset.Upper > unref(initialScanState).HuUpper &&
|
|
133
|
+
unref(scanState).Display.WindowUpper ===
|
|
134
|
+
unref(initialScanState).HuUpper))
|
|
135
|
+
) {
|
|
136
|
+
return preset.Name;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export async function setScanStateFromPayload(_: string, message: string) {
|
|
143
|
+
transactionStarted.value = true;
|
|
144
|
+
scanStateIncoming.value = JSON.stringify(JSON.parse(message), null, 2);
|
|
145
|
+
|
|
146
|
+
setScanState(message);
|
|
147
|
+
await nextTick();
|
|
148
|
+
transactionStarted.value = false;
|
|
149
|
+
}
|
|
150
|
+
export async function setInitialScanStateFromPayload(
|
|
151
|
+
_: string,
|
|
152
|
+
message: string
|
|
153
|
+
) {
|
|
154
|
+
transactionStarted.value = true;
|
|
155
|
+
|
|
156
|
+
scanStateIncoming.value = JSON.stringify(JSON.parse(message), null, 2);
|
|
157
|
+
const obj = JSON.parse(unref(scanStateIncoming)) as InitialScanState;
|
|
158
|
+
|
|
159
|
+
setScanState(JSON.stringify(obj.DefaultDisplaySettings, null, 2));
|
|
160
|
+
initialScanState.value = obj;
|
|
161
|
+
huMinMax.value.max = obj.HuUpper;
|
|
162
|
+
huMinMax.value.min = obj.HuLower;
|
|
163
|
+
sMinMax.value.max = obj.XSlices;
|
|
164
|
+
cMinMax.value.max = obj.YSlices;
|
|
165
|
+
tMinMax.value.max = obj.ZSlices;
|
|
166
|
+
currentColourPreset.value = obj.ColourPresets[0];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function setScanState(message: string) {
|
|
170
|
+
scanState.value = JSON.parse(message) as CurrentScanState;
|
|
171
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { computed, ref, unref } from "vue";
|
|
2
|
+
import { LoadViewerOptions } from "@/models/LoadViewerOptions";
|
|
3
|
+
import { LoadViewerPayload } from "@/models/LoadViewerPayload";
|
|
4
|
+
|
|
5
|
+
export function checkIsDemo(payload: LoadViewerPayload) {
|
|
6
|
+
isDemo.value =
|
|
7
|
+
payload.Url ===
|
|
8
|
+
"https://webgl-3dr.singular.health/test_scans/01440d4e-8b04-4b90-bb2c-698535ce16d6/CHEST.3vxl";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const isDemo = ref<boolean>(false);
|
|
12
|
+
export const m_demo = ref<boolean>(false);
|
|
13
|
+
export const m_demoPatient = ref<boolean>(false);
|
|
14
|
+
export const demoLicenceOptions: LoadViewerOptions = {
|
|
15
|
+
OnClosePopup: () => Promise.resolve(),
|
|
16
|
+
OnExitViewer: () => {
|
|
17
|
+
m_demo.value = true;
|
|
18
|
+
},
|
|
19
|
+
OnLoadNewDicomSeries: () => {
|
|
20
|
+
m_demo.value = true;
|
|
21
|
+
},
|
|
22
|
+
OnDownloadDicomSeries: () => {
|
|
23
|
+
m_demo.value = true;
|
|
24
|
+
},
|
|
25
|
+
OnLoadSavedSession: () => {
|
|
26
|
+
m_demo.value = true;
|
|
27
|
+
},
|
|
28
|
+
OnSaveSession: () => {
|
|
29
|
+
m_demo.value = true;
|
|
30
|
+
},
|
|
31
|
+
OnSendTo3rdParty: () => {
|
|
32
|
+
m_demo.value = true;
|
|
33
|
+
},
|
|
34
|
+
OnShareToMobile: () => {
|
|
35
|
+
m_demo.value = true;
|
|
36
|
+
},
|
|
37
|
+
OnShare: () => {
|
|
38
|
+
m_demo.value = true;
|
|
39
|
+
},
|
|
40
|
+
OnScreenshot: () => {
|
|
41
|
+
m_demo.value = true;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const demoPatientOptions: LoadViewerOptions = {
|
|
46
|
+
OnClosePopup: () => Promise.resolve(),
|
|
47
|
+
OnExitViewer: () => {
|
|
48
|
+
m_demoPatient.value = true;
|
|
49
|
+
},
|
|
50
|
+
OnLoadNewDicomSeries: () => {
|
|
51
|
+
m_demoPatient.value = true;
|
|
52
|
+
},
|
|
53
|
+
OnDownloadDicomSeries: () => {
|
|
54
|
+
m_demoPatient.value = true;
|
|
55
|
+
},
|
|
56
|
+
OnLoadSavedSession: () => {
|
|
57
|
+
m_demoPatient.value = true;
|
|
58
|
+
},
|
|
59
|
+
OnSaveSession: () => {
|
|
60
|
+
m_demoPatient.value = true;
|
|
61
|
+
},
|
|
62
|
+
OnSendTo3rdParty: () => {
|
|
63
|
+
m_demoPatient.value = true;
|
|
64
|
+
},
|
|
65
|
+
OnShareToMobile: () => {
|
|
66
|
+
m_demoPatient.value = true;
|
|
67
|
+
},
|
|
68
|
+
OnShare: () => {
|
|
69
|
+
m_demoPatient.value = true;
|
|
70
|
+
},
|
|
71
|
+
OnScreenshot: () => {
|
|
72
|
+
m_demoPatient.value = true;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const demoType = computed(() => {
|
|
77
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
78
|
+
return (urlParams.get("demoType") || "licence").toLowerCase();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export function executeDemoOption(key: keyof LoadViewerOptions) {
|
|
82
|
+
if (unref(demoType) === "licence") {
|
|
83
|
+
const functionToExecute = demoLicenceOptions[key];
|
|
84
|
+
if (functionToExecute !== undefined) {
|
|
85
|
+
functionToExecute();
|
|
86
|
+
}
|
|
87
|
+
} else if (unref(demoType) === "patient") {
|
|
88
|
+
const functionToExecute = demoPatientOptions[key];
|
|
89
|
+
if (functionToExecute !== undefined) {
|
|
90
|
+
functionToExecute();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ViewerAsyncCallback, ViewerCallback } from "@/models/Callbacks";
|
|
2
|
+
|
|
3
|
+
export interface LoadViewerOptions {
|
|
4
|
+
OnShare?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
5
|
+
OnScreenshot?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
6
|
+
OnLoadNewDicomSeries?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
7
|
+
OnDownloadDicomSeries?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
8
|
+
OnLoadSavedSession?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
9
|
+
OnShareToMobile?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
10
|
+
OnSendTo3rdParty?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
11
|
+
OnSaveSession?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
12
|
+
OnClosePopup?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
13
|
+
OnExitViewer?: ViewerCallback | ViewerAsyncCallback | undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const defaultLoadViewerOptions: LoadViewerOptions = {
|
|
17
|
+
OnClosePopup: () => Promise.resolve(),
|
|
18
|
+
OnExitViewer: () => Promise.resolve(),
|
|
19
|
+
OnLoadNewDicomSeries: () => Promise.resolve(),
|
|
20
|
+
OnDownloadDicomSeries: () => Promise.resolve(),
|
|
21
|
+
OnLoadSavedSession: () => Promise.resolve(),
|
|
22
|
+
OnSaveSession: () => Promise.resolve(),
|
|
23
|
+
OnSendTo3rdParty: () => Promise.resolve(),
|
|
24
|
+
OnShareToMobile: () => Promise.resolve(),
|
|
25
|
+
OnShare: () => Promise.resolve(),
|
|
26
|
+
OnScreenshot: () => Promise.resolve(),
|
|
27
|
+
};
|