@abi-software/scaffoldvuer 0.2.2 → 0.2.3-alpha-2

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.
Files changed (47) hide show
  1. package/.eslintrc.js +12 -12
  2. package/CHANGELOG.md +316 -316
  3. package/LICENSE +201 -201
  4. package/README.md +164 -164
  5. package/babel.config.js +14 -14
  6. package/dist/scaffoldvuer-wc.common.js +183 -35
  7. package/dist/scaffoldvuer-wc.umd.js +183 -35
  8. package/dist/scaffoldvuer-wc.umd.min.js +183 -35
  9. package/dist/scaffoldvuer.common.js +1086 -717
  10. package/dist/scaffoldvuer.common.js.map +1 -1
  11. package/dist/scaffoldvuer.css +1 -1
  12. package/dist/scaffoldvuer.umd.js +1086 -717
  13. package/dist/scaffoldvuer.umd.js.map +1 -1
  14. package/dist/scaffoldvuer.umd.min.js +1 -1
  15. package/dist/scaffoldvuer.umd.min.js.map +1 -1
  16. package/package-lock.json +18119 -18121
  17. package/package.json +89 -89
  18. package/public/index.html +17 -17
  19. package/src/App.vue +669 -714
  20. package/src/ScaffoldVuer-wc.js +13 -13
  21. package/src/{components → app}/DropZone.vue +114 -114
  22. package/src/{components → app}/ModelsInformation.js +35 -35
  23. package/src/{components → app}/ModelsTable.vue +113 -113
  24. package/src/app/TextureDemos.js +114 -0
  25. package/src/assets/_variables.scss +43 -43
  26. package/src/assets/styles.scss +7 -7
  27. package/src/components/OpacityControls.vue +222 -222
  28. package/src/components/ScaffoldTooltip.vue +142 -141
  29. package/src/components/ScaffoldVuer.md +44 -44
  30. package/src/components/ScaffoldVuer.vue +2007 -1887
  31. package/src/components/TreeControls.vue +699 -691
  32. package/src/components/index.js +7 -7
  33. package/src/components/test.pdf +0 -0
  34. package/src/main.js +14 -14
  35. package/src/scripts/BaseModule.js +80 -80
  36. package/src/scripts/RendererModule.js +289 -289
  37. package/src/scripts/WebGL.js +94 -94
  38. package/src/scripts/annotation.js +5 -5
  39. package/src/scripts/eventNotifier.js +66 -66
  40. package/src/scripts/graphicsHighlight.js +134 -134
  41. package/src/scripts/organsRenderer.js +587 -606
  42. package/src/scripts/search.js +182 -153
  43. package/src/scripts/utilities.js +146 -43
  44. package/src/searchControls.vue +122 -0
  45. package/styleguide.config.js +22 -22
  46. package/vue.config.js +41 -41
  47. package/src/credential.json +0 -12
@@ -1,1887 +1,2007 @@
1
- <template>
2
- <div
3
- ref="scaffoldContainer"
4
- v-loading="loading"
5
- class="scaffold-container"
6
- element-loading-text="Loading..."
7
- element-loading-spinner="el-icon-loading"
8
- element-loading-background="rgba(0, 0, 0, 0.3)"
9
- >
10
- <map-svg-sprite-color />
11
- <scaffold-tooltip
12
- :label="tData.label"
13
- :region="tData.region"
14
- :visible="tData.visible"
15
- :x="tData.x"
16
- :y="tData.y"
17
- />
18
- <div
19
- id="organsDisplayArea"
20
- ref="display"
21
- tabindex="-1"
22
- style="height: 100%; width: 100%"
23
- @keydown.66="backgroundChangeCallback"
24
- />
25
- <div v-show="displayUI && !isTransitioning">
26
- <el-popover
27
- v-if="displayWarning"
28
- ref="warningPopover"
29
- v-model="hoverVisibilities[6].value"
30
- :content="warningMessage"
31
- placement="right"
32
- :append-to-body="false"
33
- trigger="manual"
34
- popper-class="scaffold-popper message-popper right-popper non-selectable"
35
- />
36
- <i
37
- v-if="displayWarning"
38
- v-popover:warningPopover
39
- class="el-icon-warning message-icon warning-icon"
40
- @mouseover="showHelpText(6)"
41
- @mouseout="hideHelpText(6)"
42
- >
43
- <span class="message-text">Beta</span>
44
- </i>
45
- <el-popover
46
- v-if="displayLatestChanges"
47
- ref="latestChangesPopover"
48
- v-model="hoverVisibilities[7].value"
49
- :content="latestChangesMessage"
50
- placement="right"
51
- :append-to-body="false"
52
- trigger="manual"
53
- popper-class="scaffold-popper message-popper right-popper non-selectable"
54
- />
55
- <i
56
- v-if="displayLatestChanges && latestChangesMessage"
57
- v-popover:latestChangesPopover
58
- class="el-icon-warning message-icon latest-changesicon"
59
- @mouseover="showHelpText(7)"
60
- @mouseout="hideHelpText(7)"
61
- >
62
- <span class="message-text">What's new?</span>
63
- </i>
64
- <el-popover
65
- ref="checkBoxPopover"
66
- v-model="hoverVisibilities[5].value"
67
- content="Change region visibility"
68
- placement="right"
69
- :append-to-body="false"
70
- trigger="manual"
71
- popper-class="scaffold-popper right-popper non-selectable"
72
- />
73
- <tree-controls
74
- ref="treeControls"
75
- v-popover:checkBoxPopover
76
- :help-mode="helpMode"
77
- :isReady="isReady"
78
- :show-colour-picker="showColourPicker"
79
- @object-selected="objectSelected"
80
- @object-hovered="objectHovered"
81
- @drawer-toggled="drawerToggled"
82
- />
83
- <div class="opacity-box">
84
- <opacity-controls ref="opacityControl" />
85
- </div>
86
- <el-popover
87
- v-if="sceneData.timeVarying"
88
- ref="sliderPopover"
89
- v-model="hoverVisibilities[4].value"
90
- content="Move the slider to animate the region"
91
- placement="top"
92
- :append-to-body="false"
93
- trigger="manual"
94
- popper-class="scaffold-popper top-popper non-selectable"
95
- />
96
- <div
97
- v-if="sceneData.timeVarying"
98
- v-popover:sliderPopover
99
- class="time-slider-container"
100
- :class="[minimisedSlider ? 'minimised' : '', sliderPosition]"
101
- >
102
- <el-tabs type="card">
103
- <el-tab-pane label="Animate scaffold">
104
- <el-row class="tab-content">
105
- <map-svg-icon
106
- v-if="isPlaying"
107
- icon="pause"
108
- class="icon-button video-button"
109
- @click.native="play(false)"
110
- />
111
- <map-svg-icon
112
- v-else
113
- icon="play"
114
- class="video-button icon-button"
115
- @click.native="play(true)"
116
- />
117
- <el-slider
118
- :min="0"
119
- :max="timeMax"
120
- :value="(sceneData.currentTime / 100) * timeMax"
121
- :step="0.1"
122
- tooltip-class="time-slider-tooltip"
123
- class="slider"
124
- :format-tooltip="formatTooltip"
125
- :marks="timeStamps"
126
- @input="timeChange($event)"
127
- />
128
- </el-row>
129
- </el-tab-pane>
130
- <el-tab-pane label="Animation data">
131
- <el-row class="tab-content">
132
- <div class="animation-data">
133
- Original duration:
134
- <div class="purple">
135
- {{ orginalDuration }}
136
- </div>
137
- </div>
138
- <div class="animation-data">
139
- Animation duration:
140
- <div class="purple">
141
- {{ animateDuration }}
142
- </div>
143
- </div>
144
- <div class="animation-data">
145
- Playback speed
146
- <el-select
147
- :popper-append-to-body="true"
148
- :value="currentSpeed"
149
- placeholder="Select"
150
- class="select-box"
151
- popper-class="scaffold_viewer_dropdown"
152
- @change="speedChanged($event)"
153
- >
154
- <el-option
155
- v-for="item in playSpeed"
156
- :key="item.value"
157
- :label="item.label"
158
- :value="item.value"
159
- />
160
- </el-select>
161
- </div>
162
- </el-row>
163
- </el-tab-pane>
164
- </el-tabs>
165
- </div>
166
- <div class="bottom-right-control">
167
- <el-popover
168
- v-model="hoverVisibilities[0].value"
169
- content="Zoom in"
170
- placement="left"
171
- :append-to-body="false"
172
- trigger="manual"
173
- popper-class="scaffold-popper left-popper non-selectable"
174
- >
175
- <map-svg-icon
176
- slot="reference"
177
- icon="zoomIn"
178
- class="icon-button zoomIn"
179
- @click.native="zoomIn()"
180
- @mouseover.native="showHelpText(0)"
181
- @mouseout.native="hideHelpText(0)"
182
- />
183
- </el-popover>
184
- <el-popover
185
- v-model="hoverVisibilities[1].value"
186
- content="Zoom out"
187
- placement="top-end"
188
- :append-to-body="false"
189
- trigger="manual"
190
- popper-class="scaffold-popper popper-zoomout non-selectable"
191
- >
192
- <map-svg-icon
193
- slot="reference"
194
- icon="zoomOut"
195
- class="icon-button zoomOut"
196
- @click.native="zoomOut()"
197
- @mouseover.native="showHelpText(1)"
198
- @mouseout.native="hideHelpText(1)"
199
- />
200
- </el-popover>
201
- <el-popover
202
- v-model="hoverVisibilities[2].value"
203
- placement="top"
204
- :append-to-body="false"
205
- trigger="manual"
206
- popper-class="scaffold-popper non-selectable"
207
- >
208
- <div>
209
- Fit to
210
- <br />
211
- window
212
- </div>
213
- <map-svg-icon
214
- slot="reference"
215
- icon="fitWindow"
216
- class="icon-button fitWindow"
217
- @click.native="fitWindow()"
218
- @mouseover.native="showHelpText(2)"
219
- @mouseout.native="hideHelpText(2)"
220
- />
221
- </el-popover>
222
- </div>
223
- <el-popover
224
- ref="open-map-popover"
225
- placement="top-start"
226
- width="128"
227
- :append-to-body="false"
228
- trigger="click"
229
- popper-class="open-map-popper"
230
- >
231
- <el-row v-for="item in openMapOptions" :key="item.key">
232
- <el-button
233
- type="primary"
234
- plain
235
- @click="$emit('open-map', item.key)"
236
- >
237
- {{item.display}}
238
- </el-button>
239
- </el-row>
240
- </el-popover>
241
- <el-popover
242
- ref="backgroundPopover"
243
- placement="top-start"
244
- width="128"
245
- :append-to-body="false"
246
- trigger="click"
247
- popper-class="background-popper non-selectable"
248
- >
249
- <el-row class="backgroundText"> Change background </el-row>
250
- <el-row class="backgroundChooser">
251
- <div
252
- v-for="item in availableBackground"
253
- :key="item"
254
- :class="[
255
- 'backgroundChoice',
256
- item,
257
- item == currentBackground ? 'active' : '',
258
- ]"
259
- @click="backgroundChangeCallback(item)"
260
- />
261
- </el-row>
262
- </el-popover>
263
- <div
264
- class="settings-group"
265
- :class="{ open: drawerOpen, close: !drawerOpen }"
266
- >
267
- <el-row>
268
- <el-popover
269
- v-model="hoverVisibilities[8].value"
270
- content="Open new map"
271
- placement="right"
272
- :append-to-body="false"
273
- trigger="manual"
274
- popper-class="scaffold-popper right-popper non-selectable"
275
- >
276
- <map-svg-icon
277
- v-if="enableOpenMapUI && openMapOptions.length > 0"
278
- slot="reference"
279
- v-popover:open-map-popover
280
- icon="openMap"
281
- class="icon-button"
282
- @mouseover.native="showHelpText(8)"
283
- @mouseout.native="hideHelpText(8)"
284
- />
285
- </el-popover>
286
- </el-row>
287
- <el-row>
288
- <el-popover
289
- v-model="hoverVisibilities[3].value"
290
- content="Change background color"
291
- placement="right"
292
- :append-to-body="false"
293
- trigger="manual"
294
- popper-class="scaffold-popper right-popper non-selectable"
295
- >
296
- <map-svg-icon
297
- slot="reference"
298
- v-popover:backgroundPopover
299
- icon="changeBckgd"
300
- class="icon-button"
301
- @mouseover.native="showHelpText(3)"
302
- @mouseout.native="hideHelpText(3)"
303
- />
304
- </el-popover>
305
- </el-row>
306
- </div>
307
- </div>
308
- </div>
309
- </template>
310
-
311
- <script>
312
- /* eslint-disable no-alert, no-console */
313
- import Vue from "vue";
314
- import OpacityControls from "./OpacityControls";
315
- import ScaffoldTooltip from "./ScaffoldTooltip";
316
- import TreeControls from "./TreeControls";
317
- import { MapSvgIcon, MapSvgSpriteColor } from "@abi-software/svg-sprite";
318
- import { findObjectsWithNames, getAllObjects } from "../scripts/utilities.js";
319
- import { SearchIndex } from "../scripts/search.js";
320
- import {
321
- Button,
322
- Col,
323
- Loading,
324
- Option,
325
- Popover,
326
- Row,
327
- Select,
328
- Slider,
329
- TabPane,
330
- Tabs,
331
- } from "element-ui";
332
- import lang from "element-ui/lib/locale/lang/en";
333
- import locale from "element-ui/lib/locale";
334
-
335
- locale.use(lang);
336
- Vue.use(Button);
337
- Vue.use(Col);
338
- Vue.use(Loading.directive);
339
- Vue.use(Option);
340
- Vue.use(Popover);
341
- Vue.use(Row);
342
- Vue.use(Select);
343
- Vue.use(Slider);
344
- Vue.use(TabPane);
345
- Vue.use(Tabs);
346
-
347
- const OrgansViewer = require("../scripts/organsRenderer").OrgansViewer;
348
- const EventNotifier = require("../scripts/eventNotifier").EventNotifier;
349
-
350
- /**
351
- * A vue component of the scaffold viewer.
352
- *
353
- * @requires ./OpacityControls.vue
354
- * @requires ./TreeControls.vue
355
- */
356
- export default {
357
- name: "ScaffoldVuer",
358
- components: {
359
- MapSvgIcon,
360
- MapSvgSpriteColor,
361
- OpacityControls,
362
- ScaffoldTooltip,
363
- TreeControls,
364
- },
365
- props: {
366
- /**
367
- * URL of the zincjs metadata. This value will be ignored if a valid
368
- * state prop is also provided.
369
- * If the url needs to be updated with state present, please use
370
- * the setURL method.
371
- */
372
- url: {
373
- type: String,
374
- default: "",
375
- },
376
- /**
377
- * Show the colour control of set to true.
378
- */
379
- showColourPicker: {
380
- type: Boolean,
381
- default: false,
382
- },
383
- /**
384
- * Flag to show/hide the UI.
385
- */
386
- displayUI: {
387
- type: Boolean,
388
- default: true,
389
- },
390
- /**
391
- * Display all graphics at start.
392
- *
393
- * This setting only works when traditional is set to false.
394
- */
395
- displayAtStartUp: {
396
- type: Boolean,
397
- default: true,
398
- },
399
- /**
400
- * Use for toggling the help tooltips.
401
- */
402
- helpMode: {
403
- type: Boolean,
404
- default: false,
405
- },
406
- /**
407
- * Use for show/display beta warning icon.
408
- */
409
- displayWarning: {
410
- type: Boolean,
411
- default: true,
412
- },
413
- /**
414
- * Warning message for the hovered over text
415
- * on the warning icon.
416
- */
417
- warningMessage: {
418
- type: String,
419
- default: "Beta feature - under active development",
420
- },
421
- displayLatestChanges: {
422
- type: Boolean,
423
- default: false,
424
- },
425
- latestChangesMessage: {
426
- type: String,
427
- default: "New feature - Local search is now available",
428
- },
429
- /**
430
- * Show/hide pickable markers for regions.
431
- */
432
- displayMarkers: {
433
- type: Boolean,
434
- default: false,
435
- },
436
- /**
437
- * Show/hide minimap.
438
- */
439
- displayMinimap: {
440
- type: Boolean,
441
- default: false,
442
- },
443
- /**
444
- * Format of the input URL
445
- */
446
- format: {
447
- type: String,
448
- default: "metadata",
449
- },
450
- /**
451
- * Settings for minimap position, size and alignment.
452
- */
453
- minimapSettings: {
454
- type: Object,
455
- default: function () {
456
- return {
457
- x_offset: 16,
458
- y_offset: 16,
459
- width: 128,
460
- height: 128,
461
- align: "top-right",
462
- };
463
- },
464
- },
465
- /**
466
- * Flag to determine rather open map UI should be
467
- * presented or not.
468
- */
469
- enableOpenMapUI: {
470
- type: Boolean,
471
- default: false,
472
- },
473
- openMapOptions: {
474
- type: Array,
475
- default: function () {
476
- return [
477
- {
478
- display: "Open AC Map",
479
- key: "AC"
480
- },
481
- {
482
- display: "Open FC Map",
483
- key: "FC"
484
- },
485
- {
486
- display: "Open 3D Human Map",
487
- key: "3D"
488
- },
489
- ]
490
- },
491
- },
492
- /**
493
- * State containing state of the scaffold.
494
- */
495
- state: {
496
- type: Object,
497
- default: undefined,
498
- },
499
- /**
500
- * Optional prop for the name of the region to focus on,
501
- * this option is ignored if state or viewURL is also provided.
502
- */
503
- region: {
504
- type: String,
505
- default: "",
506
- },
507
- /**
508
- * Optional prop for an URL of containing information of a viewport.
509
- * This option is ignored if state is also provided.
510
- * It will use the provided URL as base if a relative parth is provided.
511
- */
512
- viewURL: {
513
- type: String,
514
- default: "",
515
- },
516
- /**
517
- * Settings for turning on/off rendering
518
- */
519
- render: {
520
- type: Boolean,
521
- default: true,
522
- },
523
- },
524
- data: function () {
525
- return {
526
- sceneData: this.$module.sceneData,
527
- isPlaying: false,
528
- isReady: false,
529
- /**
530
- * This is set when scene is transitioning.
531
- */
532
- isTransitioning: false,
533
- tooltipAppendToBody: false,
534
- hoverVisibilities: [
535
- { value: false },
536
- { value: false },
537
- { value: false },
538
- { value: false },
539
- { value: false },
540
- { value: false },
541
- { value: false },
542
- { value: false },
543
- { value: false },
544
- ],
545
- inHelp: false,
546
- loading: false,
547
- duration: 3000,
548
- drawerOpen: true,
549
- currentBackground: "white",
550
- availableBackground: ["white", "lightskyblue", "black"],
551
- minimisedSlider: false,
552
- sliderPosition: "",
553
- timeMax: 100,
554
- orginalDuration: "",
555
- animateDuration: "6secs",
556
- playSpeed: [
557
- {
558
- value: 0.1,
559
- label: "0.1x",
560
- },
561
- {
562
- value: 0.5,
563
- label: "0.5x",
564
- },
565
- {
566
- value: 1,
567
- label: "1x",
568
- },
569
- {
570
- value: 2,
571
- label: "2x",
572
- },
573
- {
574
- value: 5,
575
- label: "5x",
576
- },
577
- {
578
- value: 10,
579
- label: "10x",
580
- },
581
- ],
582
- currentSpeed: 1,
583
- timeStamps: {},
584
- defaultCheckedKeys: [],
585
- tData: {
586
- label: "",
587
- region: "",
588
- visible: false,
589
- x: 200,
590
- y: 200,
591
- },
592
- fileFormat: "metadata",
593
- };
594
- },
595
- watch: {
596
- format: {
597
- handler: function (value) {
598
- this.fileFormat = value;
599
- },
600
- immediate: true,
601
- },
602
- url: {
603
- handler: function (newValue) {
604
- if (this.state === undefined || this.state.url === undefined)
605
- this.setURL(newValue);
606
- },
607
- immediate: true,
608
- },
609
- region: {
610
- handler: function (region) {
611
- if (!(this.state || this.viewURL)) this.setFocusedRegion(region);
612
- },
613
- immediate: true,
614
- },
615
- state: {
616
- handler: function (state) {
617
- this.setState(state);
618
- },
619
- immediate: true,
620
- deep: true,
621
- },
622
- viewURL: {
623
- handler: function (viewURL) {
624
- this.updateViewURL(viewURL);
625
- },
626
- immediate: true,
627
- },
628
- helpMode: function (val) {
629
- this.setHelpMode(val);
630
- },
631
- displayMarkers: function (val) {
632
- this.$module.scene.displayMarkers = val;
633
- //Update pickable objects
634
- this.$module.scene.forcePickableObjectsUpdate = true;
635
- },
636
- displayMinimap: function (val) {
637
- this.$module.scene.displayMinimap = val;
638
- },
639
- "sceneData.currentTime": function () {
640
- /**
641
- * Triggers when scene time changes.
642
- *
643
- * @property {number} time Current build-in time of scene.
644
- * of selected object.
645
- */
646
- this.$emit("timeChanged", this.sceneData.currentTime);
647
- },
648
- duration: function () {
649
- this.$module.scene.setDuration(this.duration);
650
- },
651
- minimapSettings: {
652
- deep: true,
653
- handler: "updateMinimapScissor",
654
- },
655
- render: function (val) {
656
- this.toggleRendering(val);
657
- },
658
- },
659
- beforeCreate: function () {
660
- this.$module = new OrgansViewer();
661
- this.selectedObjects = [];
662
- this.hoveredObjects = [];
663
- this.currentBackground = "white";
664
- this._currentURL = undefined;
665
- this.availableBackground = ["white", "black", "lightskyblue"];
666
- this.$_searchIndex = new SearchIndex();
667
- this.$_tempId = 1;
668
- },
669
- mounted: function () {
670
- this.$refs.treeControls.setModule(this.$module);
671
- let eventNotifier = new EventNotifier();
672
- eventNotifier.subscribe(this, this.eventNotifierCallback);
673
- this.$module.addNotifier(eventNotifier);
674
- this.$module.addOrganPartAddedCallback(this.zincObjectAdded);
675
- this.$module.initialiseRenderer(this.$refs.display);
676
- this.toggleRendering(this.render);
677
- this.ro = new ResizeObserver(this.adjustLayout).observe(
678
- this.$refs.scaffoldContainer
679
- );
680
- this.defaultRate = this.$module.getPlayRate();
681
- },
682
- beforeDestroy: function () {
683
- if (this.ro) this.ro.disconnect();
684
- this.$module.destroy();
685
- this.$module = undefined;
686
- },
687
- methods: {
688
- /**
689
- * This is called when a new zinc object is read into the scene.
690
- */
691
- zincObjectAdded: function (zincObject) {
692
- this.loading = false;
693
- zincObject.searchIndexId = ++this.$_tempId;
694
- this.$_searchIndex.addZincObject(zincObject, zincObject.searchIndexId);
695
- this.$emit("zinc-object-added", zincObject);
696
- },
697
- /**
698
- * This is called when Change backgspeedround colour button
699
- * is pressed an causes the backgrouColornd colour to be changed
700
- * to one of the three preset colour: white, black and
701
- * lightskyblue.
702
- */
703
- backgroundChangeCallback: function (colour) {
704
- this.currentBackground = colour;
705
- this.$module.zincRenderer
706
- .getThreeJSRenderer()
707
- .setClearColor(this.currentBackground, 1);
708
- },
709
- /**
710
- * This is called by captueeScreenshot and after the last render
711
- * loop, it download a screenshot of the current scene with no UI.
712
- */
713
- captureScreenshotCallback: function () {
714
- //Remove the callback, only needs to happen once
715
- this.$module.zincRenderer.removePostRenderCallbackFunction(
716
- this.captureID
717
- );
718
- let screenshot = this.$module.zincRenderer
719
- .getThreeJSRenderer()
720
- .domElement.toDataURL("image/png");
721
- let hrefElement = document.createElement("a");
722
- document.body.append(hrefElement);
723
- if (!this.captureFilename) hrefElement.download = `screenshot.png`;
724
- else hrefElement.download = this.captureFilename;
725
- hrefElement.href = screenshot;
726
- hrefElement.click();
727
- hrefElement.remove();
728
- },
729
- /**
730
- * Function for capturing a screenshot of the current rendering.
731
- *
732
- * @param {String} filename filename given to the screenshot.
733
- *
734
- * @public
735
- */
736
- captureScreenshot: function (filename) {
737
- this.captureFilename = filename;
738
- this.captureID = this.$module.zincRenderer.addPostRenderCallbackFunction(
739
- this.captureScreenshotCallback
740
- );
741
- },
742
- formatTooltip(val) {
743
- if (this.timeMax >= 1000) {
744
- if (val) {
745
- let sec = ((val % 60000) / 1000).toFixed(2) + "s";
746
- let min = val > 60000 ? (val / 60000).toFixed(0) + "m " : "";
747
- return min + sec;
748
- }
749
- }
750
- return val ? val.toFixed(2) + " ms" : "0 ms";
751
- },
752
- /**
753
- * Function to reset the view to default.
754
- * Also called when the associated button is pressed.
755
- *
756
- * @public
757
- */
758
- fitWindow: function () {
759
- if (this.$module.scene) {
760
- this.$module.scene.viewAll();
761
- }
762
- },
763
- /**
764
- * Function to zoom in.
765
- * Also called when the associated button is pressed.
766
- *
767
- * @public
768
- */
769
- zoomIn: function () {
770
- if (this.$module.scene) {
771
- this.$module.scene.changeZoomByScrollRateUnit(-1);
772
- }
773
- },
774
- /**
775
- * Function to zoom out.
776
- * Also called when the associated button is pressed.
777
- *
778
- * @public
779
- */
780
- zoomOut: function () {
781
- if (this.$module.scene) {
782
- this.$module.scene.changeZoomByScrollRateUnit(1);
783
- }
784
- },
785
- /**
786
- * Function to change the current play speed.
787
- *
788
- * @public
789
- */
790
- speedChanged: function (speed) {
791
- this.currentSpeed = speed;
792
- this.$module.setPlayRate(this.defaultRate * this.currentSpeed);
793
- },
794
- /**
795
- * Function used to stop the free spin
796
- *
797
- * @public
798
- */
799
- stopFreeSpin: function () {
800
- let cameracontrol = this.$module.scene.getZincCameraControls();
801
- cameracontrol.stopAutoTumble();
802
- this.isTransitioning = false;
803
- },
804
- findObjectsWithGroupName: function (name) {
805
- let objects = [];
806
- if (name && name != "" && this.$module.scene) {
807
- objects = this.$module.scene.findObjectsWithGroupName(name);
808
- }
809
- return objects;
810
- },
811
- /**
812
- * Focus on named region
813
- */
814
- viewRegion: function (names) {
815
- const rootRegion = this.$module.scene.getRootRegion();
816
- const groups = Array.isArray(names) ? names : [names];
817
- const objects = findObjectsWithNames(rootRegion, groups, "", true);
818
- let box = this.$module.scene.getBoundingBoxOfZincObjects(objects);
819
- if (box) {
820
- if (this.$module.isSyncControl()) {
821
- this.$module.setSyncControlZoomToBox(box);
822
- } else {
823
- const dist =
824
- this.$module.scene.camera.far - this.$module.scene.camera.near;
825
- this.$module.scene.viewAllWithBoundingBox(box);
826
- this.$module.scene.camera.far = this.$module.scene.camera.near + dist;
827
- this.$module.scene.camera.updateProjectionMatrix();
828
- }
829
- return true;
830
- }
831
- return false;
832
- },
833
- setFocusedRegion: function (name) {
834
- if (name) {
835
- if (this.isReady) {
836
- this.viewRegion(name);
837
- } else {
838
- this.$module.setFinishDownloadCallback(
839
- this.setURLFinishCallback({ region: name })
840
- );
841
- }
842
- }
843
- },
844
- updateViewURL: function (viewURL) {
845
- if (viewURL) {
846
- if (this.isReady) {
847
- const url = new URL(viewURL, this.url);
848
- this.$module.scene.loadViewURL(url);
849
- } else {
850
- this.$module.setFinishDownloadCallback(
851
- this.setURLFinishCallback({ viewURL: viewURL })
852
- );
853
- }
854
- }
855
- },
856
- getRendererInfo: function () {
857
- if (this.$module.zincRenderer) {
858
- return this.$module.zincRenderer.getThreeJSRenderer().info;
859
- }
860
- return undefined;
861
- },
862
- /**
863
- * Function used to rotate the scene.
864
- * Also called when the associated button is pressed.
865
- *
866
- * @public
867
- */
868
- freeSpin: function () {
869
- if (this.$module.scene) {
870
- let cameracontrol = this.$module.scene.getZincCameraControls();
871
- this.isTransitioning = true;
872
- cameracontrol.enableAutoTumble();
873
- cameracontrol.autoTumble([1.0, 0.0], Math.PI, true);
874
- setTimeout(this.stopFreeSpin, 4000);
875
- }
876
- },
877
- /**
878
- * Callback when a region is selected/highlighted.
879
- * It will also update other controls.
880
- *
881
- */
882
- eventNotifierCallback: function (event) {
883
- const names = [];
884
- let zincObjects = [];
885
- const region = undefined;
886
- if (event.eventType == 1 || event.eventType == 2) {
887
- event.identifiers.forEach((identifier) => {
888
- if (identifier) {
889
- let id = identifier.data.id
890
- ? identifier.data.id
891
- : identifier.data.group;
892
- names.push(id);
893
- }
894
- });
895
- zincObjects = event.zincObjects;
896
- }
897
- /*
898
- * Event Type 1: Selected
899
- * Event Type 2: Highlighted
900
- * Event Type 1: Move
901
- */
902
- if (event.eventType == 1) {
903
- if (this.$refs.treeControls) {
904
- if (names.length > 0) {
905
- //this.$refs.treeControls.changeActiveByNames(names, region, false);
906
- this.$refs.treeControls.updateActiveUI(zincObjects);
907
- } else {
908
- this.hideRegionTooltip();
909
- this.$refs.treeControls.removeActive(true);
910
- }
911
- }
912
- // Triggers when an object has been selected
913
- this.$emit("scaffold-selected", event.identifiers);
914
- } else if (event.eventType == 2) {
915
- if (this.selectedObjects.length === 0) {
916
- this.hideRegionTooltip();
917
- // const offsets = this.$refs.scaffoldContainer.getBoundingClientRect();
918
- if (this.$refs.treeControls) {
919
- if (names.length > 0) {
920
- //this.$refs.treeControls.changeHoverByNames(names, region, false);
921
- this.$refs.treeControls.updateHoverUI(zincObjects);
922
- } else {
923
- this.$refs.treeControls.removeHover(true);
924
- }
925
- }
926
- if (event.identifiers.length > 0 && event.identifiers[0]) {
927
- let id = event.identifiers[0].data.id
928
- ? event.identifiers[0].data.id
929
- : event.identifiers[0].data.group;
930
- if (event.identifiers[0].coords) {
931
- this.tData.visible = true;
932
- this.tData.label = id;
933
- if (event.identifiers[0].data.region)
934
- this.tData.region = event.identifiers[0].data.region;
935
- else this.tData.region = "Root";
936
- this.tData.x = event.identifiers[0].coords.x;
937
- this.tData.y = event.identifiers[0].coords.y;
938
- }
939
- }
940
- // Triggers when an object has been highlighted
941
- this.$emit("scaffold-highlighted", event.identifiers);
942
- }
943
- } else if (event.eventType == 3) {
944
- //MOVE
945
- if (event.identifiers.length > 0 && event.identifiers[0]) {
946
- if (event.identifiers[0].coords) {
947
- const offsets =
948
- this.$refs.scaffoldContainer.getBoundingClientRect();
949
- this.tData.x = event.identifiers[0].coords.x - offsets.left;
950
- this.tData.y = event.identifiers[0].coords.y - offsets.top;
951
- }
952
- }
953
- }
954
- },
955
- /**
956
- * Get the coordinates of the current selected region.
957
- *
958
- * @public
959
- */
960
- getCoordinatesOfSelected: function () {
961
- if (this.selectedObjects && this.selectedObjects.length > 0) {
962
- return this.$module.scene.getObjectsScreenXY(this.selectedObjects);
963
- }
964
- return undefined;
965
- },
966
- /**
967
- * Return an object containing the window coordinates of the
968
- * current selected region which will be updated after each render
969
- * loop.
970
- *
971
- * @public
972
- */
973
- getDynamicSelectedCoordinates: function () {
974
- return this.$module.selectedScreenCoordinates;
975
- },
976
- /**
977
- * Callback when time is changed through the UI.
978
- */
979
- timeChange: function (event) {
980
- let normalizedTime = (event / this.timeMax) * 100;
981
- if (normalizedTime != this.sceneData.currentTime)
982
- this.$module.updateTime(normalizedTime);
983
- },
984
- /**
985
- * A callback used by children components. Set the selected zinc object
986
- *
987
- * @param {object} object Zinc object
988
- */
989
- objectSelected: function (objects, propagate) {
990
- this.selectedObjects = objects;
991
- if (this.selectedObjects && this.selectedObjects.length > 0)
992
- this.$refs.opacityControl.setObject(this.selectedObjects[0]);
993
- this.$module.setSelectedByZincObjects(objects, undefined, propagate);
994
- },
995
- /**
996
- * A callback used by children components. Set the highlighted zinc object
997
- *
998
- * @param {object} object Zinc object
999
- */
1000
- objectHovered: function (objects, propagate) {
1001
- this.hoveredObjects = objects;
1002
- this.$module.setHighlightedByZincObjects(objects, undefined, propagate);
1003
- },
1004
- /**
1005
- * Set the selected by name.
1006
- *
1007
- * @param {} name Name of the group
1008
- */
1009
- changeActiveByName: function (names, region, propagate) {
1010
- const isArray = Array.isArray(names);
1011
- if (names === undefined || (isArray && names.length === 0)) {
1012
- this.$refs.treeControls.removeActive(propagate);
1013
- } else {
1014
- let array = names;
1015
- if (!isArray) array = [array];
1016
- this.$refs.treeControls.changeActiveByNames(array, region, propagate);
1017
- }
1018
- },
1019
- /**
1020
- * Set the highlighted by name.
1021
- *
1022
- * @param {name} name Name of the group
1023
- */
1024
- changeHighlightedByName: function (names, region, propagate) {
1025
- const isArray = Array.isArray(names);
1026
- if (names === undefined || (isArray && names.length === 0)) {
1027
- this.$refs.treeControls.removeHover(propagate);
1028
- } else {
1029
- let array = names;
1030
- if (!isArray) array = [array];
1031
- this.$refs.treeControls.changeHoverByNames(array, region, propagate);
1032
- }
1033
- },
1034
- /**
1035
- * Start the animation.
1036
- *
1037
- * @param {object} object Zinc object
1038
- */
1039
- play: function (flag) {
1040
- this.$module.playAnimation(flag);
1041
- this.isPlaying = flag;
1042
- //Hide tooltip as location may
1043
- //this.hideRegionTooltip();
1044
- },
1045
- /**
1046
- * Function to toggle on/off overlay help.
1047
- */
1048
- setHelpMode: function (helpMode) {
1049
- if (helpMode) {
1050
- this.inHelp = true;
1051
- this.hoverVisibilities.forEach((item) => {
1052
- item.value = true;
1053
- });
1054
- } else {
1055
- this.inHelp = false;
1056
- this.hoverVisibilities.forEach((item) => {
1057
- item.value = false;
1058
- });
1059
- }
1060
- },
1061
- /**
1062
- * Callback function used by showRegionTooltip in the case when the tooltip
1063
- * is out of view.
1064
- */
1065
- displayTooltipOfObjectsCallback: function (
1066
- name,
1067
- objects,
1068
- resetView,
1069
- liveUpdates
1070
- ) {
1071
- const instance = this;
1072
- return function () {
1073
- instance.$module.zincRenderer.removePostRenderCallbackFunction(
1074
- instance.$_regionTooltipCallback
1075
- );
1076
- instance.$_regionTooltipCallback = undefined;
1077
- instance.displayTooltipOfObjects(name, objects, resetView, liveUpdates);
1078
- };
1079
- },
1080
- liveUpdateTooltipPosition: function () {
1081
- if (this.$module.selectedCenter) {
1082
- this.tData.x = this.$module.selectedScreenCoordinates.x;
1083
- this.tData.y = this.$module.selectedScreenCoordinates.y;
1084
- }
1085
- },
1086
- displayTooltipOfObjects: function (name, objects, resetView, liveUpdates) {
1087
- if (objects.length > 0) {
1088
- let coords = objects[0].getClosestVertexDOMElementCoords(
1089
- this.$module.scene
1090
- );
1091
- if (coords) {
1092
- //The coords is not in view, view all if resetView flag is true
1093
- if (!coords.inView) {
1094
- this.hideRegionTooltip();
1095
- if (resetView) {
1096
- this.$module.scene.viewAll();
1097
- //Use the post render callback to make sure the scene has been updated
1098
- //before getting the position of the tooltip.
1099
- if (this.$_regionTooltipCallback) {
1100
- this.$module.zincRenderer.removePostRenderCallbackFunction(
1101
- this.$_regionTooltipCallback
1102
- );
1103
- }
1104
- this.$_regionTooltipCallback =
1105
- this.$module.zincRenderer.addPostRenderCallbackFunction(
1106
- this.displayTooltipOfObjectsCallback(
1107
- name,
1108
- objects,
1109
- resetView,
1110
- liveUpdates
1111
- )
1112
- );
1113
- }
1114
- } else {
1115
- this.tData.visible = true;
1116
- this.tData.label = name;
1117
- this.tData.x = coords.position.x;
1118
- this.tData.y = coords.position.y;
1119
- const regionPath = objects[0].getRegion().getFullPath();
1120
- if (regionPath) this.tData.region = regionPath;
1121
- else this.tData.region = "Root";
1122
- if (liveUpdates) {
1123
- this.$module.setupLiveCoordinates(objects);
1124
- if (this.$_liveCoordinatesUpdated) {
1125
- this.$module.zincRenderer.removePostRenderCallbackFunction(
1126
- this.$_liveCoordinatesUpdated
1127
- );
1128
- }
1129
- this.$_liveCoordinatesUpdated =
1130
- this.$module.zincRenderer.addPostRenderCallbackFunction(
1131
- this.liveUpdateTooltipPosition
1132
- );
1133
- }
1134
- }
1135
- return true;
1136
- }
1137
- }
1138
- this.hideRegionTooltip();
1139
- return false;
1140
- },
1141
- /**
1142
- * Display the tooltip. When resetView is set to true, it will
1143
- * reset view if the tooltip is not in view.
1144
- * Setting liveUpdates to true will update the tooltip location
1145
- * at every rendering loop.
1146
- */
1147
- showRegionTooltip: function (name, resetView, liveUpdates) {
1148
- if (name && this.$module.scene) {
1149
- const rootRegion = this.$module.scene.getRootRegion();
1150
- const groups = [name];
1151
- const objects = findObjectsWithNames(rootRegion, groups, "", true);
1152
- return this.displayTooltipOfObjects(
1153
- name,
1154
- objects,
1155
- resetView,
1156
- liveUpdates
1157
- );
1158
- }
1159
- this.hideRegionTooltip();
1160
- return false;
1161
- },
1162
- hideRegionTooltip: function () {
1163
- if (this.$_liveCoordinatesUpdated) {
1164
- this.$module.zincRenderer.removePostRenderCallbackFunction(
1165
- this.$_liveCoordinatesUpdated
1166
- );
1167
- //Unset the tracking
1168
- this.$module.setupLiveCoordinates(undefined);
1169
- }
1170
- this.tData.visible = false;
1171
- this.tData.region = "Root";
1172
- },
1173
- /**
1174
- * This is called when mouse cursor enters supported elements
1175
- * with help tootltips.
1176
- */
1177
- showHelpText: function (helpTextNumber) {
1178
- if (!this.inHelp) {
1179
- this.helpTextWait = setTimeout(() => {
1180
- this.hoverVisibilities[helpTextNumber].value = true;
1181
- }, 500);
1182
- }
1183
- },
1184
- /**
1185
- * This is called when mouse cursor exits supported element..
1186
- */
1187
- hideHelpText: function (helpTextNumber) {
1188
- if (!this.inHelp) {
1189
- this.hoverVisibilities[helpTextNumber].value = false;
1190
- clearTimeout(this.helpTextWait);
1191
- }
1192
- },
1193
- search: function (text, displayLabel) {
1194
- if (this.$_searchIndex) {
1195
- if (text === undefined || text === "" ||
1196
- ((Array.isArray(text) && text.length === 0))
1197
- ) {
1198
- this.objectSelected([], true);
1199
- return false;
1200
- } else {
1201
- let zincObjectResults = [];
1202
- if (Array.isArray(text)) {
1203
- //zincObjectResults = this.$_searchIndex.search("Heart");
1204
- zincObjectResults = this.$_searchIndex.searchTerms(text);
1205
- } else {
1206
- zincObjectResults = this.$_searchIndex.search(text);
1207
- }
1208
- if (zincObjectResults.length > 0) {
1209
- this.objectSelected(zincObjectResults, true);
1210
- if (displayLabel) {
1211
- for (let i = 0; i < zincObjectResults.length; i++) {
1212
- if (zincObjectResults[i] && zincObjectResults[i].groupName) {
1213
- this.showRegionTooltip(
1214
- zincObjectResults[i].groupName,
1215
- true,
1216
- true
1217
- );
1218
- }
1219
- }
1220
- }
1221
- return true;
1222
- } else {
1223
- this.objectSelected([], true);
1224
- }
1225
- }
1226
- }
1227
- return false;
1228
- },
1229
- /**
1230
- * Get the list of suggested terms
1231
- */
1232
- fetchSuggestions: function (term) {
1233
- if (this.$_searchIndex === undefined) return [];
1234
- return this.$_searchIndex.auto_suggest(term);
1235
- },
1236
- /**
1237
- * Called when minimap settings has changed. Pass the
1238
- * parameters to ZincJS and marked it for update.
1239
- */
1240
- updateMinimapScissor: function () {
1241
- Object.keys(this.minimapSettings).forEach((key) => {
1242
- this.$module.scene.minimapScissor[key] = this.minimapSettings[key];
1243
- });
1244
- this.$module.scene.minimapScissor.updateRequired = true;
1245
- },
1246
- updateSettingsfromScene: function () {
1247
- this.currentSpeed = 1;
1248
- this.$module.setPlayRate(this.defaultRate);
1249
- this.orginalDuration =
1250
- this.$module.scene.getMetadataTag("OriginalDuration");
1251
- this.animateDuration = this.$module.scene.getMetadataTag("Duration");
1252
- let timeStamps = this.$module.scene.getMetadataTag("TimeStamps");
1253
- this.timeStamps = {};
1254
- for (const key in timeStamps) {
1255
- this.timeStamps[timeStamps[key]] = key;
1256
- }
1257
- this.timeMax = this.$module.scene.getDuration();
1258
- },
1259
- setURLFinishCallback: function (options) {
1260
- return () => {
1261
- if (options) {
1262
- if (options.viewport) {
1263
- this.$module.scene
1264
- .getZincCameraControls()
1265
- .setCurrentCameraSettings(options.viewport);
1266
- } else if (options.viewURL && options.viewURL !== "") {
1267
- const url = new URL(options.viewURL, this.url);
1268
- this.$module.scene.loadViewURL(url);
1269
- } else if (options.region && options.region !== "") {
1270
- this.viewRegion(options.region);
1271
- }
1272
- if (options.visibility) {
1273
- // Some UIs may not be ready at this time.
1274
- this.$nextTick(() => {
1275
- this.$refs.treeControls.setState(options.visibility);
1276
- });
1277
- }
1278
- }
1279
- this.updateSettingsfromScene();
1280
- this.$module.updateTime(0.01);
1281
- this.$module.updateTime(0);
1282
- this.$module.unsetFinishDownloadCallback();
1283
- this.$emit("on-ready");
1284
- this.isReady = true;
1285
- };
1286
- },
1287
- /**
1288
- * Function used for getting the current states of the scene. This exported states
1289
- * can be imported using the importStates method.
1290
- *
1291
- * @public
1292
- */
1293
- getState: function () {
1294
- let state = {
1295
- format: this.fileFormat,
1296
- url: this._currentURL,
1297
- viewport: undefined,
1298
- visibility: undefined,
1299
- };
1300
- if (this.$refs.treeControls)
1301
- state.visibility = this.$refs.treeControls.getState();
1302
- if (this.$module.scene) {
1303
- let zincCameraControls = this.$module.scene.getZincCameraControls();
1304
- state.viewport = zincCameraControls.getCurrentViewport();
1305
- }
1306
- return state;
1307
- },
1308
- /**
1309
- * Function used for importing the states of the scene. This exported states
1310
- * can be imported using the read states method.
1311
- *
1312
- * @public
1313
- */
1314
- setState: function (state) {
1315
- if (state) {
1316
- if (state.url && state.url !== this._currentURL) {
1317
- this.setURLAndState(state.url, {
1318
- fileFormat: state.fileFormat,
1319
- viewport: state.viewport,
1320
- visibility: state.visibility,
1321
- });
1322
- } else {
1323
- if (state.viewport || state.visibility) {
1324
- if (this.isReady && this.$module.scene) {
1325
- if (state.viewport)
1326
- this.$module.scene
1327
- .getZincCameraControls()
1328
- .setCurrentCameraSettings(state.viewport);
1329
- if (state.visibility)
1330
- this.$refs.treeControls.setState(state.visibility);
1331
- } else {
1332
- this.$module.setFinishDownloadCallback(
1333
- this.setURLFinishCallback({
1334
- viewport: state.viewport,
1335
- visibility: state.visibility,
1336
- })
1337
- );
1338
- }
1339
- }
1340
- }
1341
- }
1342
- },
1343
- exportGLTF: function (binary) {
1344
- return this.$module.scene.exportGLTF(binary);
1345
- },
1346
- /**
1347
- * Function used for reading in new scaffold metadata and a custom
1348
- * viewport. This function will ignore the state prop and
1349
- * read in the new url.
1350
- *
1351
- * @public
1352
- */
1353
- setURLAndState: function (newValue, state) {
1354
- if (newValue != this._currentURL) {
1355
- if (state && state.format) this.fileFormat = state.format;
1356
- let viewport = state && state.viewport ? state.viewport : undefined;
1357
- let visibility =
1358
- state && state.visibility ? state.visibility : undefined;
1359
- this._currentURL = newValue;
1360
- if (this.$refs.treeControls) this.$refs.treeControls.clear();
1361
- this.loading = true;
1362
- this.isReady = false;
1363
- this.$module.setFinishDownloadCallback(
1364
- this.setURLFinishCallback({
1365
- viewport: viewport,
1366
- region: this.region,
1367
- viewURL: this.viewURL,
1368
- visibility: visibility,
1369
- })
1370
- );
1371
- if (this.fileFormat === "gltf") {
1372
- this.$module.loadGLTFFromURL(newValue, "scene", true);
1373
- } else {
1374
- this.$module.loadOrgansFromURL(
1375
- newValue,
1376
- undefined,
1377
- undefined,
1378
- "scene",
1379
- undefined,
1380
- true
1381
- );
1382
- }
1383
- this.$_searchIndex.removeAll();
1384
- this.$_tempId = 1;
1385
- this.hideRegionTooltip();
1386
- this.$module.scene.displayMarkers = this.displayMarkers;
1387
- this.$module.scene.forcePickableObjectsUpdate = true;
1388
- this.$module.scene.displayMinimap = this.displayMinimap;
1389
- this.updateMinimapScissor();
1390
- }
1391
- },
1392
- /**
1393
- * Function used for reading in new scaffold metadata. This function will ignore
1394
- * the state prop and read in the new url.
1395
- *
1396
- * @public
1397
- */
1398
- setURL: function (newValue) {
1399
- this.setURLAndState(newValue, undefined);
1400
- },
1401
- /**
1402
- * Callback when drawer is toggled.
1403
- */
1404
- drawerToggled: function (flag) {
1405
- this.drawerOpen = flag;
1406
- this.adjustLayout();
1407
- },
1408
- /**
1409
- * Callback using ResizeObserver.
1410
- */
1411
- adjustLayout: function () {
1412
- let width = this.$refs.scaffoldContainer.clientWidth;
1413
- this.minimisedSlider = width < 812;
1414
- if (this.minimisedSlider) {
1415
- this.sliderPosition = this.drawerOpen ? "right" : "left";
1416
- } else {
1417
- this.sliderPosition = "";
1418
- }
1419
- },
1420
- toggleRendering: function (flag) {
1421
- if (this.$module.zincRenderer) {
1422
- if (flag) {
1423
- this.$module.zincRenderer.animate();
1424
- } else {
1425
- this.$module.zincRenderer.stopAnimate();
1426
- }
1427
- }
1428
- },
1429
- forceResize: function () {
1430
- if (this.$module.zincRenderer) {
1431
- this.$module.zincRenderer.onWindowResize();
1432
- }
1433
- },
1434
- syncControlCallback: function () {
1435
- const payload = this.$module.NDCCameraControl.getPanZoom();
1436
- if (this.tData.visible) {
1437
- this.showRegionTooltip(this.tData.label);
1438
- }
1439
- this.$emit("scaffold-navigated", payload);
1440
- },
1441
- /**
1442
- * Rotate mode - "none", "horizontal", "vertical", "free" but
1443
- * it will be ignored if flag is set to false.
1444
- */
1445
- toggleSyncControl: function (flag, rotateMode) {
1446
- this.$module.toggleSyncControl(flag, rotateMode);
1447
- this.$module.setSyncControlCallback(this.syncControlCallback);
1448
- },
1449
- },
1450
- };
1451
- </script>
1452
-
1453
- <!-- Add "scoped" attribute to limit CSS to this component only -->
1454
- <style scoped lang="scss">
1455
- @import "~element-ui/packages/theme-chalk/src/button";
1456
- @import "~element-ui/packages/theme-chalk/src/col";
1457
- @import "~element-ui/packages/theme-chalk/src/loading";
1458
- @import "~element-ui/packages/theme-chalk/src/option";
1459
- @import "~element-ui/packages/theme-chalk/src/popover";
1460
- @import "~element-ui/packages/theme-chalk/src/row";
1461
- @import "~element-ui/packages/theme-chalk/src/select";
1462
- @import "~element-ui/packages/theme-chalk/src/slider";
1463
- @import "~element-ui/packages/theme-chalk/src/tabs";
1464
- @import "~element-ui/packages/theme-chalk/src/tab-pane";
1465
-
1466
- .message-icon {
1467
- position: absolute;
1468
- top: 15px;
1469
- left: 37px;
1470
- text-align: left;
1471
- font-size: 25px;
1472
- color: $warning;
1473
-
1474
- &:hover {
1475
- cursor: pointer;
1476
- }
1477
- }
1478
-
1479
- .warning-icon {
1480
- color: $warning;
1481
- top: 15px;
1482
- }
1483
-
1484
- .latest-changesicon {
1485
- color: $success;
1486
- top: 45px;
1487
- }
1488
-
1489
- .message-text {
1490
- font-size: 15px;
1491
- vertical-align: 5px;
1492
- }
1493
-
1494
- ::v-deep .message-popper {
1495
- padding: 9px 10px;
1496
- min-width: 150px;
1497
- font-size: 12px;
1498
- color: #fff;
1499
- }
1500
-
1501
- #organsDisplayArea {
1502
- &:focus {
1503
- outline: none !important;
1504
- border: 0px;
1505
- }
1506
- }
1507
-
1508
- .scaffold-container {
1509
- height: 100%;
1510
- width: 100%;
1511
- position: relative;
1512
- }
1513
-
1514
- .time-slider-container {
1515
- text-align: left;
1516
- position: absolute;
1517
- right: 155px;
1518
- width: calc(100% - 530px);
1519
- bottom: 16px;
1520
- transition: all 1s ease;
1521
- outline: none;
1522
-
1523
- &.minimised {
1524
- width: calc(40%);
1525
- }
1526
-
1527
- &.left {
1528
- right: 155px;
1529
- width: calc(100% - 250px);
1530
- }
1531
-
1532
- &.right {
1533
- right: 8px;
1534
- bottom: 54px;
1535
- }
1536
- }
1537
-
1538
- .slider-display-text {
1539
- height: 20px;
1540
- color: rgb(48, 49, 51);
1541
- font-size: 14px;
1542
- font-weight: normal;
1543
- line-height: 20px;
1544
- padding-left: 8px;
1545
- text-shadow: -1px -1px #fff, 1px -1px #fff, -1px 1px #fff, 1px -1px #fff;
1546
- }
1547
-
1548
- .tab-content {
1549
- display: flex;
1550
- height: 34px;
1551
- padding-top: 8px;
1552
- font-size: 14px;
1553
- }
1554
-
1555
- .tab-content ::v-deep .el-slider__marks-text {
1556
- margin-top: 12px;
1557
- margin-left: 8px;
1558
- font-size: 10px;
1559
- }
1560
-
1561
- .tab-content ::v-deep .el-slider__stop {
1562
- width: 10px;
1563
- height: 10px;
1564
- top: -1px;
1565
- border: solid 1px $app-primary-color;
1566
- }
1567
-
1568
- .animation-data {
1569
- margin-left: 8px;
1570
- line-height: 26px;
1571
- display: flex;
1572
-
1573
- :not(:first-child) {
1574
- margin-left: 8px;
1575
- }
1576
- .purple {
1577
- padding-left: 2px;
1578
- color: $app-primary-color;
1579
- }
1580
- }
1581
- .slider {
1582
- margin-left: 30px;
1583
- width: calc(100% - 88px);
1584
- margin-top: -7px;
1585
-
1586
- ::v-deep .el-slider__runway {
1587
- height: 10px;
1588
- margin: 14px 0;
1589
- }
1590
-
1591
- ::v-deep .el-slider__button-wrapper {
1592
- top: -13px;
1593
- }
1594
- }
1595
-
1596
- .zoomOut {
1597
- padding-left: 8px;
1598
- }
1599
-
1600
- .fitWindow {
1601
- padding-left: 8px;
1602
- }
1603
-
1604
- ::v-deep .non-selectable {
1605
- user-select: none;
1606
- }
1607
-
1608
- ::v-deep .background-popper {
1609
- padding: 5px 12px;
1610
- background-color: #ffffff;
1611
- border: 1px solid $app-primary-color;
1612
- box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1613
- height: 72px;
1614
- width: 128px;
1615
- min-width: 128px;
1616
-
1617
- &.el-popper[x-placement^="top"] {
1618
- .popper__arrow {
1619
- border-top-color: $app-primary-color !important;
1620
- &:after {
1621
- border-top-color: #fff !important;
1622
- }
1623
- }
1624
- }
1625
- }
1626
-
1627
- ::v-deep .open-map-popper {
1628
- padding-top: 5px;
1629
- padding-bottom: 5px;
1630
- background-color: #ffffff;
1631
- border: 1px solid $app-primary-color;
1632
- box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1633
- width: 178px;
1634
- min-width: 178px;
1635
-
1636
- .el-row ~ .el-row {
1637
- margin-top: 8px;
1638
- }
1639
-
1640
- .el-button {
1641
- padding-top:5px;
1642
- padding-bottom:5px;
1643
- }
1644
-
1645
- &.el-popper[x-placement^="top"] {
1646
- .popper__arrow {
1647
- border-top-color: $app-primary-color !important;
1648
- &:after {
1649
- border-top-color: #fff !important;
1650
- }
1651
- }
1652
- }
1653
- }
1654
-
1655
- .settings-group {
1656
- bottom: 16px;
1657
- position: absolute;
1658
- transition: all 1s ease;
1659
-
1660
- &.open {
1661
- left: 322px;
1662
- }
1663
-
1664
- &.close {
1665
- left: 24px;
1666
- }
1667
- }
1668
-
1669
- .backgroundText {
1670
- color: rgb(48, 49, 51);
1671
- font-size: 14px;
1672
- font-weight: normal;
1673
- line-height: 20px;
1674
- }
1675
-
1676
- .backgroundChooser {
1677
- display: flex;
1678
- margin-top: 16px;
1679
- }
1680
-
1681
- .backgroundChoice {
1682
- width: 20px;
1683
- height: 20px;
1684
- border: 1px solid rgb(144, 147, 153);
1685
- margin-left: 20px;
1686
-
1687
- &.active {
1688
- border: 2px solid $app-primary-color;
1689
- }
1690
-
1691
- &:hover {
1692
- cursor: pointer;
1693
- }
1694
-
1695
- &.white {
1696
- background-color: white;
1697
- margin-left: 10px;
1698
- }
1699
-
1700
- &.black {
1701
- background-color: black;
1702
- }
1703
-
1704
- &.lightskyblue {
1705
- background-color: lightskyblue;
1706
- }
1707
- }
1708
-
1709
- .icon-button {
1710
- height: 24px !important;
1711
- width: 24px !important;
1712
-
1713
- &:hover {
1714
- cursor: pointer;
1715
- }
1716
- }
1717
-
1718
- .bottom-right-control {
1719
- position: absolute;
1720
- right: 16px;
1721
- bottom: 16px;
1722
- }
1723
-
1724
- .video-button {
1725
- margin-left: 8px;
1726
- }
1727
-
1728
- .time-slider-container {
1729
- ::v-deep .el-tabs__header {
1730
- margin: 0px;
1731
- border-bottom: 1px solid rgb(144, 147, 153);
1732
- }
1733
-
1734
- .el-row {
1735
- margin-bottom: 5px;
1736
- }
1737
-
1738
- ::v-deep .el-tabs__content {
1739
- border-left: 1px solid rgb(144, 147, 153);
1740
- border-bottom: 1px solid rgb(144, 147, 153);
1741
- border-right: 1px solid rgb(144, 147, 153);
1742
- border-radius: 0px 0px 4px 4px;
1743
- background-color: white;
1744
- }
1745
-
1746
- ::v-deep .el-tabs--card {
1747
- > .el-tabs__header {
1748
- .el-tabs__nav {
1749
- border: 1px solid rgb(144, 147, 153);
1750
- border-bottom: none;
1751
- border-radius: 4px 4px 0px 0px;
1752
- background-color: white;
1753
- }
1754
-
1755
- .el-tabs__item {
1756
- height: 24px;
1757
- line-height: 24px;
1758
- padding: 0 8px !important;
1759
- border-bottom: 1px solid;
1760
- border-left: 1px solid rgb(144, 147, 153);
1761
- &:first-child {
1762
- border-left: none;
1763
- }
1764
- &.is-active {
1765
- border-bottom: 1px solid white;
1766
- color: rgb(48, 49, 51);
1767
- }
1768
- &:hover {
1769
- color: $app-primary-color;
1770
- }
1771
- }
1772
- }
1773
- }
1774
- }
1775
-
1776
- ::v-deep .scaffold-popper {
1777
- padding: 6px 4px;
1778
- font-size: 12px;
1779
- color: rgb(48, 49, 51);
1780
- background-color: #f3ecf6;
1781
- border: 1px solid $app-primary-color;
1782
- white-space: nowrap;
1783
- min-width: unset;
1784
- pointer-events: none;
1785
-
1786
- &.left-popper {
1787
- .popper__arrow {
1788
- border-left-color: $app-primary-color !important;
1789
- &:after {
1790
- border-left-color: #f3ecf6 !important;
1791
- }
1792
- }
1793
- }
1794
-
1795
- &.right-popper {
1796
- .popper__arrow {
1797
- border-right-color: $app-primary-color !important;
1798
- &:after {
1799
- border-right-color: #f3ecf6 !important;
1800
- }
1801
- }
1802
- }
1803
-
1804
- &.el-popper[x-placement^="top"] {
1805
- .popper__arrow {
1806
- border-top-color: $app-primary-color !important;
1807
- &:after {
1808
- border-top-color: #f3ecf6 !important;
1809
- }
1810
- }
1811
- }
1812
- }
1813
-
1814
- ::v-deep .el-slider__button {
1815
- border: 2px solid $app-primary-color;
1816
- }
1817
-
1818
- ::v-deep .el-slider__bar {
1819
- background-color: $app-primary-color;
1820
- height: 10px;
1821
- }
1822
-
1823
- ::v-deep .el-loading-spinner {
1824
- i,
1825
- .el-loading-text {
1826
- color: $app-primary-color;
1827
- }
1828
- }
1829
-
1830
- ::v-deep .popper-zoomout {
1831
- padding-right: 11px;
1832
- left: -21px !important;
1833
- .popper__arrow {
1834
- left: 53px !important;
1835
- }
1836
- }
1837
-
1838
- .select-box {
1839
- width: 57px;
1840
- border-radius: 4px;
1841
- border: 1px solid rgb(144, 147, 153);
1842
- background-color: var(--white);
1843
- font-weight: 500;
1844
- color: rgb(48, 49, 51);
1845
- margin-left: 8px;
1846
-
1847
- ::v-deep .el-input__inner {
1848
- color: $app-primary-color;
1849
- height: 22px;
1850
- padding-left: 8px;
1851
- padding-right: 8px;
1852
- border: none;
1853
- font-family: "Asap", sans-serif;
1854
- line-height: 22px;
1855
- }
1856
-
1857
- ::v-deep .el-input,
1858
- ::v-deep .el-input__icon {
1859
- line-height: 22px;
1860
- }
1861
- }
1862
- </style>
1863
-
1864
- <style lang="scss">
1865
- .time-slider-tooltip {
1866
- padding: 6px 4px !important;
1867
- font-family: "Asap", sans-serif;
1868
- font-size: 12px !important;
1869
- color: rgb(48, 49, 51) !important;
1870
- background-color: #f3ecf6 !important;
1871
- border: 1px solid $app-primary-color !important;
1872
- white-space: nowrap !important;
1873
- min-width: unset !important;
1874
- }
1875
-
1876
- .scaffold_viewer_dropdown .el-select-dropdown__item {
1877
- white-space: nowrap;
1878
- text-align: left;
1879
- font-family: "Asap", sans-serif;
1880
- }
1881
-
1882
- .opacity-box {
1883
- right: 0px;
1884
- bottom: 200px;
1885
- position: absolute;
1886
- }
1887
- </style>
1
+ <template>
2
+ <div
3
+ ref="scaffoldContainer"
4
+ v-loading="loading"
5
+ class="scaffold-container"
6
+ element-loading-text="Loading..."
7
+ element-loading-spinner="el-icon-loading"
8
+ element-loading-background="rgba(0, 0, 0, 0.3)"
9
+ >
10
+ <map-svg-sprite-color />
11
+ <scaffold-tooltip
12
+ :label="tData.label"
13
+ :region="tData.region"
14
+ :visible="tData.visible"
15
+ :x="tData.x"
16
+ :y="tData.y"
17
+ />
18
+ <div
19
+ id="organsDisplayArea"
20
+ ref="display"
21
+ tabindex="-1"
22
+ style="height: 100%; width: 100%"
23
+ @keydown.66="backgroundChangeCallback"
24
+ />
25
+ <div v-show="displayUI && !isTransitioning">
26
+ <el-popover
27
+ v-if="displayWarning"
28
+ ref="warningPopover"
29
+ v-model="hoverVisibilities[6].value"
30
+ :content="warningMessage"
31
+ placement="right"
32
+ :append-to-body="false"
33
+ trigger="manual"
34
+ popper-class="scaffold-popper message-popper right-popper non-selectable"
35
+ />
36
+ <i
37
+ v-if="displayWarning"
38
+ v-popover:warningPopover
39
+ class="el-icon-warning message-icon warning-icon"
40
+ @mouseover="showHelpText(6)"
41
+ @mouseout="hideHelpText(6)"
42
+ >
43
+ <span class="message-text">Beta</span>
44
+ </i>
45
+ <el-popover
46
+ v-if="displayLatestChanges"
47
+ ref="latestChangesPopover"
48
+ v-model="hoverVisibilities[7].value"
49
+ :content="latestChangesMessage"
50
+ placement="right"
51
+ :append-to-body="false"
52
+ trigger="manual"
53
+ popper-class="scaffold-popper message-popper right-popper non-selectable"
54
+ />
55
+ <i
56
+ v-if="displayLatestChanges && latestChangesMessage"
57
+ v-popover:latestChangesPopover
58
+ class="el-icon-warning message-icon latest-changesicon"
59
+ @mouseover="showHelpText(7)"
60
+ @mouseout="hideHelpText(7)"
61
+ >
62
+ <span class="message-text">What's new?</span>
63
+ </i>
64
+ <el-popover
65
+ ref="checkBoxPopover"
66
+ v-model="hoverVisibilities[5].value"
67
+ content="Change region visibility"
68
+ placement="right"
69
+ :append-to-body="false"
70
+ trigger="manual"
71
+ popper-class="scaffold-popper right-popper non-selectable"
72
+ />
73
+ <tree-controls
74
+ ref="treeControls"
75
+ v-popover:checkBoxPopover
76
+ :help-mode="helpMode"
77
+ :isReady="isReady"
78
+ :show-colour-picker="showColourPicker"
79
+ @object-selected="objectSelected"
80
+ @object-hovered="objectHovered"
81
+ @drawer-toggled="drawerToggled"
82
+ />
83
+ <div class="opacity-box">
84
+ <opacity-controls ref="opacityControl" />
85
+ </div>
86
+ <el-popover
87
+ v-if="sceneData.timeVarying"
88
+ ref="sliderPopover"
89
+ v-model="hoverVisibilities[4].value"
90
+ content="Move the slider to animate the region"
91
+ placement="top"
92
+ :append-to-body="false"
93
+ trigger="manual"
94
+ popper-class="scaffold-popper top-popper non-selectable"
95
+ />
96
+ <div
97
+ v-if="sceneData.timeVarying"
98
+ v-popover:sliderPopover
99
+ class="time-slider-container"
100
+ :class="[minimisedSlider ? 'minimised' : '', sliderPosition]"
101
+ >
102
+ <el-tabs type="card">
103
+ <el-tab-pane label="Animate scaffold">
104
+ <el-row class="tab-content">
105
+ <map-svg-icon
106
+ v-if="isPlaying"
107
+ icon="pause"
108
+ class="icon-button video-button"
109
+ @click.native="play(false)"
110
+ />
111
+ <map-svg-icon
112
+ v-else
113
+ icon="play"
114
+ class="video-button icon-button"
115
+ @click.native="play(true)"
116
+ />
117
+ <el-slider
118
+ :min="0"
119
+ :max="timeMax"
120
+ :value="(sceneData.currentTime / 100) * timeMax"
121
+ :step="0.1"
122
+ tooltip-class="time-slider-tooltip"
123
+ class="slider"
124
+ :format-tooltip="formatTooltip"
125
+ :marks="timeStamps"
126
+ @input="timeChange($event)"
127
+ />
128
+ </el-row>
129
+ </el-tab-pane>
130
+ <el-tab-pane label="Animation data">
131
+ <el-row class="tab-content">
132
+ <div class="animation-data">
133
+ Original duration:
134
+ <div class="purple">
135
+ {{ orginalDuration }}
136
+ </div>
137
+ </div>
138
+ <div class="animation-data">
139
+ Animation duration:
140
+ <div class="purple">
141
+ {{ animateDuration }}
142
+ </div>
143
+ </div>
144
+ <div class="animation-data">
145
+ Playback speed
146
+ <el-select
147
+ :popper-append-to-body="true"
148
+ :value="currentSpeed"
149
+ placeholder="Select"
150
+ class="select-box"
151
+ popper-class="scaffold_viewer_dropdown"
152
+ @change="speedChanged($event)"
153
+ >
154
+ <el-option
155
+ v-for="item in playSpeed"
156
+ :key="item.value"
157
+ :label="item.label"
158
+ :value="item.value"
159
+ />
160
+ </el-select>
161
+ </div>
162
+ </el-row>
163
+ </el-tab-pane>
164
+ </el-tabs>
165
+ </div>
166
+ <div class="bottom-right-control">
167
+ <el-popover
168
+ v-model="hoverVisibilities[0].value"
169
+ content="Zoom in"
170
+ placement="left"
171
+ :append-to-body="false"
172
+ trigger="manual"
173
+ popper-class="scaffold-popper left-popper non-selectable"
174
+ >
175
+ <map-svg-icon
176
+ slot="reference"
177
+ icon="zoomIn"
178
+ class="icon-button zoomIn"
179
+ @click.native="zoomIn()"
180
+ @mouseover.native="showHelpText(0)"
181
+ @mouseout.native="hideHelpText(0)"
182
+ />
183
+ </el-popover>
184
+ <el-popover
185
+ v-model="hoverVisibilities[1].value"
186
+ content="Zoom out"
187
+ placement="top-end"
188
+ :append-to-body="false"
189
+ trigger="manual"
190
+ popper-class="scaffold-popper popper-zoomout non-selectable"
191
+ >
192
+ <map-svg-icon
193
+ slot="reference"
194
+ icon="zoomOut"
195
+ class="icon-button zoomOut"
196
+ @click.native="zoomOut()"
197
+ @mouseover.native="showHelpText(1)"
198
+ @mouseout.native="hideHelpText(1)"
199
+ />
200
+ </el-popover>
201
+ <el-popover
202
+ v-model="hoverVisibilities[2].value"
203
+ placement="top"
204
+ :append-to-body="false"
205
+ trigger="manual"
206
+ popper-class="scaffold-popper non-selectable"
207
+ >
208
+ <div>
209
+ Fit to
210
+ <br />
211
+ window
212
+ </div>
213
+ <map-svg-icon
214
+ slot="reference"
215
+ icon="fitWindow"
216
+ class="icon-button fitWindow"
217
+ @click.native="fitWindow()"
218
+ @mouseover.native="showHelpText(2)"
219
+ @mouseout.native="hideHelpText(2)"
220
+ />
221
+ </el-popover>
222
+ </div>
223
+ <el-popover
224
+ ref="open-map-popover"
225
+ placement="top-start"
226
+ width="128"
227
+ :append-to-body="false"
228
+ trigger="click"
229
+ popper-class="open-map-popper"
230
+ >
231
+ <el-row v-for="item in openMapOptions" :key="item.key">
232
+ <el-button
233
+ type="primary"
234
+ plain
235
+ @click="$emit('open-map', item.key)"
236
+ >
237
+ {{ item.display }}
238
+ </el-button>
239
+ </el-row>
240
+ </el-popover>
241
+ <el-popover
242
+ ref="backgroundPopover"
243
+ placement="top-start"
244
+ width="128"
245
+ :append-to-body="false"
246
+ trigger="click"
247
+ popper-class="background-popper non-selectable"
248
+ >
249
+ <el-row class="backgroundText"> Change background </el-row>
250
+ <el-row class="backgroundChooser">
251
+ <div
252
+ v-for="item in availableBackground"
253
+ :key="item"
254
+ :class="[
255
+ 'backgroundChoice',
256
+ item,
257
+ item == currentBackground ? 'active' : '',
258
+ ]"
259
+ @click="backgroundChangeCallback(item)"
260
+ />
261
+ </el-row>
262
+ </el-popover>
263
+ <div
264
+ class="settings-group"
265
+ :class="{ open: drawerOpen, close: !drawerOpen }"
266
+ >
267
+ <el-row>
268
+ <el-popover
269
+ v-model="hoverVisibilities[8].value"
270
+ content="Open new map"
271
+ placement="right"
272
+ :append-to-body="false"
273
+ trigger="manual"
274
+ popper-class="scaffold-popper right-popper non-selectable"
275
+ >
276
+ <map-svg-icon
277
+ v-if="enableOpenMapUI && openMapOptions.length > 0"
278
+ slot="reference"
279
+ v-popover:open-map-popover
280
+ icon="openMap"
281
+ class="icon-button"
282
+ @mouseover.native="showHelpText(8)"
283
+ @mouseout.native="hideHelpText(8)"
284
+ />
285
+ </el-popover>
286
+ </el-row>
287
+ <el-row>
288
+ <el-popover
289
+ v-model="hoverVisibilities[3].value"
290
+ content="Change background color"
291
+ placement="right"
292
+ :append-to-body="false"
293
+ trigger="manual"
294
+ popper-class="scaffold-popper right-popper non-selectable"
295
+ >
296
+ <map-svg-icon
297
+ slot="reference"
298
+ v-popover:backgroundPopover
299
+ icon="changeBckgd"
300
+ class="icon-button"
301
+ @mouseover.native="showHelpText(3)"
302
+ @mouseout.native="hideHelpText(3)"
303
+ />
304
+ </el-popover>
305
+ </el-row>
306
+ </div>
307
+ </div>
308
+ </div>
309
+ </template>
310
+
311
+ <script>
312
+ /* eslint-disable no-alert, no-console */
313
+ import Vue from "vue";
314
+ import OpacityControls from "./OpacityControls";
315
+ import ScaffoldTooltip from "./ScaffoldTooltip";
316
+ import TreeControls from "./TreeControls";
317
+ import { MapSvgIcon, MapSvgSpriteColor } from "@abi-software/svg-sprite";
318
+ import { findObjectsWithNames, getObjectsFromAnnotations } from "../scripts/utilities.js";
319
+ import { SearchIndex } from "../scripts/search.js";
320
+ import {
321
+ Button,
322
+ Col,
323
+ Loading,
324
+ Option,
325
+ Popover,
326
+ Row,
327
+ Select,
328
+ Slider,
329
+ TabPane,
330
+ Tabs,
331
+ } from "element-ui";
332
+ import lang from "element-ui/lib/locale/lang/en";
333
+ import locale from "element-ui/lib/locale";
334
+
335
+ locale.use(lang);
336
+ Vue.use(Button);
337
+ Vue.use(Col);
338
+ Vue.use(Loading.directive);
339
+ Vue.use(Option);
340
+ Vue.use(Popover);
341
+ Vue.use(Row);
342
+ Vue.use(Select);
343
+ Vue.use(Slider);
344
+ Vue.use(TabPane);
345
+ Vue.use(Tabs);
346
+
347
+ const OrgansViewer = require("../scripts/organsRenderer").OrgansViewer;
348
+ const EventNotifier = require("../scripts/eventNotifier").EventNotifier;
349
+
350
+ /**
351
+ * A vue component of the scaffold viewer.
352
+ *
353
+ * @requires ./OpacityControls.vue
354
+ * @requires ./TreeControls.vue
355
+ */
356
+ export default {
357
+ name: "ScaffoldVuer",
358
+ components: {
359
+ MapSvgIcon,
360
+ MapSvgSpriteColor,
361
+ OpacityControls,
362
+ ScaffoldTooltip,
363
+ TreeControls,
364
+ },
365
+ props: {
366
+ /**
367
+ * URL of the zincjs metadata. This value will be ignored if a valid
368
+ * state prop is also provided.
369
+ * If the url needs to be updated with state present, please use
370
+ * the setURL method.
371
+ */
372
+ url: {
373
+ type: String,
374
+ default: "",
375
+ },
376
+ /**
377
+ * Show the colour control of set to true.
378
+ */
379
+ showColourPicker: {
380
+ type: Boolean,
381
+ default: false,
382
+ },
383
+ /**
384
+ * Flag to show/hide the UI.
385
+ */
386
+ displayUI: {
387
+ type: Boolean,
388
+ default: true,
389
+ },
390
+ /**
391
+ * Display all graphics at start.
392
+ *
393
+ * This setting only works when traditional is set to false.
394
+ */
395
+ displayAtStartUp: {
396
+ type: Boolean,
397
+ default: true,
398
+ },
399
+ /**
400
+ * Use for toggling the help tooltips.
401
+ */
402
+ helpMode: {
403
+ type: Boolean,
404
+ default: false,
405
+ },
406
+ /**
407
+ * Use for show/display beta warning icon.
408
+ */
409
+ displayWarning: {
410
+ type: Boolean,
411
+ default: true,
412
+ },
413
+ /**
414
+ * Warning message for the hovered over text
415
+ * on the warning icon.
416
+ */
417
+ warningMessage: {
418
+ type: String,
419
+ default: "Beta feature - under active development",
420
+ },
421
+ displayLatestChanges: {
422
+ type: Boolean,
423
+ default: false,
424
+ },
425
+ latestChangesMessage: {
426
+ type: String,
427
+ default: "New feature - Local search is now available",
428
+ },
429
+ /**
430
+ * Show/hide pickable markers for regions.
431
+ */
432
+ displayMarkers: {
433
+ type: Boolean,
434
+ default: false,
435
+ },
436
+ markerLabels : {
437
+ type: Array,
438
+ default: function () {
439
+ return []
440
+ }
441
+ },
442
+ /**
443
+ * Show/hide minimap.
444
+ */
445
+ displayMinimap: {
446
+ type: Boolean,
447
+ default: false,
448
+ },
449
+ /**
450
+ * Format of the input URL
451
+ */
452
+ format: {
453
+ type: String,
454
+ default: "metadata",
455
+ },
456
+ /**
457
+ * Settings for minimap position, size and alignment.
458
+ */
459
+ minimapSettings: {
460
+ type: Object,
461
+ default: function () {
462
+ return {
463
+ x_offset: 16,
464
+ y_offset: 16,
465
+ width: 128,
466
+ height: 128,
467
+ align: "top-right",
468
+ };
469
+ },
470
+ },
471
+ /**
472
+ * Flag to determine rather open map UI should be
473
+ * presented or not.
474
+ */
475
+ enableOpenMapUI: {
476
+ type: Boolean,
477
+ default: false,
478
+ },
479
+ openMapOptions: {
480
+ type: Array,
481
+ default: function () {
482
+ return [
483
+ {
484
+ display: "Open AC Map",
485
+ key: "AC"
486
+ },
487
+ {
488
+ display: "Open FC Map",
489
+ key: "FC"
490
+ },
491
+ {
492
+ display: "Open 3D Human Map",
493
+ key: "3D"
494
+ },
495
+ ]
496
+ },
497
+ },
498
+ /**
499
+ * State containing state of the scaffold.
500
+ */
501
+ state: {
502
+ type: Object,
503
+ default: undefined,
504
+ },
505
+ /**
506
+ * Optional prop for the name of the region to focus on,
507
+ * this option is ignored if state or viewURL is also provided.
508
+ */
509
+ region: {
510
+ type: String,
511
+ default: "",
512
+ },
513
+ /**
514
+ * Optional prop for an URL of containing information of a viewport.
515
+ * This option is ignored if state is also provided.
516
+ * It will use the provided URL as base if a relative parth is provided.
517
+ */
518
+ viewURL: {
519
+ type: String,
520
+ default: "",
521
+ },
522
+ /**
523
+ * Settings for turning on/off rendering
524
+ */
525
+ render: {
526
+ type: Boolean,
527
+ default: true,
528
+ },
529
+ },
530
+ data: function () {
531
+ return {
532
+ sceneData: this.$module.sceneData,
533
+ isPlaying: false,
534
+ isReady: false,
535
+ /**
536
+ * This is set when scene is transitioning.
537
+ */
538
+ isTransitioning: false,
539
+ tooltipAppendToBody: false,
540
+ hoverVisibilities: [
541
+ { value: false },
542
+ { value: false },
543
+ { value: false },
544
+ { value: false },
545
+ { value: false },
546
+ { value: false },
547
+ { value: false },
548
+ { value: false },
549
+ { value: false },
550
+ ],
551
+ inHelp: false,
552
+ loading: false,
553
+ duration: 3000,
554
+ drawerOpen: true,
555
+ currentBackground: "white",
556
+ availableBackground: ["white", "lightskyblue", "black"],
557
+ minimisedSlider: false,
558
+ sliderPosition: "",
559
+ timeMax: 100,
560
+ orginalDuration: "",
561
+ animateDuration: "6secs",
562
+ playSpeed: [
563
+ {
564
+ value: 0.1,
565
+ label: "0.1x",
566
+ },
567
+ {
568
+ value: 0.5,
569
+ label: "0.5x",
570
+ },
571
+ {
572
+ value: 1,
573
+ label: "1x",
574
+ },
575
+ {
576
+ value: 2,
577
+ label: "2x",
578
+ },
579
+ {
580
+ value: 5,
581
+ label: "5x",
582
+ },
583
+ {
584
+ value: 10,
585
+ label: "10x",
586
+ },
587
+ ],
588
+ currentSpeed: 1,
589
+ timeStamps: {},
590
+ defaultCheckedKeys: [],
591
+ tData: {
592
+ label: "",
593
+ region: "",
594
+ visible: false,
595
+ x: 200,
596
+ y: 200,
597
+ },
598
+ fileFormat: "metadata",
599
+ previousMarkerLabels: [],
600
+ };
601
+ },
602
+ watch: {
603
+ format: {
604
+ handler: function (value) {
605
+ this.fileFormat = value;
606
+ },
607
+ immediate: true,
608
+ },
609
+ url: {
610
+ handler: function (newValue) {
611
+ if (this.state === undefined || this.state.url === undefined)
612
+ this.setURL(newValue);
613
+ },
614
+ immediate: true,
615
+ },
616
+ region: {
617
+ handler: function (region) {
618
+ if (!(this.state || this.viewURL)) this.setFocusedRegion(region);
619
+ },
620
+ immediate: true,
621
+ },
622
+ state: {
623
+ handler: function (state) {
624
+ this.setState(state);
625
+ },
626
+ immediate: true,
627
+ deep: true,
628
+ },
629
+ viewURL: {
630
+ handler: function (viewURL) {
631
+ this.updateViewURL(viewURL);
632
+ },
633
+ immediate: true,
634
+ },
635
+ helpMode: function (val) {
636
+ this.setHelpMode(val);
637
+ },
638
+ displayMarkers: function (val) {
639
+ this.$module.scene.displayMarkers = val;
640
+ //Update pickable objects
641
+ this.$module.scene.forcePickableObjectsUpdate = true;
642
+ },
643
+ displayMinimap: function (val) {
644
+ this.$module.scene.displayMinimap = val;
645
+ },
646
+ "sceneData.currentTime": function () {
647
+ /**
648
+ * Triggers when scene time changes.
649
+ *
650
+ * @property {number} time Current build-in time of scene.
651
+ * of selected object.
652
+ */
653
+ this.$emit("timeChanged", this.sceneData.currentTime);
654
+ },
655
+ duration: function () {
656
+ this.$module.scene.setDuration(this.duration);
657
+ },
658
+ minimapSettings: {
659
+ deep: true,
660
+ handler: "updateMinimapScissor",
661
+ },
662
+ render: function (val) {
663
+ this.toggleRendering(val);
664
+ },
665
+ markerLabels: function(labels) {
666
+ console.log("marker labels changed", labels)
667
+ this.previousMarkerLabels.forEach((pml)=>{
668
+ this.setMarkerModeForObjectsWithName(pml, "off");
669
+ })
670
+ labels.forEach((l)=>{
671
+ this.setMarkerModeForObjectsWithName(l, "on");
672
+ })
673
+ this.previousMarkerLabels = labels
674
+ }
675
+ },
676
+ beforeCreate: function () {
677
+ this.$module = new OrgansViewer();
678
+ this.selectedObjects = [];
679
+ this.hoveredObjects = [];
680
+ this.currentBackground = "white";
681
+ this._currentURL = undefined;
682
+ this.availableBackground = ["white", "black", "lightskyblue"];
683
+ this.$_searchIndex = new SearchIndex();
684
+ },
685
+ mounted: function () {
686
+ this.$refs.treeControls.setModule(this.$module);
687
+ let eventNotifier = new EventNotifier();
688
+ eventNotifier.subscribe(this, this.eventNotifierCallback);
689
+ this.$module.addNotifier(eventNotifier);
690
+ this.$module.addOrganPartAddedCallback(this.zincObjectAdded);
691
+ this.$module.initialiseRenderer(this.$refs.display);
692
+ this.toggleRendering(this.render);
693
+ this.ro = new ResizeObserver(this.adjustLayout).observe(
694
+ this.$refs.scaffoldContainer
695
+ );
696
+ this.defaultRate = this.$module.getPlayRate();
697
+ },
698
+ beforeDestroy: function () {
699
+ if (this.ro) this.ro.disconnect();
700
+ this.$module.destroy();
701
+ this.$module = undefined;
702
+ },
703
+ methods: {
704
+ addZincObject: function (zincObject) {
705
+ if (this.$module.scene) {
706
+ this.$module.scene.addZincObject(zincObject);
707
+ this.zincObjectAdded(zincObject);
708
+ if (this.$refs.treeControls) this.$refs.treeControls.zincObjectAdded(zincObject);
709
+ }
710
+ },
711
+ /**
712
+ * This is called when a new zinc object is read into the scene.
713
+ */
714
+ zincObjectAdded: function (zincObject) {
715
+ this.loading = false;
716
+ this.$_searchIndex.addZincObject(zincObject, zincObject.uuid);
717
+ this.$emit("zinc-object-added", zincObject);
718
+ },
719
+ /**
720
+ *
721
+ */
722
+ addRegionsToSearchIndex: function () {
723
+ const rootRegion = this.$module.scene.getRootRegion();
724
+ const regions = rootRegion.getChildRegions(true);
725
+ regions.forEach(region => {
726
+ this.$_searchIndex.addRegion(region, region.uuid);
727
+ });
728
+ },
729
+ /**
730
+ * This is called when Change backgspeedround colour button
731
+ * is pressed an causes the backgrouColornd colour to be changed
732
+ * to one of the three preset colour: white, black and
733
+ * lightskyblue.
734
+ */
735
+ backgroundChangeCallback: function (colour) {
736
+ this.currentBackground = colour;
737
+ this.$module.zincRenderer
738
+ .getThreeJSRenderer()
739
+ .setClearColor(this.currentBackground, 1);
740
+ },
741
+ /**
742
+ * This is called by captueeScreenshot and after the last render
743
+ * loop, it download a screenshot of the current scene with no UI.
744
+ */
745
+ captureScreenshotCallback: function () {
746
+ //Remove the callback, only needs to happen once
747
+ this.$module.zincRenderer.removePostRenderCallbackFunction(
748
+ this.captureID
749
+ );
750
+ let screenshot = this.$module.zincRenderer
751
+ .getThreeJSRenderer()
752
+ .domElement.toDataURL("image/png");
753
+ let hrefElement = document.createElement("a");
754
+ document.body.append(hrefElement);
755
+ if (!this.captureFilename) hrefElement.download = `screenshot.png`;
756
+ else hrefElement.download = this.captureFilename;
757
+ hrefElement.href = screenshot;
758
+ hrefElement.click();
759
+ hrefElement.remove();
760
+ },
761
+ /**
762
+ * Function for capturing a screenshot of the current rendering.
763
+ *
764
+ * @param {String} filename filename given to the screenshot.
765
+ *
766
+ * @public
767
+ */
768
+ captureScreenshot: function (filename) {
769
+ this.captureFilename = filename;
770
+ this.captureID = this.$module.zincRenderer.addPostRenderCallbackFunction(
771
+ this.captureScreenshotCallback
772
+ );
773
+ },
774
+ /**
775
+ * Function to clear current scene, the tree controls and the search index.
776
+ *
777
+ * @public
778
+ */
779
+ clearScene: function () {
780
+ if (this.$refs.treeControls) this.$refs.treeControls.clear();
781
+ if (this.$_searchIndex) this.$_searchIndex.removeAll();
782
+ if (this.$module.scene) this.$module.scene.clearAll();
783
+ },
784
+ formatTooltip(val) {
785
+ if (this.timeMax >= 1000) {
786
+ if (val) {
787
+ let sec = ((val % 60000) / 1000).toFixed(2) + "s";
788
+ let min = val > 60000 ? (val / 60000).toFixed(0) + "m " : "";
789
+ return min + sec;
790
+ }
791
+ }
792
+ return val ? val.toFixed(2) + " ms" : "0 ms";
793
+ },
794
+ /**
795
+ * Function to reset the view to default.
796
+ * Also called when the associated button is pressed.
797
+ *
798
+ * @public
799
+ */
800
+ fitWindow: function () {
801
+ if (this.$module.scene) {
802
+ this.$module.scene.viewAll();
803
+ }
804
+ },
805
+ /**
806
+ * Function to zoom in.
807
+ * Also called when the associated button is pressed.
808
+ *
809
+ * @public
810
+ */
811
+ zoomIn: function () {
812
+ if (this.$module.scene) {
813
+ this.$module.scene.changeZoomByScrollRateUnit(-1);
814
+ }
815
+ },
816
+ /**
817
+ * Function to zoom out.
818
+ * Also called when the associated button is pressed.
819
+ *
820
+ * @public
821
+ */
822
+ zoomOut: function () {
823
+ if (this.$module.scene) {
824
+ this.$module.scene.changeZoomByScrollRateUnit(1);
825
+ }
826
+ },
827
+ /**
828
+ * Function to change the current play speed.
829
+ *
830
+ * @public
831
+ */
832
+ speedChanged: function (speed) {
833
+ this.currentSpeed = speed;
834
+ this.$module.setPlayRate(this.defaultRate * this.currentSpeed);
835
+ },
836
+ /**
837
+ * Function used to stop the free spin
838
+ *
839
+ * @public
840
+ */
841
+ stopFreeSpin: function () {
842
+ let cameracontrol = this.$module.scene.getZincCameraControls();
843
+ cameracontrol.stopAutoTumble();
844
+ this.isTransitioning = false;
845
+ },
846
+ findObjectsWithGroupName: function (name) {
847
+ let objects = [];
848
+ if (name && name != "" && this.$module.scene) {
849
+ objects = this.$module.scene.findObjectsWithGroupName(name);
850
+ }
851
+ return objects;
852
+ },
853
+ /**
854
+ * Focus on named region
855
+ */
856
+ viewRegion: function (names) {
857
+ const rootRegion = this.$module.scene.getRootRegion();
858
+ const groups = Array.isArray(names) ? names : [names];
859
+ const objects = findObjectsWithNames(rootRegion, groups, "", true);
860
+ let box = this.$module.scene.getBoundingBoxOfZincObjects(objects);
861
+ if (box) {
862
+ if (this.$module.isSyncControl()) {
863
+ this.$module.setSyncControlZoomToBox(box);
864
+ } else {
865
+ const dist =
866
+ this.$module.scene.camera.far - this.$module.scene.camera.near;
867
+ this.$module.scene.viewAllWithBoundingBox(box);
868
+ this.$module.scene.camera.far = this.$module.scene.camera.near + dist;
869
+ this.$module.scene.camera.updateProjectionMatrix();
870
+ }
871
+ return true;
872
+ }
873
+ return false;
874
+ },
875
+ setFocusedRegion: function (name) {
876
+ if (name) {
877
+ if (this.isReady) {
878
+ this.viewRegion(name);
879
+ } else {
880
+ this.$module.setFinishDownloadCallback(
881
+ this.setURLFinishCallback({ region: name })
882
+ );
883
+ }
884
+ }
885
+ },
886
+ updateViewURL: function (viewURL) {
887
+ if (viewURL) {
888
+ if (this.isReady) {
889
+ const url = new URL(viewURL, this.url);
890
+ this.$module.scene.loadViewURL(url);
891
+ } else {
892
+ this.$module.setFinishDownloadCallback(
893
+ this.setURLFinishCallback({ viewURL: viewURL })
894
+ );
895
+ }
896
+ }
897
+ },
898
+ getRendererInfo: function () {
899
+ if (this.$module.zincRenderer) {
900
+ return this.$module.zincRenderer.getThreeJSRenderer().info;
901
+ }
902
+ return undefined;
903
+ },
904
+ /**
905
+ * Function used to rotate the scene.
906
+ * Also called when the associated button is pressed.
907
+ *
908
+ * @public
909
+ */
910
+ freeSpin: function () {
911
+ if (this.$module.scene) {
912
+ let cameracontrol = this.$module.scene.getZincCameraControls();
913
+ this.isTransitioning = true;
914
+ cameracontrol.enableAutoTumble();
915
+ cameracontrol.autoTumble([1.0, 0.0], Math.PI, true);
916
+ setTimeout(this.stopFreeSpin, 4000);
917
+ }
918
+ },
919
+ /**
920
+ * Callback when a region is selected/highlighted.
921
+ * It will also update other controls.
922
+ *
923
+ */
924
+ eventNotifierCallback: function (event) {
925
+ const names = [];
926
+ let zincObjects = [];
927
+ const region = undefined;
928
+ if (event.eventType == 1 || event.eventType == 2) {
929
+ event.identifiers.forEach((identifier) => {
930
+ if (identifier) {
931
+ let id = identifier.data.id
932
+ ? identifier.data.id
933
+ : identifier.data.group;
934
+ names.push(id);
935
+ }
936
+ });
937
+ zincObjects = event.zincObjects;
938
+ }
939
+ /*
940
+ * Event Type 1: Selected
941
+ * Event Type 2: Highlighted
942
+ * Event Type 1: Move
943
+ */
944
+ if (event.eventType == 1) {
945
+ if (this.$refs.treeControls) {
946
+ if (names.length > 0) {
947
+ //this.$refs.treeControls.changeActiveByNames(names, region, false);
948
+ this.$refs.treeControls.updateActiveUI(zincObjects);
949
+ } else {
950
+ this.hideRegionTooltip();
951
+ this.$refs.treeControls.removeActive(true);
952
+ }
953
+ }
954
+ // Triggers when an object has been selected
955
+ this.$emit("scaffold-selected", event.identifiers);
956
+ } else if (event.eventType == 2) {
957
+ if (this.selectedObjects.length === 0) {
958
+ this.hideRegionTooltip();
959
+ // const offsets = this.$refs.scaffoldContainer.getBoundingClientRect();
960
+ if (this.$refs.treeControls) {
961
+ if (names.length > 0) {
962
+ //this.$refs.treeControls.changeHoverByNames(names, region, false);
963
+ this.$refs.treeControls.updateHoverUI(zincObjects);
964
+ } else {
965
+ this.$refs.treeControls.removeHover(true);
966
+ }
967
+ }
968
+ if (event.identifiers.length > 0 && event.identifiers[0]) {
969
+ let id = event.identifiers[0].data.id
970
+ ? event.identifiers[0].data.id
971
+ : event.identifiers[0].data.group;
972
+ if (event.identifiers[0].coords) {
973
+ this.tData.visible = true;
974
+ this.tData.label = id;
975
+ if (event.identifiers[0].data.region)
976
+ this.tData.region = event.identifiers[0].data.region;
977
+ else this.tData.region = undefined;
978
+ this.tData.x = event.identifiers[0].coords.x;
979
+ this.tData.y = event.identifiers[0].coords.y;
980
+ }
981
+ }
982
+ // Triggers when an object has been highlighted
983
+ this.$emit("scaffold-highlighted", event.identifiers);
984
+ }
985
+ } else if (event.eventType == 3) {
986
+ //MOVE
987
+ if (event.identifiers.length > 0 && event.identifiers[0]) {
988
+ if (event.identifiers[0].coords) {
989
+ const offsets =
990
+ this.$refs.scaffoldContainer.getBoundingClientRect();
991
+ this.tData.x = event.identifiers[0].coords.x - offsets.left;
992
+ this.tData.y = event.identifiers[0].coords.y - offsets.top;
993
+ }
994
+ }
995
+ }
996
+ },
997
+ /**
998
+ * Get the coordinates of the current selected region.
999
+ *
1000
+ * @public
1001
+ */
1002
+ getCoordinatesOfSelected: function () {
1003
+ if (this.selectedObjects && this.selectedObjects.length > 0) {
1004
+ return this.$module.scene.getObjectsScreenXY(this.selectedObjects);
1005
+ }
1006
+ return undefined;
1007
+ },
1008
+ /**
1009
+ * Return an object containing the window coordinates of the
1010
+ * current selected region which will be updated after each render
1011
+ * loop.
1012
+ *
1013
+ * @public
1014
+ */
1015
+ getDynamicSelectedCoordinates: function () {
1016
+ return this.$module.selectedScreenCoordinates;
1017
+ },
1018
+ /**
1019
+ * Callback when time is changed through the UI.
1020
+ */
1021
+ timeChange: function (event) {
1022
+ let normalizedTime = (event / this.timeMax) * 100;
1023
+ if (normalizedTime != this.sceneData.currentTime)
1024
+ this.$module.updateTime(normalizedTime);
1025
+ },
1026
+ /**
1027
+ * A callback used by children components. Set the selected zinc object
1028
+ *
1029
+ * @param {object} object Zinc object
1030
+ */
1031
+ objectSelected: function (objects, propagate) {
1032
+ this.selectedObjects = objects;
1033
+ if (this.selectedObjects && this.selectedObjects.length > 0)
1034
+ this.$refs.opacityControl.setObject(this.selectedObjects[0]);
1035
+ this.$module.setSelectedByZincObjects(objects, undefined, propagate);
1036
+ },
1037
+ /**
1038
+ * A callback used by children components. Set the highlighted zinc object
1039
+ *
1040
+ * @param {object} object Zinc object
1041
+ */
1042
+ objectHovered: function (objects, propagate) {
1043
+ this.hoveredObjects = objects;
1044
+ this.$module.setHighlightedByZincObjects(objects, undefined, propagate);
1045
+ },
1046
+ /**
1047
+ * Set the selected by name.
1048
+ *
1049
+ * @param {} name Name of the group
1050
+ */
1051
+ changeActiveByName: function (names, region, propagate) {
1052
+ const isArray = Array.isArray(names);
1053
+ if (names === undefined || (isArray && names.length === 0)) {
1054
+ this.$refs.treeControls.removeActive(propagate);
1055
+ } else {
1056
+ let array = names;
1057
+ if (!isArray) array = [array];
1058
+ this.$refs.treeControls.changeActiveByNames(array, region, propagate);
1059
+ }
1060
+ },
1061
+ /**
1062
+ * Set the highlighted by name.
1063
+ *
1064
+ * @param {name} name Name of the group
1065
+ */
1066
+ changeHighlightedByName: function (names, region, propagate) {
1067
+ const isArray = Array.isArray(names);
1068
+ if (names === undefined || (isArray && names.length === 0)) {
1069
+ this.$refs.treeControls.removeHover(propagate);
1070
+ } else {
1071
+ let array = names;
1072
+ if (!isArray) array = [array];
1073
+ this.$refs.treeControls.changeHoverByNames(array, region, propagate);
1074
+ }
1075
+ },
1076
+ /**
1077
+ * Start the animation.
1078
+ *
1079
+ * @param {object} object Zinc object
1080
+ */
1081
+ play: function (flag) {
1082
+ this.$module.playAnimation(flag);
1083
+ this.isPlaying = flag;
1084
+ //Hide tooltip as location may
1085
+ //this.hideRegionTooltip();
1086
+ },
1087
+ /**
1088
+ * Function to toggle on/off overlay help.
1089
+ */
1090
+ setHelpMode: function (helpMode) {
1091
+ if (helpMode) {
1092
+ this.inHelp = true;
1093
+ this.hoverVisibilities.forEach((item) => {
1094
+ item.value = true;
1095
+ });
1096
+ } else {
1097
+ this.inHelp = false;
1098
+ this.hoverVisibilities.forEach((item) => {
1099
+ item.value = false;
1100
+ });
1101
+ }
1102
+ },
1103
+ /**
1104
+ * Callback function used by showRegionTooltip in the case when the tooltip
1105
+ * is out of view.
1106
+ */
1107
+ displayTooltipOfObjectsCallback: function (
1108
+ name,
1109
+ objects,
1110
+ regionPath,
1111
+ resetView,
1112
+ liveUpdates
1113
+ ) {
1114
+ const instance = this;
1115
+ return function () {
1116
+ instance.$module.zincRenderer.removePostRenderCallbackFunction(
1117
+ instance.$_regionTooltipCallback
1118
+ );
1119
+ instance.$_regionTooltipCallback = undefined;
1120
+ instance.displayTooltipOfObjects(name, objects, regionPath, resetView, liveUpdates);
1121
+ };
1122
+ },
1123
+ liveUpdateTooltipPosition: function () {
1124
+ if (this.$module.selectedCenter) {
1125
+ this.tData.x = this.$module.selectedScreenCoordinates.x;
1126
+ this.tData.y = this.$module.selectedScreenCoordinates.y;
1127
+ }
1128
+ },
1129
+ displayTooltipOfObjects: function (name, objects, regionPath, resetView, liveUpdates) {
1130
+ if (objects.length > 0) {
1131
+ let coords = objects[0].getClosestVertexDOMElementCoords(
1132
+ this.$module.scene
1133
+ );
1134
+ if (coords) {
1135
+ //The coords is not in view, view all if resetView flag is true
1136
+ if (!coords.inView) {
1137
+ this.hideRegionTooltip();
1138
+ if (resetView) {
1139
+ this.$module.scene.viewAll();
1140
+ //Use the post render callback to make sure the scene has been updated
1141
+ //before getting the position of the tooltip.
1142
+ if (this.$_regionTooltipCallback) {
1143
+ this.$module.zincRenderer.removePostRenderCallbackFunction(
1144
+ this.$_regionTooltipCallback
1145
+ );
1146
+ }
1147
+ this.$_regionTooltipCallback =
1148
+ this.$module.zincRenderer.addPostRenderCallbackFunction(
1149
+ this.displayTooltipOfObjectsCallback(
1150
+ name,
1151
+ objects,
1152
+ regionPath,
1153
+ resetView,
1154
+ liveUpdates
1155
+ )
1156
+ );
1157
+ }
1158
+ } else {
1159
+ this.tData.visible = true;
1160
+ this.tData.label = name;
1161
+ this.tData.x = coords.position.x;
1162
+ this.tData.y = coords.position.y;
1163
+ this.tData.region = regionPath;
1164
+ if (this.$_liveCoordinatesUpdated) {
1165
+ this.$module.zincRenderer.removePostRenderCallbackFunction(
1166
+ this.$_liveCoordinatesUpdated
1167
+ );
1168
+ }
1169
+ if (liveUpdates) {
1170
+ this.$module.setupLiveCoordinates(objects);
1171
+ this.$_liveCoordinatesUpdated =
1172
+ this.$module.zincRenderer.addPostRenderCallbackFunction(
1173
+ this.liveUpdateTooltipPosition
1174
+ );
1175
+ }
1176
+ }
1177
+ return true;
1178
+ }
1179
+ }
1180
+ this.hideRegionTooltip();
1181
+ return false;
1182
+ },
1183
+ /**
1184
+ * Display the tooltip used for displaying search result.
1185
+ * When resetView is set to true, it will
1186
+ * reset view if the tooltip is not in view.
1187
+ * Setting liveUpdates to true will update the tooltip location
1188
+ * at every rendering loop.
1189
+ */
1190
+ showRegionTooltipWithObjects: function (label, zincObjects, regionPath, resetView, liveUpdates) {
1191
+ if (label && zincObjects && zincObjects.length > 0 && this.$module.scene) {
1192
+ return this.displayTooltipOfObjects(
1193
+ label,
1194
+ zincObjects,
1195
+ regionPath,
1196
+ resetView,
1197
+ liveUpdates
1198
+ );
1199
+ }
1200
+ this.hideRegionTooltip();
1201
+ return false;
1202
+ },
1203
+ /**
1204
+ * Display the tooltip. When resetView is set to true, it will
1205
+ * reset view if the tooltip is not in view.
1206
+ * Setting liveUpdates to true will update the tooltip location
1207
+ * at every rendering loop.
1208
+ */
1209
+ showRegionTooltip: function (name, resetView, liveUpdates) {
1210
+ if (name && this.$module.scene) {
1211
+ const rootRegion = this.$module.scene.getRootRegion();
1212
+ const groups = [name];
1213
+ const objects = findObjectsWithNames(rootRegion, groups, "", true);
1214
+ let regionPath = undefined;
1215
+ if (objects && objects.length > 0) {
1216
+ regionPath = objects[0].getRegion().getFullPath();
1217
+ }
1218
+ return this.showRegionTooltipWithObjects(
1219
+ name,
1220
+ objects,
1221
+ regionPath,
1222
+ resetView,
1223
+ liveUpdates
1224
+ );
1225
+ }
1226
+ this.hideRegionTooltip();
1227
+ return false;
1228
+ },
1229
+ /**
1230
+ * Display the tooltip using the list of annotations.
1231
+ * When resetView is set to true, it will
1232
+ * reset view if the tooltip is not in view.
1233
+ * Setting liveUpdates to true will update the tooltip location
1234
+ * at every rendering loop.
1235
+ */
1236
+ showRegionTooltipWithAnnotations: function (annotations, resetView, liveUpdates) {
1237
+ if (this.$module.scene) {
1238
+ const result = getObjectsFromAnnotations(this.$module.scene, annotations);
1239
+ if (result && result.objects.length > 0) {
1240
+ return this.showRegionTooltipWithObjects(
1241
+ result.label,
1242
+ result.objects,
1243
+ result.regionPath,
1244
+ resetView,
1245
+ liveUpdates
1246
+ );
1247
+ }
1248
+ }
1249
+ this.hideRegionTooltip();
1250
+ return false;
1251
+ },
1252
+ hideRegionTooltip: function () {
1253
+ if (this.$_liveCoordinatesUpdated) {
1254
+ this.$module.zincRenderer.removePostRenderCallbackFunction(
1255
+ this.$_liveCoordinatesUpdated
1256
+ );
1257
+ //Unset the tracking
1258
+ this.$module.setupLiveCoordinates(undefined);
1259
+ }
1260
+ this.tData.visible = false;
1261
+ this.tData.region = undefined;
1262
+ },
1263
+ /**
1264
+ * Set the marker modes for objects with the provided name, mode can
1265
+ * be "on", "off" or "inherited".
1266
+ */
1267
+ setMarkerModeForObjectsWithName: function (name, mode) {
1268
+ if (name && this.$module.scene) {
1269
+ const rootRegion = this.$module.scene.getRootRegion();
1270
+ const groups = [name];
1271
+ const objects = findObjectsWithNames(rootRegion, groups, "", true);
1272
+ objects.forEach(object => object.setMarkerMode(mode));
1273
+ }
1274
+ },
1275
+ /**
1276
+ * Set the marker modes for objects specified by the list of annotations
1277
+ */
1278
+ setMarkerModeWithAnnotations: function (annotations, mode) {
1279
+ if (this.$module.scene) {
1280
+ const result = getObjectsFromAnnotations(this.$module.scene, annotations);
1281
+ if (result && result.objects.length > 0) {
1282
+ result.objects.forEach(object => object.setMarkerMode(mode));
1283
+ }
1284
+ }
1285
+ },
1286
+ /**
1287
+ * This is called when mouse cursor enters supported elements
1288
+ * with help tootltips.
1289
+ */
1290
+ showHelpText: function (helpTextNumber) {
1291
+ if (!this.inHelp) {
1292
+ this.helpTextWait = setTimeout(() => {
1293
+ this.hoverVisibilities[helpTextNumber].value = true;
1294
+ }, 500);
1295
+ }
1296
+ },
1297
+ /**
1298
+ * This is called when mouse cursor exits supported element..
1299
+ */
1300
+ hideHelpText: function (helpTextNumber) {
1301
+ if (!this.inHelp) {
1302
+ this.hoverVisibilities[helpTextNumber].value = false;
1303
+ clearTimeout(this.helpTextWait);
1304
+ }
1305
+ },
1306
+ search: function (text, displayLabel) {
1307
+ if (this.$_searchIndex) {
1308
+ if (text === undefined || text === "" ||
1309
+ ((Array.isArray(text) && text.length === 0))
1310
+ ) {
1311
+ this.objectSelected([], true);
1312
+ return false;
1313
+ } else {
1314
+ const result = this.$_searchIndex.searchAndProcessResult(text);
1315
+ const zincObjects = result.zincObjects;
1316
+ if (zincObjects.length > 0) {
1317
+ this.objectSelected(zincObjects, true);
1318
+ if (displayLabel) {
1319
+ for (let i = 0; i < zincObjects.length; i++) {
1320
+ if (zincObjects[i] && zincObjects[i].groupName) {
1321
+ this.showRegionTooltipWithObjects(
1322
+ result.label,
1323
+ zincObjects,
1324
+ result.regionPath,
1325
+ true,
1326
+ true
1327
+ );
1328
+ }
1329
+ }
1330
+ }
1331
+ return true;
1332
+ } else {
1333
+ this.objectSelected([], true);
1334
+ }
1335
+ }
1336
+ }
1337
+ return false;
1338
+ },
1339
+ /**
1340
+ * Get the list of suggested terms
1341
+ */
1342
+ fetchSuggestions: function (term) {
1343
+ if (this.$_searchIndex === undefined) return [];
1344
+ return this.$_searchIndex.auto_suggest(term);
1345
+ },
1346
+ /**
1347
+ * Called when minimap settings has changed. Pass the
1348
+ * parameters to ZincJS and marked it for update.
1349
+ */
1350
+ updateMinimapScissor: function () {
1351
+ Object.keys(this.minimapSettings).forEach((key) => {
1352
+ this.$module.scene.minimapScissor[key] = this.minimapSettings[key];
1353
+ });
1354
+ this.$module.scene.minimapScissor.updateRequired = true;
1355
+ },
1356
+ updateSettingsfromScene: function () {
1357
+ this.currentSpeed = 1;
1358
+ this.$module.setPlayRate(this.defaultRate);
1359
+ this.orginalDuration =
1360
+ this.$module.scene.getMetadataTag("OriginalDuration");
1361
+ this.animateDuration = this.$module.scene.getMetadataTag("Duration");
1362
+ let timeStamps = this.$module.scene.getMetadataTag("TimeStamps");
1363
+ this.timeStamps = {};
1364
+ for (const key in timeStamps) {
1365
+ this.timeStamps[timeStamps[key]] = key;
1366
+ }
1367
+ this.timeMax = this.$module.scene.getDuration();
1368
+ },
1369
+ setURLFinishCallback: function (options) {
1370
+ return () => {
1371
+ if (options) {
1372
+ if (options.viewport) {
1373
+ this.$module.scene
1374
+ .getZincCameraControls()
1375
+ .setCurrentCameraSettings(options.viewport);
1376
+ } else if (options.viewURL && options.viewURL !== "") {
1377
+ const url = new URL(options.viewURL, this.url);
1378
+ this.$module.scene.loadViewURL(url);
1379
+ } else if (options.region && options.region !== "") {
1380
+ this.viewRegion(options.region);
1381
+ }
1382
+ if (options.visibility) {
1383
+ // Some UIs may not be ready at this time.
1384
+ this.$nextTick(() => {
1385
+ this.$refs.treeControls.setState(options.visibility);
1386
+ });
1387
+ }
1388
+ }
1389
+ this.updateSettingsfromScene();
1390
+ this.$module.updateTime(0.01);
1391
+ this.$module.updateTime(0);
1392
+ this.$module.unsetFinishDownloadCallback();
1393
+ this.addRegionsToSearchIndex();
1394
+ this.$emit("on-ready");
1395
+ this.setMarkers();
1396
+ this.isReady = true;
1397
+ };
1398
+ },
1399
+ /**
1400
+ * Function used for getting the current states of the scene. This exported states
1401
+ * can be imported using the importStates method.
1402
+ *
1403
+ * @public
1404
+ */
1405
+ getState: function () {
1406
+ let state = {
1407
+ format: this.fileFormat,
1408
+ url: this._currentURL,
1409
+ viewport: undefined,
1410
+ visibility: undefined,
1411
+ };
1412
+ if (this.$refs.treeControls)
1413
+ state.visibility = this.$refs.treeControls.getState();
1414
+ if (this.$module.scene) {
1415
+ let zincCameraControls = this.$module.scene.getZincCameraControls();
1416
+ state.viewport = zincCameraControls.getCurrentViewport();
1417
+ }
1418
+ return state;
1419
+ },
1420
+ /**
1421
+ * Function used for importing the states of the scene. This exported states
1422
+ * can be imported using the read states method.
1423
+ *
1424
+ * @public
1425
+ */
1426
+ setState: function (state) {
1427
+ if (state) {
1428
+ if (state.url && state.url !== this._currentURL) {
1429
+ this.setURLAndState(state.url, {
1430
+ fileFormat: state.fileFormat,
1431
+ viewport: state.viewport,
1432
+ visibility: state.visibility,
1433
+ });
1434
+ } else {
1435
+ if (state.viewport || state.visibility) {
1436
+ if (this.isReady && this.$module.scene) {
1437
+ if (state.viewport)
1438
+ this.$module.scene
1439
+ .getZincCameraControls()
1440
+ .setCurrentCameraSettings(state.viewport);
1441
+ if (state.visibility)
1442
+ this.$refs.treeControls.setState(state.visibility);
1443
+ } else {
1444
+ this.$module.setFinishDownloadCallback(
1445
+ this.setURLFinishCallback({
1446
+ viewport: state.viewport,
1447
+ visibility: state.visibility,
1448
+ })
1449
+ );
1450
+ }
1451
+ }
1452
+ }
1453
+ }
1454
+ },
1455
+ exportGLTF: function (binary) {
1456
+ return this.$module.scene.exportGLTF(binary);
1457
+ },
1458
+ /**
1459
+ * Function used for reading in new scaffold metadata and a custom
1460
+ * viewport. This function will ignore the state prop and
1461
+ * read in the new url.
1462
+ *
1463
+ * @public
1464
+ */
1465
+ setURLAndState: function (newValue, state) {
1466
+ if (newValue != this._currentURL) {
1467
+ if (state && state.format) this.fileFormat = state.format;
1468
+ let viewport = state && state.viewport ? state.viewport : undefined;
1469
+ let visibility =
1470
+ state && state.visibility ? state.visibility : undefined;
1471
+ this._currentURL = newValue;
1472
+ if (this.$refs.treeControls) this.$refs.treeControls.clear();
1473
+ this.loading = true;
1474
+ this.isReady = false;
1475
+ this.$_searchIndex.removeAll();
1476
+ this.hideRegionTooltip();
1477
+ this.$module.setFinishDownloadCallback(
1478
+ this.setURLFinishCallback({
1479
+ viewport: viewport,
1480
+ region: this.region,
1481
+ viewURL: this.viewURL,
1482
+ visibility: visibility,
1483
+ })
1484
+ );
1485
+ if (this.fileFormat === "gltf") {
1486
+ this.$module.loadGLTFFromURL(newValue, "scene", true);
1487
+ } else {
1488
+ this.$module.loadOrgansFromURL(
1489
+ newValue,
1490
+ undefined,
1491
+ undefined,
1492
+ "scene",
1493
+ undefined,
1494
+ true
1495
+ );
1496
+ }
1497
+ this.$module.scene.displayMarkers = this.displayMarkers;
1498
+ this.$module.scene.forcePickableObjectsUpdate = true;
1499
+ this.$module.scene.displayMinimap = this.displayMinimap;
1500
+ this.updateMinimapScissor();
1501
+ }
1502
+ },
1503
+ /**
1504
+ * Function used for reading in new scaffold metadata. This function will ignore
1505
+ * the state prop and read in the new url.
1506
+ *
1507
+ * @public
1508
+ */
1509
+ setURL: function (newValue) {
1510
+ this.setURLAndState(newValue, undefined);
1511
+ },
1512
+ /**
1513
+ * Callback when drawer is toggled.
1514
+ */
1515
+ drawerToggled: function (flag) {
1516
+ this.drawerOpen = flag;
1517
+ this.adjustLayout();
1518
+ },
1519
+ /**
1520
+ * Callback using ResizeObserver.
1521
+ */
1522
+ adjustLayout: function () {
1523
+ let width = this.$refs.scaffoldContainer.clientWidth;
1524
+ this.minimisedSlider = width < 812;
1525
+ if (this.minimisedSlider) {
1526
+ this.sliderPosition = this.drawerOpen ? "right" : "left";
1527
+ } else {
1528
+ this.sliderPosition = "";
1529
+ }
1530
+ },
1531
+ toggleRendering: function (flag) {
1532
+ if (this.$module.zincRenderer) {
1533
+ if (flag) {
1534
+ this.$module.zincRenderer.animate();
1535
+ } else {
1536
+ this.$module.zincRenderer.stopAnimate();
1537
+ }
1538
+ }
1539
+ },
1540
+ forceResize: function () {
1541
+ if (this.$module.zincRenderer) {
1542
+ this.$module.zincRenderer.onWindowResize();
1543
+ }
1544
+ },
1545
+ syncControlCallback: function () {
1546
+ const payload = this.$module.NDCCameraControl.getPanZoom();
1547
+ if (this.tData.visible) {
1548
+ this.showRegionTooltip(this.tData.label, true, true);
1549
+ }
1550
+ this.$emit("scaffold-navigated", payload);
1551
+ },
1552
+ /**
1553
+ * Rotate mode - "none", "horizontal", "vertical", "free" but
1554
+ * it will be ignored if flag is set to false.
1555
+ */
1556
+ toggleSyncControl: function (flag, rotateMode) {
1557
+ this.$module.toggleSyncControl(flag, rotateMode);
1558
+ this.$module.setSyncControlCallback(this.syncControlCallback);
1559
+ },
1560
+
1561
+ /**
1562
+ * Set the markers for the scene.
1563
+ */
1564
+ setMarkers: function () {
1565
+ this.markerLabels.forEach((l)=>{
1566
+ this.setMarkerModeForObjectsWithName(l, "on");
1567
+ })
1568
+ },
1569
+ },
1570
+ };
1571
+ </script>
1572
+
1573
+ <!-- Add "scoped" attribute to limit CSS to this component only -->
1574
+ <style scoped lang="scss">
1575
+ @import "~element-ui/packages/theme-chalk/src/button";
1576
+ @import "~element-ui/packages/theme-chalk/src/col";
1577
+ @import "~element-ui/packages/theme-chalk/src/loading";
1578
+ @import "~element-ui/packages/theme-chalk/src/option";
1579
+ @import "~element-ui/packages/theme-chalk/src/popover";
1580
+ @import "~element-ui/packages/theme-chalk/src/row";
1581
+ @import "~element-ui/packages/theme-chalk/src/select";
1582
+ @import "~element-ui/packages/theme-chalk/src/slider";
1583
+ @import "~element-ui/packages/theme-chalk/src/tabs";
1584
+ @import "~element-ui/packages/theme-chalk/src/tab-pane";
1585
+
1586
+ .message-icon {
1587
+ position: absolute;
1588
+ top: 15px;
1589
+ left: 37px;
1590
+ text-align: left;
1591
+ font-size: 25px;
1592
+ color: $warning;
1593
+
1594
+ &:hover {
1595
+ cursor: pointer;
1596
+ }
1597
+ }
1598
+
1599
+ .warning-icon {
1600
+ color: $warning;
1601
+ top: 15px;
1602
+ }
1603
+
1604
+ .latest-changesicon {
1605
+ color: $success;
1606
+ top: 45px;
1607
+ }
1608
+
1609
+ .message-text {
1610
+ font-size: 15px;
1611
+ vertical-align: 5px;
1612
+ }
1613
+
1614
+ ::v-deep .message-popper {
1615
+ padding: 9px 10px;
1616
+ min-width: 150px;
1617
+ font-size: 12px;
1618
+ color: #fff;
1619
+ }
1620
+
1621
+ #organsDisplayArea {
1622
+ &:focus {
1623
+ outline: none !important;
1624
+ border: 0px;
1625
+ }
1626
+ }
1627
+
1628
+ .scaffold-container {
1629
+ height: 100%;
1630
+ width: 100%;
1631
+ position: relative;
1632
+ }
1633
+
1634
+ .time-slider-container {
1635
+ text-align: left;
1636
+ position: absolute;
1637
+ right: 155px;
1638
+ width: calc(100% - 530px);
1639
+ bottom: 16px;
1640
+ transition: all 1s ease;
1641
+ outline: none;
1642
+
1643
+ &.minimised {
1644
+ width: calc(40%);
1645
+ }
1646
+
1647
+ &.left {
1648
+ right: 155px;
1649
+ width: calc(100% - 250px);
1650
+ }
1651
+
1652
+ &.right {
1653
+ right: 8px;
1654
+ bottom: 54px;
1655
+ }
1656
+ }
1657
+
1658
+ .slider-display-text {
1659
+ height: 20px;
1660
+ color: rgb(48, 49, 51);
1661
+ font-size: 14px;
1662
+ font-weight: normal;
1663
+ line-height: 20px;
1664
+ padding-left: 8px;
1665
+ text-shadow: -1px -1px #fff, 1px -1px #fff, -1px 1px #fff, 1px -1px #fff;
1666
+ }
1667
+
1668
+ .tab-content {
1669
+ display: flex;
1670
+ height: 34px;
1671
+ padding-top: 8px;
1672
+ font-size: 14px;
1673
+ }
1674
+
1675
+ .tab-content ::v-deep .el-slider__marks-text {
1676
+ margin-top: 12px;
1677
+ margin-left: 8px;
1678
+ font-size: 10px;
1679
+ }
1680
+
1681
+ .tab-content ::v-deep .el-slider__stop {
1682
+ width: 10px;
1683
+ height: 10px;
1684
+ top: -1px;
1685
+ border: solid 1px $app-primary-color;
1686
+ }
1687
+
1688
+ .animation-data {
1689
+ margin-left: 8px;
1690
+ line-height: 26px;
1691
+ display: flex;
1692
+
1693
+ :not(:first-child) {
1694
+ margin-left: 8px;
1695
+ }
1696
+ .purple {
1697
+ padding-left: 2px;
1698
+ color: $app-primary-color;
1699
+ }
1700
+ }
1701
+ .slider {
1702
+ margin-left: 30px;
1703
+ width: calc(100% - 88px);
1704
+ margin-top: -7px;
1705
+
1706
+ ::v-deep .el-slider__runway {
1707
+ height: 10px;
1708
+ margin: 14px 0;
1709
+ }
1710
+
1711
+ ::v-deep .el-slider__button-wrapper {
1712
+ top: -13px;
1713
+ }
1714
+ }
1715
+
1716
+ .zoomOut {
1717
+ padding-left: 8px;
1718
+ }
1719
+
1720
+ .fitWindow {
1721
+ padding-left: 8px;
1722
+ }
1723
+
1724
+ ::v-deep .non-selectable {
1725
+ user-select: none;
1726
+ }
1727
+
1728
+ ::v-deep .background-popper {
1729
+ padding: 5px 12px;
1730
+ background-color: #ffffff;
1731
+ border: 1px solid $app-primary-color;
1732
+ box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1733
+ height: 72px;
1734
+ width: 128px;
1735
+ min-width: 128px;
1736
+
1737
+ &.el-popper[x-placement^="top"] {
1738
+ .popper__arrow {
1739
+ border-top-color: $app-primary-color !important;
1740
+ &:after {
1741
+ border-top-color: #fff !important;
1742
+ }
1743
+ }
1744
+ }
1745
+ }
1746
+
1747
+ ::v-deep .open-map-popper {
1748
+ padding-top: 5px;
1749
+ padding-bottom: 5px;
1750
+ background-color: #ffffff;
1751
+ border: 1px solid $app-primary-color;
1752
+ box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1753
+ width: 178px;
1754
+ min-width: 178px;
1755
+
1756
+ .el-row ~ .el-row {
1757
+ margin-top: 8px;
1758
+ }
1759
+
1760
+ .el-button {
1761
+ padding-top:5px;
1762
+ padding-bottom:5px;
1763
+ }
1764
+
1765
+ &.el-popper[x-placement^="top"] {
1766
+ .popper__arrow {
1767
+ border-top-color: $app-primary-color !important;
1768
+ &:after {
1769
+ border-top-color: #fff !important;
1770
+ }
1771
+ }
1772
+ }
1773
+ }
1774
+
1775
+ .settings-group {
1776
+ bottom: 16px;
1777
+ position: absolute;
1778
+ transition: all 1s ease;
1779
+
1780
+ &.open {
1781
+ left: 322px;
1782
+ }
1783
+
1784
+ &.close {
1785
+ left: 24px;
1786
+ }
1787
+ }
1788
+
1789
+ .backgroundText {
1790
+ color: rgb(48, 49, 51);
1791
+ font-size: 14px;
1792
+ font-weight: normal;
1793
+ line-height: 20px;
1794
+ }
1795
+
1796
+ .backgroundChooser {
1797
+ display: flex;
1798
+ margin-top: 16px;
1799
+ }
1800
+
1801
+ .backgroundChoice {
1802
+ width: 20px;
1803
+ height: 20px;
1804
+ border: 1px solid rgb(144, 147, 153);
1805
+ margin-left: 20px;
1806
+
1807
+ &.active {
1808
+ border: 2px solid $app-primary-color;
1809
+ }
1810
+
1811
+ &:hover {
1812
+ cursor: pointer;
1813
+ }
1814
+
1815
+ &.white {
1816
+ background-color: white;
1817
+ margin-left: 10px;
1818
+ }
1819
+
1820
+ &.black {
1821
+ background-color: black;
1822
+ }
1823
+
1824
+ &.lightskyblue {
1825
+ background-color: lightskyblue;
1826
+ }
1827
+ }
1828
+
1829
+ .icon-button {
1830
+ height: 24px !important;
1831
+ width: 24px !important;
1832
+
1833
+ &:hover {
1834
+ cursor: pointer;
1835
+ }
1836
+ }
1837
+
1838
+ .bottom-right-control {
1839
+ position: absolute;
1840
+ right: 16px;
1841
+ bottom: 16px;
1842
+ }
1843
+
1844
+ .video-button {
1845
+ margin-left: 8px;
1846
+ }
1847
+
1848
+ .time-slider-container {
1849
+ ::v-deep .el-tabs__header {
1850
+ margin: 0px;
1851
+ border-bottom: 1px solid rgb(144, 147, 153);
1852
+ }
1853
+
1854
+ .el-row {
1855
+ margin-bottom: 5px;
1856
+ }
1857
+
1858
+ ::v-deep .el-tabs__content {
1859
+ border-left: 1px solid rgb(144, 147, 153);
1860
+ border-bottom: 1px solid rgb(144, 147, 153);
1861
+ border-right: 1px solid rgb(144, 147, 153);
1862
+ border-radius: 0px 0px 4px 4px;
1863
+ background-color: white;
1864
+ }
1865
+
1866
+ ::v-deep .el-tabs--card {
1867
+ > .el-tabs__header {
1868
+ .el-tabs__nav {
1869
+ border: 1px solid rgb(144, 147, 153);
1870
+ border-bottom: none;
1871
+ border-radius: 4px 4px 0px 0px;
1872
+ background-color: white;
1873
+ }
1874
+
1875
+ .el-tabs__item {
1876
+ height: 24px;
1877
+ line-height: 24px;
1878
+ padding: 0 8px !important;
1879
+ border-bottom: 1px solid;
1880
+ border-left: 1px solid rgb(144, 147, 153);
1881
+ &:first-child {
1882
+ border-left: none;
1883
+ }
1884
+ &.is-active {
1885
+ border-bottom: 1px solid white;
1886
+ color: rgb(48, 49, 51);
1887
+ }
1888
+ &:hover {
1889
+ color: $app-primary-color;
1890
+ }
1891
+ }
1892
+ }
1893
+ }
1894
+ }
1895
+
1896
+ ::v-deep .scaffold-popper {
1897
+ padding: 6px 4px;
1898
+ font-size: 12px;
1899
+ color: rgb(48, 49, 51);
1900
+ background-color: #f3ecf6;
1901
+ border: 1px solid $app-primary-color;
1902
+ white-space: nowrap;
1903
+ min-width: unset;
1904
+ pointer-events: none;
1905
+
1906
+ &.left-popper {
1907
+ .popper__arrow {
1908
+ border-left-color: $app-primary-color !important;
1909
+ &:after {
1910
+ border-left-color: #f3ecf6 !important;
1911
+ }
1912
+ }
1913
+ }
1914
+
1915
+ &.right-popper {
1916
+ .popper__arrow {
1917
+ border-right-color: $app-primary-color !important;
1918
+ &:after {
1919
+ border-right-color: #f3ecf6 !important;
1920
+ }
1921
+ }
1922
+ }
1923
+
1924
+ &.el-popper[x-placement^="top"] {
1925
+ .popper__arrow {
1926
+ border-top-color: $app-primary-color !important;
1927
+ &:after {
1928
+ border-top-color: #f3ecf6 !important;
1929
+ }
1930
+ }
1931
+ }
1932
+ }
1933
+
1934
+ ::v-deep .el-slider__button {
1935
+ border: 2px solid $app-primary-color;
1936
+ }
1937
+
1938
+ ::v-deep .el-slider__bar {
1939
+ background-color: $app-primary-color;
1940
+ height: 10px;
1941
+ }
1942
+
1943
+ ::v-deep .el-loading-spinner {
1944
+ i,
1945
+ .el-loading-text {
1946
+ color: $app-primary-color;
1947
+ }
1948
+ }
1949
+
1950
+ ::v-deep .popper-zoomout {
1951
+ padding-right: 11px;
1952
+ left: -21px !important;
1953
+ .popper__arrow {
1954
+ left: 53px !important;
1955
+ }
1956
+ }
1957
+
1958
+ .select-box {
1959
+ width: 57px;
1960
+ border-radius: 4px;
1961
+ border: 1px solid rgb(144, 147, 153);
1962
+ background-color: var(--white);
1963
+ font-weight: 500;
1964
+ color: rgb(48, 49, 51);
1965
+ margin-left: 8px;
1966
+
1967
+ ::v-deep .el-input__inner {
1968
+ color: $app-primary-color;
1969
+ height: 22px;
1970
+ padding-left: 8px;
1971
+ padding-right: 8px;
1972
+ border: none;
1973
+ font-family: "Asap", sans-serif;
1974
+ line-height: 22px;
1975
+ }
1976
+
1977
+ ::v-deep .el-input,
1978
+ ::v-deep .el-input__icon {
1979
+ line-height: 22px;
1980
+ }
1981
+ }
1982
+ </style>
1983
+
1984
+ <style lang="scss">
1985
+ .time-slider-tooltip {
1986
+ padding: 6px 4px !important;
1987
+ font-family: "Asap", sans-serif;
1988
+ font-size: 12px !important;
1989
+ color: rgb(48, 49, 51) !important;
1990
+ background-color: #f3ecf6 !important;
1991
+ border: 1px solid $app-primary-color !important;
1992
+ white-space: nowrap !important;
1993
+ min-width: unset !important;
1994
+ }
1995
+
1996
+ .scaffold_viewer_dropdown .el-select-dropdown__item {
1997
+ white-space: nowrap;
1998
+ text-align: left;
1999
+ font-family: "Asap", sans-serif;
2000
+ }
2001
+
2002
+ .opacity-box {
2003
+ right: 0px;
2004
+ bottom: 200px;
2005
+ position: absolute;
2006
+ }
2007
+ </style>